4. 高速时钟与SERDES:高速收发器时钟域、参考时钟与恢复时钟的分配
各位同学,今天我们来聊聊SERDES的时钟。说实话,这部分内容在Lattice FPGA设计中,是真正考验功力的地方。我见过不少工程师,逻辑写得飞起,一到SERDES时钟分配就翻车。嗯,我自己也踩过坑,所以今天把这些经验掰开揉碎了讲给你听。
4.1 高速收发器的时钟架构
Lattice的SERDES模块,说白了就是一组高速串并转换器。它内部有两个核心时钟域:PCS层(物理编码子层)和PMA层(物理介质适配层)。
PMA层跑的是高速串行时钟,比如3.125Gbps的线速率,对应的串行时钟就是3.125GHz。PCS层则跑并行时钟,通常是串行时钟的1/8、1/10或1/20。这两个时钟域之间,需要通过FIFO或弹性缓冲来同步。
我个人习惯把SERDES时钟域画成一张图,这样思路清晰。下面是我常用的结构图:
4.2 参考时钟的选择与分配
参考时钟是SERDES的"心脏起搏器"。它决定了PLL能否锁定,也决定了输出时钟的抖动性能。我在项目里见过最惨的案例,就是参考时钟选错了频率,导致整个板子重做。
Lattice FPGA的SERDES参考时钟,通常支持以下频率:
| 线速率 | 参考时钟频率 | PLL倍频系数 | 常用协议 |
|---|---|---|---|
| 1.25 Gbps | 125 MHz | ×10 | 千兆以太网 |
| 2.5 Gbps | 125 MHz | ×20 | XAUI |
| 3.125 Gbps | 156.25 MHz | ×20 | PCIe Gen1 |
| 5.0 Gbps | 100 MHz | ×50 | PCIe Gen2 |
| 6.25 Gbps | 156.25 MHz | ×40 | 10G以太网 |
关键点:参考时钟的抖动必须满足SERDES的规范。Lattice通常要求参考时钟的RMS抖动小于1ps(12kHz-20MHz积分区间)。如果抖动超标,PLL可能无法锁定,或者锁定后误码率飙升。
我曾经在一个项目中,用了板载的普通晶振给SERDES做参考时钟。结果呢?PLL死活锁不住。折腾了两天,最后换成专用的低抖动振荡器,问题秒解。所以,别在参考时钟上省钱,该用专用晶振就用专用晶振。
4.3 恢复时钟的生成与使用
恢复时钟是从接收到的串行数据流中提取出来的。CDR(时钟数据恢复)电路会跟踪数据边沿,生成一个与发送端同频同相的时钟。
恢复时钟有两个重要用途:
- 驱动PCS逻辑:恢复时钟作为PCS层的处理时钟,确保数据采样正确
- 作为参考时钟输出:有些应用需要将恢复时钟输出到FPGA内部,供其他模块使用
但要注意,恢复时钟的抖动通常比参考时钟大。为什么?因为数据在传输过程中会受到通道损耗、串扰等因素影响,导致边沿位置偏移。CDR虽然能跟踪,但无法完全消除这些抖动。
警告:不要直接用恢复时钟驱动FPGA内部的大扇出逻辑。恢复时钟的抖动较大,如果驱动大量寄存器,会导致时序收敛困难。我建议的做法是:用恢复时钟驱动弹性缓冲的读侧,然后用FPGA内部的PLL重新生成干净的时钟供用户逻辑使用。
4.4 时钟分配策略与避坑指南
好了,理论讲完了,咱们来点实战的。下面是我总结的几条时钟分配策略:
- 参考时钟走专用引脚:Lattice FPGA有专用的参考时钟输入引脚,比如REFCLK0/REFCLK1。这些引脚直接连接到SERDES的PLL,延迟最小,抖动最小。别图省事把参考时钟接到普通IO上。
- 使用全局时钟网络:恢复时钟如果需要进入FPGA内部,一定要走全局时钟网络(Global Clock Network)。Lattice的ECP5系列有8条全局时钟线,足够用了。
- 弹性缓冲深度要留余量:两个时钟域之间的频率偏差,会导致弹性缓冲溢出或欠载。我一般把弹性缓冲深度设为最大值的75%,留出25%的余量。
- 注意时钟域交叉:从PCS时钟域到用户逻辑时钟域,必须经过同步器。别直接用寄存器打一拍就完事,要用两级或三级同步器。
个人经验:我曾经在一个项目中,把恢复时钟直接接到了普通逻辑上,结果时序分析报了几百条违规。后来改成全局时钟网络,违规全部消失。所以,时钟分配这件事,真的不能偷懒。
4.5 代码示例:时钟分配与同步
下面是一个简单的Verilog代码示例,展示了如何将恢复时钟同步到用户逻辑时钟域:
// 恢复时钟域到用户时钟域的同步器
module clock_domain_crossing (
input wire rx_clk, // 恢复时钟
input wire user_clk, // 用户逻辑时钟
input wire [7:0] rx_data, // 恢复时钟域的数据
output reg [7:0] user_data // 用户时钟域的数据
);
// 两级同步器
reg [7:0] sync_stage1;
reg [7:0] sync_stage2;
always @(posedge rx_clk) begin
// 在恢复时钟域捕获数据
// 这里假设数据已经与rx_clk对齐
end
always @(posedge user_clk) begin
sync_stage1 <= rx_data; // 第一级同步
sync_stage2 <= sync_stage1; // 第二级同步
user_data <= sync_stage2; // 输出到用户逻辑
end
endmodule
这段代码看起来简单,但实际项目中要注意:同步器只能解决单比特信号的跨时钟域问题。如果是多比特数据总线,建议用FIFO或握手协议来处理。
4.6 总结
高速时钟与SERDES的时钟分配,说白了就是三个字:稳、准、狠。参考时钟要稳,恢复时钟要准,时钟分配策略要狠——该用全局时钟就用,该加同步器就加,别犹豫。
我在Lattice FPGA上做过好几个SERDES项目,从PCIe到千兆以太网,从XAUI到JESD204B。每次遇到时钟问题,我都会回到这张结构图,重新梳理时钟路径。你想想看,时钟是数字系统的脉搏,脉搏乱了,整个系统就乱了。
好了,这一章的内容就到这里。记住我今天讲的这些坑,你以后做SERDES设计时,能少走不少弯路。
公众号:蓝海资料掘金营,微信deep3321