前言
本文首先介紹了根據(jù)網(wǎng)上博文簡單介紹了8B/10B的原理,并根據(jù)Aurora IP的相關(guān)使用方法,參考IP手冊進行設(shè)計遞增數(shù)測試用例,并下板進行實際驗證。
8B/10B簡介
8B/10B,也叫做8比特/10比特或8b10b。8b/10b方式最初由IBM公司于1983年發(fā)明并應(yīng)用于ESCON(200M互連系統(tǒng)),由Al Widmer和Peter Franaszek在IBM的刊物“研究與開發(fā)”上描述。
8b/10b編碼的特性之一是保證DC 平衡,采用8b/10b編碼方式,可使得發(fā)送的“0”、“1”數(shù)量保持基本一致,連續(xù)的“1”或“0”不超過5位,即每5個連續(xù)的“1”或“0”后必須插入一位“0”或“1”,從而保證信號DC平衡,它就是說,在鏈路超時時不致發(fā)生DC失調(diào)。通過8b/10b編碼,可以保證傳輸?shù)臄?shù)據(jù)串在接收端能夠被正確復(fù)原,除此之外,利用一些特殊的代碼( 在PCI-Express總線中為K碼) ,可以幫助接收端進行還原的工作,并且可以在早期發(fā)現(xiàn)數(shù)據(jù)位的傳輸錯誤,抑制錯誤繼續(xù)發(fā)生。
8b/10b編碼是將一組連續(xù)的8位數(shù)據(jù)分解成兩組數(shù)據(jù),一組3位,一組5位,經(jīng)過編碼后分別成為一組4位的代碼和一組6位的代碼,從而組成一組10位的數(shù)據(jù)發(fā)送出去。相反,解碼是將1組10位的輸入數(shù)據(jù)經(jīng)過變換得到8位數(shù)據(jù)位。數(shù)據(jù)值可以統(tǒng)一的表示為DX.Y或KX.Y,其中D表示為數(shù)據(jù)代碼,K表示為特殊的命令代碼,X表示輸入的原始數(shù)據(jù)的低5位EDCBA,Y 表示輸入的原始數(shù)據(jù)的高3位HGF。
8b/10b編碼是目前許多高速串行總線采用的編碼機制,如 USB3.0、1394b、Serial ATA、PCI Express、Infini-band、Fibre Channel(光纖通道)、RapidIO等總線或網(wǎng)絡(luò)等。
工作原理
8B/10B編碼是目前高速串行通信中經(jīng)常用到的一種編碼方式。將8bit編碼成10bit后,10B中0和1的位數(shù)只可能出現(xiàn)3種情況:
- 有5個0和5個1
- 有6個0和4個1
- 有4個0和6個1
這樣引出了一個新術(shù)語“不均等性(Disparity)”,就是1的位數(shù)和0的位數(shù)的差值,根據(jù)上面3種情況就有對應(yīng)的3個Disparity 0、-2、+2。8bit原始數(shù)據(jù)會分成兩部分,其低5位會進行5B/6B編碼,高3位則進行3B/4B編碼,這兩種映射關(guān)系在當時已經(jīng)成為了一個標準化的表格。人們喜歡把8bit數(shù)據(jù)表示成Dx.y的形式,其x=5LSB(least significant bit最低有效位),y=3MSB(most significant bit最高有效位)。
例如一個8bit數(shù)據(jù)101 10101,x=10101(21) y="101"(5),現(xiàn)在我們就把這8bit數(shù)據(jù)寫成D21.5。
對于8bit數(shù)據(jù),它在表中的位序為HGFEDCBA,即H為最高位,A為最低位,EDCBA經(jīng)過5B/6B編碼為abcdei,HGF經(jīng)過3B/4B編碼為fghj。傳送10bit編碼的順序為abcdeifghj。
創(chuàng)建工程
新建一個空白工程,這里我選擇的芯片型號為xc7k160tffg676-2,米聯(lián)客MK7160FA。
配置Aurora IP
創(chuàng)建完工程,將Aurora IP添加到工程下,并修改相關(guān)配置。lane寬度設(shè)置為4,接口則生成32位寬的數(shù)據(jù),若選擇2,則生成16位寬的數(shù)據(jù)。
由于只是進行傳輸測試,所以該界面的相關(guān)參數(shù)就根據(jù)板卡的相關(guān)時鐘進行配置即可,Link層選擇幀模式,其余默認即可。
米聯(lián)客的板卡在MGT的116對外引出,所以這里對應(yīng)連接到GTXQ1上。
共享邏輯選擇包含在內(nèi)核中,并使能初始化時鐘的單端口時鐘選項,Aurora IP配置完成。
程序架構(gòu)設(shè)計
參考例程的思路,這里設(shè)計了Aurora發(fā)送遞增數(shù)的測試示例,主要包含四個模塊,復(fù)位模塊,Aurora發(fā)送數(shù)據(jù)生成模塊,Aurora接收數(shù)據(jù)模塊,Aurora IP。
- 復(fù)位模塊主要用于對reset和gt_reset兩個信號進行初始化復(fù)位,確保Aurora IP能正常工作。
- Aurora發(fā)送數(shù)據(jù)生成模塊,根據(jù)AXI總線協(xié)議進行生成遞增測試數(shù)據(jù),然后通過Aurora IP將數(shù)據(jù)通過光口傳輸。
- Aurora接收數(shù)據(jù)模塊,根據(jù)AXI總線協(xié)議接收數(shù)據(jù),并通過接收計數(shù)信號進行比對遞增數(shù)傳輸是否存在錯誤,用于錯誤指示。
復(fù)位模塊
復(fù)位模塊使用start_en作為復(fù)位的指示信號,當檢測到start_en的上升沿時,模塊對系統(tǒng)復(fù)位信號和收發(fā)器的復(fù)位信號進行復(fù)位。reset和gt_reset根據(jù)手冊中推薦的復(fù)位時序進行復(fù)位。在該文中有詳細介紹,Aurora8B10B IP使用 -02- IP功能設(shè)計技巧。
`timescale 1ns / 1ps
module rst_ctrl(
input wire clk ,
input wire start_en ,
output wire reset ,// 系統(tǒng)復(fù)位
output wire gt_reset // 收發(fā)器復(fù)位
);
//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;//復(fù)位忙指示信號
reg [1:0] start_flag_dly =0;//復(fù)位使能信號延時
reg [10:0] cnt_rst =0;//用于產(chǎn)生復(fù)位信號的計數(shù)器
//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
//復(fù)位忙指示信號
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
//復(fù)位計數(shù)器
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
發(fā)送模塊
發(fā)送部分就是簡單的模擬AXI總線協(xié)議進行發(fā)送,這里指定了幾個參數(shù),方便后期移植修改,同時又例化了一個ILA IP用于檢測上板時數(shù)據(jù)正確性。
`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總線協(xié)議接收數(shù)據(jù),這里也指定了幾個參數(shù),方便后期移植修改,同時又例化了一個ILA IP用于檢測上板時數(shù)據(jù)正確性。
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
頂層連接
根據(jù)前面繪制的框圖連接上述各個模塊,例化了一個VIO IP進行檢測和控制復(fù)位。
`timescale 1ns / 1ps
module Aurora_Top(
//clock---------------------------------
input clk_in, //系統(tǒng)鐘
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
下板測試
設(shè)置loopback的默認值為0,或者不為0的先設(shè)置為0,然后設(shè)置為0 ,然后設(shè)置模式為近端回環(huán)測試,1或2都可。則可以看到channel_up和lane_up都拉高,連接建立。start信號拉高復(fù)位,然后置零可去發(fā)送接收的窗口觀察信號收發(fā)是否正確。
發(fā)送部分的ILA窗口抓取到數(shù)據(jù)正常發(fā)送,同時滿足AXI的協(xié)議。
接收部分的ILA窗口抓取到數(shù)據(jù)正常接收,同時滿足AXI的協(xié)議,error指示信號常為低,證明數(shù)據(jù)傳輸正常,鏈路穩(wěn)定。
reference
- PG046
- Aurora例程
- 8B/10B編碼基本原理
- 8B/10B編碼
- 8B/10B百度百科