前言
本文首先介绍了根据网上博文简单介绍了8B/10B的原理,并根据Aurora IP的相关使用方法,参考IP手册进行设计递增数测试用例,并下板进行实际验证。
8B/10B简介
8B/10B,也叫做8比特/10比特或8b10b。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 表示输入的原始数据的高3位HGF。
8b/10b编码是目前许多高速串行总线采用的编码机制,如 USB3.0、1394b、Serial ATA、PCI Express、Infini-band、Fibre Channel(光纤通道)、RapidIO等总线或网络等。
工作原理
8B/10B编码是目前高速串行通信中经常用到的一种编码方式。将8bit编码成10bit后,10B中0和1的位数只可能出现3种情况:
- 有5个0和5个1
- 有6个0和4个1
- 有4个0和6个1
这样引出了一个新术语“不均等性(Disparity)”,就是1的位数和0的位数的差值,根据上面3种情况就有对应的3个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层选择帧模式,其余默认即可。
米联客的板卡在MGT的116对外引出,所以这里对应连接到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百度百科