Aurora8B10B IP使用 -05- 收发测试应用示例
时间:2023-09-26 21:37:02
前言
本文首先介绍了根据在线博客文章简要介绍的8B/10B并根据原则Aurora IP参考相关使用方法IP手册设计递增测试用例,下板实际验证。
8B/10B简介
8B/10B,也叫8比特/10比特或8比特b10b。8b/10b方式最初由IBM公司于1983年发明并应用于ESCON(200M互联系统),由Al Widmer和Peter Franaszek在IBM对出版物研发的描述。
**8b/10b编码的特点之一是保证编码DC 平衡,**采用8b/10b编码方法可以使发送的0和1数量基本相同,连续的1或0不超过5,即每5个连续的1或0必须插入一个0或1,以确保信号DC平衡意味着链路超时不会发生DC失调。通过8b/10b此外,使用一些特殊代码可以保证传输的数据串在接收端正确恢复( 在PCI-Express总线为K码) ,它可以帮助接收端恢复工作,并在早期阶段发现数据位的传输错误,抑制错误继续发生。
8b/10b编码是将一组连续8位数据分解成两组数据,一组3位,一组5位,编码后分别成为一组4位代码和一组6位代码,形成一组10位数据发送。相反,解码是将1组10位的输入数据转换为8位数据位。数据值可以统一的表示为DX.Y或KX.Y,D表示数据代码,K表示为特殊命令代码,X原始数据输入低5位EDCBA,Y 原始数据的高三位表示输入HGF。
8b/10b编码是目前许多高速串行总线采用的编码机制,如 USB3.0、1394b、Serial ATA、PCI Express、Infini-band、Fibre Channel(光纤通道),RapidIO等总线或网络等。
工作原理
8B/10B编码是目前高速串行通信中常用的编码方法。bit编码成10bit后,10B中0和1的位数只能出现三种情况:
- 有5个0和5个1
- 有6个0和4个1
- 有4个0和6个1
这导致了一个新术语不平等(Disparity)根据上述三种情况,1的位数与0的位数之间存在差异Disparity 0、-2、 2。8bit原始数据将分为两部分,其低5位将进行5B/6B编码,高3位3B/4B这两种映射关系在当时已经成为标准化的表格。人们喜欢把8bit数据表示成Dx.y的形式,其x=5LSB(least significant bit最低有效位),y=3MSB(most significant bit最高有效位)。
例如一个8bit数据101 10101,x=10101(21) y=101(5),现在让我们把这8bit数据写成D21.5。
对于8bit数据在表中的位序是HGFEDCBA,H是最高的,A为最低位,EDCBA经过5B/6B编码为abcdei,HGF经过3B/4B编码为fghj。传送10bit编码顺序为abcdeifghj。
创建工程
新建一个空白工程,我在这里选择的芯片型号是xc7k160tffg676-2,米联客MK7160FA。
配置Aurora IP
完成工程,将Aurora IP将其添加到工程中,并修改相关配置。lane宽度设置为4,接口生成32位宽数据,如果选择2,则生成16位宽数据。
由于只进行传输测试,界面的相关参数可以根据板卡的相关时钟进行配置,Link选择帧模式,其余默认。
米联客板卡在MGT116对外引出,所以这里相应连接GTXQ1上。
内核中包含共享逻辑选项,并使单端口时钟选项初始化时钟,Aurora IP配置完成。
程序架构设计
这里设计了参考例程的思路Aurora主要包括四个模块,复位模块,Aurora发送数据生成模块,Aurora接收数据模块,Aurora IP。
- 主要用于复位模块reset和gt_reset初始化复位两个信号,确保Aurora IP能正常工作。
- Aurora根据发送数据生成模块AXI通过总线协议生成递增测试数据Aurora IP通过光口传输数据。
- Aurora根据接收数据模块AXI通过接收计数信号,总线协议接收数据,比较递增数传输是否存在错误,用于错误指示。
复位模块
使用复位模块start_en当检测到复位指示信号时start_en当上升时,模块复位系统复位信号和收发器复位信号。reset和gt_reset根据手册中推荐的复位顺序进行复位。本文详细介绍,Aurora8B10B IP使用 -02- IP功能设计技巧。
`timescale 1ns / 1ps module rst_ctrl( input wire clk , input wire start_en , output wire reset ,// 系统复位 output wire gt_reset // 收发器复位 ); //parameter define parameter GT_RESET_START = 128 ; parameter GT_RESET_END = 384 ; parameter RESET_MAX = GT_RESET_END GT_RESET_START; //reg define reg reset_r ; reg gt_reset_r ; reg reset_busy =0.///复位忙指示信号 reg [1:0] start_flag_dly =0.//复位使信号延迟 reg [10:0] cnt_rst =0.//用于生成复位信号的计数器 //wire define assign reset = reset_r ; assign gt_reset = gt_reset_r; ///开始信号边缘 wire link_up_n = !start_flag_dly[1]&&start_flag_dly[0]; //start_flag_dly always @(posedge clk) begin start_flag_dly <= {start_flag_dly[0], start_en}; end ///复位忙指示信号 always @(posedge clk) begin if(link_up_n==1) begin reset_busy <= 1; end else if(cnt_rst== RESET_MAX - 1)begin reset_busy <= 0; end end //复位计数器 always @(posedge clk) begin if (reset_busy == 'd1) begin if(reset_busy == 'd1 && (cnt_rst == RESET_MAX - 1)) cnt_rst <= 'd0; else cnt_rst <= cnt_rst 1'b1; end else begin cnt_rst <= 'd0; end end //gt_reset always @(posedge clk) begin if (reset_busy == 'd1) begin if(cnt_rst == GT_RESET_START - 1) begin gt_reset_r <= 1'b1; end else if (cnt_rst == GT_RESET_END - 1|| cnt_rst == 0 ) begin gt_reset_r <= 1'b0; end else begin gt_reset_r <= gt_reset_r; end end else begin gt_reset_r <= 1'b0; end end //reset always @(posedge clk) begin if (reset_busy == 'd1) begin reset_r <= 1'b1; end else begin reset_r <= 1'b0; end end endmodule
发送模块
发送部分是简单的模拟AXI发送总线协议,这里指定了几个参数,方便后期移植修改,同时也例化了一个ILA IP用于检测上板时数据的正确性。
`timescale 1ns / 1ps module Aurora_Tx #( parameter DATA_WIDTH = 32, // DATA bus width parameter TKEEP_WIDTH = DATA_WIDTH/8, // TKEEP width
parameter STREAM_LEN = 1024 ,
parameter REG_MAX_BURST = 15
)
(
input wire clk ,
input wire rst ,
output wire [0 : DATA_WIDTH-1] s_axi_tx_tdata ,
output wire [0 : TKEEP_WIDTH-1] s_axi_tx_tkeep ,
output wire s_axi_tx_tlast ,
output wire s_axi_tx_tvalid ,
input wire s_axi_tx_tready
);
//reg define
reg [0:DATA_WIDTH-1] axi_tx_tdata ;
reg axi_tx_tlast ;
reg axi_tx_tvalid ;
reg [REG_MAX_BURST:0] cnt_data ;
//wire define
wire cnt_data_tlast ;
assign s_axi_tx_tdata = axi_tx_tdata;
assign s_axi_tx_tkeep = 4'hF ;
assign s_axi_tx_tlast = axi_tx_tlast;
assign s_axi_tx_tvalid = axi_tx_tvalid;
assign cnt_data_tlast = (s_axi_tx_tready==1) && (axi_tx_tvalid==1) && (cnt_data == STREAM_LEN - 1);
//cnt_data
always @(posedge clk) begin
if(rst==1)begin
cnt_data <= 'd0;
end
else if ((s_axi_tx_tready==1) && (axi_tx_tvalid==1)) begin
if(cnt_data_tlast==1)
cnt_data <= 'd0;
else
cnt_data <= cnt_data + 1'b1;
end
end
//axi_tx_tlast
always @(*) begin
axi_tx_tlast = cnt_data_tlast;
end
//axi_tx_tvalid
always @(posedge clk) begin
if(rst==1)begin
axi_tx_tvalid <= 1'b0;
end
else if (cnt_data_tlast == 1'b1) begin
axi_tx_tvalid <= 1'b0;
end
else if (axi_tx_tvalid == 1'b0 && s_axi_tx_tready == 1'b1) begin
axi_tx_tvalid <= 1'b1;
end
end
//axi_tx_tdata
always @(*) begin
axi_tx_tdata = cnt_data;
end
wire [63:0] probe0;
assign probe0 = {
s_axi_tx_tdata ,//32
s_axi_tx_tkeep ,//4
s_axi_tx_tlast ,//1
s_axi_tx_tvalid ,//1
s_axi_tx_tready ,//1
cnt_data //16
};
ila_0 u_tx (
.clk(clk), // input wire clk
.probe0(probe0) // input wire [63:0] probe0
);
endmodule
接收模块
接收部分就是简单的模拟AXI总线协议接收数据,这里也指定了几个参数,方便后期移植修改,同时又例化了一个ILA IP用于检测上板时数据正确性。
module Aurora_Rx #(
parameter DATA_WIDTH = 32, // DATA bus width
parameter TKEEP_WIDTH = DATA_WIDTH/8, // TKEEP width
parameter STREAM_LEN = 1024 ,
parameter REG_MAX_BURST = 15
)(
input wire clk ,
input wire rst ,
input wire [0 : DATA_WIDTH-1] m_axi_rx_tdata ,
input wire [0 : TKEEP_WIDTH-1] m_axi_rx_tkeep ,
input wire m_axi_rx_tlast ,
input wire m_axi_rx_tvalid
);
//reg define
reg [REG_MAX_BURST:0] cnt_burst ;
reg error_r ;
always @(posedge clk) begin
if(rst==1)begin
cnt_burst <= 'd0;
end
else if (m_axi_rx_tvalid == 1) begin
if(m_axi_rx_tvalid == 1 && cnt_burst == STREAM_LEN - 1)
cnt_burst <= 'd0;
else
cnt_burst <= cnt_burst + 1'b1;
end
end
//check
always @(posedge clk) begin
if(rst==1)begin
error_r <= 'd0;
end
else if (m_axi_rx_tvalid==1 && (m_axi_rx_tdata != cnt_burst)) begin
error_r <= 1'b1;
end
end
wire [63:0] probe0;
assign probe0 = {
m_axi_rx_tdata ,
m_axi_rx_tkeep ,
m_axi_rx_tlast ,
m_axi_rx_tvalid ,
error_r ,
cnt_burst
};
ila_0 u_rx (
.clk(clk), // input wire clk
.probe0(probe0) // input wire [63:0] probe0
);
endmodule
顶层连接
根据前面绘制的框图连接上述各个模块,例化了一个VIO IP进行检测和控制复位。
`timescale 1ns / 1ps
module Aurora_Top(
//clock---------------------------------
input clk_in, //系统钟
input gt_refclk116_p , //差分参考钟
input gt_refclk116_n , //差分参考钟
//Serial I/O----------------------------
input rxp ,
input rxn ,
output txp ,
output txn ,
//--------------------------------------
output tx_dis ,
output led_link_up
);
//==========================================
//wire define
wire start;
//------------------------------SEND--------------------------------------------
// AXI TX Interface
wire [0 : 31] s_axi_tx_tdata ;
wire [0 : 3] s_axi_tx_tkeep ;
wire s_axi_tx_tlast ;
wire s_axi_tx_tvalid ;
wire s_axi_tx_tready ;
//------------------------------RECEIVE-----------------------------------------
// AXI RX Interface
wire [0 : 31] m_axi_rx_tdata ;
wire [0 : 3] m_axi_rx_tkeep ;
wire m_axi_rx_tlast ;
wire m_axi_rx_tvalid ;
//------------------------------SYSTEM------------------------------------------
// reset IO,test
wire reset;
wire gt_reset;
wire [2 : 0] loopback;
//------------------------------clock-------------------------------------------
// GT Reference Clock Interface
wire gt_refclk1_p;
wire gt_refclk1_n;
wire init_clk_in ;
wire user_clk_out;
wire sync_clk_out;
wire gt_refclk1_out;
//--------------------------------drp-------------------------------------------
//drp Interface
wire drpclk_in ;
//-----------------------------Status Detection--------------------------------
// Error Detection Interface
wire hard_err ;
wire soft_err ;
wire frame_err;
// Status link
wire channel_up;
wire lane_up;
wire sys_reset_out;
//==========================================
//wire assign
assign tx_dis = 0;
assign gt_refclk116_p =gt_refclk1_p;
assign gt_refclk116_n =gt_refclk1_n;
assign led_link_up = channel_up & lane_up;
assign init_clk_in = clk_in;
assign drpclk_in = clk_in;
wire sys_rst = ~(channel_up & lane_up & (~sys_reset_out));
rst_ctrl u_rst_ctrl(
.clk ( init_clk_in ),
.start_en ( start ),
.reset ( reset ),
.gt_reset ( gt_reset )
);
Aurora_Tx u_Aurora_Tx(
.clk ( user_clk_out ),
.rst ( start ),
.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 )
);
Aurora_Rx u_Aurora_Rx(
.clk ( user_clk_out ),
.rst ( start ),
.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 )
);
aurora_8b10b_0 u_aurora(
//------------------------------SEND--------------------------------------------
// AXI TX Interface
.s_axi_tx_tdata (s_axi_tx_tdata ), // input wire [0 : 31] s_axi_tx_tdata
.s_axi_tx_tkeep (s_axi_tx_tkeep ), // input wire [0 : 3] s_axi_tx_tkeep
.s_axi_tx_tlast (s_axi_tx_tlast ), // input wire s_axi_tx_tlast
.s_axi_tx_tvalid(s_axi_tx_tvalid), // input wire s_axi_tx_tvalid
.s_axi_tx_tready(s_axi_tx_tready), // output wire s_axi_tx_tready
//------------------------------RECEIVE-----------------------------------------
// AXI RX Interface
.m_axi_rx_tdata (m_axi_rx_tdata ), // output wire [0 : 31] m_axi_rx_tdata
.m_axi_rx_tkeep (m_axi_rx_tkeep ), // output wire [0 : 3] m_axi_rx_tkeep
.m_axi_rx_tlast (m_axi_rx_tlast ), // output wire m_axi_rx_tlast
.m_axi_rx_tvalid(m_axi_rx_tvalid), // output wire m_axi_rx_tvalid
//------------------------------SYSTEM------------------------------------------
// reset IO,test
.reset(reset), // input wire reset
.gt_reset(gt_reset), // input wire gt_reset
.loopback(loopback), // input wire [2 : 0] loopback
//--------------------------------Serial I/O------------------------------------
.txp(txp), // output wire [0 : 0] txp
.txn(txn), // output wire [0 : 0] txn
.rxp(rxp), // input wire [0 : 0] rxp
.rxn(rxn), // input wire [0 : 0] rxn
//------------------------------clock-------------------------------------------
// GT Reference Clock Interface
.gt_refclk1_p(gt_refclk1_p), // input wire gt_refclk1_p
.gt_refclk1_n(gt_refclk1_n), // input wire gt_refclk1_n
.init_clk_in(init_clk_in), // input wire init_clk_in
//--------------------------------drp-------------------------------------------
//drp Interface
.drpclk_in(drpclk_in), // input wire drpclk_in
.drpaddr_in(0), // input wire [8 : 0] drpaddr_in
.drpen_in(0), // input wire drpen_in
.drpdi_in(0), // input wire [15 : 0] drpdi_in
.drprdy_out(), // output wire drprdy_out
.drpdo_out(), // output wire [15 : 0] drpdo_out
.drpwe_in(0), // input wire drpwe_in
//-----------------------------Status Detection--------------------------------
// Error Detection Interface
.hard_err (hard_err ), // output wire hard_err
.soft_err (soft_err ), // output wire soft_err
.frame_err(frame_err), // output wire frame_err
// Status
.power_down(0), // input wire power_down
.channel_up(channel_up), // output wire channel_up
.lane_up(lane_up), // output wire [0 : 0] lane_up
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
.tx_lock(tx_lock), // output wire tx_lock
.tx_resetdone_out(tx_resetdone_out), // output wire tx_resetdone_out
.rx_resetdone_out(rx_resetdone_out), // output wire rx_resetdone_out
.pll_not_locked_out(pll_not_locked_out), // output wire pll_not_locked_out
//reset_out
.gt_reset_out(gt_reset_out), // output wire gt_reset_out
.link_reset_out(link_reset_out), // output wire link_reset_out
.sys_reset_out(sys_reset_out), // output wire sys_reset_out
//clk_out
.user_clk_out(user_clk_out), // output wire user_clk_out
.sync_clk_out(), // output wire sync_clk_out
.gt_refclk1_out(), // output wire gt_refclk1_out
//qplllock_out
.gt0_qplllock_out(), // output wire gt0_qplllock_out
.gt0_qpllrefclklost_out(), // output wire gt0_qpllrefclklost_out
.gt_qpllclk_quad2_out(), // output wire gt_qpllclk_quad2_out
.gt_qpllrefclk_quad2_out() // output wire gt_qpllrefclk_quad2_out
);
vio_0 Status_dect (
.clk(user_clk_out), // input wire clk
.probe_in0(hard_err), // input wire [0 : 0] probe_in0
.probe_in1(soft_err), // input wire [0 : 0] probe_in1
.probe_in2(frame_err), // input wire [0 : 0] probe_in2
.probe_in3(channel_up), // input wire [0 : 0] probe_in3
.probe_in4(lane_up), // input wire [0 : 0] probe_in4
.probe_out0(loopback), // output wire [2 : 0] probe_out0
.probe_out1(start) // output wire [0 : 0] probe_out1
);
endmodule
下板测试
设置loopback的默认值为0,或者不为0的先设置为0,然后设置为0 ,然后设置模式为近端回环测试,1或2都可。则可以看到channel_up和lane_up都拉高,连接建立。start信号拉高复位,然后置零可去发送接收的窗口观察信号收发是否正确。
发送部分的ILA窗口抓取到数据正常发送,同时满足AXI的协议。
接收部分的ILA窗口抓取到数据正常接收,同时满足AXI的协议,error指示信号常为低,证明数据传输正常,链路稳定。
reference
- PG046
- Aurora例程
- 8B/10B编码基本原理
- 8B/10B编码
- 8B/10B百度百科