简介:面向 FPGA/ADDA 调试场景的完整测试工程,适合使用 Xilinx 平台做数字波形合成与采集验证的开发者,也适合想通过实例理解 DDS、DAC、ADC 数据链路的入门者。工程基于“DDS IP 数字波形合成 DAC”方案,以 50MHz 时钟驱动 DAC 输出正弦波,并将输出自环至 ADC;经 MMCM 分频后为 ADC 提供 25MHz 采样时钟,配套 ILA 捕获不少于 2048 样点,利用 VIO 切换频率字可生成 1MHz 与 3MHz 正弦信号,再通过 Matlab 分析频谱验证链路正确性。资源内对黑金 AN108 低通滤波器通带(约 0-20MHz)及 34 针插头装插位置也做了说明,便于规避常见硬件错误。压缩包共 613 个文件,约 46.11MB,主要包含 Verilog/VHDL 源码、Vivado 工程与约束(xdc/xci)、仿真与运行脚本(do/sh/tcl/bat)、ILA 采集数据 CSV 以及 Matlab 的 m 文件,覆盖从工程构建、板级调试到频谱分析的主要环节,整体结构清晰,便于按模块查阅。已有 319 人学习下载,适合快速复现 DDS-DAC-ADC 自环链路,并熟悉 ILA/VIO/Matlab 联合调试流程。 拿到一块带AD/DA的FPGA板卡,我向来习惯先扔给它一个DDS信号,再看它吐出来的波形对不对。这套动作看着简单,做扎实了,整个信号链路的健康度一测便知;做不好,后面调算法的时候最容易被一堆相互纠缠的问题拖死。今天就把我平时一直在用的DDS ADDA测试工程从头到尾拆一遍,从DDS IP配置、DAC输出、ADC回采到踩过的坑,全部按实测经验讲清楚,给所有需要快速验证ADDA板卡的新手和硬件工程师做个参考。
这套测试工程解决的核心问题很简单:在你还没开始写复杂的数字信号处理逻辑之前,先用一个已知频点、已知幅度的正弦波,把FPGA到DAC、DAC到外部回环、外部回环再到ADC、ADC回FPGA这整条信号链路跑通,确认每一个环节的数据格式、时序、幅度关系都是对的。它能帮你把"板子坏了""芯片没焊好""配置不对""代码写错了"这四类问题快速区分开,省下大量瞎猜的时间。
1. 测试工程的整体思路与信号链路设计
1.1 为什么测试工程的本质是"建立一个可信基准"
做ADDA调试最怕的不是板子有问题,而是你不知道问题出在哪一环。比如ADC读回来的数据乱跳,到底是DAC输出的信号本身不干净,还是PCB走线串扰,还是ADC配置错误,又或者是FPGA侧数据采集逻辑有时序问题?如果没有一个可信基准,排查起来就是大海捞针。
DDS正好能提供这个基准。直接数字频率合成的最大特点就是频率、相位、幅度全部可控且可预测。我只要在FPGA里配置一个1kHz的正弦波,理论上就能算出DAC输出引脚上每个时刻的电压值,也能算出ADC采样后回到FPGA的数值应该长什么样。有了这个"预期值",再去对比实测值,哪里不对一目了然。所以DDS ADDA测试工程的重点不只是"把DDS跑起来",而是"建立一个能让你快速判断链路健康状况的对照坐标系"。
在设计这个工程时,我遵循了几个原则:第一,信号链路尽量简单,减少可变因素;第二,所有关键参数必须能从代码或寄存器里直接读出来;第三,测试点要留足,DDS输出、DAC输出、ADC输入、ADC输出都要能观测。这套工程我在多个项目里复用,每次新板卡上手都是先跑一遍它,稳定可靠。
1.2 信号链路组成与DDS原理速览
完整测试链路由四段组成:
- FPGA内部:DDS IP核生成数字正弦波数据
- FPGA到DAC:数据总线按DAC芯片时序输出,DAC转换为模拟信号
- 模拟回环:DAC输出信号通过板载走线或跳线连接到ADC输入
- ADC回采:ADC将模拟信号数字化,数据回到FPGA供ILA或逻辑分析
整个链路里,DDS是信号源头,它的工作原理用一句话概括就是"用一个累加器去查一张正弦表"。核心结构是相位累加器,每个时钟周期累加一个固定的相位增量,累加器的溢出频率就是输出频率;累加器的当前值作为查找表的地址,吐出对应的正余弦幅度值。
我一般把DDS比作一个钟表:相位累加器就像秒针,走得快慢由相位增量决定,查表则是把"指针位置"翻译成"具体数值"。只要时钟稳定,这个表查出来的波形就一定稳定,不受温度、供电影响,这也是DDS能当基准信号源的根本原因。
2. DDS部分的工程实现与参数选择
2.1 DDS IP核配置与频率分辨率估算
在Vivado里,DDS对应的是Xilinx的DDS Compiler IP核。配置界面里几个关键选项,我在测试工程里是这么设置的:
- Configuration:选择Sine wave(也可以用Sin and Cos,但纯测试波形用单路正弦就够了,少占资源)
- Phase Increment Programmability:选择Programmable,方便运行时改频率
- Output Width:12位或14位,取决于后级DAC的位宽
- Phase Width:32位,这是相位累加器的位宽,直接决定频率分辨率
频率分辨率的计算公式是:
[ \Delta f = \frac{f_{clk}}{2^{PhaseWidth}} ]
举个例子,如果系统时钟是100MHz,相位宽度32位,那么频率分辨率大约是 ( 100000000 / 4294967296 \approx 0.0233 ) Hz。也就是说,在这个配置下,你理论上可以输出精确到0.0233Hz的任意频率信号。对于ADDA测试来说,这个精度绰绰有余。
而输出频率的计算公式是:
[ f_{out} = \frac{\Delta \theta \times f_{clk}}{2^{PhaseWidth}} ]
其中 ( \Delta \theta ) 就是你在配置或运行时写入的相位增量。反过来,如果你想知道输出某个频率需要写入多大的相位增量,用:
[ \Delta \theta = \frac{f_{out} \times 2^{PhaseWidth}}{f_{clk}} ]
我实际测试时,经常直接在测试文件里算好这个值再写进寄存器,省得每次在IP配置界面里翻来翻去。
2.2 波形输出与位宽处理的几个细节
DDS IP输出的数据位宽通常比DAC输入位宽要大,比如DDS输出12位,DAC也是12位,那就直接对接,不需要额外处理。但如果你在DDS里面配置了16位输出、DAC只有12位,那就要注意截断策略。个人建议在IP核内部就把输出位宽设置成和DAC一致,让工具自动处理截断,不要在外部逻辑里手动截位,那样容易引入不必要的直流偏置。
另外还有一个容易被忽略的点:DDS Compiler IP的Axi接口必须正确握手,否则数据不会更新。很多新手只把s_axis_phase_tvalid拉高就完事,但忽略了s_axis_phase_tdata的同步变化时序,导致改频率没反应。我的习惯是把tvalid直接拉高,tdata作为频率控制字寄存器,每次更新时先给tvalid拉低一个周期、更新tdata、再拉高tvalid,这套时序百试不爽。
如果你像我一样用SystemVerilog,DDS IP的示例代码可以直接生成,然后包一层自己写的寄存器读写逻辑,用AXI-Lite总线去控制频率控制字,这样在测试时就能通过上位机实时改频率,不需要重新综合。工程刚起来时我甚至用VIO(Virtual IO)来改频率,连上位机都省了,特别适合纯硬件验证阶段。
3. ADDA链路验证与数据核对
3.1 实测回环排查:第一步先看直流,再看正弦
拿到一块新板卡,我不会一上来就测高频信号,而是按"直流 -> 低频正弦 -> 高频正弦"的顺序逐步推进。整个过程分三步走:
第一步,把DDS配置成输出一个正满量程的直流电平(就是相位增量写0,查表输出固定最大值)。这时候DAC输出应该是一个恒定的高电平,用万用表或者示波器直流档一测就知道DAC的编码极性有没有搞反。如果配置的是正满量程、测出来是最低电平,那大概率是DAC的数据格式或偏移二进制编码出了问题。
第二步,把DDS配置成输出一个低频正弦波,比如100Hz。这个频点下,示波器可以非常直观地看到波形形状、幅度大小,也能验证DAC输出端的运放增益和滤波是否正常。
第三步,再把频率往上抬,测1kHz、10kHz、100kHz等常用频点,观察幅度是否平坦、波形是否失真。ADC回采数据也是同样的思路:先让DAC直连通路上没有任何衰减地送到ADC输入端,查看ILA采回来的数据是否与DDS送出的数据一致或呈线性关系。
有一个细节特别重要:DAC输出到ADC输入的模拟回环,幅度一定要控制在ADC的输入量程以内。ADC输入饱和后采回来的正弦波会削顶,波形看起来像一个方波,有人会误以为是链路异常,其实是幅度超了。我一般会留6dB以上的余量,宁可信号小一点也不要削波。
3.2 用ILA和MATLAB做时域与频域双重确认
ADDA测试里,ILA(Integrated Logic Analyzer)是最重要的观测工具,但只盯着ILA的波形图远远不够。我的习惯是:先把ILA采到的数据导出成CSV文件,然后丢到MATLAB或Python里做FFT分析。
具体做法是,在Vivado里配置ILA的采样深度为65536或131072,采样时钟用ADC的输出时钟,捕获通道选择ADC数据总线。触发条件用ADC数据的上升沿或DDS输出数据的某个固定值,能稳定的抓到一段数据。导出的CSV第一列一般是采样序号,第二列开始是数据;导入MATLAB后,先减掉直流分量,再做FFT。
这一步能看出很多东西:如果频谱图上在设定频率处有一个干净的峰值,底噪平坦,说明链路工作正常;如果出现明显的谐波分量,可能是DAC的非线性或运放削波;如果在非目标频率处出现峰值,那可能是时钟串扰或电源噪声耦合。
我还习惯在FFT之后顺手算一下SFDR(无杂散动态范围)和SNR(信噪比)。测试工程中得到的SFDR如果比芯片手册标称值低很多,别急着怪芯片,先检查DAC输出端的重构滤波和ADC输入端的抗混叠滤波有没有焊错。
4. 常见问题与排查技巧实录
4.1 典型问题速查表
这里把我在DDS ADDA测试中遇到过的高频问题整理成一张表,方便大家排查时对照:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| DAC输出为恒定高/低电平 | 编码格式配置错,如用二进制补码当偏移二进制 | 先输出直流,对比编码表和实际电压 |
| 输出正弦波有明显台阶 | 输出端没有重构滤波电路,或示波器没开带宽限制 | 加RC滤波或用示波器20MHz带宽限制 |
| ADC采回的数据幅值只有预期的一半 | DAC和ADC之间阻抗不匹配,或者信号被电阻分压 | 实测DAC输出引脚电压,对比ADC输入引脚电压 |
| ADC数据乱跳、无规律 | 采样时钟与数据线时序未满足建立保持时间 | 检查PCB走线长度,或调整IDELAY/时序约束 |
| 高频信号幅度显著下跌 | 运放带宽不足,或走线寄生电容过大 | 对照运放手册增益带宽积,用短走线 |
| ILA里看到波形有断点 | 采样时钟不稳定,跨时钟域造成亚稳态 | 确保ILA和ADC使用同一时钟域采样 |
4.2 三个"不写在手册里"的避坑经验
第一个坑:DAC输出和ADC输入之间,永远不要直接飞线连。我最早在某块评估板上图省事,用杜邦线把DAC输出和ADC输入接在一起,结果高频信号一路衰减得厉害,波形还带毛刺。原因是杜邦线的分布电感和电容在高频下会形成谐振峰。正规做法是走PCB上的回环通路,如果板上没有,至少要用同轴跳线,并且越短越好。
第二个坑:用ILA观测ADC数据时,采样时钟不要随便用系统时钟,最好直接用ADC的采样时钟。很多开发板为了让数据对齐,ADC输出数据随路时钟是DDR形式的,需要用逻辑把它转成单沿再采样。如果你图省事直接用系统时钟采样,那么采到的数据大概率会周期性错位。稳妥的办法是让ILA的采样时钟和数据采集逻辑使用同一个由MMCM生成的、与ADC随路时钟同步的时钟。
第三个坑:DDS配置成高频输出时,注意杂散。DDS在输出频率接近 ( f_{clk}/2 ) 时,输出频谱里会出现大量镜像频率,这是奈奎斯特采样的固有问题,不是bug。你需要确保DAC末级有重构滤波器把它滤掉,或者在测高频时挑选低于 ( 0.4 \times f_{clk} ) 的频率点,用的时候心里有数。
有一次我在测试时发现DAC波形纹波特别大,排查了半天,最后发现是DDS输出的数据位宽和DAC输入位宽不匹配,截断后引起的量化噪声被后级运放放大了。从那以后,我每次创建工程都会先核对一遍数据位宽,确保从DDS到DAC再到ADC的整条数据通路位宽一致或明确处理过,绝不留下隐性截断。
做ADDA测试工程,我的核心体会是:先笨后巧。先用最直白的直流电平测试确认编码方向,再用低频正弦确认基本波形,然后才逐步上频率、上精度。每一步都把预期值写在纸上,实测数据出来直接对比,不合格就停下来查,合格再往前走。这套工程文件我保存了好几个版本,从最初的简单DDS回环,到后来加了频率控制寄存器、加了FFT分析脚本,每次新项目都能在此基础上快速迭代。芯片型号会变,频率指标会变,但"创建一个可信基准再去验证链路"的思路从来没变过,这就是这套测试工程最大的价值。
本文还有配套的精品资源,点击获取