一、GMII接口信号定义与时序
GMII,全称Gigabit Media Independent Interface。
说白了,就是MAC层和PHY层之间的“翻译官”。
我刚开始接触千兆以太网时,总觉得这接口名字挺唬人。后来发现,它其实就是一组并行的数据线加上时钟和控制信号。
1.1 信号定义
GMII接口有8位数据总线。发送和接收各占8位。
我个人习惯把信号分成三组来看:
- 发送组:GTX_CLK(125MHz)、TXD[7:0]、TX_EN、TX_ER
- 接收组:RX_CLK(125MHz)、RXD[7:0]、RX_DV、RX_ER
- 管理组:MDC、MDIO
这里有个坑。GTX_CLK和RX_CLK虽然都是125MHz,但来源不同。
GTX_CLK由MAC提供,RX_CLK由PHY恢复出来。
我曾经在项目中遇到过时钟不同步导致数据采错的问题,后来加了异步FIFO才解决。
1.2 时序要求
GMII的时序其实挺宽松的。数据在时钟上升沿采样。
但要注意,TXD和TX_EN必须在GTX_CLK上升沿之前建立好。
关键参数:
- 建立时间(Tsu):至少2ns
- 保持时间(Th):至少2ns
- 时钟周期:8ns(125MHz)
你想想看,8ns的周期里,留给你的裕量其实不多。
特别是当PCB走线长了,或者温度变化了,时序裕量会进一步缩小。
二、RGMII双沿采样机制
RGMII的出现,说白了就是为了省引脚。
GMII需要25根线,而RGMII只需要13根。
怎么做到的?答案就是双沿采样。
2.1 双沿采样原理
RGMII在时钟的上升沿和下降沿都传输数据。
上升沿传低4位,下降沿传高4位。
这样一来,数据线就从8根减到了4根。
嗯,这里要注意。RGMII的时钟频率还是125MHz,但数据速率翻倍到了250Mbps每根线。
所以RGMII对时序的要求比GMII严格得多。
我的经验:
做RGMII设计时,一定要考虑PCB走线等长。我曾经因为数据线和时钟线差了5mm,导致高速模式下频繁丢包。后来用示波器一看,数据采样点已经跑到边沿上了。
2.2 时钟延迟调整
RGMII有个特殊要求:时钟需要相对于数据延迟至少1.5ns。
为什么?因为数据是在时钟边沿变化的,如果时钟和数据对齐,采样点就在数据跳变处,根本采不准。
延迟可以通过两种方式实现:
- PCB走线延迟:时钟线比数据线长一些
- 内部延迟:使用IDELAY或IODELAY原语
我个人更推荐用内部延迟。因为PCB走线延迟受工艺影响大,而且不好调整。
三、IDELAY与IODELAY原语使用
IDELAY和IODELAY是Xilinx FPGA里的专用硬件资源。
它们可以精细调整信号的延迟,精度能达到几十皮秒。
3.1 IDELAY原语
IDELAY用于输入路径的延迟调整。
它的工作原理很简单:把输入信号通过一串抽头延迟线,每个抽头约78ps(具体取决于器件)。
// IDELAYE2原语例化
IDELAYE2 #(
.IDELAY_TYPE("FIXED"), // 固定延迟模式
.DELAY_SRC("IDATAIN"), // 从IOB输入
.IDELAY_VALUE(16), // 延迟值,约1.25ns
.HIGH_PERFORMANCE_MODE("TRUE")
) IDELAYE2_inst (
.DATAOUT(data_delayed), // 延迟后的数据
.DATAIN(1'b0), // 内部数据输入
.C(CLK_200M), // 参考时钟
.CE(1'b0), // 使能
.INC(1'b0), // 递增/递减
.IDATAIN(rgmii_rx_data), // 来自IOB的输入
.LD(1'b0), // 加载
.LDPIPEEN(1'b0), // 流水线使能
.REGRST(1'b0) // 复位
);
我记得第一次用IDELAY时,调了半天发现延迟值不对。
后来才意识到,参考时钟频率会影响延迟精度。200MHz时钟下,每个抽头约78ps;如果换成300MHz,精度会更高。
3.2 IODELAY原语
IODELAY是IDELAY的升级版,同时支持输入和输出延迟。
在7系列及以后的FPGA中,推荐使用IODELAY。
注意事项:
IODELAY的延迟值在配置后就不能动态改了(除非使用VAR_LOAD模式)。
所以,如果你需要在运行时调整延迟,记得选对模式。
四、接口时序约束实战
时序约束,说白了就是告诉工具你的信号什么时候该到。
不做约束,工具就会乱优化,结果就是你的设计在板上跑不起来。
4.1 输入延迟约束
对于RGMII输入,我们需要约束数据相对于时钟的到达时间。
# RGMII输入约束示例
# 时钟周期8ns,数据在时钟边沿变化
# 假设PCB走线延迟0.5ns
create_clock -name rgmii_rx_clk -period 8.0 [get_ports rgmii_rxc]
# 上升沿数据
set_input_delay -clock rgmii_rx_clk -max 0.5 [get_ports rgmii_rxd[*]]
set_input_delay -clock rgmii_rx_clk -min -0.5 [get_ports rgmii_rxd[*]]
# 下降沿数据(需要生成负沿时钟)
create_generated_clock -name rgmii_rx_clk_90 -source [get_ports rgmii_rxc] \
-divide_by 1 -invert [get_pins clk_90_bufg/O]
set_input_delay -clock rgmii_rx_clk_90 -max 0.5 [get_ports rgmii_rxd[*]]
set_input_delay -clock rgmii_rx_clk_90 -min -0.5 [get_ports rgmii_rxd[*]]
这里有个技巧。下降沿的数据约束,我习惯用反向时钟来做。
这样工具能正确分析两个沿的时序。
4.2 输出延迟约束
输出约束相对简单。告诉工具数据从时钟沿到输出的时间。
# RGMII输出约束
set_output_delay -clock rgmii_tx_clk -max 1.0 [get_ports rgmii_txd[*]]
set_output_delay -clock rgmii_tx_clk -min -1.0 [get_ports rgmii_txd[*]]
我曾经在输出约束上吃过亏。约束太松,工具把逻辑推到了IOB外面,结果时序不满足。
后来我学乖了,输出延迟一般设到时钟周期的10%-20%。
4.3 跨时钟域处理
GMII/RGMII设计中,最头疼的就是跨时钟域。
RX_CLK和系统时钟是异步的,数据过来必须做同步处理。
推荐做法:
- 用IDELAY把输入数据延迟到最佳采样点
- 用双触发器同步控制信号(RX_DV)
- 数据信号用异步FIFO跨时钟域
- 最后用系统时钟读出数据
嗯,这里要注意。异步FIFO的深度至少要有4,防止读写指针碰撞。
五、知识体系总览
下面这张图,是我梳理的本章知识结构。
你可以把它当作一个检查清单,看看自己掌握了哪些。
避坑指南:
我曾经在调试RGMII时,发现数据总是偶尔出错。查了三天,最后发现是IDELAY的参考时钟没接对。
IDELAY的参考时钟必须是一个稳定的高频时钟(至少200MHz),不能用125MHz的以太网时钟。
否则延迟精度不够,采样点会飘。
好了,这一章的内容就到这里。
GMII和RGMII的接口信号、时序要求、延迟调整和约束方法,我都结合自己的项目经验讲了一遍。
你如果在实际开发中遇到问题,可以回头看看这张知识体系图,定位一下是哪个环节出了问题。
公众号:蓝海资料掘金营,微信deep3321