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时钟域画成一张图,这样思路清晰。下面是我常用的结构图:

SERDES时钟域结构图 外部参考时钟 (100MHz/125MHz) PLL/CDR (频率合成) 串行时钟域 (PMA: 3.125GHz) 并行时钟域 (PCS: 312.5MHz) 弹性缓冲 (时钟域同步) 用户逻辑时钟域 (FPGA Fabric) 恢复时钟 (从数据中提取) 图例: 参考时钟 PLL/CDR 串行时钟 并行时钟 弹性缓冲 用户逻辑

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 时钟分配策略与避坑指南

好了,理论讲完了,咱们来点实战的。下面是我总结的几条时钟分配策略:

  1. 参考时钟走专用引脚:Lattice FPGA有专用的参考时钟输入引脚,比如REFCLK0/REFCLK1。这些引脚直接连接到SERDES的PLL,延迟最小,抖动最小。别图省事把参考时钟接到普通IO上。
  2. 使用全局时钟网络:恢复时钟如果需要进入FPGA内部,一定要走全局时钟网络(Global Clock Network)。Lattice的ECP5系列有8条全局时钟线,足够用了。
  3. 弹性缓冲深度要留余量:两个时钟域之间的频率偏差,会导致弹性缓冲溢出或欠载。我一般把弹性缓冲深度设为最大值的75%,留出25%的余量。
  4. 注意时钟域交叉:从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