基于Vivado的AD9680高速ADC采集与JESD204B接收工程实战

发布时间:2026/11/3 3:20:24
基于Vivado的AD9680高速ADC采集与JESD204B接收工程实战 简介面向具备一定FPGA开发经验、希望攻克高速ADC采集链路的工程师这份PDF文档围绕AD9680在Vivado平台上的Verilog测试程序展开聚焦1G采样率、Lane4的JESD204B接收实现可帮助读者理解从芯片配置到链路同步的完整流程。内容覆盖AD9680寄存器初始化、基于PLL的采样时钟生成以及采用状态机组织的JESD204B接收模块并穿插时钟相位对齐、SPI配置时序、链路同步等调试经验与常见问题处理思路适合作为高速数据采集系统开发时的对照参考。资源包共1个文件为单个PDF文档大小约111KB篇幅精炼便于随查随用。目前已有135人学习适合需要快速梳理AD9680配置要点与JESD204B接收框架的开发者参考借鉴。1. 高速ADC采集链路为什么1G采样率Lane4的JESD204B接收值得单独拆一遍去年帮一个做雷达前端的朋友看板子AD9680 输出端示波器眼图漂亮得很FPGA 侧 ILA 抓出来却全是错位数据链路每隔几十秒失锁一次。问题不在 ADC也不在收发器而是 SPI 配置时序、MMCM 相位约束、JESD204B 字符对齐这三件事里有一件没做对。这套基于 Vivado 的 AD9680 测试工程用 Verilog 把 AD 配置、时钟生成、JESD204B 接收三段链路完整串了起来目标就是 1G 采样率、4 Lane、线速率 5Gbps 的稳定接收。适合手里有高速采集板、正在被 Lane 对齐和时钟树折磨的 FPGA 开发者也适合想照着跑通一遍 JESD204B 全流程的新手。2. 工程骨架与时钟架构从 125MHz 输入到 5Gbps 线速率的推导链2.1 目录与模块划分这套工程在 Vivado 下的组织方式比较典型按功能切成配置层、时钟层、协议层、验证层四块顶层只做例化和连线不塞逻辑。常见做法是这样分目录ad9680_test/ ├── rtl/ │ ├── top.v // 顶层例化各子模块 │ ├── spi_cfg/ │ │ ├── spi_master.v // SPI 时序发生器 │ │ └── ad9680_cfg.v // 寄存器配置序列状态机 │ ├── clk/ │ │ └── clk_gen.v // MMCM/PLL 时钟生成 │ ├── jesd/ │ │ ├── jesd204b_rx.v // 接收核心状态机 │ │ └── lane_align.v // 字符对齐与相位调整 │ └── check/ │ └── data_check.v // 递增数据校验、错误计数 ├── constr/ │ ├── pin.xdc // 管脚约束 │ └── timing.xdc // 时钟组、false_path、max_delay ├── sim/ │ └── tb_top.v // 行为级仿真 └── ip/ └── jesd204b_rx_ip/ // Xilinx JESD204B 接收 IP这个划分的好处是配置序列和协议接收解耦改寄存器只动ad9680_cfg.v调对齐只动lane_align.v不会牵一发动全身。顶层top.v里主要就是三件事把 SPI 配置跑完、等 MMCM 锁定、拉起 JESD 接收状态机。我一般会在顶层加一个cfg_done信号只有 SPI 全部写完且回读校验通过才允许 JESD 链路开始同步否则 ADC 还没进入正确模式收发器就在那儿空转ILA 里看到的全是无效字符。2.2 时钟推导312.5MHz Device Clock 与 5Gbps 线速率1G 采样率、4 Lane 的 JESD204B 链路线速率算法是采样率 × 每样本位数 × 过采样因子 ÷ Lane 数。AD9680 是 14 位输出JESD204B 里通常按 16 位2 字节封装M2、L4、F2 的配置下线速率 1000Msps × 16bit × 10/8 ÷ 4 5000Mbps。这个 5Gbps 是收发器参考时钟和 MMCM 输出必须对上的目标值。工程里输入时钟是 125MHz 晶振MMCM 的配置思路是先把 VCO 推到 1GHz再分频出各路// clk_gen.v 核心参数 mmcm_adv #( .CLKIN1_PERIOD (8.0), // 125MHz 输入周期 8ns .CLKFBOUT_MULT_F (8.0), // VCO 125MHz × 8 1GHz .DIVCLK_DIVIDE (1), .CLKOUT0_DIVIDE_F (2.0), // 500MHzJESD core clock .CLKOUT1_DIVIDE (4), // 250MHz数据处理时钟 .CLKOUT2_DIVIDE (8), // 125MHzSPI 配置时钟域 .CLKOUT3_DIVIDE (16) // 62.5MHz辅助逻辑 ) mmcm_adc ( .CLKIN1 (clk_125m), .CLKFBIN (clk_fb), .CLKFBOUT (clk_fb), .CLKOUT0 (jesd_core_clk), .CLKOUT1 (proc_clk), .CLKOUT2 (cfg_clk), .CLKOUT3 (aux_clk), .LOCKED (mmcm_locked), .RST (~rst_n) );参数说明CLKFBOUT_MULT_F决定 VCO 频率CLKOUT0_DIVIDE_F决定 JESD 核时钟500MHz 对应 5Gbps 线速率下每周期 2 位DDR 收发器内部再倍频。CLKIN1_PERIOD必须和实际输入周期一致填错会导致 Vivado 时序分析按错误频率算综合能过、上板必挂。LOCKED信号一定要参与复位逻辑MMCM 没锁定时下游逻辑不能释放复位这是血泪经验。提示AD9680 的 Device Clock 通常要求 312.5MHz 或 614.4MHz 这类与采样率整数相关的频率具体看寄存器分频配置。工程里如果 MMCM 直接出不了这个频率常见做法是让 MMCM 出一路 500MHz 给收发器ADC 侧时钟由外部时钟芯片单独供给两边靠 SYSREF 对齐。2.3 时序约束set_clock_groups 与 set_max_delay 的分工时钟树搭好后XDC 里必须把异步时钟域隔开否则时序分析器会把 JESD 核时钟和配置时钟之间的路径当成同步路径报一堆假违例。工程里timing.xdc的关键几条# 异步时钟组MMCM 各输出之间不分析跨域路径 set_clock_groups -asynchronous \ -group [get_clocks -include_generated_clocks jesd_core_clk] \ -group [get_clocks -include_generated_clocks cfg_clk] \ -group [get_clocks -include_generated_clocks proc_clk] # MMCM 输出到 JESD IP 参考时钟的路径手动约束 set_max_delay -from [get_pins mmcm_adc/CLKOUT0] \ -to [get_pins jesd_rx_inst/refclk] 0.5 # SYSREF 相对帧时钟的建立保持 set_input_delay -clock [get_clocks jesd_core_clk] -max 1.2 [get_ports sysref] set_input_delay -clock [get_clocks jesd_core_clk] -min 0.3 [get_ports sysref]set_clock_groups解决的是“别乱分析”set_max_delay解决的是“该分析的路径给个合理上限”。这两条不写Vivado 要么报大量跨域违例让你无从下手要么把真实的关键路径漏掉。我一般先跑一遍综合看时序报告把报红的跨域路径逐条确认是异步就加 clock_groups是同步但工具没识别就补 max_delay。3. AD9680 配置链路SPI 状态机、寄存器序列与回读校验3.1 SPI 时序状态机AD9680 的 SPI 是 24 位帧格式1 位读写标志 15 位地址 8 位数据时钟空闲低电平片选拉低后第一个上升沿采样。工程里用状态机实现比直接调 IP 核更可控// spi_master.v 发送单帧 localparam S_IDLE 3d0, S_ADDR 3d1, S_DATA 3d2, S_WAIT 3d3, S_DONE 3d4; always (posedge spi_clk or negedge rst_n) begin if (!rst_n) begin state S_IDLE; spi_cs_n 1b1; spi_sclk 1b0; bit_cnt 5d0; end else begin case (state) S_IDLE: begin if (cfg_start) begin spi_cs_n 1b0; shift_reg {1b0, cfg_addr, cfg_data}; // 写标志地址数据 bit_cnt 5d24; state S_ADDR; end end S_ADDR: begin spi_sclk ~spi_sclk; if (spi_sclk) begin // 下降沿移出数据 spi_mosi shift_reg[23]; shift_reg {shift_reg[22:0], 1b0}; bit_cnt bit_cnt - 1; if (bit_cnt 1) state S_WAIT; end end S_WAIT: begin spi_cs_n 1b1; // 片选拉高帧结束 state S_DONE; end S_DONE: begin cfg_done 1b1; state S_IDLE; end endcase end end逻辑说明shift_reg拼出 24 位帧spi_sclk每翻转一次移出一位bit_cnt计到 0 时拉高片选结束一帧。参数上spi_clk建议不超过 30MHzAD9680 手册给的 SPI 上限是 50MHz但实测 30MHz 以下更稳尤其是长排线连接时。cfg_addr和cfg_data由上层配置序列模块给出这个模块只管时序不管写什么。3.2 寄存器配置序列与关键寄存器配置序列用一个小 ROM 或者 case 状态机逐条发顺序不能乱。工程里最关键的三条// ad9680_cfg.v 配置序列片段 // 0x14采样率模式bit4 置 1 选 1G 模式 spi_write(8h14, 8b0001_0000); // 0x16Lane 配置bit5-3 100 表示 4 Lane spi_write(8h16, 8b0000_0100); // 0x15JESD 线速率相关分频0xA3 对应 5Gbps spi_write(8h15, 8hA3); // 0x5ESYSREF 模式选连续或单次 spi_write(8h5E, 8h01);参数说明0x16的 bit5-3 控制 Lane 数100是 4 Lane0x15的0xA3里 bit2-0 是时钟分频比配合 1G 采样率把线速率定在 5Gbps。这些值必须对着 AD9680 手册逐位核对网上抄来的配置换个板子就可能不对。我一般会在配置序列末尾加一条回读把0x16读回来比对不一致就拉高cfg_error顶层据此决定是否重配。3.3 上电延迟与回读校验AD9680 上电后需要等内部基准稳定才能操作 SPI手册要求至少 500us。工程里用一个计数器卡住配置启动// 上电延迟500us 125MHz localparam PWR_DELAY 20d62500; always (posedge cfg_clk or negedge rst_n) begin if (!rst_n) begin pwr_cnt 20d0; cfg_start 1b0; end else if (pwr_cnt PWR_DELAY) begin pwr_cnt pwr_cnt 1b1; cfg_start 1b0; end else begin cfg_start 1b1; // 延迟结束启动配置 end end这段逻辑看着简单但不加就是随机翻车上电瞬间 ADC 内部还没准备好SPI 写进去的配置被吞掉后面 JESD 链路怎么都同步不上ILA 里看sync一直低。回读校验的做法是配置完所有寄存器后逐条读关键寄存器和写入值比对全部通过才置cfg_ok。这一步多花几十微秒能省掉大量“到底是配置没写进去还是链路有问题”的排查时间。4. JESD204B 接收核心状态机、字符对齐与弹性缓冲4.1 接收状态机与同步流程JESD204B 接收分几个阶段检测 SYNC 信号、等待 K28.5 字符、多帧对齐、ILAS 参数解析、数据通道使能。工程里用状态机串起来// jesd204b_rx.v 状态定义 localparam S_IDLE 3d0, S_WAIT_K 3d1, S_ALIGN 3d2, S_ILAS 3d3, S_DATA 3d4, S_ERROR 3d5; always (posedge rx_clk or negedge rst_n) begin if (!rst_n) begin state S_IDLE; lane_align 1b0; err_cnt 16d0; end else begin case (state) S_IDLE: begin if (!sync_status) state S_WAIT_K; // ADC 拉低 SYNC 请求同步 end S_WAIT_K: begin if (rx_data 8hBC) begin // K28.5 字符 lane_align 1b1; state S_ALIGN; end else if (timeout_cnt 16d2000) begin state S_ERROR; // 超时未收到 K 字符 end end S_ALIGN: begin if (align_done) state S_ILAS; // 4 Lane 字符边界对齐完成 end S_ILAS: begin if (ilas_valid) state S_DATA; // ILAS 参数校验通过 end S_DATA: begin if (sync_status) state S_IDLE; // SYNC 拉高链路重同步 if (rx_error) err_cnt err_cnt 1b1; end S_ERROR: begin if (retrain_req) state S_IDLE; // 触发重训练 end endcase end end逻辑说明sync_status来自 ADC拉低表示请求同步S_WAIT_K等 K28.5 字符超时进错误态S_ALIGN做 4 Lane 字符边界对齐S_ILAS解析 ILAS 多帧里的链路参数M、L、F、S 等和本地配置比对S_DATA是正常数据态一旦sync_status变高就回S_IDLE重新同步。参数上timeout_cnt的阈值要按多帧周期算太短会误判太长失锁恢复慢。4.2 字符对齐与相位调整4 Lane 对齐的核心是找到每个 Lane 的字符边界然后调整相位让 4 个 Lane 的数据同时有效。工程里用 K28.5 检测加相位扫描// lane_align.v 相位调整 always (posedge rx_clk) begin if (!lane_align) begin // 逐 Lane 检测 K28.5记录边界位置 for (i 0; i 4; i i 1) begin if (lane_data[i] 8hBC) begin lane_boundary[i] bit_cnt[i]; end end end else if (!align_done) begin // 4 Lane 边界都找到后统一相位 if (lane_boundary[0] lane_boundary[1] lane_boundary[1] lane_boundary[2] lane_boundary[2] lane_boundary[3]) begin align_done 1b1; end else begin ctrl_adj_phase 1b1; // 相位不一致触发调整 end end end这段的关键是“4 个 Lane 边界必须一致才算对齐完成”不一致就发相位调整请求。实际调试时ILA 抓lane_boundary四个值如果某个 Lane 总是差 1 到 2 个 bit多半是 PCB 走线长度差或者收发器内部延迟没配平。工程里还留了一个手动扫描接口通过 VIO 改ctrl_adj_phase的延迟 tap 值从 0 扫到 31看哪个值下align_done稳定拉高。4.3 弹性缓冲与数据校验对齐完成后数据进弹性缓冲跨时钟域送到处理逻辑。工程里用 AXIS FIFO 接axis_data_fifo_0 adc_fifo ( .s_axis_aresetn (rst_n), .s_axis_aclk (rx_clk), .s_axis_tvalid (jesd_rx_tvalid), .s_axis_tready (jesd_rx_tready), .s_axis_tdata ({adc_q, adc_i}), // I/Q 拼接各 16 位 .m_axis_aclk (proc_clk), .m_axis_tvalid (proc_tvalid), .m_axis_tdata (proc_data) );FIFO 深度建议不小于 2048突发流量下深度不够会溢出表现为proc_tvalid断续、数据丢点。数据校验模块做递增检测ADC 配成测试模式输出递增码FPGA 侧逐样本比对adc_data ! prev_data 1就累加error_count。这个方法能快速区分是配置问题数据完全不对还是时序问题偶发错位比直接上 FFT 分析定位快得多。5. 避坑与排查链路失锁、时序违例、配置被吞的现场记录5.1 现象SYNC 一直拉低链路无法同步原因SPI 配置没写进去ADC 还停在默认模式Lane 数和线速率和 FPGA 侧对不上。最常见的是上电延迟不够或者 SPI 时钟太快导致某几位采样错。解决先加 500us 上电延迟SPI 时钟降到 25MHz 以下配置完逐条回读关键寄存器0x14、0x15、0x16比对不一致就重配。回读通过后 SYNC 才会正常拉低。5.2 现象链路能同步但每隔几十秒失锁原因SYSREF 相对帧时钟的建立保持时间不够温度或电压波动时相位漂移多帧边界错位。解决在顶层用 IDELAY 调 SYSREF 相位扫描 0 到 31 个 tap找jesd_status最稳的值。实测 12 到 18 个 tap 之间最稳。同时 XDC 里补 SYSREF 的 input_delay 约束别让工具随便猜。5.3 现象Vivado 时序报告大量跨时钟域违例原因MMCM 多路输出之间没加set_clock_groups工具把异步路径当同步分析。解决按 2.3 节的写法加 clock_groups把 JESD 核时钟、配置时钟、处理时钟分成三组。加完后违例数量会大幅下降剩下的逐条看是不是真实路径真实的补 max_delay 或改逻辑。5.4 现象ILA 抓到的数据偶发不定态原因rx_valid抖动数据在有效窗口边缘被采样或者复位释放不同步。解决对rx_valid做两级打拍滤波复位用同步释放SystemVerilog 断言加一条rx_valid |- !$isunknown(rx_data)仿真阶段就能抓到不定态传播。5.5 现象FIFO 溢出丢点原因FIFO 深度不够或者读写时钟频率不匹配导致长期净流入。解决深度加到 4096 以上检查proc_clk频率是否不低于rx_clk的有效数据率用 ILA 抓 FIFO 的almost_full信号看溢出前的余量。6. 二次开发把测试工程改成可复用的采集前端这套工程跑通后下一步通常是把它从“测试程序”改成“能接实际业务的采集前端”。我的做法是先把配置层参数化把ad9680_cfg.v里的寄存器序列抽成一张表用parameter或localparam数组存换采样率或 Lane 数只改表不改逻辑。比如把L4改成L2同步改0x16的 Lane 位、MMCM 分频、JESD IP 的NUM_LANES三处对齐就不会漏。然后是数据通路。测试工程里 FIFO 后面直接接校验模块实际项目要接的是触发逻辑、降采样、或者 DMA。我一般会在 FIFO 和业务逻辑之间加一层 AXIS 开关用 VIO 控制旁路调试时走校验、上线时走业务不用反复改综合。验证方法上除了递增码校验建议加一个片上 FFT 或者简单的频谱峰值检测接固定频率信号源看峰值位置对不对。这一步能抓到“数据能对齐但样本顺序错”的问题递增码校验抓不到这种。改造点测试工程现状二次开发建议寄存器配置硬编码 case 序列抽成参数表按模式索引Lane 数固定 4 Lane参数化同步改 IP 和约束数据出口校验模块AXIS 开关 业务逻辑链路监控ILA 手动抓加错误计数和自动重训练时钟约束固定 XDC按 MMCM 配置生成约束模板最后说个习惯这套工程里我踩得最狠的一次是改了 MMCM 分频忘了同步改 XDC 里的CLKIN1_PERIOD综合过、实现过、上板数据全错查了两天才定位到约束和实际时钟对不上。从那以后我每次改时钟参数都强制走一遍“改 MMCM 参数 → 改 XDC 周期 → 跑时序报告确认 → 上板 ILA 看时钟质量”四步一步不省。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询