文章目录
- 前言
- 一、IP核设置
- 二、基于IP核进行设计
- 2.1、设计框图
- 2.2、aurora_8b10b_0模块
- 2.3、aurora_8b10b_0_CLOCK_MODULE
- 2.4、aurora_8b10b_0_SUPPORT_RESET_LOGIC
- 2.5、aurora8b10b_channel模块
- 2.6、IBUFDS_GTE2模块
- 2.7、aurora_8b10b_0_gt_common_wrapper模块
- 2.8、aurora8b10b_module模块
- 总结
前言
参考xilinx文档pg046
Aurora 是一个用于在点对点串行链路间移动数据的可扩展轻量级链路层协议。这为物理层提供透明接口,让专有协议或业界标准协议上层能方便地使用高速收发器。虽然使用的逻辑资源非常少,但 Aurora 能提供低延迟高带宽和高度可配置的特性集。在 Xilinx FPGA 上使用是免费的,而且在 ASIC 上能以名义成本通过单独的许可证协议得到支持。
主要特性:
- 高带宽,仅受限于收发器的数据速率
- 支持大量键合线路,实现较高的总带宽
- 支持全双工和单工通道
- 无限帧尺寸/灵活组帧
- 小型逻辑封装,采用标准的 AXI-ST 接口。
- 内置流程控制和热插拔支持
Aurora 广泛用于需要背板、电路板间和芯片间连接的应用。细分市场包括有线通信、存储、服务器、测试测量、工业、消费和医疗等。此外,Aurora 也可用作嵌入式处理器应用中的调试端口。
一、IP核设置
IP核配置很简单,相当于只有第一页。
Lane WIDTH:用户侧数据位宽
Line Rate:线速率
GT Refclk :Aurora IP核是基于GT作为物理层实现的,所以时钟GT Refclk就是GT的参考时钟。GT的时钟参考只能使用外部的差分时钟,所以具体的值取决你板卡的硬件设计。
INIT CLK :初始化阶段的时钟 ,在上电初始化阶段,可以使用该时钟来驱动一些逻辑。
DRP CLK : DRP时钟,动态重配置,一般不用。(通常一个内部模块需要进行配置,使用两种方法:端口控制和配置参数控制。一个常见的应用就是线速率切换。这时候就需要利用DRP端口来调整部分参数的值,然后复位GTX,使GTX工作在不同的线速率下。)。
上面这三个时钟,是IP核工作所需要的时钟,也是我们需要提供给IP核的。此外,还有一个时钟是IP核提供给我们的:user_clk。这个时钟,是IP核根据设置的线速率及Lane的位宽计算出来的用户时钟,用户需要传输的数据必须是该时钟域下的数据,否则会存在亚稳态风险。
Link Layer:选择工作模式(一般为全双工)、数据帧接口或者是流接口(一般使用帧接口,也就是AXIS),流控
第二页则是选择通道Lane数目以及位置,第三页选择共享逻辑位置。根据自己需求进行选择。
二、基于IP核进行设计
基于example design进行设计,许多模块也是从其中获取,然后进行简单的修改以方便我们使用。
2.1、设计框图
2.2、aurora_8b10b_0模块
该模块即为例化aurora8b10b IP核。如果看过之前的基于GTX IP核的设计,这些接口应该比较熟悉了。具体含义查看xilinx手册pg046
aurora_8b10b_0 aurora_8b10b_0_u0 (
.s_axi_tx_tdata (s_axi_tx_tdata ),
.s_axi_tx_tkeep (s_axi_tx_tkeep ),
.s_axi_tx_tlast (s_axi_tx_tlast ),
.s_axi_tx_tvalid (s_axi_tx_tvalid ),
.s_axi_tx_tready (s_axi_tx_tready ),
.m_axi_rx_tdata (m_axi_rx_tdata ),
.m_axi_rx_tkeep (m_axi_rx_tkeep ),
.m_axi_rx_tlast (m_axi_rx_tlast ),
.m_axi_rx_tvalid (m_axi_rx_tvalid ),
.hard_err (o_hard_err ),
.soft_err (o_soft_err ),
.frame_err (o_frame_err ),
.channel_up (o_channel_up ),
.lane_up (o_lane_up ),
.txp (gt_txp ),
.txn (gt_txn ),
.reset (w_system_rst ),
.gt_reset (w_gt_reset ),
.loopback (i_loopback ),
.rxp (gt_rxp ),
.rxn (gt_rxn ),
.drpclk_in (i_clk_100M ),
.drpaddr_in (0 ),
.drpen_in (0 ),
.drpdi_in (0 ),
.drprdy_out ( ),
.drpdo_out ( ),
.drpwe_in (0 ),
.power_down (0 ),
.tx_lock (w_gt_clk_locked ),
.tx_resetdone_out (),
.rx_resetdone_out (),
.link_reset_out (),
.gt0_qplllock_in (gt0_qplllock_in ),
.gt0_qpllrefclklost_in (gt0_qpllrefclklost_in ),
.gt0_qpllreset_out (gt0_qpllreset_out ),
.gt_qpllclk_quad3_in (gt_qpllclk_quad3_in ),
.gt_qpllrefclk_quad3_in (gt_qpllrefclk_quad3_in ),
.init_clk_in (i_clk_100M ),
.pll_not_locked (w_pll_not_locked ),
.tx_out_clk (w_tx_out_clk ),
.sys_reset_out (w_sys_reset_out ),
.user_clk (w_user_clk ),
.sync_clk (w_sycn_clk ),
.gt_refclk1 (i_gtref_clk )
);
2.3、aurora_8b10b_0_CLOCK_MODULE
该模块主要作用是根据GT的TXOUTCLK(此处为tx_out_clk)产生w_user_clk和w_sync_clk,w_user_clk是提供给用户逻辑的,也就是TXUSRCLK2 ,w_sync_clk 则是作为TXUSRCLK 。以下是手册当中的介绍,很清楚了已经(前提是要看GT的介绍):
user_clk和sync_clk是由tx_out_clk驱动的PLL或BUFG的输出。这些时钟生成在_clock_module文件中可用。user_clk作为txusrclk2输入信号传递给收发器。sync_clk作为txusrclk输入信号传递给收发器
此处对于该模块进行了简单的修改,将INIT_CLK的差分转单端的过程放到了外部顶层模块。(忘了在哪里看到的一句话,说过最好在顶层模块使用clock_wizard)
module aurora_8b10b_0_CLOCK_MODULE
(
// INIT_CLK_P,
// INIT_CLK_N,
// INIT_CLK_O,
GT_CLK,
GT_CLK_LOCKED,
USER_CLK,
SYNC_CLK,
PLL_NOT_LOCKED
);
//***********************************Port Declarations*******************************
// input INIT_CLK_P;
// input INIT_CLK_N;
// output INIT_CLK_O;
input GT_CLK;
input GT_CLK_LOCKED;
output USER_CLK;
output SYNC_CLK;
output PLL_NOT_LOCKED;
//wire INIT_CLK_I;
//*********************************Main Body of Code**********************************
// Input buffering
//------------------------------------
BUFG user_clk_buf_i
(
.I(GT_CLK),
.O(USER_CLK)
);
assign SYNC_CLK = USER_CLK;
assign PLL_NOT_LOCKED = !GT_CLK_LOCKED;
// Assign an IBUFDS to INIT_CLK
// IBUFDS init_clk_ibufg_i
// (
// .I(INIT_CLK_P),
// .IB(INIT_CLK_N),
// .O(INIT_CLK_I)
// );
BUFG init_clk_buf_i
(
.I(INIT_CLK_I),
.O(INIT_CLK_O)
);
endmodule
2.4、aurora_8b10b_0_SUPPORT_RESET_LOGIC
该模块用于产生复位信号reset和gt_reset。根据手册内容,我们可以看到,reset是针对于user_clk时钟域的,gt_reset是针对于init_clk时钟域的。那么代码内容就很好理解了,就是简单的跨时钟处理,将异步复位信号同步到相应的时钟域下。
具体的复位过程有兴趣可以看看手册里面的介绍,我们使用IP核的时候直接关注channel_up信号就可以了。
module aurora_8b10b_0_SUPPORT_RESET_LOGIC
(
// User IO
RESET,
USER_CLK,
INIT_CLK_IN,
GT_RESET_IN,
SYSTEM_RESET,
GT_RESET_OUT
);
`define DLY #1
//***********************************Port Declarations*******************************
// User I/O
input RESET;
input USER_CLK;
input INIT_CLK_IN;
input GT_RESET_IN;
output SYSTEM_RESET;
output GT_RESET_OUT;
//**************************Internal Register Declarations****************************
(* ASYNC_REG = "true" *) (* shift_extract = "{no}" *) reg [0:3] debounce_gt_rst_r = 4'd0;
reg [0:3] reset_debounce_r = 4'd0;
reg reset_debounce_r2 = 1'b1;
reg gt_rst_r;
//********************************Wire Declarations**********************************
wire gt_rst_sync;
wire SYSTEM_RESET;
//*********************************Main Body of Code**********************************
//Reset sync from INIT_CLK to USER_CLK
aurora_8b10b_0_cdc_sync_exdes
#(
.c_cdc_type (1 ),
.c_flop_input (1 ),
.c_reset_state (0 ),
.c_single_bit (1 ),
.c_vector_width (2 ),
.c_mtbf_stages (5 )
)gt_rst_r_cdc_sync
(
.prmry_aclk (INIT_CLK_IN ),
.prmry_rst_n (1'b1 ),
.prmry_in (gt_rst_r ),
.prmry_vect_in (2'd0 ),
.scndry_aclk (USER_CLK ),
.scndry_rst_n (1'b1 ),
.prmry_ack ( ),
.scndry_out (gt_rst_sync ),
.scndry_vect_out ( )
);
//_________________Debounce the Reset and PMA init signal___________________________
// Simple Debouncer for Reset button. The debouncer has an
// asynchronous reset tied to GT_RESET_IN. This is primarily for simulation, to ensure
// that unknown values are not driven into the reset line
always @(posedge USER_CLK or posedge gt_rst_sync)
if(gt_rst_sync)
reset_debounce_r <= 4'b1111;
else
reset_debounce_r <= {RESET,reset_debounce_r[0:2]};
always @ (posedge USER_CLK)
reset_debounce_r2 <= &reset_debounce_r;
assign SYSTEM_RESET = reset_debounce_r2;
// Debounce the GT_RESET_IN signal using the INIT_CLK
always @(posedge INIT_CLK_IN)
debounce_gt_rst_r <= {GT_RESET_IN,debounce_gt_rst_r[0:2]};
always @(posedge INIT_CLK_IN)
gt_rst_r <= &debounce_gt_rst_r;
assign GT_RESET_OUT = gt_rst_r;
endmodule
2.5、aurora8b10b_channel模块
该模块就是例化上述三个模块,讲一下关键接口给到上层。看一看接口信号即可:
module aurora8b10b_channel(
input i_clk_100M ,
input i_rst ,
input i_gtref_clk ,
output gt_txp ,
output gt_txn ,
input gt_rxp ,
input gt_rxn ,
input [31:0] s_axi_tx_tdata ,
input [3 :0] s_axi_tx_tkeep ,
input s_axi_tx_tlast ,
input s_axi_tx_tvalid ,
output s_axi_tx_tready ,
output [31:0] m_axi_rx_tdata ,
output [3 :0] m_axi_rx_tkeep ,
output m_axi_rx_tlast ,
output m_axi_rx_tvalid ,
output o_hard_err ,
output o_soft_err ,
output o_frame_err ,
output o_channel_up ,
output o_lane_up ,
input [2 :0] i_loopback ,
output o_user_clk ,
output o_user_rst ,
input gt0_qplllock_in ,
input gt0_qpllrefclklost_in ,
output gt0_qpllreset_out ,
input gt_qpllclk_quad3_in ,
input gt_qpllrefclk_quad3_in
);
2.6、IBUFDS_GTE2模块
这已经是老朋友了,GT的外部输入参考时钟原语。
IBUFDS_GTE2 #(
.CLKCM_CFG ("TRUE" ),
.CLKRCV_TRST ("TRUE" ),
.CLKSWING_CFG (2'b11 )
)
IBUFDS_GTE2_inst (
.O (gt_ref_clk ),
.ODIV2 ( ),
.CEB (0 ),
.I (i_gtref_clk_p ),
.IB (i_gtref_clk_n )
);
2.7、aurora_8b10b_0_gt_common_wrapper模块
一样是老朋友了,里面包含了QPLL原语GTXE2_COMMON。不过我们选择的线速率只有6.25Gbps,CPLL就可以处理了,QPLL其实压根没用到,走完整流程吧。这里的i_clk_100M时钟就是当时在配IP时候的INIT_CLK,我选择的是100M。
注:将该模块放到这里是因为方便我们在aurora8b10b_module当中可以例化多个aurora8b10b_channel,老生常谈了,因为一个QUAD只有一个QPLL。同样注意一个问题,那就是gt_qpllreset_out信号选择一个aurora8b10b_channel的复位信号就行。
aurora_8b10b_0_gt_common_wrapper gt_common_support_u0
(
.gt_qpllclk_quad3_i (gt_qpllclk_quad3_in ),
.gt_qpllrefclk_quad3_i (gt_qpllrefclk_quad3_in ),
.gt0_gtrefclk0_common_in (gt_ref_clk ),
.gt0_qplllock_out (gt_qplllock_in ),
.gt0_qplllockdetclk_in (i_clk_100M ),
.gt0_qpllrefclklost_out (gt_qpllrefclklost_in ),
.gt0_qpllreset_in (gt_qpllreset_out )
);
2.8、aurora8b10b_module模块
在该模块当中我们可以例化多个aurora8b10b_channel模块,就和之前使用GT是一样一样的。以下为例化俩个通道的代码。
module aurora8b10b_module(
input i_gtref_clk_p ,
input i_gtref_clk_n ,
input i_clk_100M ,
input i_rst ,
output [1 :0] gt_txp ,
output [1 :0] gt_txn ,
input [1 :0] gt_rxp ,
input [1 :0] gt_rxn ,
input [31:0] s_axi_c0_tx_tdata ,
input [3 :0] s_axi_c0_tx_tkeep ,
input s_axi_c0_tx_tlast ,
input s_axi_c0_tx_tvalid ,
output s_axi_c0_tx_tready ,
output [31:0] m_axi_c0_rx_tdata ,
output [3 :0] m_axi_c0_rx_tkeep ,
output m_axi_c0_rx_tlast ,
output m_axi_c0_rx_tvalid ,
input [31:0] s_axi_c1_tx_tdata ,
input [3 :0] s_axi_c1_tx_tkeep ,
input s_axi_c1_tx_tlast ,
input s_axi_c1_tx_tvalid ,
output s_axi_c1_tx_tready ,
output [31:0] m_axi_c1_rx_tdata ,
output [3 :0] m_axi_c1_rx_tkeep ,
output m_axi_c1_rx_tlast ,
output m_axi_c1_rx_tvalid ,
output o_c0_hard_err ,
output o_c0_soft_err ,
output o_c0_frame_err ,
output o_c0_channel_up ,
output o_c0_lane_up ,
input [2 :0] i_c0_loopback ,
output o_c1_hard_err ,
output o_c1_soft_err ,
output o_c1_frame_err ,
output o_c1_channel_up ,
output o_c1_lane_up ,
input [2 :0] i_c1_loopback ,
output o_c0_user_clk ,
output o_c0_user_rst ,
output o_c1_user_clk ,
output o_c1_user_rst
);
wire gt_ref_clk ;
wire gt_qplllock_in ;
wire gt_qpllrefclklost_in ;
wire gt_qpllreset_out ;
wire gt_qpllclk_quad3_in ;
wire gt_qpllrefclk_quad3_in ;
IBUFDS_GTE2 #(
.CLKCM_CFG ("TRUE" ),
.CLKRCV_TRST ("TRUE" ),
.CLKSWING_CFG (2'b11 )
)
IBUFDS_GTE2_inst (
.O (gt_ref_clk ),
.ODIV2 ( ),
.CEB (0 ),
.I (i_gtref_clk_p ),
.IB (i_gtref_clk_n )
);
aurora_8b10b_0_gt_common_wrapper gt_common_support_u0
(
.gt_qpllclk_quad3_i (gt_qpllclk_quad3_in ),
.gt_qpllrefclk_quad3_i (gt_qpllrefclk_quad3_in ),
.gt0_gtrefclk0_common_in (gt_ref_clk ),
.gt0_qplllock_out (gt_qplllock_in ),
.gt0_qplllockdetclk_in (i_clk_100M ),
.gt0_qpllrefclklost_out (gt_qpllrefclklost_in ),
.gt0_qpllreset_in (gt_qpllreset_out )
);
aurora8b10b_channel aurora_channel_u0(
.i_clk_100M (i_clk_100M ),
.i_rst (i_rst ),
.i_gtref_clk (gt_ref_clk ),
.gt_txp (gt_txp[0] ),
.gt_txn (gt_txn[0] ),
.gt_rxp (gt_rxp[0] ),
.gt_rxn (gt_rxn[0] ),
.s_axi_tx_tdata (s_axi_c0_tx_tdata ),
.s_axi_tx_tkeep (s_axi_c0_tx_tkeep ),
.s_axi_tx_tlast (s_axi_c0_tx_tlast ),
.s_axi_tx_tvalid (s_axi_c0_tx_tvalid ),
.s_axi_tx_tready (s_axi_c0_tx_tready ),
.m_axi_rx_tdata (m_axi_c0_rx_tdata ),
.m_axi_rx_tkeep (m_axi_c0_rx_tkeep ),
.m_axi_rx_tlast (m_axi_c0_rx_tlast ),
.m_axi_rx_tvalid (m_axi_c0_rx_tvalid ),
.o_hard_err (o_c0_hard_err ),
.o_soft_err (o_c0_soft_err ),
.o_frame_err (o_c0_frame_err ),
.o_channel_up (o_c0_channel_up ),
.o_lane_up (o_c0_lane_up ),
.i_loopback (i_c0_loopback ),
.o_user_clk (o_c0_user_clk ),
.o_user_rst (o_c0_user_rst ),
.gt0_qplllock_in (gt_qplllock_in ),
.gt0_qpllrefclklost_in (gt_qpllrefclklost_in ),
.gt0_qpllreset_out (gt_qpllreset_out ),
.gt_qpllclk_quad3_in (gt_qpllclk_quad3_in ),
.gt_qpllrefclk_quad3_in (gt_qpllrefclk_quad3_in )
);
aurora8b10b_channel aurora_channel_u1(
.i_clk_100M (i_clk_100M ),
.i_rst (i_rst ),
.i_gtref_clk (gt_ref_clk ),
.gt_txp (gt_txp[1] ),
.gt_txn (gt_txn[1] ),
.gt_rxp (gt_rxp[1] ),
.gt_rxn (gt_rxn[1] ),
.s_axi_tx_tdata (s_axi_c1_tx_tdata ),
.s_axi_tx_tkeep (s_axi_c1_tx_tkeep ),
.s_axi_tx_tlast (s_axi_c1_tx_tlast ),
.s_axi_tx_tvalid (s_axi_c1_tx_tvalid ),
.s_axi_tx_tready (s_axi_c1_tx_tready ),
.m_axi_rx_tdata (m_axi_c1_rx_tdata ),
.m_axi_rx_tkeep (m_axi_c1_rx_tkeep ),
.m_axi_rx_tlast (m_axi_c1_rx_tlast ),
.m_axi_rx_tvalid (m_axi_c1_rx_tvalid ),
.o_hard_err (o_c1_hard_err ),
.o_soft_err (o_c1_soft_err ),
.o_frame_err (o_c1_frame_err ),
.o_channel_up (o_c1_channel_up ),
.o_lane_up (o_c1_lane_up ),
.i_loopback (i_c1_loopback ),
.o_user_clk (o_c1_user_clk ),
.o_user_rst (o_c1_user_rst ),
.gt0_qplllock_in (gt_qplllock_in ),
.gt0_qpllrefclklost_in (gt_qpllrefclklost_in ),
.gt0_qpllreset_out ( ),
.gt_qpllclk_quad3_in (gt_qpllclk_quad3_in ),
.gt_qpllrefclk_quad3_in (gt_qpllrefclk_quad3_in )
);
endmodule
总结
至此工作就已经准备差不多了,编写相应的用户数据产生模块,我们已经可以直接使用aurora了。下文进行介绍。