千兆以太网_接收模块设计_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接收模块:
验证成功。