前4篇讲的是单点协议的坑------I2C、SPI、UART、以太网。这篇换个角度:当多个器件通过同一接口级联在一起时,时序问题不再是"接口对不对",而是"整体能不能协同"。
级联(Cascade/Daisy Chain)是FPGA工程中常踩的隐形地雷------单个器件测都正常,一接入系统就莫名其妙。以下是5个真实踩坑案例,都是拿示波器怼着信号线查了几个小时才找到的元凶。
一、SPI Daisy Chain------一片没事,三片就乱
踩坑现场
产品需要在一片FPGA上通过SPI控制3片ADC采样芯片。按照数据手册的Daisy Chain接法:FPGA的MOSI→ADC1的DIN,ADC1的DOUT→ADC2的DIN,ADC2的DOUT→ADC3的DIN,ADC3的DOUT→FPGA的MISO。
一片ADC单独控制时一切正常。两片级联也OK。加上第三片,FPGA读回来的数据永远是错的,而且位置和预期都不一样------写进ADC1的配置字节被读成了ADC3的,数据乱跳。
根因分析
SPI Daisy Chain的原理很简单:master发一个帧(N个字节,N=级联数量),每片从设备在内部shift register中前移一个字节,最终所有片都拿到了自己的数据。
问题出在DOUT到DIN的传播延迟上。
每个SPI slave在内部将DIN采样到shift register再输出到DOUT不是零延迟的:
-
标准的SPI slave:DIN在SCK上升沿采样,DOUT在SCK下降沿更新
-
从DIN采样到DOUT有效输出,中间经过:采样寄存器 → shift logic → output buffer → PCB走线
以最常见的AD76xx系列ADC为例,从DIN采样完成到DOUT数据有效,延迟大约1~2个SCK周期(取决于芯片内部实现)。3片级联意味着从master发出数据到最后一层的回传,总延迟大约是级联数 × (1~2)个SCK。
假设SCK=10MHz(周期100ns),3级级联延迟就是300~600ns。大部分MCU/FPGA的SPI master会在这段时间内完成传输并开始采样MISO------结果就是读到的是上一帧的数据或者完全不相关的值。
解决方案
方案一:降速(简单粗暴)
把SCK降到足够低,确保总传播延迟小于半个SCK周期。3级级联时,假设每级延迟1.5个SCK,总延迟=4.5个SCK------那就需要发N+4.5个SCK脉冲然后才采样MISO。实际操作就是降低SCK频率,让总延迟远小于半周期。
verilog示例:
// 示例:3级级联降低SCK频率,仅演示参数配置逻辑
// 对于3级级联,假设SCK=1MHz(周期=1000ns)
// 每级传播延迟约150ns(需查芯片数据手册),总延迟≈450ns < 500ns(半周期)
// SCK可以设到1MHz,再多级联就只能继续降
// 注意:实际延迟值取决于具体芯片,务必实测验证
方案二:增加时钟数量(半双工技巧)
发N+M个SCK脉冲,其中M是多余的用于"推出"延迟数据的时钟。多出来的MISO采样都在M个额外脉冲中完成。适合master支持自定义传输长度的场景。
方案三:改用独立CS的非级联连接
这是最保险的做法------每个SPI从设备用独立的CS引脚。虽然要多占几个FPGA引脚,但避免了所有级联时序问题,每片可以独立配置不同的SCK频率。
现场总结
Daisy Chain的级联数量不是数据手册说"最多支持"多少片,而是要看你的SCK频率和每级的传播延迟。3片用1MHz SCK可能是极限,换成另一款芯片的传播延迟大一倍,2片就撑不住了。不要只看理论值。
二、I2C总线上5个从设备------远端器件永远不ACK
踩坑现场
一个I2C总线上挂了5个器件:温度传感器、EEPROM、RTC、ADC、DAC。FPGA做主控。所有器件单独都测试过,均正常工作。全部焊接在一个板子上后,近端(靠近FPGA)的器件通信正常,远端(离FPGA最远)的RTC和DAC随机不回应ACK,有时重试几次又能通信,但时序不太稳定。
根因分析
问题出在I2C总线的分布电容上。
NXP(原Philips)的I2C规范规定:
-
标准模式(100kHz):最大总线电容400pF
-
快速模式(400kHz):最大总线电容400pF
-
快速+模式(1MHz):最大总线电容550pF
每个I2C器件管脚贡献3~10pF(看封装),再加上PCB走线(每厘米约0.5~1pF),5个器件加长走线轻松到400pF+。
电容过高导致两个后果:
-
上升时间(Tr)超标:I2C是开漏输出,靠上拉电阻充放电。电容越大,边沿越慢。400kHz下要求最大Tr=300ns,400pF用4.7kΩ上拉,RC常数=400pF×4.7kΩ=1880ns,远超标。
-
SCL信号到达远端时已严重变形:不再是方波,变成了三角波。远端的RTC/ADC看到的时钟沿已经模糊不清,误采样时产生ACK失败。
示波器实测
在RTC的SDA/SCL管脚上测量波形,SCL的上升时间约1.2μs------远超400kHz允许的300ns。再用I2C协议分析器看,SCL在低电平保持时间比规范短(因为波形变形导致逻辑门限判断出错),数据采样的时刻已经偏移到了边界上。
解决方案
方案一:增强上拉(最直接但有限)
把上拉电阻从标准4.7kΩ降到1.5kΩ或更低(但注意IOL最大3mA限制,太低会拉不低)。

