ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

FPGA实战:AD9680 1GSPS采样与JESD204B四Lane接收链路

FPGA实战:AD9680 1GSPS采样与JESD204B四Lane接收链路

简介:面向具备一定FPGA开发经验、希望攻克高速ADC采集链路的工程师,这份资料聚焦Vivado平台下AD9680的Verilog测试程序实现,围绕1G采样率、Lane4的JESD204B接收场景,拆解寄存器初始化配置、PLL稳定时钟生成与接收状态机设计三个关键环节,并附时钟相位对齐、SPI配置时序、链路同步等调试经验与常见问题处理思路。资源包共1个PDF文件,约111KB,以图文与代码片段结合的方式呈现,便于对照工程实践查阅。已有135人学习,适合需要快速理解AD9680配置流程与JESD204B接收框架、并在实际项目中调整优化的开发者参考。

1. 一块板子跑通 1G 采样率:AD9680 与 JESD204B 的 FPGA 接收链路

第一次把 AD9680 接上板子、烧进 bit 流、打开 ILA 却看到满屏乱跳的数据时,我盯着屏幕愣了很久——时钟给了、SPI 也写了,链路就是不同步。这个项目要解决的正是这类问题:在 Vivado 下用 Verilog 搭一套完整的 AD9680 测试程序,覆盖 SPI 寄存器配置、MMCM 时钟树生成、JESD204B 四 Lane 接收与数据对齐,目标是把 1GSPS 采样率、Lane4 的数据流稳定收进 FPGA。它适合已经摸过 FPGA、但第一次啃高速 ADC 与 JESD204B 协议的开发者,也适合手上有一块 ADC 采集板、需要一份能跑起来的参考工程的人。下面按“能干什么、怎么跑、怎么改”的顺序拆开讲。

2. 工程结构与技术选型:三个模块撑起整条接收链路

2.1 顶层划分与目录组织

这套工程在 Vivado 里的组织方式不复杂,核心就是三个功能块加一个顶层把它们串起来。我一般会按下面的结构建工程,方便后面单独仿真和上板调试:

ad9680_jesd_test/ ├── rtl/ │ ├── top.v # 顶层,例化各子模块并连时钟/复位 │ ├── ad9680_spi_cfg.v # SPI 状态机,写 AD9680 寄存器 │ ├── clk_gen.v # MMCM/PLL,生成 device clk 与 gt refclk │ ├── jesd204b_rx.v # JESD204B 接收与 Lane 对齐状态机 │ └── data_check.v # 递增数据检测 / 简单 FFT 前处理 ├── sim/ │ └── tb_top.v # 行为级仿真,注入同步字与测试数据 ├── constr/ │ └── top.xdc # 时钟约束、set_clock_groups、引脚分配 └── ip/ └── jesd204_rx/ # Xilinx JESD204B RX IP 配置

技术选型上,器件侧用 Xilinx 7 系列或 UltraScale 的 GTX/GTH 收发器承载 JESD204B 物理层,协议层直接例化 JESD204B RX IP 核,省掉 8B/10B 编解码和弹性缓冲这些容易写错的活;SPI 配置和对齐逻辑则自己用 Verilog 写状态机,因为这两块恰恰是最需要按板子实际情况调的地方。选 Verilog 而不是 SystemVerilog 做主体,主要是为了兼容老工程和综合工具链,断言部分可以单独用 SystemVerilog 写在一个文件里。

2.2 关键参数:Lane4、1GSPS 与线速率怎么对上

很多人卡在第一步:采样率和线速率到底怎么换算。AD9680 双通道 14 位、1GSPS 采样,JESD204B 用 4 个 Lane 传输时,线速率不是拍脑袋定的。按 M=2(两个转换器)、L=4、F=2、S=1 这组参数,每帧 2 个字节、每多帧 1 帧,线速率大致落在 5Gbps 量级。工程里 IP 核的 Line Rate 就填 5000Mbps,这个值必须和 ADC 端配置的 Lane 数、分频比严格对应,差一点链路就起不来。

