64B/66B编码
简介
64b/66b 编码技术是 IEEE 802.3 工作组为 10G 以太网提出的,目的是减少编码开销,降低硬件的复杂性,并作为8b/10b 编码的另一种选择,以支持新的程序和数据。当前,64b/66b 编码主要应用于Fiber Channel 10GFC和16GFC、10G 以太网、100G 以太网、10G EPON、InfiniBand、Thunderbolt 和 Xilinx 的 Aurora 协议。
原理
64b/66b 编码将 64bit 数据或控制信息编码成 66bit 块传输,66bit 块的前两位表示同步头,主要由于接收端的数据对齐和接收数据位流的同步。
下图是万兆网收发数据 PCS 端的处理流程:
-
发送流程
将两个32位的TXD数据拼接为64位,然后经过加扰(Scrambling)来保证零一均衡,避免直流失调和时钟恢复困难。之后在64位数据之前加入2位的同步头(Sync header),用来指示后面的64位数据是数据帧还是控制帧,用户每发送64位数据,高速收发器需要传输64位数据和2位同步头
变速器(Gearbox)可以看成一个深度为64位的存储器,每个用户时钟输入64位数据,高速收发器在一个用户时钟内也只能发送64位编码后的数据,就会导致每个时钟会有2位数据没有被发送,暂存在变速器(Gearbox)。
经过32个时钟后,变速器(Gearbox)就会存满64位数据,用户下个时钟周期需要暂停输入数据,高速收发器在下个时钟将变速器(Gearbox)的64位数据发送,之后用户就可以继续输入需要发送的数据了,就这样循环往复。
-
接收流程
先对接收到的数据进行数据对齐同步,然后再进行解扰(Descrambling),最后将同步头和数据发给用户进行处理
具体 64b/66b 编码格式图如下图所示:
-
同步头有 “01” 和 “10” 两种,“01“ 表示后面的 64bit 都是数据,“10” 表示后面的64bit 是数据和控制信息的混合
-
紧挨着同步头的 8bit 是类型域,后面的 56bit 是控制信息或者数据或者两者的混合。
-
D 表示数据编码,每个数据码 8bit;
-
C 表示控制码,每个控制码7bit;
-
S 表示包的开始,S 只会出现在 8 字节中的第 0 和第 4 字节;
-
T 表示包的结束,T 能够出现在任意的字节。
-
除同步码外,64bit 的数据必须经过扰码以后才能进行传输。10G 以太网的 64b/66b 编码所使用的扰码器为 X58+X39+1。
GT 的 64B66B 编码发送原理
前面讲解了64B66B的原理,从此开始讲解GTX相关内容,首先需要明确GTX内部不能对待发送数据加扰,也不能对接收的数据解扰,需要用户在FPGA逻辑中自己完成加扰和解扰。
TX Gearbox的作用就是前文所说的变速器,工作方式如下图所示,下图把用户数据位宽设置成32位,并且PCS每次也只能传输32位数据,两个时钟才能发送一个64位数据。
因此需要两个时钟用户才能发送64位数据,第一个时钟向GTX发送2位同步码和32位数据,变速箱先发送2位同步码和高30位数据,第2位数据留在TX Gearbox中。第二个时钟用户发送剩余32位数据,TX Gearbox需要先把上个时钟剩余2位数据发送,然后发送本次接收的高30位数据,最后还是会剩余2位数据。
因此每经过2个时钟,TX Gearbox中就会增加2位数据,当经过64个时钟后,TX Gearbox中存在64位数据,与一个用户数据位宽一致。后两个时钟周期用户不能往GTX发送数据,TX Gearbox会将内部64位数据发送出去,完成清空。
在使用64B66B编码时,一般用户端口位宽使用64位,会更方便,原理都是一样的。
GTX 内部包含一个计数器,用于指示什么时候用户需暂停数据发送。该计数器也可以由用户向GTX提供,但是后续GTH等高速收发器都不支持内部计数器的方案,因此在使用时还是推荐使用外部计数器,与其他收发器统一。
-
外部计数器方式
下图是使用外部计数器的框图,使用64B66B编码传输数据,需要用到几个信号。其中TXDATA[63:0]是用户待发送64位数据,TXHEADER[2:0]用来传输2位同步码,最高位不使用,接地即可。
TXSEQUENCE[6:0]是用户向GTX提供的外部计数器,使用64B66B编码时,计数器的取值范围是[0,32],TXSEQUENCE[6]接地处理。使用64B67B编码时,计数器取值范围是[0,66]。
下表是64B66B编码不同数据位宽下,用户数据停止发送数据的时钟周期数。TX_DATA_WIDTH表示用户数据位宽,与用户时钟(TXUSRCLK2)对齐。TX_INT_DATAWIDTH表示PCS内部传输并行数据位宽,可以理解成TX Gearbox输出数据位宽,与TXUSRCLK对齐。
TX_DATA_WIDTH与TX_INT_DATAWIDTH相同时,计数器必须每两个用户时钟递增一次,数据暂停两个用户时钟周期。
TX_DATA_WIDTH是TX_INT_DATAWIDTH两倍时,计数器每个用户时钟递增一次,数据暂停1个时钟周期,有效数据传输在下一个用户时钟恢复。几个用户时钟能把TX Gearbox内部数据清空,用户就需要暂停几个时钟周期。
下图表示使用外部计数器,用户数据位宽为64位,PCS内部传输并行数据位宽为4字节,使用64B66B编码的时序图,当计数器计数到32时,用户数据停止发送,Dd数据持续到计数器等于0之后才能变化。
- 内部计数器方式
TXSTARTSEQ用来指示复位后的第一个有效字节数据。初始为低电平,高速收发器复位完成之后,用户开始传输第一个数据时拉高,之后可以保持为任意值。
TXGEARBOXREADY其实就是内部计数器计数到固定数值后,用来告诉用户暂停发送数据的一个信号。
当内部计数器计数到固定数值后,如果用户数据位宽(TX_DATA_WIDTH)等于PCS传输数据位宽(TX_INT_DATAWIDT),TXGEARBOXREADY会拉低三个时钟,如果用户数据位宽是PCS传输数据位宽两倍,TXGEARBOXREADY会拉低两个时钟。
注意TXGEARBOXREADY拉低的第一个时钟用户可以向GTX发送数据,从拉低的第二个时钟开始,用户数据需要保持不变,直到TXGEARBOXREADY拉高为止。如下图所示,TXGEARBOXREADY拉低之后第一个时钟,用户数据依旧变化,TXGEARBOXREADY拉高一个时钟后,用户数据才开始变化。
GT 的 64B66B 编码接收原理
GTX的接收通道也有一个RX Gearbox把接收的66位数据转换位2位同步头和64位数据输出给用户,接收通道内部是没有解扰器的,需要用户在GTX外部自己解扰。
GTX使用64B66B编码时,接收端与用户端口信号连接如下图所示,包含数据信号RXDATA、数据有效指示信号RXDATAVALID、同步头RXHEADER[2:0]、同步头有效指示信号RXHEADRVALID、RXSTARTOFSEQ、滑块对齐信号RXGEARBOXSLIP。
时序如下图所示,有效指示信号为高电平时,对应数据有效。下图中PCS在每个用户时钟只能传输64位数据,但每个时钟需要输出2位同步头和64位数据RXDATA,共66位数据,因此每经过32个时钟Gearbox就需要先缓存64位数据,此时不输出数据,才能维持这种数据差。
下图解释了 RX Gearbox 的工作原理(用户数据位宽和 PCS 位宽都为 4 byte):
接收通道的Gearbox只能使用内部计数器,RXSTARTOFSEQ为高电平表示内部计数器的值为0,但用户对内部计数器的取值其实并不用关心,只需要保证输出数据对齐即可。
加扰源码
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library UNISIM;
use UNISIM.VCOMPONENTS.ALL;
--***********************************Entity Declaration*******************************
entity gtwizard_1_SCRAMBLER is
generic
(
TX_DATA_WIDTH : integer := 32
);
port
(
-- User Interface
UNSCRAMBLED_DATA_IN : in std_logic_vector((TX_DATA_WIDTH-1) downto 0);
SCRAMBLED_DATA_OUT : out std_logic_vector((TX_DATA_WIDTH-1) downto 0);
DATA_VALID_IN : in std_logic;
-- System Interface
USER_CLK : in std_logic;
SYSTEM_RESET : in std_logic
);
end gtwizard_1_SCRAMBLER;
architecture RTL of gtwizard_1_SCRAMBLER is
--***********************************Parameter Declarations********************
constant DLY : time := 1 ns;
--***************************Internal Register Declarations********************
signal poly : std_logic_vector(57 downto 0);
signal scrambler : std_logic_vector(57 downto 0);
signal scrambled_data_i : std_logic_vector((TX_DATA_WIDTH-1) downto 0);
signal tempdata : std_logic_vector((TX_DATA_WIDTH-1) downto 0);
--*********************************Main Body of Code***************************
begin
process( scrambler,UNSCRAMBLED_DATA_IN )
variable poly_i : std_logic_vector(57 downto 0);
variable tempData_i : std_logic_vector((TX_DATA_WIDTH-1) downto 0);
variable xorBit : std_logic;
variable i : integer;
begin
poly_i := scrambler;
for i in 0 to (TX_DATA_WIDTH-1) loop
xorBit := UNSCRAMBLED_DATA_IN(i) xor poly_i(38) xor poly_i(57);
poly_i := (poly_i(56 downto 0) & xorBit);
tempData_i(i) := xorBit;
end loop;
poly <= poly_i;
tempdata <= tempdata_i;
end process;
--________________ Scrambled Data assignment to output port _______________
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
if (SYSTEM_RESET = '1') then
scrambler <= "0101010101010101010101010101010101010101010101010101010101" after DLY;
SCRAMBLED_DATA_OUT <= (others => '0') after DLY;
elsif (DATA_VALID_IN = '1') then
scrambler <= poly after DLY;
SCRAMBLED_DATA_OUT <= tempdata after DLY;
end if;
end if;
end process;
end RTL;
解扰源码
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library UNISIM;
use UNISIM.VCOMPONENTS.ALL;
--***********************************Entity Declaration*******************************
entity gtwizard_1_DESCRAMBLER is
generic
(
RX_DATA_WIDTH : integer := 16
);
port
(
-- User Interface
SCRAMBLED_DATA_IN : in std_logic_vector((RX_DATA_WIDTH-1) downto 0);
UNSCRAMBLED_DATA_OUT : out std_logic_vector((RX_DATA_WIDTH-1) downto 0);
DATA_VALID_IN : in std_logic;
-- System Interface
USER_CLK : in std_logic;
SYSTEM_RESET : in std_logic
);
end gtwizard_1_DESCRAMBLER;
architecture RTL of gtwizard_1_DESCRAMBLER is
--***********************************Parameter Declarations********************
constant DLY : time := 1 ns;
--***************************Internal Register Declarations********************
signal descrambler : std_logic_vector(57 downto 0);
signal poly : std_logic_vector(57 downto 0);
signal tempdata : std_logic_vector((RX_DATA_WIDTH-1) downto 0);
signal unscrambled_data_i : std_logic_vector((RX_DATA_WIDTH-1) downto 0);
--*********************************Main Body of Code***************************
begin
process( descrambler,SCRAMBLED_DATA_IN )
variable poly_i : std_logic_vector(57 downto 0);
variable tempData_i : std_logic_vector((RX_DATA_WIDTH-1) downto 0);
variable xorBit : std_logic;
variable i : std_logic;
begin
poly_i := descrambler;
for i in 0 to (RX_DATA_WIDTH-1) loop
xorBit := SCRAMBLED_DATA_IN(i) xor poly_i(38) xor poly_i(57);
poly_i := (poly_i(56 downto 0) & SCRAMBLED_DATA_IN(i));
tempData_i(i) := xorBit;
end loop;
poly <= poly_i;
tempdata <= tempdata_i;
end process;
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
if (SYSTEM_RESET = '1') then
unscrambled_data_i <= (others => '0') after DLY;
descrambler <= "0101010101010101010101010101010101010101010101010101010101" after DLY;
elsif (DATA_VALID_IN = '1') then
descrambler <= poly after DLY;
unscrambled_data_i <= tempdata after DLY;
end if;
end if;
end process;
--_______________ Unscrambled Data assignment to output port ______________
UNSCRAMBLED_DATA_OUT <= unscrambled_data_i;
end RTL;
接收对齐状态机及源码
状态机
时序
源码
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library UNISIM;
use UNISIM.VCOMPONENTS.ALL;
entity gtwizard_1_BLOCK_SYNC_SM is
generic
(
SH_CNT_MAX : integer := 64;
SH_INVALID_CNT_MAX : integer := 16
);
port
(
-- User Interface
BLOCKSYNC_OUT : out std_logic;
RXGEARBOXSLIP_OUT : out std_logic;
RXHEADER_IN : in std_logic_vector(2 downto 0);
RXHEADERVALID_IN : in std_logic;
-- System Interface
USER_CLK : in std_logic;
SYSTEM_RESET : in std_logic
);
end gtwizard_1_BLOCK_SYNC_SM;
architecture RTL of gtwizard_1_BLOCK_SYNC_SM is
--***********************************Parameter Declarations********************
constant DLY : time := 1 ns;
--********************************* Wire Declarations**************************
signal next_begin_c : std_logic;
signal next_sh_invalid_c : std_logic;
signal next_sh_valid_c : std_logic;
signal next_slip_c : std_logic;
signal next_sync_done_c : std_logic;
signal next_test_sh_c : std_logic;
signal sh_count_equals_max_i : std_logic;
signal sh_invalid_cnt_equals_max_i : std_logic;
signal sh_invalid_cnt_equals_zero_i : std_logic;
signal slip_done_i : std_logic;
signal slip_pulse_i : std_logic;
signal sync_found_i : std_logic;
---***************************Internal Register Declarations***************************
signal begin_r : std_logic;
signal blocksync_out_i : std_logic;
signal rxgearboxslip_out_i : std_logic;
signal sh_invalid_r : std_logic;
signal sh_valid_r : std_logic;
signal slip_count_i : std_logic_vector(31 downto 0);
signal slip_r : std_logic;
signal sync_done_r : std_logic;
signal sync_header_count_i : unsigned(9 downto 0);
signal sync_header_invalid_count_i : unsigned(9 downto 0);
signal test_sh_r : std_logic;
---**************************** Main Body of Code *******************************
begin
sync_found_i <= '1' when ((RXHEADER_IN(1 downto 0) = "01") or (RXHEADER_IN(1 downto 0) = "10")) else '0';
---________________________________ State machine __________________________
--- State registers
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
if(SYSTEM_RESET = '1') then
begin_r <= '1' after DLY;
test_sh_r <= '0' after DLY;
sh_valid_r <= '0' after DLY;
sh_invalid_r <= '0' after DLY;
slip_r <= '0' after DLY;
sync_done_r <= '0' after DLY;
else
begin_r <= next_begin_c after DLY;
test_sh_r <= next_test_sh_c after DLY;
sh_valid_r <= next_sh_valid_c after DLY;
sh_invalid_r <= next_sh_invalid_c after DLY;
slip_r <= next_slip_c after DLY;
sync_done_r <= next_sync_done_c after DLY;
end if;
end if;
end process;
--- Next state logic
next_begin_c <= sync_done_r
or (slip_r and slip_done_i)
or (sh_valid_r and sh_count_equals_max_i and not sh_invalid_cnt_equals_max_i)
or (sh_invalid_r and sh_count_equals_max_i and not sh_invalid_cnt_equals_max_i and blocksync_out_i);
next_test_sh_c <= begin_r
or (test_sh_r and not RXHEADERVALID_IN)
or (sh_valid_r and not sh_count_equals_max_i)
or (sh_invalid_r and not sh_count_equals_max_i and not sh_invalid_cnt_equals_max_i and blocksync_out_i);
next_sh_valid_c <= (test_sh_r and RXHEADERVALID_IN and sync_found_i);
next_sh_invalid_c <= (test_sh_r and RXHEADERVALID_IN and not sync_found_i);
next_slip_c <= (sh_invalid_r and (sh_invalid_cnt_equals_max_i or not blocksync_out_i))
or (sh_valid_r and sh_count_equals_max_i and not sh_invalid_cnt_equals_zero_i and (sh_invalid_cnt_equals_max_i or not blocksync_out_i))
or (slip_r and not slip_done_i);
next_sync_done_c <= (sh_valid_r and sh_count_equals_max_i and sh_invalid_cnt_equals_zero_i);
---________________ Counter keep track of sync headers counted _____________
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
if(begin_r = '1') then
sync_header_count_i <= (others => '0') after DLY;
elsif ((sh_valid_r= '1') or (sh_invalid_r = '1')) then
sync_header_count_i <= sync_header_count_i + 1 after DLY;
end if;
end if;
end process;
sh_count_equals_max_i <= '1' when (sync_header_count_i=SH_CNT_MAX) else '0';
---________________ Counter keep track of invalid sync headers ____________
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
if(begin_r = '1') then
sync_header_invalid_count_i <= (others => '0') after DLY;
elsif (sh_invalid_r = '1') then
sync_header_invalid_count_i <= sync_header_invalid_count_i + 1 after DLY;
end if;
end if;
end process;
--- signal to indicate max number of invalid sync headers has been reached
sh_invalid_cnt_equals_max_i <= '1' when (sync_header_invalid_count_i=SH_INVALID_CNT_MAX)
else '0';
--- signal to indicate no invalid sync headers
sh_invalid_cnt_equals_zero_i <= '1' when (sync_header_invalid_count_i=0) else '0';
---_______ Counter wait for 16 cycles to ensure that slip is complete _______
slip_pulse_i <= next_slip_c and not slip_r;
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
rxgearboxslip_out_i <= slip_pulse_i after DLY;
end if;
end process;
---_____________ Ouput assignment to indicate block sync complete _________
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
if(slip_r = '0') then
slip_count_i <= (others => '0') after DLY;
else
slip_count_i <= (slip_count_i(30 downto 0) & rxgearboxslip_out_i) after DLY;
end if;
end if;
end process;
slip_done_i <= slip_count_i(31);
---_____________ Pulse GEARBOXSLIP port to slip the data by 1 bit _________
process( USER_CLK )
begin
if(USER_CLK'event and USER_CLK = '1') then
if((SYSTEM_RESET='1') or (slip_r= '1')) then
blocksync_out_i <= '0' after DLY;
elsif (sync_done_r = '1') then
blocksync_out_i <= '1' after DLY;
end if;
end if;
end process;
--__________________________ Ouput Port Assignment ________________________
BLOCKSYNC_OUT <= blocksync_out_i;
RXGEARBOXSLIP_OUT <= rxgearboxslip_out_i;
end RTL;