千兆以太网_接收模块设计_udp_rgmii_rx

一.功能:从rgmii接口(物理芯片)接收数据(4位),并通过gmii转rgmii接口(fpga)把数据转化为8位,验证udp帧的正确性,并把用户数据提取出来(去掉帧头帧尾)存储。

二.接收过程(gmii):

①分段判断:

Ⅰ.前导码

Ⅱ.mac帧头:mac地址以太网类型(IP还是arp);

Ⅲ.ip帧头:IP地址上层协议类型(udp还是TCP);

以上要素都正确则接收帧,有一个出错则舍弃帧。

②技巧:

Ⅰ.存下ip首部长度,用以判断ip帧头是否接受完(因为ip帧头长度不固定,有可填充字段);

Ⅱ.存下udp帧长度,用以确定用户数据的长度。(用户数据长度 = udp帧长度 - udp帧头长度);

Ⅲ.依旧是用计数器来接收数据,每个时钟上升沿到来接收一次数据,(因此接收数据的速率是固定的)。

三.实现(gmii):三段式状态机3个always块)

根据udp帧数据的分段定义出几个状态

定义接收正确和接收错误的跳变信号

定义两个状态寄存器

第一个always块:用时序逻辑时刻更新状态寄存器;

 第二个always块:用组合逻辑根据跳变信号判断是否要实现状态跳变:

 

 第三个always块:用时序逻辑根据当前接收的数据设定跳变信号的值:

 

 四.状态描述(gmii)

如图:根据udp段分出以上状态。要用状态机实现接收模块时,应该先画出以上状态转移图,然后再根据改图写代码。

本接收模块没有设置udp校验,ip报头校验和mac帧crc32校验。

如果想要校验的话,得把每个接收到的数据传入crc32计算模块进行计算,得到计算结果与接收的crc32值比对。这个过程得用一个存储器把接收的用户数据先存储起来,等校验正确再输出,校验错误则舍弃。

五.系统框图

 

根据上述状态机的介绍设计完udpg_gmii_rx模块之后,接下来设计gmii转rgmii模块,此模块是难点。

 六.GMII转RGMII接收模块

RGMII接口的数据位宽为4,想要在125MHz的时钟频率下实现千兆的速率,就必须在时钟上升沿和下降沿都采样。

在Xilinx中, 想要实现GMII转RGMII接收接口转换,需要调用原语。在上节设计发送模块中已经介绍了输出寄存器ODDR的双沿采样原理。

在设计接收模块时,为了在时钟变化沿的数据是稳定的,我们需要对输入的数据进行一定的延时,如下:

想要实现信号延迟,有两种方法:

①针对时钟信号,用锁相环产生相移,得到另外一个延迟的时钟信号

②对于一般的信号,调用Xilinx原语:IDELAY2(或者IDELAY3等其他原语...根据不同芯片来选择)

对于Artix-7系列的芯片,可以选用IDELAY2原语来实现延时,一旦调用IDELAY2,就需要同时调用IDELAYCTRL来保证延时的精度,此外,需要调用BUFG和BUFIO原语来产生极小脉动的时钟,一个用于全局时钟,一个仅用于IO口(接入IDDR)。

rgmii模块框图:

原语代码解释:

//全局时钟缓存
BUFG BUFG_inst (
  .I            (rgmii_rx_clk),     // 1-bit input: Clock input
  .O            (rgmii_rx_bufg) // 1-bit output: Clock output
);
//全局时钟IO缓存
BUFIO BUFIO_inst (
  .I            (rgmii_rx_clk),      // 1-bit input: Clock input
  .O            (rgmii_rx_bufio) // 1-bit output: Clock output
);