参数取值含义
L4物理 Lane 数量
M2转换器个数(I/Q 双通道)
F2每帧字节数
S1每多帧包含的帧数
Line Rate5000Mbps收发器线速率
SCR1加扰使能,需与 ADC 端一致

这张表建议直接抄进工程注释里,改参数时逐项核对。我见过有人只改了 L 没改 F,综合能过、上板必挂,排查半天才发现是帧结构对不上。

2.3 时钟架构:MMCM 同时喂 ADC 和收发器

时钟是这套系统里最容易翻车的地方。AD9680 需要一路 device clock,JESD204B 收发器又需要独立的 gt refclk,两者最好同源但要在约束里声明异步关系。工程里用 MMCM 从板载 100MHz 或 125MHz 晶振派生:

// MMCM 生成 ADC 采样时钟与 JESD 参考时钟 mmcm_adv #( .CLKIN1_PERIOD(8.0), // 输入 125MHz .CLKFBOUT_MULT_F(8.0), // VCO = 1GHz .CLKOUT0_DIVIDE_F(2.0), // 500MHz 给逻辑 .CLKOUT1_DIVIDE(4) // 250MHz 给 gt refclk ) mmcm_adc ( .clkout0(adc_clk), .clkout1(gt_refclk), .locked(mmcm_locked) );

CLKFBOUT_MULT_F决定 VCO 频率,CLKOUT0_DIVIDE_F和CLKOUT1_DIVIDE分别决定两路输出。参数不是随便填的:VCO 要落在器件允许范围内,输出频率要满足 ADC 采样时钟和收发器参考时钟的规格。改频率时先算 VCO,再算分频,最后回 XDC 里同步改create_clock的周期值,三处对不上时序分析就会报一堆假错。

提示:MMCM 输出到 JESD IP 的参考时钟路径,Vivado 有时会报时序违例,常见做法是在 XDC 里对该路径加set_max_delay手动约束,而不是放任工具去猜。

3. SPI 配置 AD9680:状态机写法与时序细节

3.1 上电延迟与寄存器写入顺序

AD9680 的 SPI 配置是整条链路的地基,地基没打好,后面 JESD 怎么调都白搭。第一个血泪经验:上电后必须等一段时间再操作 SPI,芯片内部电源和基准还没稳,急着写寄存器它会随机不响应。工程里用一个计数器做上电延迟:

// 上电延迟,等待 ADC 内部稳定后再发起 SPI 配置 always @(posedge clk_25m or posedge rst) begin if (rst) begin pwr_delay_cnt <= 0; cfg_start <= 1'b0; end else if (pwr_delay_cnt < PWR_DELAY_MAX) begin pwr_delay_cnt <= pwr_delay_cnt + 1; end else begin cfg_start <= 1'b1; // 延迟结束,启动配置状态机 end end

PWR_DELAY_MAX按芯片手册的上电时序要求取值,宁长勿短。cfg_start拉高后才允许 SPI 状态机开始跑,这样避免了上电瞬间写寄存器被忽略的问题。

寄存器写入要按顺序来,一般先配采样率模式、再配 Lane 数量和分频、最后配 JESD 相关项。工程里用任务封装单次写操作:

task spi_write; input [7:0] addr; input [7:0] data; begin cs_n = 1'b0; spi_send({1'b0, addr}); // 最高位 0 表示写 spi_send(data); cs_n = 1'b1; end endtask // 关键寄存器配置 spi_write(8'h14, 8'b0001_0000); // 1G 采样率模式 spi_write(8'h16, 8'b0000_0100); // Lane4 使能

spi_send内部按位在 SPI 时钟沿移出数据,cs_n的拉低拉高包住整个地址加数据的传输。注意 AD9680 的 SPI 时钟空闲电平要求为低,用状态机逐位实现比直接套 IP 核更可控,实测也更稳。

3.2 回读校验:别写完就当成功

