FPGA设计心得(5)Aurora 例子工程分析与仿真实例分析(streaming版)

背景

熬夜写完了上两篇博客:
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 #1

module aurora_8b10b_streaming_FRAME_CHECK
(
    // User Interface
    RX_D, 
    RX_SRC_RDY_N,

    // System Interface
    USER_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 registers

reg   [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 ready
  assign reset_c = RESET;

// SLACK registers

always @ (posedge USER_CLK)
begin
  RX_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 asserted
    assign  data_valid_c    =   !RX_SRC_RDY_N_SLACK;
   

    //generate expected RX_D using LFSR
    always @(posedge USER_CLK)
        if(reset_c)
        begin
            data_lfsr_r          <=  `DLY    16'hD5E6;  //random seed value
        end
        else if(CHANNEL_UP)
        begin
          if(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]};
        end
        else 
        begin
           data_lfsr_r          <=  `DLY    16'hD5E6;  //random seed value
        end 

    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 port
    assign  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 logic
    always @(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)
        begin
          if(&err_count_r)
            err_count_r       <=  `DLY    err_count_r;
          else if(data_err_detected_r)
            err_count_r       <=  `DLY    err_count_r + 1;
        end
	else
        begin	       	
          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 output
    assign  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 asserted
    assign  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 data
    always @(posedge USER_CLK)
        if(reset_c)
        begin
            data_lfsr_r          <=  `DLY    16'hABCD;  //random seed value
            TX_SRC_RDY_N    <=  `DLY    1'b1;   
        end
        else if(!TX_DST_RDY_N)
        begin
            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]};
            TX_SRC_RDY_N    <=  `DLY    1'b0;
        end
   
    //Connect TX_D to the DATA LFSR register
    assign  TX_D    =   {1{data_lfsr_r}};

不需要多言!
下面给出完整发送程序:


`timescale 1 ns / 1 ps
`define DLY #1

module aurora_8b10b_streaming_FRAME_GEN
(
    // User Interface
    TX_D, 
    TX_SRC_RDY_N,
    TX_DST_RDY_N,

    // System Interface
    USER_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)
  begin
    if(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;
    else
      channel_up_cnt <= `DLY 5'd0;
  end

  assign dly_data_xfer = (&channel_up_cnt);

  //Generate RESET signal when Aurora channel is not ready
  assign 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 data
    always @(posedge USER_CLK)
        if(reset_c)
        begin
            data_lfsr_r          <=  `DLY    16'hABCD;  //random seed value
            TX_SRC_RDY_N    <=  `DLY    1'b1;   
        end
        else if(!TX_DST_RDY_N)
        begin
            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]};
            TX_SRC_RDY_N    <=  `DLY    1'b0;
        end
   
    //Connect TX_D to the DATA LFSR register
    assign  TX_D    =   {1{data_lfsr_r}};
   

endmodule

仿真预警:

在这里插入图片描述这边是发送数据。

例子程序仿真文件分析

先给出仿真文件(然后在简单分析仿真文件):


`timescale 1 ns / 1 ps

module 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;

    initial
        begin
            gts_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____________________________

    initial
        reference_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;



    initial
        reference_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;



    initial
        drp_clk_r = 1'b0;


    always 
        #(DRP_CLOCKPERIOD / 2) drp_clk_r = !drp_clk_r;

    initial
        init_clk_p = 1'b0;


    always 
        #(INIT_CLOCKPERIOD / 2) init_clk_p = !init_clk_p;

    assign init_clk_n =  !init_clk_p;
 
    //____________________________Resets____________________________
   
    initial
    begin
        reset_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____________________________

    initial
        reference_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;



    initial
        reference_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;



    initial
        drp_clk_r = 1'b0;


    always 
        #(DRP_CLOCKPERIOD / 2) drp_clk_r = !drp_clk_r;

    initial
        init_clk_p = 1'b0;


    always 
        #(INIT_CLOCKPERIOD / 2) init_clk_p = !init_clk_p;

    assign init_clk_n =  !init_clk_p;
 
    //____________________________Resets____________________________
   
    initial
    begin
        reset_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数据手册

交个朋友

交个朋友,共同进步

  • 20
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
FPGA时序约束与分析是一种用于FPGA设计的重要工具,它可以帮助设计工程师确保他们的设计在时序上满足要求,并且可以在FPGA中正确运行。时序约束是一种指定FPGA设计中信号延迟和时序关系的方法,它可以帮助设计工程师确保FPGA中各个信号的传输时间和顺序满足要求。时序分析则是通过对FPGA设计进行仿真和验证,来确定设计中是否存在时序违例,并且帮助设计工程师优化设计以满足时序要求。 FPGA时序约束与分析pdf完整是一份详细的文档,其中包含了FPGA时序约束和分析的完整指南和技术细节。这份文档通常包括了时序约束的语法和语义规则,以及时序分析的流程和方法。同时,它还可能包含一些实际的案例和示例,帮助设计工程师更好地理解如何应用时序约束和分析来优化他们的FPGA设计。 通过阅读FPGA时序约束与分析pdf完整设计工程师可以了解如何编写时序约束,如何对设计进行时序分析,并且可以学习到一些优化设计的技巧和方法。这将帮助他们更好地掌握FPGA设计中关键的时序问题,提高设计的稳定性和性能。 总之,FPGA时序约束与分析pdf完整是一份对于FPGA设计工程师非常有价值的文档,通过学习和应用其中的内容,设计工程师可以更好地优化他们的FPGA设计,确保设计在时序上满足要求,从而提高设计的可靠性和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李锐博恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值