直接输入时钟,再定义一个信号输出即可。输出的时钟相比输入的时钟脉动小,而频率相位等参数不变。

 (* IODELAY_GROUP = "rgmii" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
 //复制原语得到的<iodelay_group_name>这个是组名,给分组,使用时<>要修改成以上“”形式.输出不用连接 
  IDELAYCTRL IDELAYCTRL_inst (
      .RDY(),       // 1-bit output: Ready output输出悬空
      .REFCLK(refer_clk), // 1-bit input: Reference clock input/参考时钟,用锁相环mmcm来产生200MHz时钟
      .RST(0)        // 1-bit input: Active high reset input复位信号
   );

这里注意参考时钟频率。

 (* IODELAY_GROUP = "rgmii" *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
    // 延时模块
   IDELAYE2 #(//以下参数在ug471说明书中可以找到解释
      .CINVCTRL_SEL("FALSE"),          // (FALSE, TRUE),动态切换输入时钟的极性。
      .DELAY_SRC("IDATAIN"),           // (IDATAIN(IBUF), DATAIN(FPGA内部逻辑))输入延迟数据来源
      .HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")减少抖动。减少功耗
      .IDELAY_TYPE("FIXED"),           // FIXED固定延迟值, VARIABLE动态加载tap值, VAR_LOAD, VAR_LOAD_PIPE 
      .IDELAY_VALUE(5'd0),             //(0-31)tap 要延迟的时长 = (0-31)x REFCLK_FREQUENCY对应的单位值
      .PIPE_SEL("FALSE"),              // (FALSE, TRUE)只有当IDELAY_TYPE选VAR_LOAD_PIPE 时这里才写true
      .REFCLK_FREQUENCY(200.0),        //  (190.0-210.0, 290.0-310.0) MHz.参考时钟频率,确定每个tap的延时精度。200MHz对应1tap=78ps
      .SIGNAL_PATTERN("DATA")          // (DATA,CLOCK)要延时的是数据还是时钟
   )
   IDELAYE2_rx_en (
      .CNTVALUEOUT(), // 5-bit output: tap值的监控输出
      .DATAOUT(rgmii_rx_en_delay),         // 1-bit output: Delayed data output
      .C(1'b0),                     // 1-bit input: Clock input,used in VARIABLE, VAR_LOAD, or VAR_LOAD_PIPE mode
      .CE(1'b0),                   // 1-bit input: Active high enable increment/decrement input
      .CINVCTRL(1'b0),       // 1-bit input: Dynamic clock inversion input动态转换时钟极性
      .CNTVALUEIN(5'b0),   // 5-bit input: Counter value input 动态加载tap值
      .DATAIN(1'b0),           // 1-bit input: Internal delay data input
      .IDATAIN(rgmii_rx_en),         // 1-bit input: Data input from the I/O
      .INC(1'b0),                 // 1-bit input: Increment / Decrement tap delay input
      .LD(1'b0),                   // 1-bit input: Load IDELAY_VALUE input
      .LDPIPEEN(1'b0),       // 1-bit input: Enable PIPELINE register to load data input
      .REGRST(1'b0)    

①对于延时模块,我们一般固定延时的值,即选择使用FIXED模式,很多输入和参数置0即可。参数的设置见代码后面的解释。

②如果有其他需求,需要延时值动态变化,则选择其他模式,这时候就要注意很多个输入和参数的设置,因为tap值与这些有关:

 

genvar i;//学习语法:genver产生一个变量i,generate for 和endgenerate为一个模块,
generate for ( i = 0 ; i < 4 ;i = i + 1)
    begin//generate模块是偷懒写法,
      IDDR #(
      .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" 
                                      //    or "SAME_EDGE_PIPELINED" 双沿采样的模式,输出的时间不同,见ug471
      .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" 复位同步还是'async异步'
   ) IDDR_rx_data (
      .Q1(gmii_rx_data[i]), // 1-bit output for positive edge of clock 
      .Q2(gmii_rx_data[i+4]), // 1-bit output for negative edge of clock
      .C(rgmii_rx_bufio),   // 1-bit clock input/时钟输入
      .CE(1'b1), // 1-bit clock enable input/输入时钟使能,置1就行
      .D(rgmii_rx_data_delay[i]),   // 1-bit DDR data input   /这里要连接延迟后的数据
      .R(1'b0),   // 1-bit reset   /复位:置0就行
      .S(1'b0)    // 1-bit set     /复位:置0就行
   );
   end
endgenerate

双沿采样代码类似于ODDR,较为简单,SAME_EDGE_PIPELINED模式采样过程如下:

 

 其他模式见说明书。

 采样时钟的频率跟数据输入的频率是有要求的,要满足上面的形式才可以。

经过这次设计,学会两种方法来延迟信号,在调试IDDR过程中用好信号的延迟便可采样成功。

七.仿真结果:

①gmii转rgmii接收模块:

 

②gmii接收模块:

 

 验证成功。

posted @ 2022-08-27 16:15  little_breeze  阅读(746)  评论(0编辑  收藏  举报