FPGA 高速接口Aurora8B/10B 协议详解与IP仿真
1 摘要
Aurora 8B/10B 是一种用于高速串行通信的协议,通常用于 FPGA 设计和其他数字通信应用。即一种编码方案,旨在在传输数据时提供可靠性、时钟恢复和错误检测。主要用于在点对点串行链路间移动数据的可扩展轻量级链路层协议,为板对板、芯片对芯片以及一些单向链接提供低成本低资源的解决方案。
2 Aurora8B/10B协议IP原理及适用场景及
2.1 IP核的结构原理
Aurora 8B/10B IP 核心框如图
①Lane Logic :每一个驱动一个GT(GTP,GTX,GTH)收发器,并且初始化对应的GT收发器和处理控制字符的编码和解码以及错误检测。
②Global Logic:执行通道初始化的绑定和验证阶段。在运行过程中,该模块生成 Aurora 协议所需的随机空闲字符,并监控所有通道逻辑模块是否存在错误。
③RX User Interface:AXI4-Stream RX 用户接口将数据从通道移动到应用程序并执行流控制功能。
④TX User Interface:AXI4-Stream TX 用户界面将数据从应用程序移动到通道并执行流控制 TX 功能。标准时钟补偿模块嵌入在内核中。该模块控制时钟补偿 (CC) 字符的定期传输
2.2 适用场景
Aurora 8B/10B协议常用于芯片与芯片之间的通信,特别是在FPGA与FPGA之间的通信中。它可以通过一个或多个收发器在设备之间传输数据,支持全双工和单工模式。最多可实现16个收发器,吞吐量可从480 Mb/s扩展到84.48 Gb/s。Aurora 8B/10B IP核是全双工模式,数据通过多条Lane发送到另一个Aurora IP核,并通过用户接口将接收到的数据发送给用户。
1)、芯片到芯片的链路:
替换高速串行连接的芯片之间的并联连接可以显着减少 PCB 上所需的迹线和层数。 核心提供了使用 GTP,GTX 和 GTH 收发器所需的逻辑,FPGA 资源成本最低。
2)、板对板和背板连接:
IP CORE 使用标准的 8B / 10B 编码,使其与现有的电缆和背板硬件标准兼容。 Aurora 8B / 10B内核可以在线速率和通道宽度上进行缩放,以便在新的高性能系统中使用便宜的传统硬件。
3)、单向连接(单向):
Aurora 协议提供了替代方法执行单向通道初始化,使 GTP,GTX 和 GTH 收发器在没有反向通道的情况下使用,并降低由于未使用的全双工资源而造成的成本。
3 信号定义
3.1 时钟
信号 | 源 | 信号描述 |
ref_clk | 时钟源 | GT的外部时钟 |
INIT CLK | 时钟源 | 全局复位,低电平有效 |
DRP CLK | 时钟源 | DRP时钟,动态重配置时钟 |
user_clk_out | 时钟源 | 用户传输的数据钟及逻辑时钟域 |
3.2 状态信号
Aurora IP还提供了一系列的指示接口信号
信号 | 方向 | 信号描述 |
loopback | 输入 | 回环模式,实际使用一般接到低 |
power down | 输入 | 掉电信号,GT会进入非工作、低功耗的模式 |
lane_up | 输出 | 当对应的Lane初始化完成指示信号 |
hard_err | 输出 | 硬件类错误指示 |
soft_er | 输出 | 软件类错误指示 |
frame_err | 输出 | 帧类错误指示 |
3.3 用户信号
用户接口完全兼容使用 axi-stream协议,我们最关注的信号主要是:TVALID、TREADY、TLAST、TDATA。
发送端接口主要使用的信号
信号 | 方向 | 信号描述 |
s_axi_tx_tdata[(8n–1):0] | 输入 | 用户要发送的数据 |
s_axi_tx_tready | 输出 | 准备接收数据指示信号 |
s_axi_tx_tlast | 输入 | 发送的最后一个数据指示信号 |
s_axi_tx_tkeep[(n–1):0] | 输入 | 指示发送的最后一个数据的有效字节 |
s_axi_tx_tvalid | 输入 | 用户发送的数据有效信号 |
接收端接口主要使用的信号
信号 | 方向 | 信号描述 |
m_axi_rx_tdata[8(n–1):0] | 输出 | 接收到的数据 |
m_axi_rx_tlast | 输出 | 接收的最后一个数据指示信号 |
m_axi_rx_tkeep[(n–1):0] | 输出 | 指示接收的最后一个数据的有效字节 |
m_axi_rx_tvalid | 输入 | 指示当前接收的数据有效 |
4 用户数据接口
用户接口主要分为两种
(1)Framing接口(帧传输接口)。在AXI4-Stream的基础上添加了帧头、帧尾等控制信号,使得传输更准确,但是会降低传输效率和使用较多资源
(2)Streaming接口(流传输接口)。基本上就是一个非常简化的AXI4-Stream接口,只有数据有效、握手和数据信号,此种方式传输效率高,但无法保证传输的准确性
4.1 Framing接口
1)帧组成结构
发送子模块将每个接收的用户帧通过TX接口转换为Aurora 8B / 10B帧。 帧开始(SOF)通过在帧开始处添加2字节的SCP代码组来指示。 帧结束(EOF)是通过在帧的末尾添加一个2字节的信道结束通道协议(ECP)码组来确定。 数据不可用时插入空闲代码组。 代码组是8B / 10B编码字节对,所有数据都作为代码对发送,因此具有奇数个字节的用户帧具有称为PAD的控制字符,附加到帧的末尾以填写最终的代码组。 下图显示了具有偶数数据字节的典型Aurora 8B / 10B帧。
2)发送案例
①Simple Data Transfer(简单数据传输)
时序图可知,当valid信号与ready信号握手成功期间传输数据,传输到最后一个数据DATA2时,拉高tlast信号,表明此时传输的是最后一个数据。tkeep信号表示最后一个数据的那些字节是有效的。
②Data Transfer with Pad(奇数字节数据传输)
时序图可知,当valid信号与ready信号握手成功期间传输数据,传输到最后一个数据DATA2时,拉高tlast信号,表明此时传输的是最后一个数据。tkeep信号表示最后一个数据的那些字节是有效的。根据协议要求,Aurora 8B/10B 内核会为字节数为奇数的帧添加一个填充字符。由于此时传输的是奇数个字节,所以最后一个数据中存在无效字节,故tkeep信号的值为N-1。
③Data Transfer with Pause(带有暂停的数据传输)
时序图可知,当valid信号与ready信号握手成功期间传输数据,传输到最后一个数据DATA2时,拉高tlast信号,表明此时传输的是最后一个数据。tkeep信号表示最后一个数据的那些字节是有效的。在握手期间,用户通过拉低valid信号中断了握手,实现了数据发送的暂停(流控)。用户应用程序在传输完前 n 个字节后,会通过断开 s_axi_tx_tvalid 来暂停数据流,转而传输空闲数据。暂停将一直持续到 s_axi_tx_tvalid 失效。
④Data Transfer with Clock Compensation(带时钟补偿的数据传输)
在Aurora 8B/10B 内核发送时钟补偿序列时会自动中断数据传输。时钟补偿序列每 10,000 字节对每个通道会造成 12 字节的开销。
4.2 Streaming接口
相比于 Framing接口Streaming接口用法比较简,使用Framing接口的帧框架使得需要使用keep和last这两个信号来控制帧的长度,而Streaming接口则没有帧框架,使用数据流模式通道,不再关注keep和last这两个信号。在发送端发送数据只要在tvalid信号和tready信号握手成功时就可以发送;而接收端接收数据就只要关注tvalid信号,当tvalid为高时接收的数据有效。
典型的数据发送
典型的数据接收
5 仿真实践
5.1 工程搭建及IP使用
1)使用官方IP生成例程
2)选择共享逻辑
3)生成工程
)
5.2 仿真测试
TestBench代码
`timescale 1 ns / 1 psmodule aurora_8b10b_0_TB;//*************************Parameter Declarations**************************parameter SIM_MAX_TIME = 9500000; //To quit the simulation//125.0MHz GT Reference clock
parameter CLOCKPERIOD_1 = 8.0 ;
parameter CLOCKPERIOD_2 = 8.0 ;
//parameter CLOCKPERIOD_1 = 8.0;
//parameter CLOCKPERIOD_2 = 8.0;
parameter DRP_CLOCKPERIOD = 20.000 ; //GT DRP Clock
parameter INIT_CLOCKPERIOD = 20.0 ; // Board/System Clock//************************Internal Register Declarations*****************************//Freerunning Clock
reg reference_clk_1_n_r;
reg reference_clk_2_n_r;
reg drp_clk_r;
reg init_clk_p;//Global signals
reg gt_reset_in;
reg gsr_r;
reg gts_r;
reg reset_i;//********************************Wire Declarations**********************************//Freerunning Clock
wire reference_clk_1_p_r;
wire reference_clk_2_p_r; wire init_clk_n;
//Dut1//Error Detection Interface
wire hard_err_1_i;
wire soft_err_1_i; //Status
wire channel_up_1_i;
wire lane_up_1_i;//GT Serial I/O
wire rxp_1_i;
wire rxn_1_i; wire txp_1_i;
wire txn_1_i; // Error signals from the Local Link packet checker
wire [0:7] err_count_1_i; //Dut2//Error Detection Interface
wire hard_err_2_i;
wire soft_err_2_i; //Status
wire channel_up_2_i;
wire lane_up_2_i;//GT Serial I/O
wire rxp_2_i;
wire rxn_2_i; wire txp_2_i;
wire txn_2_i; // Error signals from the Local Link packet checker
wire [0:7] err_count_2_i; //*********************************Main Body of Code**********************************//_________________________Serial Connections________________assign rxn_1_i = txn_2_i;assign rxp_1_i = txp_2_i;assign rxn_2_i = txn_1_i;assign rxp_2_i = txp_1_i;//__________________________Global Signals_____________________________//Simultate the global reset that occurs after configuration at the beginning//of the simulation. Note that both GT smart models use the same global signals.assign glbl.GSR = gsr_r;assign glbl.GTS = gts_r;initialbegingts_r = 1'b0; gsr_r = 1'b1;gt_reset_in = 1'b1;#5000;gsr_r = 1'b0;gt_reset_in = 1'b0;repeat(10) @(posedge init_clk_p);gt_reset_in = 1'b1;repeat(10) @(posedge init_clk_p);gt_reset_in = 1'b0;end//____________________________Clocks____________________________initialreference_clk_1_n_r = 1'b0;always #(CLOCKPERIOD_1 / 2) reference_clk_1_n_r = !reference_clk_1_n_r;assign reference_clk_1_p_r = !reference_clk_1_n_r;initialreference_clk_2_n_r = 1'b0;always #(CLOCKPERIOD_2 / 2) reference_clk_2_n_r = !reference_clk_2_n_r;assign reference_clk_2_p_r = !reference_clk_2_n_r;initialdrp_clk_r = 1'b0;always #(DRP_CLOCKPERIOD / 2) drp_clk_r = !drp_clk_r;initialinit_clk_p = 1'b0;always #(INIT_CLOCKPERIOD / 2) init_clk_p = !init_clk_p;assign init_clk_n = !init_clk_p;//____________________________Resets____________________________initialbeginreset_i = 1'b1;#1000 reset_i = 1'b0;end//________________________Instantiate Dut 1 ________________aurora_8b10b_0_exdes example_design_1_i
(// User IO.RESET(reset_i),// Error signals from Aurora .HARD_ERR(hard_err_1_i),.SOFT_ERR(soft_err_1_i),// Status Signals.LANE_UP(lane_up_1_i),.CHANNEL_UP(channel_up_1_i),.INIT_CLK_P(init_clk_p),.INIT_CLK_N(init_clk_n),.DRP_CLK_IN(drp_clk_r), .GT_RESET_IN(gt_reset_in),// Clock Signals.GTXQ0_P(reference_clk_1_p_r),.GTXQ0_N(reference_clk_1_n_r),// GT I/O.RXP(rxp_1_i),.RXN(rxn_1_i),.TXP(txp_1_i),.TXN(txn_1_i),// Error signals from the Local Link packet checker.ERR_COUNT(err_count_1_i)
);//________________________Instantiate Dut 2 ________________aurora_8b10b_0_exdes example_design_2_i
(// User IO.RESET(reset_i),// Error signals from Aurora .HARD_ERR(hard_err_2_i),.SOFT_ERR(soft_err_2_i),// Status Signals.LANE_UP(lane_up_2_i),.CHANNEL_UP(channel_up_2_i),.INIT_CLK_P(init_clk_p),.INIT_CLK_N(init_clk_n),.DRP_CLK_IN(drp_clk_r), .GT_RESET_IN(gt_reset_in),// Clock Signals.GTXQ0_P(reference_clk_2_p_r),.GTXQ0_N(reference_clk_2_n_r),// GT I/O.RXP(rxp_2_i),.RXN(rxn_2_i),.TXP(txp_2_i),.TXN(txn_2_i),// Error signals from the Local Link packet checker.ERR_COUNT(err_count_2_i)
);always @ (posedge channel_up_1_i or posedge channel_up_2_i)
beginif((channel_up_1_i == 1'b1) && (channel_up_2_i == 1'b1)) begin $display("\naurora_8b10b_0_TB : INFO : @Time : %t CHANNEL_UP is asserted in both DUT\n", $time);#5000 $display("\naurora_8b10b_0_TB : INFO : Test Completed Successfully\n");$finish;end
endalways @ (posedge err_count_1_i[7] or posedge err_count_2_i[7])
beginif((err_count_1_i >= 8'b0000_0001) || (err_count_2_i >= 8'b0000_0001)) begin $display("\nAURORA_TB : ERROR : TEST FAIL\n");$display("\nAURORA_TB : INFO : ERR_COUNT1 = %b ERR_COUNT2 = %b\n",err_count_1_i,err_count_2_i);#1000 $display("AURORA_TB : INFO : Exiting from simulation ....\n");$finish;end
end//Abort the simulation when it reaches to max time limit
initial
begin#(SIM_MAX_TIME) $display("\nAURORA_TB : INFO : Reached max. simulation time limit\n");$finish;
endendmodule
5.3 仿真工程结构说明
①support模块(aurora_8b10b_0_support.v):核心,包含了对IP、GT等的例化处理等一系列操作;后续在应用中此部分不需要修改。
②frame_gen( aurora_8b10b_0_FRAME_GEN.v):数据生成模块,采用LFSR的方式生成伪随机序列;后续在我们的应用中此部分可替换成我们的数据输入模块(建议加入FIFO,这样代码的复用性更佳)。
③frame_check(aurora_8b10b_0_FRAME_CHECK.v):数据检验模块,对接受的数据进行检验以验证传输的正确性;后续在应用中此部分可替换成数据检查模块或者删除。
④LL_AXI(aurora_8b10b_0_LL_TO_AXI_EXDES.v):LL总线转AXI总线(据说该例程原本的接口是LL接口,后面Xilinx为了推广AXI总线。
5.4 Aurora 8B/10B IP核 延迟开销
Aurora 8B/10B IP的延迟是由协议引擎(编解码、流水线处理、)和GT收发器的管道延迟引起的。随着用户接口数据宽度的增加,协议引擎的延迟也会增加;GT收发器的延迟取决于所选收发器的功能和属性。在K7器件的仿真实验大约在34个User_clk周期,其他器件请另测试,手册给出V7系列最小37个周期。
5.5 发送和接收数据
1)channel_up信号
2)s_axi_tx_tdata用户发送数据
3)m_axi_rx_tdata用户接收数据
通过仿真当channel_up信号置位拉高时,意味着Aurora 8b/10b IP核成功建立链路通道。s_axi_tx_tready和s_axi_tx_tvalid高有效时,用户发送数据有效,接收数据当m_axi_rx_tvalid有效时,接收端数据有效。在设计使用时需要注意:
①数据: 在用户发送每一帧数据的时候,Aurora8B10都会在数据的开始位置增加2byte的SCP和末尾增加2btye的EOF来表示数据帧的开始和结束标志(如果用户的数据btye数为奇数的话,会为其增加额外的一个PAD byte来使数据为偶数)。所以为了最大传输带宽,用户每次传输的数据byte数最好为偶数。
②时钟补偿: 传输通道每10000 bytes需要进行一次时钟补偿,每次时钟补偿需要发送12 bytes的数据,需要消耗6个或者3个时钟周期开销。
③传输延时:从发送端发送第一个数据开始,到接收端接收到第一个数据结束所消耗的时间,最小的延时为37/41个用户时钟周期,如有对时延有要求。
6 总结
本文介绍了高速接口Aurora8B/10B 协议,通过对协议的接口时序理解及握手机制及一些相关术语的学习,详细了解了高速接口Aurora8B/10B 协议的使用方法。通过FPGA仿真验证了该IP的使用方法和一些主要的关键信号。为使用高速串行接口设计提供基础。后续则继续总结分享FPGA中的一些技术应用,开发不易珍惜每一分原创和劳动成果,同时注意平时开发过程中的经验积累总结。