串行器与解串器设计:Serializer架构、Deserializer架构、时钟恢复(CDR)原理
好,咱们今天聊聊SerDes的核心——串行器和解串器。说白了,就是把一堆并行数据压缩成一根线传过去,再在接收端还原回来。听起来简单吧?但实际做起来,坑可不少。
我刚开始接触SerDes时,总觉得这玩意儿不就是个并串转换嘛,有什么难的?直到第一次调试时,眼图乱成一团麻,我才意识到——嗯,这里面的门道深着呢。
一、Serializer架构:把宽数据变窄
Serializer的任务很明确:把N位的并行数据,变成1位的串行数据流。但怎么变,就有讲究了。
常见的架构有两种:
- 基于移位寄存器的Serializer:最直接的方式。用N个触发器组成移位链,并行加载后,按时钟逐位移出。
- 基于树形MUX的Serializer:用多级2:1选择器,逐级压缩数据宽度。比如16:1的Serializer,需要4级2:1 MUX。
我个人习惯用树形MUX架构。为什么?因为移位寄存器在高频下容易出问题——时钟偏斜、建立时间紧张,你想想看,几百兆甚至几G的速率下,一个触发器的延迟偏差就可能让数据错位。
关键设计要点:
- 并行时钟与串行时钟的关系:串行时钟 = 并行时钟 × N(N为串行化倍数)
- 相位对齐:并行数据加载时刻必须与串行时钟边沿对齐
- 占空比失真:高速时钟的占空比必须控制在45%~55%以内
我在项目中遇到过一个问题:16:1的Serializer,速率跑到10Gbps时,输出眼图总是闭合的。查了半天,发现是时钟树上的一个缓冲器驱动能力不够,导致时钟边沿变缓。换了个大驱动力的缓冲器,问题就解决了。所以啊,Serializer设计时,时钟质量是第一位的。
下面是一个简单的8:1 Serializer的Verilog代码示例:
module serializer_8to1 (
input wire clk_ser, // 串行时钟,8倍频
input wire clk_par, // 并行时钟
input wire [7:0] data_par, // 并行数据
output reg data_ser // 串行数据输出
);
reg [7:0] shift_reg;
reg [2:0] bit_cnt;
always @(posedge clk_par) begin
shift_reg <= data_par; // 并行加载
end
always @(posedge clk_ser) begin
data_ser <= shift_reg[7]; // 输出最高位
shift_reg <= {shift_reg[6:0], 1'b0}; // 左移
end
endmodule
注意看,这里用了两个时钟域。并行时钟负责加载,串行时钟负责移位。两个时钟必须同源,否则会出现亚稳态。我曾经吃过这个亏——用了两个独立的PLL,结果数据时不时就跳一个bit,查了三天才找到原因。
二、Deserializer架构:把窄数据变宽
Deserializer是Serializer的逆过程。但有个关键区别:接收端不知道数据的起始位置。所以Deserializer必须配合CDR(时钟恢复)一起工作。
常见的Deserializer架构:
- 基于移位寄存器的Deserializer:串行数据逐位移入,达到N位后并行输出。
- 基于1:N分接器的Deserializer:用多级1:2分接器,逐级扩展数据宽度。
我建议用分接器架构。为什么?因为移位寄存器需要N个时钟周期才能输出一次数据,延迟太大。而分接器架构可以用流水线方式,每级只处理一半的数据,延迟小得多。
设计小技巧:
Deserializer的并行输出需要与恢复时钟同步。我一般会在输出端加一级FIFO,用来跨时钟域同步。这样即使CDR有抖动,也不会影响后端逻辑。
下面是一个4:1 Deserializer的示例:
module deserializer_4to1 (
input wire clk_rec, // 恢复时钟
input wire data_ser, // 串行数据
output reg [3:0] data_par // 并行数据
);
reg [3:0] shift_reg;
always @(posedge clk_rec) begin
shift_reg <= {shift_reg[2:0], data_ser}; // 右移
end
always @(negedge clk_rec) begin
data_par <= shift_reg; // 在时钟下降沿输出
end
endmodule
这里有个细节:我在时钟下降沿输出并行数据。为什么?因为上升沿用来采样串行数据,下降沿输出并行数据,这样给后端逻辑留出了半个时钟周期的建立时间。嗯,这个小技巧在很多高速设计中都管用。
三、时钟恢复(CDR)原理:从数据里抠出时钟
CDR是SerDes中最核心、也最头疼的部分。它的任务是从串行数据流中提取出时钟信号,并用这个时钟来采样数据。
你想想看,发送端把时钟和数据一起发出来,但接收端只有数据线,没有时钟线。怎么恢复时钟?靠数据本身的跳变沿。
CDR的基本原理:
- 检测数据的边沿跳变(0→1或1→0)
- 用本地振荡器(通常是LC-VCO或Ring-VCO)产生一个与数据速率相近的时钟
- 通过反馈环路(PLL或DLL)调整时钟相位,使其与数据边沿对齐
常见的CDR架构有两种:
| 架构类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 基于PLL的CDR | 抖动小,频率捕获范围宽 | 锁定时间长,面积大 | 高速SerDes(>10Gbps) |
| 基于DLL的CDR | 锁定快,面积小 | 抖动大,频率范围窄 | 中低速SerDes(<10Gbps) |
我个人更倾向于PLL架构。虽然面积大一点,但抖动性能好。我记得有一次做25Gbps的SerDes,用了DLL架构,结果抖动太大,误码率死活降不下来。换成PLL后,问题迎刃而解。
避坑指南:
我曾经在CDR设计中犯过一个低级错误:没有考虑数据中的长连0或长连1。当数据长时间没有跳变时,CDR会失去相位参考,导致时钟漂移。后来我强制要求发送端做8B/10B编码或加扰,保证数据有足够的跳变密度。
CDR的工作流程可以用下面这张图来理解:
这张图展示了CDR的核心反馈环路。数据进来后,先检测边沿,然后与VCO产生的时钟比较相位。如果相位有偏差,环路滤波器会输出一个控制电压,调整VCO的频率和相位,直到采样时钟对准数据的眼图中心。
这里有个关键概念:相位锁定。CDR不是锁定在数据边沿上,而是锁定在数据眼图的中心。为什么?因为眼图中心是数据最稳定的时刻,采样误码率最低。
CDR设计的关键指标:
- 锁定时间:从启动到稳定锁定所需的时间,通常要求<1μs
- 抖动容限:CDR能容忍的最大输入抖动,通常要求>0.5UI
- 抖动传递:CDR对输入抖动的抑制能力,高频抖动应被滤除
- 频率捕获范围:CDR能锁定的频率偏差范围,通常要求±500ppm
我记得有一次调试CDR,锁定时间总是超标。查了半天,发现是环路滤波器的带宽设得太窄了。带宽窄虽然能抑制抖动,但锁定时间会变长。后来我用了自适应带宽技术——锁定时用宽带宽快速锁定,锁定后切换到窄带宽抑制抖动。效果立竿见影。
好了,关于Serializer、Deserializer和CDR的核心内容就这些。实际设计中,还有很多细节需要考虑,比如阻抗匹配、信号完整性、电源噪声等等。但掌握了这些基本原理,你就有了解决大部分问题的底气。
个人经验总结:
做SerDes设计,不要一上来就追求高性能。先跑通基本功能,再逐步优化。我见过太多人一开始就想着做100Gbps,结果连10Gbps都跑不稳。稳扎稳打,才是正道。
可可精品资料库