我是靠谱客的博主 爱笑美女,最近开发中收集的这篇文章主要介绍以太网(UDP)开源Verilog专题(二),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1809543ce06a42f1747be61e68e98203.jpeg

导言

上一期主要讲解了一些概念性的东西以及工程的恢复,本期主要简单讲一下用到的原语以及仿真FIFO,先仿真FIFO最主要的原因是FIFO很简单,需要频繁的调用。

3顶层以及原语

3.1项目顶层

上一期我们讲解过了工程的恢复,下图是该rgmii项目综合完的顶层原理图(kintex7),顶层涉及到的模块有,差分时钟buffer,输入延迟,时钟分频倍频(时钟管理MMCM,和PLL相比的优势是可以动态调整相位),同步复位异步释放模块(关于这个知识点请参阅“”一文,这里不多赘述),延迟模块(IDELAYE2),延迟控制模块(IDELAYCTRL),项目核心代码以及一些BUFG(全局时钟网络)。这里主要讲解一下延迟以及IDDR/ODDR(该模块调用放在fpga_core内部,因为要兼容quartus和vivado平台,所以单独放在一个文件里面)。

7d45e6ef1f62c68e15aa0926569dcab1.jpeg

原理图顶层

3.2 IDELAYE2以及IDELAYCTRL

前面在谈到RGMII时序时讲到,输入数据和时钟有延迟模式,IDELAYE2就是用来延迟的原语(Primitive,属于底层硬件功能单元,各个厂商都不太一样)。延迟的类型有FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE,默认为FIXED, 即延迟的数值固定,当然也有动态设置的模式,一般常用FIXED模式。值得注意的是REFCLK_FREQUENCY尽管可以设置为190-210MHz,290-310MHz,390-410MHz,但并不是所有的器件速度等级都支持290-310MHz以及390-410MHz,具体参阅自己的芯片手册。其他参数根据需求使用。

1828033c3deb0061411c29943f5c55f4.jpeg

关于IDELAYCTRL,只要例化了IDELAYE2/ODELAYE2,就必须例化IDELAYCTRL,这个模块主要为了校准IDELAYE2/ODELAYE2,注意时钟接口连接入模块必须经由全局时钟buffer,如本项目TOP原理图所示,RDY可不接。

26119003ca608a89cc173f8de158e860.jpeg

下面代码分别为IDELAYE2IDELAYCTRL例化模版(引自Vivado2022.1,Kintex7模版)

//  IDELAYE2   : In order to incorporate this function into the design,
//   Verilog   : the following instance declaration needs to be placed
//  instance   : in the body of the design code.  The instance name
// declaration : (IDELAYE2_inst) and/or the port declarations within the
//    code     : parenthesis may be changed to properly reference and
//             : connect this function to the design.  All inputs
//             : and outputs must be connected.

