资讯详情 AD9680 1GSPS Lane4 JESD204B接收链路调试与实现
📅 2026/10/29 8:50:44
简介面向具备一定FPGA开发基础、需要开发或调试高速数据采集系统的工程师这份PDF文档围绕AD9680在Vivado平台上的Verilog测试程序展开重点解决1G采样率、4通道Lane4JESD204B数据接收的实现与联调问题。内容按AD9680寄存器初始化、PLL时钟生成、JESD204B接收状态机三条主线组织并穿插时钟相位对齐、SPI配置时序、链路同步等调试经验与常见问题处理思路。包内共1个PDF文件约111KB篇幅紧凑便于随查随用。已有135人学习。读者可从中获得可参考的Verilog模块框架、寄存器配置与时钟参数设置方法以及链路建立和排错的具体切入点适合在具体采集方案中对照调整与验证。1. 一块 1GSPS 采样板卡的三次失锁这套 AD9680 测试工程能干什么上板第一天就跑不起来。频谱上没有信号链路状态灯一直在闪sync 拉低又拉高链路反复失锁。板子上挂着一颗 14 位双通道、1G 采样率的高速 ADCFPGA 侧用 Vivado 带着 Verilog 代码去收 4 条 JESD204B Lane。这套工程正是为这种场景准备的把 AD9680 的 SPI 配置、MMCM 时钟树、JESD204B 接收对齐三段链路完整写成可综合的 Verilog跑通 1G 采样率 Lane4 的数据通路同时配好 ILA 与数据自检逻辑方便定位问题出在哪一段。这套工程没打算做成通用 IP它更像一份能直接改的参考实现。寄存器序列、约束文件、对齐状态机、上板排查顺序都写在里面。适合已经写过基本 Verilog、正在对接高速 ADC 或 JESD204B 链路的人也能照着跑通但需要清楚这类链路最后能不能通八成取决于时钟和约束跟代码写得多漂亮关系不大。2. 工程骨架拆解三条主线与 Lane4 的速率账在 Vivado 里打开这套工程第一眼能看到的不是代码量而是结构。高速采集工程的难点从来不在于写多少个模块而在于三条主线各自的时钟域能不能对得上。一旦把这三条线分开后面的模块划分、目录组织、约束写法都会变得很固定。2.1 三条主线配置面、时钟面、数据面配置面是慢速、低速、可重试的。它负责通过 SPI 把 AD9680 内部几十个寄存器按顺序写进去掉电控制、输出模式、JESD204B 参数、Lane 数量、加扰使能。这条线的工作时钟一般是板上 25MHz 或 50MHz 的普通晶振跟采样时钟没有任何关系出错了可以复位重来风险最低但最容易被忽略因为大多数人第一次上板就卡在这里。时钟面是整条链路的基础。AD9680 需要一路器件时钟device clock才能工作FPGA 内部的 JESD204B 收发器需要一路参考时钟refclkJESD204B IP 核的核时钟core_clk又是一路。这三路时钟通常同源但必须按异步时钟组处理否则时序分析器会在同源路径上报出大量虚假违例开发者又不敢直接忽略。数据面是真正跑高速的部分。4 条 Lane 各自的串行数据进入收发器8b/10b 解码后拼成帧再按 M/S/F 参数拆成 I/Q 两路样本最后落到 AXI-Stream 上接 FIFO、接自检、接 FFT。这条线的每一个握手信号都跟着 core_clk 走任何一次跨时钟域处理不当表现都是“数据看起来对但每隔几百个样本错一个”。把这三条线在顶层显式分开是我在这个工程里最坚持的一件事。顶层只做例化和连线不写任何逻辑每条线内部自己管自己的复位、自己的状态机、自己的错误计数。这样调试的时候看哪个计数器在涨就知道问题在哪条线上不至于对着波形图从早看到晚。2.2 文件清单与目录约定RTL、XDC、仿真、脚本工程按功能分目录不按文件类型平铺。这一点在后期二次开发时差别很大——当工程从“跑通”变成“交付”你会不断往里加模块平铺的目录两周就会乱。目录内容说明rtl/cfg/ad9680_spi_cfg.v、cfg_rom.vSPI 状态机与寄存器序列表rtl/clk/clk_gen.v、sysref_gen.vMMCM 例化与 sysref 产生rtl/jesd/jesd204b_rx_top.v、lane_align_fsm.vIP 核封装与对齐状态机rtl/data/axis_fifo_wrap.v、sample_check.v数据面缓冲与递增自检rtl/top/ad9680_top.v顶层只例化不写逻辑xdc/ad9680_pin.xdc、ad9680_clk.xdc管脚与时钟约束分开两份sim/tb_jesd_align.v、tb_spi_cfg.v两个核心模块的行为级 testbenchtcl/build.tcl、program.tcl一键建工程与下载脚本管脚约束和时钟约束拆成两份文件是踩过坑之后改的。管脚约束跟着板子走时钟约束跟着器件速度等级走两者变更频率完全不同。混在一个文件里换一块板子就得整份重写很容易把时钟约束顺手删掉。build.tcl里把器件型号、IP 核版本、综合策略都写死任何人拿到仓库跑一条命令就能重建工程不用在 GUI 里点十几下。这条习惯在多人协作时价值极高尤其是在核对“我这边能过、他那边不过”的问题时。2.3 先把速率账算明白1G 采样率 × 4 Lane 的线速率动手写代码之前先在一张纸上把速率账算清楚。JESD204B 的线速率公式是固定的Lane Rate (M × S × N × 10/8 × fs) / LM是参与传输的转换器数量S是每帧每转换器的样本数N是每个样本在链路上占的比特数14 位样本一般补齐到 1610/8是 8b/10b 编码开销fs是采样率L是 Lane 数量。工程实际配置是M1, S1, N16, L4, fs1GHz1 × 1 × 16 × 1.25 × 1e9 / 4 5 Gbps5Gbps 正好对上工程里LINE_RATE 5000这组参数。如果两个通道全开M2线速率直接翻倍到 10Gbps参考时钟、收发器参数、XDC 里的周期约束全都要跟着改这也是二次开发时第一个要确认的地方。注意线速率算错的表现非常隐蔽。IP 核不会报错只是永远收不到有效的对齐字符日志里看到的就是 sync 反复拉低。先把 M、L、F、S 和 fs 对一遍再去看波形。参考时钟不是随便给的JESD204B 的子类 1 通常要求refclk 线速率 / 40// 5Gbps / 40 125MHz这就是 MMCM 输出 CLKOUT0 要给的频率 localparam integer LINE_RATE_MBPS 5000; localparam integer REFCLK_KHZ LINE_RATE_MBPS * 1000 / 40; // 125000kHz这条关系写进代码里而不是写在注释里后面改线速率只要动一个LINE_RATE_MBPS参考时钟的期望值会跟着变仿真里可以直接断言实际测到的 refclk 频率对不对。这种“把假设写成可检查的常量”的做法在高速链路里能省掉一半的排查时间。顶层例化保持极简三条线之间只通过少量状态信号连接ad9680_top u_top ( .clk_125_p (clk_125_p), // 板载差分晶振 .rst_n (rst_n), .spi_cs_n (ad_spi_cs_n), // 配置面输出 .spi_sclk (ad_spi_sclk), .spi_sdin (ad_spi_sdin), .spi_sdout (ad_spi_sdout), .ad_devclk_p (ad_devclk_p), // 时钟面输出给 ADC .ad_devclk_n (ad_devclk_n), .sysref_p (ad_sysref_p), // 多帧同步 .sync_n (ad_sync_n), // ADC 请求建链 .gt_rx_p (gt_rx_p), // 数据面4 条 Lane .gt_rx_n (gt_rx_n), .cfg_done (cfg_done), // 配置完成其他两条线才启动 .link_up (link_up), .err_cnt (err_cnt) );cfg_done这条线很关键时钟面一上电就可以跑但数据面必须等 SPI 配置全部写完、回读校验通过之后才允许启动对齐状态机。少了这个门控状态机在 ADC 还没进 JESD204B 模式时就开始找 K28.5必然超时然后触发重试逻辑日志里全是噪声。3. 配置与时钟两条腿SPI 寄存器序列和 MMCM 输出约束配置和时钟这两块单独看都不难难在它们互相依赖SPI 写完寄存器ADC 才会把内部 PLL 锁到指定采样率采样率定了线速率才定线速率定了refclk 才定refclk 定了MMCM 参数才能反推。这个链条是单向的顺序搞反就会反复返工。3.1 SPI 为什么手写状态机时序余量与回读AD9680 的 SPI 是标准四线制SCLK、CS_N、SDIO 双向。工程里没有用厂商的 SPI IP 核而是手写了一个 16 位移位状态机原因有三个。第一AD9680 的 SPI 时序要求SCLK空闲为低电平很多通用 IP 核默认空闲为高改配置反而比手写麻烦。第二写完之后需要立刻把 SDIO 切成输入做回读校验通用 IP 核的双向切换要额外加三态控制手写反而清晰。第三也是最实际的手写状态机出问题时每一拍的信号都在自己眼皮底下用 ILA 一抓就明白IP 核出问题只能看手册猜。// 可综合的 SPI 写时序地址 8 位 数据 8 位MSB first localparam S_PWRUP 3d0, // 上电等待 S_CS_LOW 3d1, // 片选拉低 S_SHIFT 3d2, // 移出 16 位 S_CS_HIGH 3d3, // 片选拉高 S_GAP 3d4, // 寄存器间间隔 S_READBK 3d5, // 回读校验 S_DONE 3d6; always (posedge clk_25m or negedge rst_n) begin if (!rst_n) begin state S_PWRUP; bit_cnt 5d0; cfg_done 1b0; spi_sclk 1b0; // 空闲低电平符合 AD9680 要求 spi_cs_n 1b1; end else begin case (state) S_PWRUP: begin // 上电后必须等够 500us25MHz 下是 12500 个周期 if (pwr_cnt 16d12500) state S_CS_LOW; else pwr_cnt pwr_cnt 1b1; end S_CS_LOW: begin spi_cs_n 1b0; shift_reg {rom_addr, rom_data}; // 高位在前 bit_cnt 5d16; state S_SHIFT; end S_SHIFT: begin spi_sdin shift_reg[15]; shift_reg {shift_reg[14:0], 1b0}; spi_sclk ~spi_sclk; // 半周期翻转 if (!spi_sclk) bit_cnt bit_cnt - 1b1; if (bit_cnt 5d1 spi_sclk) state S_CS_HIGH; end S_CS_HIGH: begin spi_sclk 1b0; spi_cs_n 1b1; state S_GAP; end S_GAP: begin // 寄存器之间留 2us 间隔避免内部逻辑来不及更新 if (gap_cnt 8d50) begin gap_cnt 8d0; state S_READBK; end else gap_cnt gap_cnt 1b1; end S_READBK: begin if (!rb_match retry_cnt 3) begin retry_cnt retry_cnt 1b1; state S_CS_LOW; // 回读不一致重写 end else begin rom_addr rom_addr 1b1; state (rom_addr LAST_ADDR) ? S_DONE : S_CS_LOW; end end S_DONE: cfg_done 1b1; endcase end end这段状态机的关键是S_PWRUP和S_READBK两个状态。上电延迟计数器看着多余实际上不做这件事ADC 有相当概率完全不响应后续的 SPI 命令表现是“第一次上电必须按复位才能用”。回读校验则是把“玄学问题”变成“可观测问题”的唯一办法只要rb_match拉低retry_cnt就开始涨涨到 3 还不过就停在S_DONE之前的错误态ILA 一抓就能定位到是哪一条寄存器写不进去。S_SHIFT里spi_sclk每拍翻转一次25MHz 时钟下得到 12.5MHz 的 SCLK远低于 AD9680 30MHz 的上限余量足够。想提速就改分频比但别贴着上限跑板子温度一高就容易出错。3.2 寄存器写入序列0x14 采样率模式与 0x16 Lane 使能寄存器序列在代码里做成 ROM 表比一堆if好维护得多reg [15:0] cfg_rom [0:15]; initial begin cfg_rom[0] {8h00, 8h3C}; // 软复位先清干净 cfg_rom[1] {8h00, 8h18}; // 退出掉电模式 cfg_rom[2] {8h08, 8h00}; // 输出接口选 JESD204B cfg_rom[3] {8h14, 8h10}; // 采样率模式 / 时钟分频 cfg_rom[4] {8h15, 8hA3}; // 链路参数组Lane 数与分频比 cfg_rom[5] {8h16, 8h04}; // Lane 数量 4 cfg_rom[6] {8h2B, 8h01}; // JESD204B 链路使能 cfg_rom[7] {8h5E, 8h00}; // 关闭测试图样 end0x15那个0xA3是最容易写错的一个。bit5~bit3 决定 Lane 数量写100就是 4 条 Lanebit2~bit0 决定时钟分频比写011对应分频后得到 1G 采样率所需的内部时钟。这两个字段挤在一个字节里改 Lane 数量时很容易顺手把分频比改掉结果采样率变了线速率跟着变refclk 对不上链路直接不通。0x16单独留一个寄存器写 Lane 使能是这块板子上位机工具的习惯做法实际芯片内部0x15已经带了一部分 Lane 信息。两个寄存器都要写只写一个的表现是“有时能通有时不能通”非常折磨人。提示每次改寄存器表之后先把整张表打印出来对着手册逐条核一遍字段位。这类错误没有仿真能替你发现。3.3 MMCM 参数反推从 125MHz 参考时钟到 500MHz 器件时钟时钟这一块反推的思路是线速率 → refclk → VCO → 各输出分频。线速率 5Gbps 对应 refclk 125MHz。板载晶振是 125MHz 差分正好可以拿来做 MMCM 的输入。VCO 要落在器件允许的范围内取 1000MHz 比较稳那么CLKFBOUT_MULT_F 8.0。剩下三个输出CLKOUT0 1000/8 125MHz给 JESD204B 收发器做 refclkCLKOUT1 1000/4 250MHz给 IP 核做 core_clkCLKOUT2 1000/2 500MHz差分输出给 AD9680 做器件时钟芯片内部再 ×2 得到 1GSPS三个分频都是整数没有小数分频带来的相位抖动这是刻意的选择。312.5MHz 那一档理论上也能凑但 VCO 要么超范围要么得用小数分频两种都不划算。MMCME2_ADV #( .BANDWIDTH (OPTIMIZED), .CLKFBOUT_MULT_F (8.000), // 125MHz x 8 1000MHz VCO .DIVCLK_DIVIDE (1), .CLKIN1_PERIOD (8.000), // 125MHz 输入 .CLKOUT0_DIVIDE_F (8.000), // 125MHz refclk .CLKOUT1_DIVIDE (4), // 250MHz core_clk .CLKOUT2_DIVIDE (2), // 500MHz device clock .CLKOUT2_DUTY_CYCLE (0.500), .STARTUP_WAIT (FALSE) ) u_mmcm ( .CLKIN1 (clk_125_ibuf), .CLKFBIN (clk_fb), .CLKFBOUT (clk_fb), .CLKOUT0 (jesd_refclk), .CLKOUT1 (core_clk), .CLKOUT2 (ad_devclk), .LOCKED (mmcm_locked), .RST (~rst_n), .PWRDWN (1b0) );ad_devclk要送出演变为差分用OBUFDS推出去注意在引脚约束里把它标成时钟专用管脚别随便找个普通 IO。走普通 IO 的时候综合能过上板眼图直接闭合这类问题查起来非常费劲。mmcm_locked必须参与复位逻辑rst_n mmcm_locked才能释放后续模块的复位。少了这一条上电瞬间 MMCM 还没锁core_clk 是抖的状态机跑飞的概率相当高。3.4 XDC 里必须手写的那几行自动生成的约束只管管脚时钟约束得自己写# 输入主时钟 create_clock -name clk_125 -period 8.000 [get_ports clk_125_p] # MMCM 三路输出的生成时钟 create_generated_clock -name jesd_refclk -source [get_pins u_mmcm/CLKIN1] \ -divide_by 8 [get_pins u_mmcm/CLKOUT0] create_generated_clock -name core_clk -source [get_pins u_mmcm/CLKIN1] \ -divide_by 4 [get_pins u_mmcm/CLKOUT1] create_generated_clock -name ad_devclk -source [get_pins u_mmcm/CLKIN1] \ -divide_by 2 [get_pins u_mmcm/CLKOUT2] # refclk 与 core_clk 虽然同源但对 IP 核内部来说是异步关系 set_clock_groups -asynchronous \ -group [get_clocks jesd_refclk] \ -group [get_clocks core_clk] # 收发器参考时钟路径单独约束别让工具自己算 set_max_delay -datapath_only \ -from [get_pins u_mmcm/CLKOUT0] \ -to [get_pins u_jesd_rx/gt_refclk] 0.500set_clock_groups -asynchronous那一段是血泪经验。JESD204B IP 核内部有跨时钟域的逻辑工具不知道你打算怎么处理就按同步时钟去分析然后报出一堆路径违例周期余量是负的。手写这一句之后违例清零但要注意它同时也屏蔽了真正需要检查的路径所以每加一条异步组都要确认相关路径上确实有握手或异步 FIFO。set_max_delay -datapath_only那一行针对的是 refclk 进 IP 核的那一段。这段路径在 IP 核内部没有时序弧工具算不出来只能手动给一个上限。0.5ns 是个经验值实际按板级走线长度调整走线长了就放宽到 0.8ns。4. JESD204B 接收链路IP 核参数、字符对齐与数据面落地数据面是这套工程真正跑起来的部分。链路能不能建起来八成看前三章的配置和时钟能不能稳定收到数据就看这一章的对齐和数据缓冲。4.1 IP 核参数怎么填L/M/F/S 与 SCRJESD204B 接收 IP 核的参数必须和 ADC 侧的寄存器严格对应任何一项对不上都表现为“永远等不到对齐字符”。参数取值说明L4Lane 数量对应0x16 0x04M1参与传输的转换器数单通道模式F2每帧字节数2 字节 16bit正好一个样本S1每帧每转换器样本数K32每个多帧包含的帧数SCR1加扰使能必须与 ADC 端一致CS0控制位不额外传输CF0每帧控制字数SUBCLASS1使用 sysref 做确定性延迟F和S相乘要等于每帧承载的字节数M × S × N / 8要能被 8 整除否则帧结构不成立。IP 核在参数化阶段会检查这些但报错信息往往只提示“参数非法”不会告诉你是哪一项所以填之前先在纸上把帧结构画出来。SUBCLASS选 1 意味着链路依赖 sysref 做多帧对齐。sysref 如果没接IP 核会一直等表现是link_up永远不拉高。切到 Subclass 0 可以绕开 sysref但就失去了确定性延迟同时多帧对齐也失效一般不用。例化时把参数写成localparam分组别直接塞数字localparam integer JESD_L 4; localparam integer JESD_M 1; localparam integer JESD_F 2; localparam integer JESD_S 1; localparam integer JESD_K 32; jesd204b_rx_core #( .L (JESD_L), .M (JESD_M), .F (JESD_F), .S (JESD_S), .K (JESD_K), .SCRAMBLER_EN (1b1), // 必须与 ADC 0x2B 配置一致 .BUFFER_EN (1b1), // 弹性缓冲吸收多 Lane 间偏斜 .LANE_MAP ((0,1,2,3)) // 物理 Lane 到逻辑通道的映射 ) u_jesd_rx ( .refclk (jesd_refclk), .core_clk (core_clk), .sysref (sysref), .rx_serial_p (gt_rx_p), .rx_serial_n (gt_rx_n), .sync_n (adian_sync_n), .m_axis_tdata (jesd_tdata), .m_axis_tvalid (jesd_tvalid), .m_axis_tready (jesd_tready), .link_up (link_up), .lane_align_done(lane_align_done) );LANE_MAP这行不要随便改。板子上 4 条 Lane 的物理顺序和 PCB 走线顺序不一定一致写反了以后数据能收到但 I/Q 是错位的FFT 出来的频谱看起来像是有信号一测 ENOB 只有三四位。4.2 对齐状态机K28.5 检测与相位调整IP 核负责 8b/10b 解码和帧解构但字符边界对齐这件事工程里自己做了一层状态机做兜底。原因是 IP 核的自动对齐偶尔会锁在错误的位置上lane_align_done拉高了数据却是错的。localparam S_WAIT_SYNC 2d0, S_ALIGN 2d1, S_DATA 2d2, S_RELOCK 2d3; always (posedge core_clk or negedge rst_n) begin if (!rst_n) begin state S_WAIT_SYNC; align_cnt 8d0; phase_adj 1b0; end else begin case (state) S_WAIT_SYNC: begin phase_adj 1b0; if (!sync_n) begin // ADC 拉低 sync 请求建链 align_cnt 8d0; state S_ALIGN; end end S_ALIGN: begin if (rx_char_k28_5) begin // 收到 K28.5 起始字符 state S_DATA; end else if (align_cnt 8d255) begin align_cnt 8d0; phase_adj ~phase_adj; // 超时翻转采样相位重试 state S_RELOCK; end else begin align_cnt align_cnt 1b1; end end S_DATA: begin if (!lane_align_done) state S_WAIT_SYNC; end S_RELOCK: begin state S_ALIGN; // 相位已翻转重新找 K28.5 end endcase end end三个细节值得单独说。sync_n拉低之后的 255 拍窗口不是随便定的JESD204B 要求在若干个多帧周期内完成对齐超时就该重试而不是死等。phase_adj翻转的是收发器内部采样相位相当于换一个相位点去找字符边界比整条链路复位快得多。S_DATA里还要持续监视lane_align_done一旦丢了就退回S_WAIT_SYNC这套“自愈”逻辑在温度变化的现场环境里是必需品。4.3 sysref 的建立保持IDELAY 扫描 tapsysref 是 Subclass 1 的确定性延迟基准它必须满足相对于帧时钟的建立保持时间。这一条在仿真里永远看不出来只有上板才暴露而且表现是“跑半小时突然失锁”。工程里用 IDELAY 对 sysref 做延迟扫描从 0 到 31 个 tap 逐个试记录每一档跑多久会失锁// sysref 延迟扫描找到最稳的那一档 tap 值 always (posedge core_clk or negedge rst_n) begin if (!rst_n) begin tap 5d0; scan_state S_SCAN_INIT; end else begin case (scan_state) S_SCAN_INIT: begin idelay_set(tap); // 写入当前 tap run_cnt 32d0; scan_state S_SCAN_RUN; end S_SCAN_RUN: begin // 在这一档上跑 100ms期间失锁次数记到一个表里 if (run_cnt 32d10_000_000) begin ber_log[tap] err_cnt; err_cnt 32d0; tap tap 5d1; scan_state (tap 5d31) ? S_SCAN_DONE : S_SCAN_INIT; end else run_cnt run_cnt 32d1; end S_SCAN_DONE: idelay_set(best_tap); // 取错误最少的那档 endcase end end实测下来有效区间通常只有中间七八个 tap两端都会失锁。取有效区间正中间那一档不要取边缘。扫描过程中顺便就验证了 PCB 上 sysref 走线的质量如果整个 0~31 都跑不通基本可以判定是走线问题而不是代码问题。4.4 数据面AXIS 接 FIFO 与递增自检数据从 IP 核出来是 AXI-Stream 接口接一个异步 FIFO 跨到处理时钟域后面接自检和 FFT。axis_data_fifo_0 u_adc_fifo ( .s_axis_aresetn (rst_n), .s_axis_aclk (core_clk), .s_axis_tvalid (jesd_tvalid), .s_axis_tready (jesd_tready), .s_axis_tdata ({adc_q, adc_i}), // I/Q 各 16bit 拼成 32bit .m_axis_aclk (proc_clk), .m_axis_tvalid (proc_tvalid), .m_axis_tready (proc_tready), .m_axis_tdata (proc_tdata) );FIFO 深度不要低于 2048。突发流量下的瞬时速率远高于平均速率深度不够的表现是偶发丢点而这种丢点在频谱上看不出异常只有做递增自检才能发现。自检逻辑让 ADC 工作在图样模式输出连续递增的样本FPGA 侧比对步进// ADC 测试模式下的连续数据检查步进为 2I/Q 各 1 拼成 32bit always (posedge proc_clk) begin if (proc_tvalid proc_tready) begin if (proc_tdata ! prev_data 32d2) err_cnt err_cnt 1b1; prev_data proc_tdata; end end这个方法比直接看 FFT 快得多。FFT 只能告诉你“有问题”递增自检能告诉你“第几个样本开始有问题”配合 ILA 抓取错误样本前后的波形基本能定位到是配置、时序还是走线。提示自检通过之后再切回正常采样模式不要一上来就接信号源。图样模式是唯一能把“链路问题”和“信号问题”分开的手段。5. 上板才暴露的五个问题现象、原因与处理顺序仿真里全绿、上板全红是高速链路的常态。下面这五条都是上板之后才暴露出来的按出现频率排序。5.1 SPI 写完没反应第一次上电必须手动复位现象工程下载进去cfg_done永远不拉高SPI 总线上看不到任何波形或者有波形但 ADC 完全不理。原因AD9680 上电后内部需要一段时间完成初始化这段时间内对 SPI 命令是无响应的。原代码里没有上电延迟复位一释放就开始写寄存器第一条软复位命令直接丢掉后续所有寄存器的状态都建立在错误的基础上。解决在 SPI 状态机最前面加上电计数器25MHz 时钟下数满 12500 个周期500us再开始。实测这一条加上之后“第一次上电必须复位”的现象彻底消失。计数器数值不要贴着手册下限留一倍余量。5.2 时序分析报大量负余量但功能实际是通的现象综合实现能过时序报告里 JESD 相关路径一片红色负余量从 -2ns 到 -8ns 不等。上板跑起来功能正常不敢忽略又不知道从哪下手。原因MMCM 输出的 refclk 和 core_clk 同源工具默认按同步时钟分析把 IP 核内部本就不需要检查的跨域路径全算进去了。这些路径在设计上就是异步的靠握手和弹性缓冲保证正确性。解决在 XDC 里用set_clock_groups -asynchronous显式声明两组时钟异步同时给 refclk 进 IP 核的那一段加set_max_delay -datapath_only。加完之后违例清零。但每加一条异步组都要回头确认这条路径上确实有异步 FIFO 或者握手信号不是靠“加了就不报了”蒙过去。5.3 sync 拉低后链路反复失锁日志里全是重试现象sync_n周期性拉低状态机在S_ALIGN和S_RELOCK之间来回跳align_cnt每次都数到 255 才超时。原因两个可能。一是 K28.5 检测的时机不对IP 核输出的字符指示信号有延迟状态机在字符真正有效之前就开始判断二是加扰配置不一致ADC 端开了加扰IP 核没开收到的数据永远不可能是 K28.5。解决先查SCR参数和 ADC 的0x2B寄存器是否一致这一条占七成。剩下的三成把rx_char_k28_5打两拍再送进状态机消除 IP 核输出的组合逻辑毛刺。两拍延迟对 255 拍的窗口来说完全可以接受。5.4 数据能收到但每隔几百个样本错一个现象递增自检的err_cnt缓慢增长FFT 频谱底噪抬高ENOB 只有 8 位左右。波形上看不出任何异常样本值也是合理的。原因弹性缓冲深度不够或者多 Lane 之间的偏斜没有被完全吸收。4 条 Lane 的走线长度不可能完全等长PCB 上差 300mil 就对应几十 ps 的偏斜累积到一定程度就吃掉了缓冲余量。解决先把 FIFO 深度从 1024 提到 4096如果错误间隔变长但没消失那就是物理偏斜。这时候去查 PCB 上每条 Lane 的差分对走线长度用热风枪局部加热能加速复现——温度升高时偏斜会变大错误频率明显上升的那条 Lane 就是长度最不匹配的那条。5.5 室温下跑得好好的机箱一热就失锁现象实验室常温下连续跑几小时都没事装进机箱跑四十分钟开始周期性失锁断电冷却再跑又正常。原因sysref 的建立保持时间余量不足。温度升高后器件和走线的延迟特性变化本来刚好卡在边界上的 sysref 相位就滑出去了。解决用 IDELAY 扫描 tap 值把有效区间找出来取中间值别用默认值也别用边界值。同时打开收发器的自适应均衡让接收端在温度变化时自己调均衡参数。这两条一起上之后同一套硬件在机箱里连续跑几天都没再失锁。6. 二次开发把测试工程改成可交付采集链路的改造顺序这套工程能跑通 1G 采样率 Lane4但它本质上是个测试程序。真要往产品上走改造顺序建议按“先改本文还有配套的精品资源点击获取