写完寄存器一定要回读校验,尤其是 JESD 相关配置项。SPI 读操作的地址最高位置 1,读回的数据和写入值比对,不一致就重试或报错。这一步看着繁琐,但能帮你把“配置没生效”和“链路本身有问题”两类故障分开,省下大量瞎猜的时间。

// 写后回读校验 spi_write(8'h16, 8'b0000_0100); spi_read (8'h16, rd_data); if (rd_data[7:0] !== 8'b0000_0100) begin cfg_error <= 1'b1; // 配置未生效,置错误标志 end

cfg_error可以接到 LED 或 ILA 触发条件上,上板时一眼就能看出配置阶段有没有过。

4. JESD204B 接收与 Lane 对齐:状态机怎么写才不丢锁

4.1 IP 核参数与 SYNC 信号时序

JESD204B 接收端直接例化 Xilinx 的 RX IP 核,参数必须和 ADC 端一一对应:L=4、M=2、F=2、SCR=1,Line Rate 填 5000Mbps。IP 核例化后别急着跑数据,先确认 SYNC 信号时序——ADC 拉低 SYNC 请求同步后,FPGA 端要在规定帧周期内收到有效数据,否则链路会反复重训。

jesd204b_rx #( .NUM_LANES(4), .NUM_LINKS(1), .LANE_MAP("(0,1,2,3)"), .SCRAMBLER_EN(1), // 必须与 ADC 端加扰设置一致 .BUFFER_EN(1) // 弹性缓冲,吸收时钟域差异 ) jesd_rx_inst ( .sysref(sysref), .sync(sync_status), .rx_clk(rx_clk), .rx_data(rx_data), .rx_valid(rx_valid) );

SCRAMBLER_EN和BUFFER_EN是最容易配错的两个开关,加扰不一致数据全是乱的,弹性缓冲不开则跨时钟域容易溢出。sysref必须和 ADC 端同步发送,相位关系靠 IDELAY 调。

4.2 字符对齐状态机与 K28.5 检测

IP 核输出数据后,还要自己做 Lane 对齐。核心思路是检测 K28.5 同步字符,确定字符边界后启动对齐。工程里的状态机大致是这样:

always @(posedge rx_clk or posedge rst) begin if (rst) begin state <= SYNC_INIT; lane_align <= 1'b0; end else begin case (state) SYNC_INIT: if (rx_sync) begin lane_align <= 1'b1; state <= DATA_ALIGN; end DATA_ALIGN: if (!rx_valid) begin align_counter <= align_counter + 1; if (align_counter > 16) begin ctrl_adj_phase <= 1'b1; // 调整采样相位 state <= SYNC_INIT; end end endcase end end

rx_sync有效表示链路进入同步,lane_align置位后开始对齐;DATA_ALIGN状态下如果rx_valid持续无效超过阈值,就触发相位调整并回到初始态重试。这个阈值不能设太小,否则正常抖动也会触发重训,链路永远稳不下来。

4.3 数据校验:递增模式先跑通再上真实信号

对齐完成后,别急着接信号源看频谱。先把 ADC 配成测试模式输出递增数据,在 FPGA 里做连续性检查:

reg [15:0] prev_data; always @(posedge adc_clk) begin if (adc_data != prev_data + 1) begin error_count <= error_count + 1; // 记录不连续次数 end prev_data <= adc_data; end

error_count为零说明配置和时序都没问题,这时再接真实信号做 FFT 才有意义。这个方法能快速区分“配置错”和“时序错”,比一上来就看频谱高效得多。

5. 避坑与排查:五个上板才会暴露的问题

5.1 链路反复失锁,ILA 里 sync 一直抖

现象是 ILA 抓到的sync_status频繁跳变,链路始终进不了稳定接收。原因多半是 SYNC 信号相对帧时钟的建立保持时间不够,或者 IP 核的 ILAS 超时设置过短。解决办法是用 IDELAY 扫描 sysref 相位,找到稳定窗口;同时把sysref_capture_mode改成手动控制,放宽超时阈值。我一般会写个扫描循环,从 0 到 31 逐个 tap 试,遇到jesd_status == GOOD就停。