//  <-----Cut code below this line---->

   // IDELAYE2: Input Fixed or Variable Delay Element
   //           Kintex-7
   // Xilinx HDL Language Template, version 2022.1

   (* IODELAY_GROUP = <iodelay_group_name> *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL

   IDELAYE2 #(
      .CINVCTRL_SEL("FALSE"),          // Enable dynamic clock inversion (FALSE, TRUE)
      .DELAY_SRC("IDATAIN"),           // Delay input (IDATAIN, DATAIN)
      .HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
      .IDELAY_TYPE("FIXED"),           // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
      .IDELAY_VALUE(0),                // Input delay tap setting (0-31)
      .PIPE_SEL("FALSE"),              // Select pipelined mode, FALSE, TRUE
      .REFCLK_FREQUENCY(200.0),        // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
      .SIGNAL_PATTERN("DATA")          // DATA, CLOCK input signal
   )
   IDELAYE2_inst (
      .CNTVALUEOUT(CNTVALUEOUT), // 5-bit output: Counter value output
      .DATAOUT(DATAOUT),         // 1-bit output: Delayed data output
      .C(C),                     // 1-bit input: Clock input
      .CE(CE),                   // 1-bit input: Active high enable increment/decrement input
      .CINVCTRL(CINVCTRL),       // 1-bit input: Dynamic clock inversion input
      .CNTVALUEIN(CNTVALUEIN),   // 5-bit input: Counter value input
      .DATAIN(DATAIN),           // 1-bit input: Internal delay data input
      .IDATAIN(IDATAIN),         // 1-bit input: Data input from the I/O
      .INC(INC),                 // 1-bit input: Increment / Decrement tap delay input
      .LD(LD),                   // 1-bit input: Load IDELAY_VALUE input
      .LDPIPEEN(LDPIPEEN),       // 1-bit input: Enable PIPELINE register to load data input
      .REGRST(REGRST)            // 1-bit input: Active-high reset tap-delay input
   );

   // End of IDELAYE2_inst instantiation
   // IDELAYCTRL: IDELAYE2/ODELAYE2 Tap Delay Value Control
   //             Kintex-7
   // Xilinx HDL Language Template, version 2022.1

   (* IODELAY_GROUP = <iodelay_group_name> *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL

   IDELAYCTRL IDELAYCTRL_inst (
      .RDY(RDY),       // 1-bit output: Ready output
      .REFCLK(REFCLK), // 1-bit input: Reference clock input
      .RST(RST)        // 1-bit input: Active high reset input
   );

   // End of IDELAYCTRL_inst instantiation

3.3 IDDR/ODDR

同样的,前面讲的RGMII的时序,输入/输出是双边沿数据信号(DDR),但对于FPGA内部而言,需要把双边沿转化为单边信号(SDR),而IDDR就是把输入双边信号转化为单边,ODDR则相反,把FPGA内部单沿信号转化为输出的双沿信号。如下方IDDR模版所示,DDR_CLK_EDGE控制时序模式,一共三种"OPPOSITE_EDGE", "SAME_EDGE"以及"SAME_EDGE_PIPELINED",它们决定了时钟C,输出Q1/Q2(单沿)以及输入D(双沿)的时序关系。本项目使用的IDDR时序模式是SAME_EDGE_PIPELINED,即转换后的单沿数据是同时输入FPGA的。ODDR时序模式是SAME_EDGE,即内部数据对齐发送,具体请参阅下文时序图。

7b8696b2885eb23daf8a8004d9ca4683.jpeg

   // IDDR: Input Double Data Rate Input Register with Set, Reset
   //       and Clock Enable.
   //       Kintex-7
   // Xilinx HDL Language Template, version 2022.1

   IDDR #(
      .DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE" 
                                      //    or "SAME_EDGE_PIPELINED" 
      .INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
      .INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
      .SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC" 
   ) IDDR_inst (
      .Q1(Q1), // 1-bit output for positive edge of clock
      .Q2(Q2), // 1-bit output for negative edge of clock
      .C(C),   // 1-bit clock input
      .CE(CE), // 1-bit clock enable input
      .D(D),   // 1-bit DDR data input
      .R(R),   // 1-bit reset
      .S(S)    // 1-bit set
   );

   // End of IDDR_inst instantiation   

「OPPOSITE_EDGE」

55074bcb86d65dfd6cef19715cf592d9.png

「SAME_EDGE」

d4a719466cc223e5f01bb97dc38d803b.png

「SAME_EDGE_PIPELINED」

cf6a40f80bcb93177d0622ede1697abc.png

对于ODDR模版如下所示所示:

   // ODDR: Output Double Data Rate Output Register with Set, Reset
   //       and Clock Enable.
   //       Kintex-7
   // Xilinx HDL Language Template, version 2022.1

   ODDR #(
      .DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" 
      .INIT(1'b0),    // Initial value of Q: 1'b0 or 1'b1
      .SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC" 
   ) ODDR_inst (
      .Q(Q),   // 1-bit DDR output
      .C(C),   // 1-bit clock input
      .CE(CE), // 1-bit clock enable input
      .D1(D1), // 1-bit data input (positive edge)
      .D2(D2), // 1-bit data input (negative edge)
      .R(R),   // 1-bit reset
      .S(S)    // 1-bit set
   );

   // End of ODDR_inst instantiation 

「OPPOSITE_EDGE」

eefa0b2191a28c7452c0bf618905fd64.png

「SAME_EDGE」

562170c002a065cad5ad19d0cb5757a7.png

差分SelectIO原语在设备pads之间有两个引脚,以显示差分对中的P和N通道引脚。N通道引脚有一个B后缀。IBUFDS和IBUFGDS原语相同,当差分输入缓冲区用作时钟输入时,使用IBUFGDS。(这是手册的原话,但这个项目原工程尽管接了时钟也是使用了IBUFDS)

   // IBUFDS: Differential Input Buffer
   //         Kintex-7
   // Xilinx HDL Language Template, version 2022.1

   IBUFDS #(
      .DIFF_TERM("FALSE"),       // Differential Termination
      .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE" 
      .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
   ) IBUFDS_inst (
      .O(O),  // Buffer output
      .I(I),  // Diff_p buffer input (connect directly to top-level port)
      .IB(IB) // Diff_n buffer input (connect directly to top-level port)
   );

   // End of IBUFDS_inst instantiation

