3. 串行化与解串行化原理:并行数据转串行数据,串行数据转并行数据,时钟数据恢复(CDR)概念
各位同学,今天我们来聊聊SerDes最核心的两个动作——串行化和解串行化。说白了,就是把一堆并行的数据挤到一根线上传过去,到了对端再把它还原回来。听起来简单吧?但这里面的门道,我当年可是踩过不少坑的。
3.1 并行数据转串行数据:为什么要“挤”在一起?
先问大家一个问题:为什么非要把并行转成串行?直接并行传不好吗?
好,当然好。但问题是,并行传输需要很多根线。你想想看,一个32位的数据总线,就需要32根数据线,再加上时钟、控制信号,板子上密密麻麻全是走线。到了高速场景,比如10Gbps以上,这些并行线之间的串扰、时序偏差(skew)会让你痛不欲生。
串行化的核心思想:用一根线,把数据一比特一比特地传过去。代价是频率要提上去。比如原来32位并行数据跑100MHz,串行化之后就要跑3.2GHz(32×100MHz)。
关键公式:串行速率 = 并行位宽 × 并行时钟频率
举个例子:32位数据 @ 100MHz → 串行速率 = 32 × 100M = 3.2Gbps
我在项目中遇到过一种情况:设计人员为了省事,直接把并行数据用MUX(多路选择器)一拍一拍地送出去。结果发现输出波形歪歪扭扭,眼图根本睁不开。为什么?因为MUX的选通信号和数据的相位没对齐。
正确的做法:使用专用的并串转换器(Serializer),内部有精确的相位调整和时钟分频逻辑。一般结构如下:
// 伪代码:并串转换核心逻辑
always @(posedge clk_ser or negedge rst_n) begin
if (!rst_n) begin
shift_reg <= 0;
bit_cnt <= 0;
end else begin
if (load_en) begin
shift_reg <= parallel_data; // 加载并行数据
bit_cnt <= WIDTH - 1;
end else if (bit_cnt > 0) begin
shift_reg <= {shift_reg[WIDTH-2:0], 1'b0}; // 左移
bit_cnt <= bit_cnt - 1;
end
end
end
assign serial_out = shift_reg[WIDTH-1]; // 输出最高位
嗯,这里要注意:load_en信号必须和串行时钟clk_ser同步,而且只在bit_cnt计数到0时才拉高。否则你会把数据发串了。
3.2 串行数据转并行数据:还原的挑战
串行数据到了接收端,我们要把它还原成并行数据。这个过程叫解串行化(Deserialization)。
听起来像是串行化的逆过程?没错,但难点在于:你不知道数据从哪里开始。
举个例子:发送端连续发送了32个比特,接收端怎么知道哪一个是第0位?如果对不准,你拿到的并行数据就是错位的——第0位变成了第1位,整个数据全乱了。
解决方案:使用字对齐(Word Alignment)机制。发送端在数据流中插入特定的对齐码(比如K码),接收端检测到这个码后,就知道数据边界在哪里了。
我的经验:我曾经在一个PCIe项目中,字对齐逻辑写错了,导致链路训练一直失败。查了三天才发现是K码检测的窗口宽度设窄了,漏掉了一个比特。后来我把检测窗口设成了2倍宽度,问题就解决了。
解串行化的典型结构:
// 伪代码:串并转换核心逻辑
always @(posedge clk_rec or negedge rst_n) begin
if (!rst_n) begin
shift_reg <= 0;
bit_cnt <= 0;
data_valid <= 0;
end else begin
shift_reg <= {serial_in, shift_reg[WIDTH-2:0]}; // 右移
bit_cnt <= bit_cnt + 1;
if (bit_cnt == WIDTH-1) begin
parallel_data <= shift_reg; // 输出并行数据
data_valid <= 1;
end else begin
data_valid <= 0;
end
end
end
注意:这里的clk_rec是从串行数据中恢复出来的时钟,不是发送端的时钟。这就引出了下一个核心概念——CDR。
3.3 时钟数据恢复(CDR):没有时钟线,怎么同步?
串行传输的一大特点就是:不传独立的时钟信号。为什么?因为时钟线和数据线之间会有skew,频率高了根本对不准。
那接收端怎么知道什么时候该采样数据?答案就是CDR——从数据流里把时钟“挖”出来。
CDR的基本原理:
- 数据流中必须有足够的跳变(0→1或1→0)
- CDR电路检测这些跳变,调整本地振荡器的相位
- 最终让采样时钟的边沿对准数据眼的中心
核心指标:CDR的抖动容限(Jitter Tolerance)和锁定时间(Lock Time)
抖动容限:CDR能容忍多大的输入抖动而不失锁
锁定时间:从开始接收到数据到CDR稳定锁定需要多长时间
我建议大家在设计CDR时,重点关注两个参数:
- 环路带宽:带宽越宽,锁定越快,但对抖动的抑制能力越差
- 相位步进:每次调整相位的步长,步长太大容易过冲,太小锁定太慢
避坑指南:我曾经在一个25Gbps的项目中,CDR的环路带宽设得太窄,结果数据流中有一段连续的“00000000”,CDR以为没信号了,直接失锁。后来我加了一个“运行长度限制”电路,确保数据流中不会出现超过5个连续的相同比特。
3.4 知识体系总览
下面这张图总结了串行化与解串行化的核心逻辑,我建议你把它记在脑子里:
3.5 实际项目中的注意事项
最后,我总结几个实际项目中容易踩的坑:
| 问题 | 现象 | 解决方案 |
|---|---|---|
| 串行化时钟抖动过大 | 眼图闭合,误码率升高 | 使用低抖动PLL,增加时钟缓冲器 |
| 字对齐失败 | 接收数据全部错位 | 增加K码检测容错,使用2倍窗口 |
| CDR失锁 | 数据流中出现长连0/1 | 加扰(Scrambling)或限制运行长度 |
| 解串行化时序违例 | setup/hold时间不满足 | 调整采样相位,增加延迟链 |
个人习惯:我一般在仿真阶段会特意注入一些极端情况——比如连续32个0、32个1,看看CDR会不会失锁。如果仿真都过不了,流片回来肯定出问题。
好了,串行化与解串行化的核心内容就这些。说白了就是三件事:把并行数据挤到一根线上、从一根线上把数据还原回来、以及从数据里把时钟挖出来。这三件事做好了,SerDes就成功了一大半。
公众号:蓝海资料掘金营,微信deep3321