5.2 时序分析报一堆跨时钟域违例

现象是综合实现后时序报告里 rx_clk 和 adc_clk 之间大量违例。原因是两个时钟同源但相位关系没声明,工具按同步路径去分析。解决是在 XDC 里加set_clock_groups -asynchronous,把 JESD 接收时钟和 ADC 采样时钟声明为异步组,同时对 MMCM 到 IP 的参考时钟路径加set_max_delay手动约束。

5.3 温度一升高就偶发误码

现象是常温跑得好好的,环境温度上来后 BER 上升甚至失锁。原因是收发器均衡参数在温漂下不再最优。解决是开启 RX 自适应均衡,把RX_DFE_KL_CFG2和RX_OS_CFG按器件手册推荐值配置,并在初始化后加自动重训练机制,检测到连续错误就重新发同步请求。这类问题仿真发现不了,只能上板实测。

5.4 FIFO 在大流量下溢出丢数

现象是突发数据段出现丢样本。原因是采集 FIFO 深度不够,处理时钟跟不上接收速率。解决是把 FIFO 深度加到 2048 以上,并确认m_axis_aclk的处理侧时钟频率足够;同时用 System ILA 直接触发抓异常波形,比逐级打印高效。

5.5 SPI 写完寄存器 ADC 没反应

现象是配置流程跑完,但 ADC 输出不符合预期。原因可能是上电延迟不够、SPI 时钟空闲电平不对,或者写后没回读。解决是加长上电延迟、确认 SPI 模式设置、每次写完做回读校验,把cfg_error接到 ILA 触发条件上,配置阶段的问题就不会被带到后面。

6. 二次开发:把测试工程改成你自己的采集系统

这套工程跑通之后,真正的工作才刚开始——把它改成能用的采集系统。我的习惯是先动数据通路,再动触发逻辑,最后才碰时钟。数据通路方面,把data_check模块换成 AXIS 接口的 FIFO 加 DMA,I/Q 两路拼成 32 位送进axis_data_fifo,下游接处理时钟域:

axis_data_fifo_0 adc_fifo ( .s_axis_aresetn(rst_n), .s_axis_aclk(jesd_rx_coreclk), .s_axis_tvalid(jesd_rx_tvalid), .s_axis_tready(jesd_rx_tready), .s_axis_tdata({adc_q, adc_i}), // I/Q 拼接 .m_axis_aclk(processing_clk) );

jesd_rx_coreclk是接收侧时钟,processing_clk是处理侧时钟,FIFO 负责跨域;深度按突发长度留余量,别卡着最小值用。触发逻辑可以加一个阈值比较器,超过门限就锁存前后若干样本,配合 ILA 做在线抓取。

验证方法上,我建议分三层走:第一层用递增数据检查确认链路无误,第二层接固定频率信号做 FFT 看 ENOB,第三层才是真实信号长时间跑误码统计。每层过了再进下一层,出问题能立刻定位到是哪一段。

改造点原工程二次开发建议
数据出口简单校验AXIS FIFO + DMA
触发无阈值比较 + 样本锁存
时钟固定 MMCM参数化,按板子改
校验递增检测加 SystemVerilog 断言

断言这块值得单独提一句,用assert property在rx_valid有效时检查rx_data无不定态,仿真阶段就能拦住不少问题:

assert property (@(posedge rx_clk) disable iff (!reset) rx_valid |-> !$isunknown(rx_data)) else $error("ADC 数据出现不定态!");

最后说个教训:JESD204B 的 Lane 延迟补偿一定要放在最后调,先把时钟树完全稳定下来再碰对齐参数,顺序反了就是无限重训。从那以后我每次上板都强制按“上电延迟 → SPI 回读 → 时钟锁定 → 递增校验 → 真实信号”这个顺序走一遍,再没出现过调了半天发现是地基问题的窘境。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表