4仿真工程

前面简单讲了顶层涉及的一些模块以及相关知识,接着我们讨论逻辑代码内部的模块分层以及对相关模块进行仿真。

我们先分模块仿真,首先传输数据本身,分为「axis接口的fifo」「udp_complete」(包含udp和ip),「eth_axis_rx」「eth_axis_tx」,以及「eth_mac_1g_rgmii_fifo」

4.1AXIS仿真

由于axis同步fifo太简单,我仅仿真异步的axis的fifo,涉及的文件如下,主要实现了AXIS接口的异步FIFO,可以实现输入输出位宽调整,以及可以实现Frame模式。

axis_adapter.v                     : Parametrizable bus width adapter
axis_async_fifo.v                  : Parametrizable asynchronous FIFO
axis_async_fifo_adapter.v          : FIFO/width adapter wrapper

testbech代码参数配置如下:

parameter DEPTH = 4096;// FIFO深度
parameter S_DATA_WIDTH = 32;// S接口位宽
parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8);// KEEP信号使能
parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8);
parameter M_DATA_WIDTH = 8;// M接口位宽
parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8);// KEEP接口使能
parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8);
parameter PIPELINE_OUTPUT = 2;// 输出信号寄存器数
parameter FRAME_FIFO = 0;// 是否开启Frame模式
parameter USER_BAD_FRAME_VALUE = 1'b1;
parameter USER_BAD_FRAME_MASK = 1'b1;
parameter DROP_BAD_FRAME = 0;
parameter DROP_WHEN_FULL = 0;

时钟配置:

always #20 s_clk = ~s_clk;// 25M
always #5 m_clk = ~m_clk;// 100M

简单的数据传输:

integer i;
parameter len = 64;// 传输数据长度为64

initial begin
    wait (s_rst == 0);
    #40;
    for (i = 0; i < len; i=i+1 ) begin
        wait(s_axis_tready == 1);
        s_axis_tvalid <= 1;
        s_axis_tdata <= s_axis_tdata +1;
        #40;
    end
    s_axis_tlast <= 1;// 传输结束拉高s_axis_tlast
    #40;
    s_axis_tlast <= 0; 
    s_axis_tvalid <= 0; 
end

使用modeslsim仿真:

uploading.4e448015.gif正在上传…重新上传取消

25M时钟域输入,100M时钟域采样,输入32bit位宽,输出8bit位宽,仿真和预期一致。

FIFO的FRAME模式

我们把异步FIFO修改为FRAME模式,即一直到一帧数据传输完成(last拉高),FIFO才继续开始输出数据,配置如下:

parameter FRAME_FIFO = 1;// 是否开启Frame模式
parameter USER_BAD_FRAME_VALUE = 1'b1;
parameter USER_BAD_FRAME_MASK = 1'b1;
parameter DROP_BAD_FRAME = 1;
parameter DROP_WHEN_FULL = 1;

使用modeslsim仿真:

14beac458520b0c3038f7e275400e1f2.jpeg

上图可见,直到last拉起前,FIFO才开始传输,仿真波形达到预期。

「总结」:本期主要介绍top层的一些原语知识点,逻辑结构先开个头仿真AXIS接口的异步FIFO,下期我们再分析项目的代码结构并进行仿真。

参考文献

  1. ADI模拟对话53-03(2019年3月期刊)作者:Volker E. Goller

  2. TCP/IP入门经典【美】Joe Casad 著(第5版)

  3. TI,以太网PHY简介

  4. TI,RGMII时序

  5. 赛灵思官网文档:PG292

  6. 赛灵思官网文档:PG210

  7. 赛灵思官网文档:PG211

  8. 米联FPGA 以太网 UDP 通信方案(PL)

  9. 赛灵思官网文档:UG471

 

 

最后

以上就是爱笑美女为你收集整理的以太网(UDP)开源Verilog专题(二)的全部内容,希望文章能够帮你解决以太网(UDP)开源Verilog专题(二)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(69)

评论列表共有 0 条评论

立即
投稿
返回
顶部