Rmin=VCC/IOL_max=3.3V/3mA=1.1kΩ
综合波形质量与器件电流限制,2.2kΩ是兼顾性能与安全的折中取值 。
⚠️ 注意:上拉电阻不可小于1.1kΩ,否则灌电流超标会损坏器件。
方案二:加I2C总线buffer/中继器(推荐)
用PCA9517、PCA9508之类的I2C总线buffer将总线分两段。输出端重新整形时钟和数据信号,恢复边沿陡峭度,并且隔离两侧的电容:

每个子段电容独立,各400pF以内。
方案三:降速到100kHz
如果硬件已经无法修改,降速到100kHz可行。100kHz下最快上升时间限制更宽松(1000ns),电容400pF配4.7kΩ上拉的RC=1880ns,仍然超标------所以还是要换小上拉电阻或者加buffer。
现场总结
板级设计时就要估算I2C总线负载。不是"挂多少器件"的问题,而是"总电容多少"的问题。超过4个器件建议上I2C多路复用器或buffer分总线。不要把这句话留着到调试阶段才发现。
三、多ADC同步采样------信号源没变,采样结果相差几毫伏
踩坑现场
设计一个同时采样系统:4片ADC(AD9680,1Gsps)共享同一个采样时钟,同步采集4个通道的模拟信号。
结果发现:输入同一校准信号(相同幅度、相同频率的正弦波),4个ADC采出来的数据相位和幅度都不一样。各通道之间误差达3%~5%,远超预期的0.1%一致性。
根因分析
核心问题是时钟偏斜(Clock Skew)------同样一个时钟信号,到达4颗ADC的管脚的时间不一样。
时钟分配拓扑是这样的:时钟源 → 50Ω同轴电缆 → PCB → 扇出4路到4颗ADC。PCB走线用等长设计,理论上长度一样。但实际测试发现:
-
理论等长 ≠ 实际等长:PCB走线在转角、过孔处的有效长度和直走不一样,等长蛇形线的相位补偿也不是精确匹配的。
-
时钟缓冲器的偏斜:使用1:4时钟扇出缓冲器(如HMC987),其通道间的skew随温度变化。datasheet写的典型值±15ps看起来不大,但对于1Gsps ADC,采样间隔1ns,15ps就相当于相位误差15/1000×360°=5.4°。
-
温度梯度下各通道的时钟偏斜会动态漂移。靠近热源的那颗ADC的时钟路径延迟更大。
解决方案
方案一:使用JESD204B接口的ADC + SYSREF(推荐)
JESD204B的确定性延迟特性可以自动补偿时钟skew,通过SYSREF信号复位各ADC内部的时钟分频器,确保采样对齐。这是高速多通道同步系统的首选方案。
方案二:物理层走线 + 延迟测量校准
verilog
// FPGA内部通过ILA测量各ADC的采样时钟相位
// 在系统初始化阶段,向所有ADC输入同一校准信号
// 采集数据后计算各通道之间的相位差
// 写寄存器补偿(如果有可编程延迟的ADC)
// 此为示意,实际需实现状态机完成测量、比较、补偿全过程
方案三:使用可编程延迟的时钟扇出芯片
LMK04828、HMC7044等精密时钟芯片支持每路输出的独立精细延迟调节(步进约10~50ps)。可以在系统启动时自动校准各通道延迟。
现场总结
等长走线不是万能药。在Gsps采样率下,等长只能保证粗对齐(±50ps级别),精细校准需要芯片级的延迟补偿或者确定性延迟协议(JESD204B SYSREF)。如果你的系统需要<1°的采样相位一致性,老老实实用JESD204B或者加延迟芯片。
四、菊花链引脚共享------链尾信号眼图完全闭合
踩坑现场
FPGA通过一组LVDS差分对,以菊花链方式将数据传给3个从板(每个从板都有一个LVDS接收器+LVDS转发器)。第1块从板接收正常,第2块偶尔丢包,第3块完全收不到数据------信号不存在。
根因分析
这种菊花链连接的模式在高能物理、雷达信号处理、分布式数据采集系统中很常见------主FPGA用LVDS差分对依次经过多个子卡,每个子卡接收数据并转发给下一个。
问题在于:信号经过每个接收器/转发器后,抖动(Jitter)和信号幅度都会劣化。
菊花链每级的损耗:
-
阻抗不连续:每个抽头(tap)都会在传输线上引入寄生电容和阻抗变化,导致信号反射
-
接收器输入电容:每个LVDS接收器约有2~5pF输入电容,挂在传输线上相当于多点加载
-
转发器的PLL抖动:每个转发器内部的CDR或PLL会引入额外抖动
实测表明:3级菊花链后,LVDS的眼图开度从初始的60%降低到不足20%。链尾的信号已经无法被可靠的接收器识别。(经验上,眼图开度大致与级数平方成反比)
解决方案
方案一:改用点对点星型拓扑(最推荐)
放弃菊花链。FPGA每个LVDS通道只驱动一个从板,从板不需要转发功能。占用引脚多一些,但信号质量可控。
方案二:使用高速缓冲器/中继器
在菊花链的每级之间插入LVDS中继器(如DS90LV004),它可以重新整形信号------接收→均衡→重新输出。但中继器本身也引入2~4ns延迟,会影响时序。
方案三:降低数据速率
如果必须在菊花链拓扑下工作,降低速率让眼图开度恢复。例如原本500Mbps下眼图闭合,降到250Mbps时可能恢复至40%开度------虽然不理想,但还能工作。
现场总结
菊花链和总线共享拓扑在低速时代没问题,但在高速LVDS/Gb级速率下要慎用。超过2个节点就考虑星型或者加中继器。实在没办法用菊花链,先仿真传输线的S参数和眼图再投板,别靠运气。
五、CS切换时的总线争用------数据帧全乱了
踩坑现场
多个SPI从设备共享同一组MOSI/MISO/SCK线,FPGA通过CS0/CS1/CS2独立选通。单独操作每个设备都正常。在CS0释放后快速拉低CS1,第一次从MISO读回的数据全是错误的,后面就恢复了。
根因分析
这是三态总线切换时的常见问题------前一个设备释放总线和后一个设备驱动总线之间有一个时间窗口,在这个窗口内总线处于不确定状态:
-
FPGA拉高CS0的上升沿
-
Slave 0检测到CS高 → 三态其MISO引脚
-
FPGA检测到MISO高阻态 → 拉低CS1
-
Slave 1检测到CS低 → 驱动MISO
问题出在第2步和第4步之间------Slave 0的输出从有效变为高阻需要时间(TOFF),Slave 1开始驱动MISO也需要时间(TON)。如果CS1在TOFF完成之前就拉低,Slave 0和Slave 1都会同时驱动MISO------两个输出冲突,MISO上的电压可能是悬空、残余电平或者毛刺。
典型SPI slave的TOFF/TON参数:
-
常见slave:TOFF = 10~30ns,TON = 5~20ns
-
FPGA本身的IO:TOFF = 2~5ns,TON = 2~5ns
最危险的情况:如果CS释放和拉高之间没有任何间隔(背靠背操作),总线冲突几乎是确定的。
解决方案
verilog
// SPI 片选切换逻辑:插入死区时间,规避三态总线冲突
always @(posedge clk) begin
case (spi_state)
SPI_DONE: begin
cs_n <= `CS_INACTIVE; // 取消当前片选
spi_state <= SPI_DEAD_TIME;
dead_cnt <= DEAD_CYCLES; // 配置死区时钟周期(至少1个SCK周期)
end
SPI_DEAD_TIME: begin
if (dead_cnt == 0) begin
cs_n <= next_cs; // 选通下一个从设备
spi_state <= SPI_START;
end else begin
dead_cnt <= dead_cnt - 1;
end
end
endcase
end
具体要求:
-
CS释放到下次CS选通之间至少插入1个SCK周期的空闲时间(5~10MHz下200~100ns,足以让三态切换完成)
-
最好的做法是一帧结束后等待MISO恢复到高阻态再操作下一帧
-
如果用多CS线,可以考虑在CS切换前先将MOSI/MISO方向改为输入(三态FPGA的IO),等下一个CS稳定后再切回输出
现场总结
高速切换CS时,三态总线的"余晖"效应是实时工程师最容易忽视的时序隐患。每帧之间留出间隙不仅是为了IFG,也是为了给三态缓冲器足够的时间切换。这个间隙在低速时无所谓(几微秒的软件延时),但在FPGA这种纳秒级操作场景下,忘记了就是数据冲突。
⚠️ 注意事项
-
所有接口的级联数量都不是无限制的------数据手册标注的"最大级联数"通常是在理想条件下(短走线、常温、极低频率),实际工程至少要打50%折扣。
-
级联拓扑和星型拓扑的时序分析方法完全不同------级联(Daisy Chain)要考虑累积延时;星型(Star)要考虑skew和负载电容。混用或搞混了分析方法会导致误判。
-
先做IBIS/SI仿真再投板------在高速级联场景下不是可选项,是必选项。一个3级SPI Daisy Chain的成本不足以支撑多次PCB改版,但一次仿真的时间完全值得。
-
I2C的上拉电阻不是"按公式算"就行------算出来的理论值要乘上PCB的实际分布电容(包括过孔、连接器、排线),建议留20%~30%裕量。
-
多芯片同步系统的时钟分配一定要用树形拓扑(H-Tree)或者专用时钟缓冲器,不要靠等长走线"祈祷"对齐。等长只能保证传输延迟相近,不能保证相位对齐。
-
JESD204B的SYSREF信号是解决多芯片同步的"王炸"------它能彻底消除时钟skew的影响。如果预算支持,优先选支持JESD204B的器件。
-
CS切换之间的死区时间在SPI IP核中经常被忽略------Xilinx的SPI AXI IP默认支持CS空闲周期设置,如果在用自研SPI master,一定要确认此参数。
FAQ
Q1: 级联和菊花链是一回事吗?
不完全相同。级联(Cascade)泛指多器件通过同一接口串行连接。菊花链(Daisy Chain)是级联的一种特例------每个节点接收数据、处理、再转发给下一个节点。两者共享同一个核心问题:时序累积。
Q2: I2C总线上挂多少个器件算"太多"?
没有绝对数字,取决于PCB走线长度、器件输入电容、上拉电阻和通信速率。一个经验法则:标准模式(100kHz)下不要超过8个器件,快速模式(400kHz)下不要超过4个------这还是在PCB设计良好的前提下。
Q3: 多ADC同步采样的关键是时钟还是数据?
时钟。数据路径上的延迟差异可以通过FIFO补偿,但采样时钟的skew决定了每个ADC看到的模拟信号时刻不同------这个误差是原理性的,无法通过后处理完全消除(除非你加延时校准)。所以时钟分配是第一步,数据对齐是第二步。
Q4: 菊花链层级越多,时序问题越严重吗?
是的。大部分菊花链的时序劣化是超线性的------因为每级不仅引入固定延迟,还会劣化信号质量,使后续级的判决更困难。经验上,眼图开度大致与级数平方成反比,在第3级以后信号质量会急剧恶化。
Q5: SPI Daisy Chain能不能超过3片?
可以,但要降SCK频率。比如3片用1MHz,换到5片就要降到500kHz甚至更低。或者可以改用"独立CS"模式------每片单独CS线,MOSI/MISO/SCK共享,这样不受级联延迟限制,但占用更多GPIO。
Q6: 多从设备共享I2C总线,能不能用不同的地址来避免信号冲突?
地址不同能避免总线上的地址冲突,但不能解决电容负载和信号质量的问题。地址只解决了"谁应答"的问题,没解决"信号能不能传过去"的问题。两个问题的本质不同,不要混为一谈。
Q7: 有没有"万能"的级联时序解决方案?
没有。每种子拓扑(Daisy Chain、共享总线、星型)都有自己的局限。但有一条通用原则:尽可能减少链上的在役节点数。把多级级联改成两级甚至单级,或者用中继器/缓冲器切分长链。复杂度换稳定性,往往是值得的。
承接FPGA定制开发、项目调试、IP定制,需要开发服务可私信咨询。
觉得有收获?点赞在看转发,让更多FPGA工程师避免被这些坑折磨。关注我,下一篇:接口项目实战**。**