简介:面向具备一定FPGA开发经验、希望攻克高速ADC采集链路的工程师,这份资料聚焦AD9680在Vivado平台上的Verilog测试实现,覆盖1G采样率、Lane4的JESD204B接收场景。内容围绕三条主线展开:AD9680寄存器初始化与采样模式配置、借助PLL生成稳定采样时钟、以及基于状态机的JESD204B接收流程,并穿插时钟相位对齐、SPI配置时序、链路同步等调试经验与常见问题处理思路,便于读者对照自身系统排查问题。资源包共1个文件,为111KB的pdf文档,以图文与代码片段形式组织,适合作为开发调试时的参考手册。目前已有135人学习,适合需要快速理解AD9680配置要点与JESD204B接收框架的开发者查阅借鉴。
1. JESD204B 链路起不来:从一次“sync 拉低后再也没起来”说起
板子上电,SPI 配置回读全对,MMCM 的 LOCKED 也拉高了,可 JESD204B 那条链路就是起不来——sync信号被 ADC 拉低之后就再也没回过高,ILA 里rx_data全是 K 字符,rx_valid一动不动。这种故障在 1G 采样率、Lane4 的 AD9680 采集系统里出现频率极高,折腾人的地方在于:它不像编译报错那样给你一行红字,而是所有“看起来该对的”信号都对,唯独数据不流。新手容易一头扎进 Verilog 里改状态机,熟手则知道先怀疑时钟树和链路参数。这篇笔记按我踩过的顺序,把 AD9680 配置、时钟管理、JESD204B 接收三段里最常见的几类翻车现场拆开讲,每个都落到能直接抄的命令、约束和参数上,适合正在调高速 ADC 采集、被链路失锁卡住的 FPGA 开发者。
2. 链路始终不同步:sync 拉低不回升与 ILAS 超时的排查
JESD204B 的链路建立是一条严格的状态链:ADC 拉低sync请求同步 → FPGA 侧发送 K28.5 字符 → 双方完成字符对齐 → 接收 ILAS 多帧 → 校验参数 →sync拉高、链路进入数据模式。任何一环断掉,表现都是“sync 不回升”。下面按根因分两类讲。
2.1 先分清是“参数不匹配”还是“物理层没通”
很多人一上来就改状态机,其实第一步应该判断故障层级。我的做法是先看两个信号:rx_valid和rx_char_is_k。如果rx_valid有跳动、能抓到 K28.5(0xBC,注意 8B/10B 编码后是 10 位),说明物理层通了,问题在参数或对齐逻辑;如果rx_valid全程为低,那多半是参考时钟、线速率或收发器根本没工作,改协议层代码毫无意义。
判断物理层是否工作的最小验证,是在收发器例化后直接观察gt_pll_lock和rx_cdr_lock:
// 收发器锁定状态观测,接 ILA 观察 ila_0 u_ila_phy ( .clk (rx_clk), .probe0 (gt_pLL_lock), // 收发器 PLL 锁定,应为 1 .probe1 (rx_cdr_lock), // 接收端时钟恢复锁定,应为 1 .probe2 (rx_valid), // 接收数据有效 .probe3 (rx_char_is_k), // 当前字符是否为 K 字符 .probe4 (sync_status) // ADC 送来的 sync );这段例化的意义在于把“链路状态”和“物理状态”分开看。gt_pLL_lock为 0 说明参考时钟频率或质量不对,常见原因是 MMCM 输出的gt_refclk频率算错——1G 采样率、Lane4、14bit、8B/10B 编码下,线速率 = 1G × 4 lane 分摊后每 lane 的比特率,工程上常按 5Gbps 量级配置,参考时钟必须严格匹配收发器要求的分频关系,差一点就锁不上。rx_cdr_lock为 0 而 PLL 锁了,则要查差分对极性、AC 耦合电容和走线。
参数不匹配这一类,最典型的是 M、L、F、S、K 五个参数和 ADC 端不一致。AD9680 在 Lane4、双通道 14bit 下,常见配置是 M=2(每帧每 converter 的样本数相关)、L=4、F=2、S=1、K=32。这些值必须和 SPI 写进 ADC 的寄存器完全对应,任何一项错位,ILAS 阶段的参数校验就会失败,sync永远不回升。我一般会在接收 IP 例化处把参数写成参数化形式,方便和 SPI 配置表逐项对照:
jesd204b_rx_core #( .L (4), // lane 数,必须等于 ADC 寄存器 0x16 的 lane 配置 .M (2), // converter 数相关参数 .F (2), // 每帧字节数 .S (1), // 每多帧帧数 .K (32), // 每多帧帧数上限,决定 ILAS 长度 .SCR (1), // 加扰使能,必须与 ADC 端一致 .LINE_RATE (5000) // Mbps,与参考时钟分频关系绑定 ) u_jesd_rx ( .refclk (gt_refclk), .rx_clk (rx_clk), .sysref (sysref), .sync (sync_status), .rx_data (rx_data), .rx_valid (rx_valid) );参数说明:SCR是最容易被忽略的一项,ADC 端开了加扰而 FPGA 端没开(或反之),ILAS 数据全是乱码,表现和参数错位一模一样;K决定 ILAS 多帧长度,设小了会在参数校验前就超时。改完参数后不要急着全速跑,先把 ADC 配到测试模式输出固定递增码,链路能稳定进数据模式再切回真实采样。
2.2 ILAS 超时:把超时窗口和 sysref 捕获模式调对
链路能对齐字符、但卡在 ILAS 阶段,是第二类高频故障。现象是sync拉低后 ILA 里能看到 K28.5 对齐完成,但rx_valid短暂有效后又断,反复重试。根因通常有两个:ILAS 超时窗口设得太短,或者sysref捕获模式不对导致多帧边界判断错误。
常见做法是把 IP 的 ILAS 超时从默认值放宽到 200 个帧周期以上,并把sysref捕获改成手动控制,避免上电瞬间sysref抖动被误采。XDC 里对sysref做输入延迟约束,保证它相对帧时钟的建立保持时间:
# sysref 相对帧时钟的输入延迟约束,数值需按板级实测调整 set_input_delay -clock [get_clocks frame_clk] -max 1.500 [get_ports sysref] set_input_delay -clock [get_clocks frame_clk] -min 0.500 [get_ports sysref] # 收发器参考时钟与核心时钟异步,必须声明时钟组 set_clock_groups -asynchronous \ -group [get_clocks gt_refclk] \ -group [get_clocks rx_clk] \ -group [get_clocks frame_clk]set_clock_groups -asynchronous这行是血泪经验:gt_refclk、rx_clk、frame_clk三者同源但经过不同分频路径,不声明异步关系,时序分析器会报大量跨时钟域违例,有人为了消报错去乱加set_false_path,结果把真正需要约束的路径也放过了,上板后偶发失锁。正确做法是只对确实异步的时钟组声明,数据跨域处用同步器或异步 FIFO 处理。
如果放宽超时后仍然反复,就要怀疑sysref相位。我一般用 IDELAY 扫描sysref延迟 tap,边扫边看链路状态:
// sysref 延迟扫描,找到链路稳定的 tap 值 always @(posedge scan_clk) begin if (scan_en) begin if (jesd_link_state == LINK_UP) begin stable_tap <= current_tap; // 记录稳定时的 tap scan_en <= 1'b0; end else if (current_tap < 31) begin current_tap <= current_tap + 1; end end end实测中稳定区间往往只有十几个 tap 宽,落在 12 到 18 之间比较常见,具体值跟 PCB 走线和器件批次有关,必须实测。扫描逻辑本身要放在独立时钟域,别和接收状态机抢资源。
3. SPI 配置回读不对:上电时序、寄存器顺序与回读校验
AD9680 的 SPI 配置是整条链路的地基,配错了后面全是白费。这一类故障的表现很直接:回读寄存器和写入值不一致,或者 ADC 干脆不响应。下面两个坑我各踩过不止一次。
3.1 上电后必须等够时间再碰 SPI
最隐蔽的一个坑:上电立刻发 SPI,ADC 随机不响应,复位几次又好了,看起来像“玄学”。根因是 AD9680 内部基准和 PLL 需要上电稳定时间, datasheet 里明确要求上电后等待数百微秒再操作 SPI。代码里必须加一个上电延迟计数器,而不是靠复位信号碰运气:
// 上电延迟:复位释放后等待 500us 再启动 SPI 配置 localparam POWERUP_DELAY = 16'd50000; // 25MHz 时钟下约 2ms,留足余量 always @(posedge clk_25m or posedge rst) begin if (rst) begin delay_cnt <= 16'd0; spi_enable <= 1'b0; end else if (delay_cnt < POWERUP_DELAY) begin delay_cnt <= delay_cnt + 1'b1; spi_enable <= 1'b0; end else begin spi_enable <= 1'b1; // 延迟结束才允许 SPI 状态机启动 end end参数说明:POWERUP_DELAY按实际时钟频率换算,宁大勿小,多等几毫秒不影响功能,等少了就是随机故障。spi_enable作为 SPI 状态机的启动条件,比在状态机内部判断计数器更清晰,也方便后续加“配置失败重试”。
3.2 寄存器写入顺序和回读校验不能省
AD9680 的寄存器有依赖关系:时钟分频、lane 配置、JESD 参数这几组必须按特定顺序写,先写 JESD 参数再改 lane 数,ADC 内部状态机会错乱。我的做法是把配置表做成 ROM,按顺序逐条写,每条写完立刻回读比对:
// 配置表:地址 + 数据,按依赖顺序排列 // 0x14 采样率模式 -> 0x16 lane 配置 -> 0x1E 等 JESD 参数 localparam [15:0] CFG_ROM [0:15] = '{ 16'h1403, // 采样率模式 16'h1604, // lane 数 = 4 16'h1E01, // 加扰使能,与 FPGA 端 SCR 一致 16'h2002, // 多帧长度相关 16'hFF00 // 末尾哨兵 }; // 写后回读:地址发 0x80|addr 进入读模式 task spi_write_verify(input [7:0] addr, input [7:0] data); begin spi_write(addr, data); spi_read (addr | 8'h80, rd_data); if (rd_data !== data) cfg_error <= cfg_error + 1'b1; // 记录不一致,供 ILA 观察 end endtask逻辑说明:spi_write先发地址后发数据,spi_read用地址最高位置 1 进入读模式。cfg_error计数器接到 ILA,上板后一眼就能看出哪条配置没写进去。注意 SPI 时钟不要超过器件上限(常见做法是控制在 25MHz 以内),且空闲电平为低,用状态机实现比直接例化通用 SPI IP 更可控——通用 IP 的空闲电平和片选时序经常和 ADC 要求对不上,这是很多人“IP 能用但 ADC 不认”的原因。
4. 时钟树与时序约束:MMCM 配了但眼图闭合、时序疯狂报错
1G 采样率系统里,时钟是命门。MMCM 参数算对只是第一步,相位、约束、跨域处理任何一处不到位,表现都是“功能偶尔对、跑久了错”或者时序报告一片红。
4.1 MMCM 输出相位与 ADC 采样时钟对齐
AD9680 需要一路采样时钟和一路 JESD 参考时钟,两者同源但用途不同。常见错误是只关心频率、不关心相位,结果采样时钟和参考时钟的相对相位随温度漂移,链路偶发失锁。MMCM 例化时要把两路输出的相位关系显式定义,并在约束里锁死:
// MMCM:125MHz 输入,生成采样时钟与 JESD 参考时钟 mmcm_adv #( .CLKIN1_PERIOD (8.000), // 125MHz 输入 .CLKFBOUT_MULT_F (8.000), // VCO = 1GHz .CLKOUT0_DIVIDE_F (2.000), // 500MHz 采样时钟 .CLKOUT0_PHASE (0.000), .CLKOUT1_DIVIDE (4), // 250MHz JESD 参考时钟 .CLKOUT1_PHASE (0.000) // 与采样时钟同相起点 ) u_mmcm ( .CLKIN1 (clk_125m), .CLKOUT0 (adc_clk), .CLKOUT1 (gt_refclk), .LOCKED (mmcm_locked) );参数说明:CLKFBOUT_MULT_F和CLKOUTx_DIVIDE共同决定输出频率,改任何一个都要重新核对收发器要求的参考时钟范围;CLKOUTx_PHASE设 0 只是起点,实际相位补偿靠 IDELAY 或 MMCM 的 fine phase shift 微调。上板后一定要用 ILA 或示波器看adc_clk和gt_refclk的实际相位差,别信仿真。
4.2 时序约束:set_false_path 不能乱加,set_max_delay 要算清楚
时序报告里 JESD 接收路径报错,很多人的第一反应是加set_false_path一把梭。这是最危险的操作——把真实需要约束的路径放过了,上板后表现为“综合过了、实现过了、跑起来偶发错码”。正确做法是区分路径性质:跨异步时钟域的数据路径用异步 FIFO 隔离后声明时钟组异步;同源但经过 MMCM 不同输出的路径,用set_max_delay约束实际延迟上限。
# MMCM 输出到 JESD 接收核参考时钟的路径,约束最大延迟 set_max_delay -from [get_pins u_mmcm/CLKOUT1] \ -to [get_pins u_jesd_rx/refclk] 0.500 # 收发器恢复时钟与核心时钟异步 set_clock_groups -asynchronous \ -group [get_clocks rx_clk] \ -group [get_clocks adc_clk] # 数据跨域处已用异步 FIFO,此处声明异步是安全的 set_false_path -from [get_clocks rx_clk] -to [get_clocks adc_clk]逻辑说明:set_max_delay的 0.5ns 是经验起点,实际值要按 MMCM 输出到接收核的布线延迟估算,约束太紧会导致布局布线失败,太松等于没约束。set_false_path只加在已经用异步 FIFO 做了跨域隔离的路径上,加之前必须确认 FIFO 的读写指针同步器齐全。我一般会在约束文件里给每条set_false_path写一行注释说明依据,方便后来人(包括三个月后的自己)判断能不能删。
5. 避坑清单:五个让我返工最多次的细节
这一节按“现象 → 原因 → 解决”记录五条具体踩坑,都是上板才会暴露、仿真发现不了的。
现象一:SPI 配置回读全对,但链路就是不同步。原因:ADC 端加扰开了,FPGA 接收核SCR参数没开,ILAS 数据全乱。解决:把 SPI 配置表里的加扰寄存器和接收核SCR参数做成同一个宏定义,改一处两处同步,杜绝人为不一致。
现象二:常温正常,跑十几分钟或温度升高后失锁。原因:收发器均衡参数按常温调死,温度漂移后眼图闭合。解决:开启接收端自适应均衡,并在初始化后加自动重训练——检测到连续错误计数超阈值就重新发同步请求,而不是等人工复位。
现象三:时序报告全绿,上板偶发错码。原因:跨时钟域路径被set_false_path误放行,实际没有同步器。解决:逐条审查set_false_path,凡是没有异步 FIFO 或两级同步器保护的路径一律删掉约束,改用set_max_delay或补同步逻辑。
现象四:FIFO 深度设小了,突发流量下溢出丢数。原因:按平均速率算深度,没考虑 JESD 链路重同步瞬间的突发。解决:深度按至少 2048 起步,配合 System ILA 触发抓溢出时刻的波形,确认是深度问题还是下游反压问题。
现象五:换一块板子就不同步,同一套 bit 流。原因:sysref的 IDELAY tap 值是按上一块板扫出来的,板间走线差异导致相位偏移。解决:把 tap 扫描逻辑做成上电自校准,每次启动自动扫一遍,而不是写死常量。
6. 快速定位:把“玄学失锁”变成可复现的排查路径
调这类系统,最怕的是“时好时坏、说不清”。我的习惯是把排查顺序固定成一条链,每次故障都从链头走到链尾,不跳步。第一步看电源和上电时序,确认 SPI 延迟够不够;第二步看收发器锁定,gt_pLL_lock和rx_cdr_lock必须都为 1;第三步看字符对齐,ILA 里能不能稳定抓到 K28.5;第四步看 ILAS,参数校验过不过;第五步才看数据通路和 FIFO。这条链上任何一步不过,后面的调试都是浪费。
下面这张对照表是我贴在工位上的,故障现象和第一检查点一一对应:
| 现象 | 第一检查点 | 常见根因 |
|---|---|---|
| sync 拉低不回升,rx_valid 全低 | gt_pLL_lock / rx_cdr_lock | 参考时钟频率错、差分对极性反 |
| 能抓 K28.5 但 sync 不回升 | M/L/F/S/K 参数与 SPI 配置表 | 参数错位、加扰不一致 |
| 链路反复重试 | ILAS 超时窗口、sysref 相位 | 超时太短、tap 值不适配本板 |
| 常温正常高温失锁 | 均衡参数、眼图 | 未开自适应均衡、无重训练 |
| 时序全绿但偶发错码 | set_false_path 清单 | 跨域路径缺同步器被误放行 |
| 突发丢数 | FIFO 深度、下游反压 | 深度按均值算、无溢出监测 |
验证手段上,我优先用 ADC 测试模式的递增码做数据通路自检,比接信号源做 FFT 快得多:让 ADC 输出连续递增码,FPGA 侧逐拍比对adc_data是否等于上一拍加一,错误计数直接反映链路质量。这一步过了,再切真实信号做频谱和 ENOB 验证才有意义。另外,System ILA 的触发条件要设得“刁钻”一点——比如触发在sync下降沿前后各抓一段,比漫无目的地看波形高效得多。
从那以后我每次上板,都强制先跑一遍“电源 → 锁定 → 对齐 → ILAS → 数据”这条链,任何一步不过绝不往下改代码。这套资源里把 AD 配置、时钟生成、JESD204B 接收三段的状态机和约束都整理成了可直接复用的模块,参数表和排查顺序也一并放在里面,照着链子走一遍,大部分失锁问题都能在半小时内定位到具体环节。希望帮到你。
本文还有配套的精品资源,点击获取