.Проектирование устройств и систем с высокоскоростными соединениями
.pdf
|
Deserializer |
ddr_clock |
Wr |
|
op_code (1…0) |
Rx |
en |
|
data_out10(9…0) |
|
|
Рис. 3.13. Интерфейс узла Deserializer
Представим процесс разбора кадра в виде синхронного автомата (рис. 3.14), в котором состояния интерпретируются следующим образом:
wait_comma1 – ожидание первого символа стартового маркера кадра;
wait_comma2 – ожидание второго символа стартового маркера кадра;
data_0 – прием первого символа поля данных;
data_i – прием последующих символов поля данных и выдача всех принятых символов поля данных;
wait_end – обнаружен первый символ конечного маркера кадра;
wait_idle – обнаружен второй символ конечного маркера кадра или второй символ маркера ожидания кадра;
wait_idle_next – обнаружен первый символ маркера ожидания кадра;
lock_up – состояние блокировки приема кадра из-за искажения его структуры.
Рис. 3.14. Автомат разбора кадра
71
Ниже приведена программа-спецификация на VHDL узла «Десериалайзер», содержащая процессы WALKING_ONE_
EVEN, WALKING_ONE_ODD, SERIAL_TO_PARALLEL и STATE_MACHINE.
VHDL программа-спецификация узла «Десериалайзер»
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all; use IEEE.NUMERIC_STD.all;
entity Deserializer is
port( ddr_clock : in STD_LOGIC; reset_n : in STD_LOGIC; Rx : in STD_LOGIC;
en : out STD_LOGIC;
op_code : out STD_LOGIC_VECTOR(1 downto 0); wr: out STD_LOGIC;
data_out10 : out STD_LOGIC_VECTOR(9 downto 0)
);
end Deserializer;
architecture Deserializer of Deserializer is
constant K28_5_RDm : STD_LOGIC_VECTOR(9 downto 0) := "0101111100"; constant K28_0_RDm : STD_LOGIC_VECTOR(9 downto 0) := "0010111100"; constant K29_7_RDm : STD_LOGIC_VECTOR(9 downto 0) := "0001011101"; function cross_6 (x: std_logic_vector(5 downto 0)) return std_logic_vector is
variable y: std_logic_vector(5 downto 0); begin
for I in 0 to 5 loop y(I):=x(5-I); end loop; return y;
end;
function cross_4 (x: std_logic_vector(3 downto 0)) return std_logic_vector is variable y: std_logic_vector(3 downto 0);
begin
for I in 0 to 3 loop y(I):=x(3-I); end loop; return y;
end;
function comma_detect (x: std_logic_vector(19 downto 0)) return unsigned is variable TEMP: unsigned(3 downto 0);
begin TEMP:="0000"; for I in 0 to 9 loop
if x(9+i downto i)= K28_5_RDm or x(9+i downto i)= not K28_5_RDm then return TEMP+i;
72
end if; end loop;
return "1010";
end;
signal phase: STD_LOGIC_VECTOR(4 downto 0); signal clock: STD_LOGIC_VECTOR(9 downto 0); signal sreg: STD_LOGIC_VECTOR(9 downto 0); signal data20: STD_LOGIC_VECTOR(19 downto 0); signal offset: STD_LOGIC_VECTOR(3 downto 0);
type TYPE_STATE is (wait_comma1, wait_comma2, data_0,data_i, wait_end, wait_idle, wait_idle_next, lock_up);
signal state: TYPE_STATE;
signal op_code_t : STD_LOGIC_VECTOR(1 downto 0); begin
en<=clock(0) when state= data_i else '0';
wr<=clock(4) when state= data_i and (op_code_t="01" or op_code_t="10") else '0'; op_code<=op_code_t;
WALKING_ONE_EVEN: process(ddr_clock,reset_n) begin
if reset_n='0' then phase<="00001"; clock(0)<='0'; clock(2)<='0'; clock(4)<='0'; clock(6)<='0'; clock(8)<='0';
elsif rising_edge(ddr_clock) then
phase(4 downto 1)<=phase(3 downto 0); phase(0)<=phase(4); clock(0)<=phase(0); clock(2)<=phase(1); clock(4)<=phase(2); clock(6)<=phase(3); clock(8)<=phase(4);
end if; end process;
WALKING_ONE_ODD:process(ddr_clock,reset_n) begin
if reset_n='0' then clock(1)<='0'; clock(3)<='0'; clock(5)<='0'; clock(7)<='0'; clock(9)<='0';
elsif falling_edge(ddr_clock) then
73
clock(1)<=phase(1);
clock(3)<=phase(2);
clock(5)<=phase(3);
clock(7)<=phase(4);
clock(9)<=phase(0); end if;
end process; SERIAL_TO_PARALLEL:process(clock,reset_n) begin
if reset_n='0' then sreg<=(others=>'0');
else
for I in 0 to 9 loop
if clock(i)'event and clock(i)='1' then sreg(i)<= Rx;
end if; end loop;
end if; end process;
DESERIALIZER_STATE_MACHINE: process(clock(0),reset_n,clock(3)) variable v_offset: STD_LOGIC_VECTOR(3 downto 0);
variable v_data10: |
STD_LOGIC_VECTOR(9 downto 0); |
begin |
|
if reset_n='0' then |
|
data_out10<=(others=>'0'); offset<="1010"; data20<=(others=>'0'); op_code_t<=(others=>'0');
elsif clock(3)='1' then
elsif rising_edge(clock(0)) then data20(19 downto 10)<=sreg;
data20(9 downto 0)<=data20(19 downto 10); v_offset:=
STD_LOGIC_VECTOR(comma_detect (sreg&data20(19 downto 10))); if v_offset /= "1010" then
offset<=v_offset; end if; BOUNDARY_ALIGN: case offset is
when x"0" => v_data10:=data20(9 downto 0);
when x"1" => v_data10:=data20(10 downto 1);
when x"2" => v_data10:=data20(11 downto 2);
74
when x"3" => v_data10:=data20(12 downto 3);
when x"4" => v_data10:=data20(13 downto 4);
when x"5" =>
v_data10:=data20(14 downto 5); when x"6" =>
v_data10:=data20(15 downto 6); when x"7" =>
v_data10:=data20(16 downto 7); when x"8" =>
v_data10:=data20(17 downto 8); when x"9" =>
v_data10:=data20(18 downto 9); when others =>
v_data10:=data20(9 downto 0); end case;
data_out10<=v_data10; RX_FRAME_STATE_MACHINE: case state is
when wait_comma1 =>
if |
(v_data10 =K28_5_RDm) or (v_data10 =not K28_5_RDm) then |
|
state<= wait_comma2; |
end if; |
|
op_code_t<=(others=>'0'); |
|
when wait_comma2 => |
|
if |
(v_data10 =K28_5_RDm) or (v_data10 =not K28_5_RDm) then |
|
state<= data_0; |
else
state<= lock_up; end if;
when data_0 => state<= data_i;
when data_i =>
if v_data10 =K28_0_RDm then state<= wait_end;
end if;
if op_code_t=2 then op_code_t<=(others=>'0');
else
op_code_t<= op_code_t+1; end if;
when wait_end =>
if (v_data10(5 downto 0)= cross_6("100111") and v_data10(9 downto 6)=cross_4("1011")) or
75
(v_data10(5 downto 0)= cross_6("100111") and v_data10(9 downto 6)=cross_4(not"1011")) or (v_data10(5 downto 0)= cross_6(not"100111") and v_data10(9 downto 6)=cross_4("1011")) or (v_data10(5 downto 0)= cross_6(not"100111") and v_data10(9 downto 6)=cross_4(not"1011")) then
state<= |
wait_idle; |
else |
|
state<=lock_up; |
end if;
when wait_idle =>
if v_data10 =K29_7_RDm then state<= wait_idle_next;
elsif (v_data10 =K28_5_RDm) or (v_data10 =not K28_5_RDm) then state<= wait_comma2;
else |
|
state<=lock_up; |
|
end if; |
|
when wait_idle_next |
=> |
if (v_data10 =K28_5_RDm) or (v_data10 =not K28_5_RDm) then state<= wait_idle;
else state<=lock_up;
end if; when others =>
null; end case;
end if; end process;
end Deserializer;
Процессы WALKING_ONE_EVEN и WALKING_ONE_ ODD из ddr_clock формируют сигналы многофазной синхронизации clock(0), …, clock(9), так что сигналы с четными индексами инициируются фронтом ddr_clock, а с нечетными – срезом
ddr_clock.
Процесс SERIAL_TO_PARALLEL преобразует последовательные входные данные в параллельные с помощью 10-битного сигнала sreg и многофазного синхросигнала clock.
Процесс STATE_MACHINE выполняет разбор структуры кадра, выделяя символы из поля данных для их последующего декодирования. В процессе постоянно отслеживаются границы 10-битных символов с помощью функции comma_detect. Эта
76
функция определяет положение К-символа К28.5 («запятая»)
впоследних принятых 20 битах кадра относительно clock(0). Найденное смещение используется для формирования выходных символов data_out10 с помощью оператора case, помеченного
впрограмме как BOUNDARY_ALIGN.
Узел «Декодер 8b/10b» выполняет преобразование 10-бит- ных символов кода 8b/10b в 8-битные данные и сборку из них 12-разрядных данных. На рис. 3.15 приведен интерфейс decoder_ 8b10b узла «Декодер 8b/10b», а ниже – программа-спецификация узла на VHDL.
decoder_8b10b
op_code (1…0 ) |
data_out12 (11…0) |
en |
|
data_in8 (7…0 ) |
|
Рис. 3.15. Интерфейс узла decoder_8b10b
VHDL программа-спецификация узла «Декодер 8b/10b»
library IEEE;
use IEEE.STD_LOGIC_1164.all; use IEEE.NUMERIC_STD.all; entity decoder_8b10b is
port(
reset_n : in STD_LOGIC; en : in STD_LOGIC;
data_in : in STD_LOGIC_VECTOR(9 downto 0); op_code : in STD_LOGIC_VECTOR(1 downto 0); data_out12 : out STD_LOGIC_VECTOR(11 downto 0)
);
end decoder_8b10b;
architecture decoder_8b10b of decoder_8b10b is
function cross_6 (x: std_logic_vector(5 downto 0)) return std_logic_vector is variable y: std_logic_vector(5 downto 0);
begin
for I in 0 to 5 loop y(I):=x(5-I); end loop; return y;
end;
function cross_4 (x: std_logic_vector(3 downto 0)) return std_logic_vector is variable y: std_logic_vector(3 downto 0);
77
begin
for I in 0 to 3 loop y(I):=x(3-I); end loop; return y;
end;
type Code5b6b_type is array (0 to 31) of STD_LOGIC_VECTOR(5 downto 0);
constant Code5b6b_ROM_RDm : Code5b6b_type := ( |
|
|||||||
"100111", |
"011101", |
|
"101101", "110001", |
|
|
|||
"110101", "101001", |
"011001", "111000", |
|
|
|||||
"111001", |
"100101", |
|
"010101", "110100", |
|
|
|||
"001101", |
"101100", |
|
"011100", "010111", |
|
|
|||
"011011", |
"100011", "010011", |
"110010", |
|
|
||||
"001011", |
"101010", |
|
"011010", "111010", |
|
|
|||
"110011", "100110", "010110","110110", |
|
|
||||||
"001110", "101110", "011110", "101011" ); |
|
|
||||||
constant Code5b6b_ROM_RDp : Code5b6b_type := ( |
|
|||||||
0=> |
not Code5b6b_ROM_RDm(0), |
|
|
|
||||
1=> |
not Code5b6b_ROM_RDm(1), |
|
|
|
||||
2=> |
not Code5b6b_ROM_RDm(2), |
|
|
|
||||
3=> |
"110001", |
|
|
|
|
|
|
|
4=> |
not Code5b6b_ROM_RDm(4), |
|
|
|
||||
5=> |
"101001", 6=> |
|
"011001", |
|
|
|
|
|
7=> |
not Code5b6b_ROM_RDm(7), |
|
|
|
||||
8=> |
not Code5b6b_ROM_RDm(8), |
|
|
|
||||
9=> |
"100101", 10=> |
|
"010101", |
11=> |
"110100", 12=> |
"001101", |
||
13=> |
"101100", 14=> |
"011100", |
|
|
|
|||
15=> |
not Code5b6b_ROM_RDm(15), |
|
|
|||||
16=> |
not Code5b6b_ROM_RDm(16), |
|
|
|||||
17=> |
"100011",18=> |
|
"010011", |
19=> |
"110010",20=> |
"001011", |
||
21=> |
"101010",22=> |
|
"011010", |
|
|
|
||
23=> |
not Code5b6b_ROM_RDm(23), |
|
|
|||||
24=> |
not Code5b6b_ROM_RDm(24), |
|
|
|||||
25=> |
"100110",26=> |
|
"010110", |
|
|
|
||
27=> |
not Code5b6b_ROM_RDm(27), |
|
|
|||||
28=> |
"001110", |
|
|
|
|
|
|
|
29=> |
not Code5b6b_ROM_RDm(29), |
|
|
|||||
30=> |
not Code5b6b_ROM_RDm(30), |
|
|
|||||
31=> |
not Code5b6b_ROM_RDm(31) ); |
|
|
type Code3b4b_type is array (0 to 7) of STD_LOGIC_VECTOR(3 downto 0); constant Code3b4b_ROM_RDm : Code3b4b_type := (
0=> |
"1011",1=> |
"1001", 2=> |
"0101",3=> |
"1100", |
4=> |
"1101",5=> |
"1010", 6=> |
"0110",7=> |
"1110"); |
constant Code3b4b_ROM_RDp : Code3b4b_type := ( 0=> not Code3b4b_ROM_RDm(0),
1=> "1001", 2=> "0101",
3=> not Code3b4b_ROM_RDm(3),
78
4=> not Code3b4b_ROM_RDm(4), 5=> "1010", 6=> "0110",
7=> not Code3b4b_ROM_RDm(7) );
function Decoder5b6b (x: std_logic_vector(5 downto 0)) return std_logic_vector is variable y: std_logic_vector(4 downto 0);
begin y:="00000";
for I in 0 to 31 loop
if x=cross_6(Code5b6b_ROM_RDm(i)))or(x=cross_6(Code5b6b_ROM_ RDp(i))) then
y:= std_logic_vector(to_unsigned(i,5)); return y;
end if; end loop;
return y; end;
function Decoder3b4b (x: std_logic_vector(3 downto 0)) return std_logic_vector is variable y: std_logic_vector(2 downto 0);
begin y:="000";
for I in 0 to 7 loop
if (x=cross_4(Code3b4b_ROM_RDm(i))) or (x=cross_4(Code3b4b_ ROM_RDp(i)))
then
y:= std_logic_vector(to_unsigned(i,3)); return y;
end if; end loop; return y;
end;
signal data8: std_logic_vector(7 downto 0); begin
DECODER_8B_10B: process(reset_n,en) variable v_code8: std_logic_vector(7 downto 0); begin
if reset_n='0' then data_out12<=x"000"; data8<=(others =>'0');
elsif falling_edge(en) then
v_code8(4 downto 0):= Decoder5b6b( data_in(5 downto 0)); v_code8(7 downto 5):= Decoder3b4b( data_in(9 downto 6)); case op_code is
when "00"=> data8<=v_code8;
when "01"=>
data_out12(7 downto 0)<=data8;
79
data_out12(11 downto 8)<=v_code8(3 downto 0); data8(3 downto 0)<=v_code8(7 downto 4);
when "10"=>
data_out12(3 downto 0)<=data8(3 downto 0); data_out12(11 downto 4)<=v_code8;
when others=> null;
end case; end if;
end process;
end decoder_8b10b;
Функции Decoder5b6b и Decoder3b4b представляют комбинационную составляющую ассоциативной памяти, позволяющей за один такт выполнить декодирование символа кода 8b/10b. В процессе DECODER_8B_10B по срезу сигнала en выполняется декодирование и сборка 12-разрядных данных. Сигнал на входе op_code определяет один из трех этапов преобразования.
На рис. 3.16 приведена модель трансивера с пользовательским протоколом, в которой для целей отладки выход Tx соединен со входом Rx. Узел CDR является цифроаналоговым элементом, который не может быть полноценно представлен в рамках данной модели. Поэтому CDR моделируется просто делителем на два частоты входного потока rx_clock (частота передачи на противоположной стороне). Узел Power_on моделирует сброс системы в начальное состояние при включении питания.
Рис. 3.16. Модель верхнего уровня трансивера с пользовательским протоколом
80