FPGA设计心得(5)Aurora 例子工程分析与仿真实例分析(streaming版)
文章目錄
- 背景
- 例子工程預覽
- 例子程序用戶模塊邏輯分析
-
- 收(CHECK)
- 發(GEN)
- 例子程序仿真文件分析
- 寫在最后
- 工程分享
- 參考資料
- 交個朋友
背景
熬夜寫完了上兩篇博客:
Aurora IP core 的理論學習記錄
Aurora IP core 的定制詳情記錄
到這一篇應該就是分析例子程序了,最重要地還是通過仿真來認識Aurora通信。
Aurora IP核的定制,基本都是默認的,為了簡單起見,GT Selection中選擇了一個通道(lane)。
文章末尾會分享工程文件!
例子工程預覽
由于本IP核定制選擇了:
因此,程序加入了一些debug的IP核例化。
如下:
而對于我們要仿真而言,這些都是沒有必要的。
對于我們用戶應用來說,最最重要的模塊為:
一個check,一個gen。
gen代表發數據的模塊,而check則為收數據的模塊。
如數據手冊:
而如何對二者進行仿真呢?
形成一個回環,示意圖如下:
一方發,另一方收,反之亦然!
而testbench的作用就是將二者聯系起來:
下圖清晰說明:
例子程序中帶有仿真文件:
可見,例化了兩次例子程序,就是為了將一方的tx送給另一方的rx,同時,另一方的tx送入一方的rx,形成一個閉環。
例子程序用戶模塊邏輯分析
收(CHECK)
我們知道收模塊與aurora IP streaming用戶接口之間的關系是:
因此,用戶接口很簡單,就兩個信號進來就好,m_axi_rx_data以及m_axi_rx_valid即可,valid有效,則data數據為有效數據,至于收到的數據做什么處理,隨你!例子程序的處理,我也不想深究,收過來按照自己的需求搞就完事了。
給出源碼:
`timescale 1 ns / 1 ps
`define DLY #1module aurora_8b10b_streaming_FRAME_CHECK
(// User InterfaceRX_D, RX_SRC_RDY_N,// System InterfaceUSER_CLK, RESET,CHANNEL_UP,ERR_COUNT
);//***********************************Port Declarations*******************************// User Interface
input [0:15] RX_D;
input RX_SRC_RDY_N;// System Interface
input USER_CLK;
input RESET;
input CHANNEL_UP;output [0:7] ERR_COUNT;
//***************************Internal Register Declarations***************************
// Slack registersreg [0:15] RX_D_SLACK;
reg RX_SRC_RDY_N_SLACK;reg [0:8] err_count_r = 9'd0;// RX Data registers
reg [0:15] data_lfsr_r;//*********************************Wire Declarations**********************************wire reset_c;
wire [0:15] data_lfsr_concat_w;
wire data_valid_c;wire data_err_detected_c;
reg data_err_detected_r;//*********************************Main Body of Code**********************************//Generate RESET signal when Aurora channel is not readyassign reset_c = RESET;// SLACK registersalways @ (posedge USER_CLK)
beginRX_D_SLACK <= `DLY RX_D;RX_SRC_RDY_N_SLACK <= `DLY RX_SRC_RDY_N;
end//______________________________ Capture incoming data ___________________________ //Data is valid when RX_SRC_RDY_N is assertedassign data_valid_c = !RX_SRC_RDY_N_SLACK;//generate expected RX_D using LFSRalways @(posedge USER_CLK)if(reset_c)begindata_lfsr_r <= `DLY 16'hD5E6; //random seed valueendelse if(CHANNEL_UP)beginif(data_valid_c)data_lfsr_r <= `DLY {!{data_lfsr_r[3]^data_lfsr_r[12]^data_lfsr_r[14]^data_lfsr_r[15]},data_lfsr_r[0:14]};endelse begindata_lfsr_r <= `DLY 16'hD5E6; //random seed valueend assign data_lfsr_concat_w = {1{data_lfsr_r}};//___________________________ Check incoming data for errors __________________________//An error is detected when LFSR generated RX data from the data_lfsr_concat_w register,//does not match valid data from the RX_D portassign data_err_detected_c = (data_valid_c && (RX_D_SLACK != data_lfsr_concat_w));//We register the data_err_detected_c signal for use with the error counter logicalways @(posedge USER_CLK)data_err_detected_r <= `DLY data_err_detected_c; //Compare the incoming data with calculated expected data.//Increment the ERROR COUNTER if mismatch occurs.//Stop the ERROR COUNTER once it reaches its max value (i.e. 255)always @(posedge USER_CLK)if(CHANNEL_UP)beginif(&err_count_r)err_count_r <= `DLY err_count_r;else if(data_err_detected_r)err_count_r <= `DLY err_count_r + 1;endelsebegin err_count_r <= `DLY 9'd0;end //Here we connect the lower 8 bits of the count (the MSbit is used only to check when the counter reaches//max value) to the module outputassign ERR_COUNT = err_count_r[1:8];endmodule
仿真預告:
這便是收到的數據。
對了,程序中的這個輸入變量:
RX_SRC_RDY_N
就是valid的反而已:
//______________________________ Capture incoming data ___________________________ //Data is valid when RX_SRC_RDY_N is assertedassign data_valid_c = !RX_SRC_RDY_N_SLACK;
其他的不言而喻!
發(GEN)
發要比收需要的信號多一個,那就是ready信號,具體為:
// User Interface
output [0:15] TX_D;
output TX_SRC_RDY_N;
input TX_DST_RDY_N;
根據streaming格式的關系:
我們用戶邏輯需要得到一個ready有效信號,然后置位valid的同時,發送有效數據data。
例子發送程序根據lsfr產生隨機數據發送:
//______________________________ Transmit Data __________________________________ //Transmit data when TX_DST_RDY_N is asserted.//Random data is generated using XNOR feedback LFSR//TX_SRC_RDY_N is asserted on every cycle with dataalways @(posedge USER_CLK)if(reset_c)begindata_lfsr_r <= `DLY 16'hABCD; //random seed valueTX_SRC_RDY_N <= `DLY 1'b1; endelse if(!TX_DST_RDY_N)begindata_lfsr_r <= `DLY {!{data_lfsr_r[3]^data_lfsr_r[12]^data_lfsr_r[14]^data_lfsr_r[15]},data_lfsr_r[0:14]};TX_SRC_RDY_N <= `DLY 1'b0;end//Connect TX_D to the DATA LFSR registerassign TX_D = {1{data_lfsr_r}};
不需要多言!
下面給出完整發送程序:
`timescale 1 ns / 1 ps
`define DLY #1module aurora_8b10b_streaming_FRAME_GEN
(// User InterfaceTX_D, TX_SRC_RDY_N,TX_DST_RDY_N,// System InterfaceUSER_CLK, RESET,CHANNEL_UP
);
//*****************************Parameter Declarations****************************//***********************************Port Declarations*******************************// User Interface
output [0:15] TX_D;
output TX_SRC_RDY_N;
input TX_DST_RDY_N;// System Interface
input USER_CLK;
input RESET;
input CHANNEL_UP;//***************************External Register Declarations***************************reg TX_SRC_RDY_N;//***************************Internal Register Declarations***************************reg [0:15] data_lfsr_r; wire reset_c;wire dly_data_xfer;reg [4:0] channel_up_cnt;//*********************************Main Body of Code**********************************always @ (posedge USER_CLK)beginif(RESET)channel_up_cnt <= `DLY 5'd0;else if(CHANNEL_UP)if(&channel_up_cnt)channel_up_cnt <= `DLY channel_up_cnt;else channel_up_cnt <= `DLY channel_up_cnt + 1'b1;elsechannel_up_cnt <= `DLY 5'd0;endassign dly_data_xfer = (&channel_up_cnt);//Generate RESET signal when Aurora channel is not readyassign reset_c = RESET || !dly_data_xfer;//______________________________ Transmit Data __________________________________ //Transmit data when TX_DST_RDY_N is asserted.//Random data is generated using XNOR feedback LFSR//TX_SRC_RDY_N is asserted on every cycle with dataalways @(posedge USER_CLK)if(reset_c)begindata_lfsr_r <= `DLY 16'hABCD; //random seed valueTX_SRC_RDY_N <= `DLY 1'b1; endelse if(!TX_DST_RDY_N)begindata_lfsr_r <= `DLY {!{data_lfsr_r[3]^data_lfsr_r[12]^data_lfsr_r[14]^data_lfsr_r[15]},data_lfsr_r[0:14]};TX_SRC_RDY_N <= `DLY 1'b0;end//Connect TX_D to the DATA LFSR registerassign TX_D = {1{data_lfsr_r}};endmodule
仿真預警:
這邊是發送數據。
例子程序仿真文件分析
先給出仿真文件(然后在簡單分析仿真文件):
`timescale 1 ns / 1 psmodule aurora_8b10b_streaming_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_streaming_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_streaming_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)
);
分為幾個部分,
- 端口聲明:
一般而言,輸入聲明為reg類型,輸出為wire。
例如:
//GT Serial I/O
wire rxp_1_i;
wire rxn_1_i; wire txp_1_i;
wire txn_1_i;
但也不盡然如此,例如輸入的差分時鐘,我們就可以將其中一個聲明為reg,至于差分的另一半,聲明為wire,之后通過取反操作來實現:
//Freerunning Clock
reg reference_clk_1_n_r;
reg reference_clk_2_n_r;
reg drp_clk_r;
reg init_clk_p;//********************************Wire Declarations**********************************//Freerunning Clock
wire reference_clk_1_p_r;
wire reference_clk_2_p_r; wire init_clk_n;
assign reference_clk_1_p_r = !reference_clk_1_n_r;
assign reference_clk_2_p_r = !reference_clk_2_n_r;
assign init_clk_n = !init_clk_p;
- 產生時鐘
時鐘較多,差分時鐘產生方法是先生成p時鐘或者n時鐘,之后取反得到另一方:
//____________________________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
- 例化待測試模塊
例化兩次aurora例子程序,作為通信的雙方:
//________________________Instantiate Dut 1 ________________aurora_8b10b_streaming_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_streaming_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)
);
雙方如何形成一個通路的呢?
1發接到2的收,2發接到1的收:
//_________________________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;
由于數據是自己的gen模塊以及產生的,故在設計文件中設計即可。
下面仿真實踐,看看仿真圖吧!
先宏觀地看第一個仿真圖:
可見,一方(簡稱partner1)和另一方(簡稱partner2)串行數據完全一致,理所當然如此,因為二者是直接相連的回環。
當然,這種串行數據我們是看不懂的,我們要看的是用戶邏輯,模塊check以及gen的數據,繼續把二者拉出來仿真:
從partner1的gen中提取出valid,data,ready信號;
再從partner2的check中提取出valid和data信號。
二者構成一個通路。
上圖中,TX_D就是s_axi_tx_data,而TX_DST_RDY_N取反就是s_axi_tx_tready,同理,TX_SRC_RDY_N取反是s_axi_tx_valid,故而,當TX_SRC_RDY_N和TX_DST_RDY_N都有效的時候,代表發送的數據TX_D為有效數據。
這一點從代碼的端口定義可見等價關系:
同理,接收情況也是如此!
按照streaming用戶接口的時序圖:
當valid有效的時候,接收的數據才有效,因此,我們查看接收數據時刻應該在:
我們通過放大,來看看,這兩個時刻上的數據是否一致?
可見,都是d5e6,而且后續數據也一致,可見,發送與接收的通道是沒有問題的,至少從仿真上看是沒有問題的。(這里提醒一句,這里對齊的時鐘肯定是用戶時鐘,這里沒有拉出來。)
從s_axi_tx_valid到s_axi_rx_valid有效的延遲時間,大家也可以算下,延遲了多久?(大概40多個user_clk 時鐘周期了)。
寫在最后
熬了個夜,學習了下aurora的理論以及到現在仿真了下streaming數據格式的aurora例子程序,由于疫情原因,還沒有能到學校,因此上板是不可能了,也許再也不可能了,只能等到公司了。
下面會給出本例子程序的整個工程。
工程分享
提取碼:03ii
aurora streaming工程例子工程分享
參考資料
Aurora IP核例子程序
Aurora數據手冊
交個朋友
交個朋友,共同進步
總結
以上是生活随笔為你收集整理的FPGA设计心得(5)Aurora 例子工程分析与仿真实例分析(streaming版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mapreduce介绍_MapReduc
- 下一篇: 从数百万个光纤(而不是数千个线程)中查询