ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

FPGA DDS ADDA测试工程:数字波形闭环与回环验证

FPGA DDS ADDA测试工程:数字波形闭环与回环验证 简介面向FPGA数字信号处理学习者的DDS与ADDA回环测试工程基于Zynq底板与黑金AN108模块以50MHz时钟驱动DDS IP合成正弦波经DAC输出再自环回ADC由MMCM分频提供25MHz采样时钟用ILA采集不少于2048个样点并配合Matlab做频谱分析还可通过VIO改频率字生成1MHz与3MHz信号验证工程特别提示AN108为34针插头、1脚须与底板对齐低通滤波器通带约0-20MHz。包内共613个文件、约46.11MB以75个Verilog源文件、25个VHDL文件、16个XDC约束、11个XCI配置及19个DCP检查点为主另含DO/SH脚本、CSV采样数据、ILA与BIT文件覆盖源码、约束到验证数据的完整链路。已有321人学习读者可获得可复现的工程与约束、现成的采样数据与Matlab分析脚本并借助脚本快速搭建环境、排查插装与时钟配置问题。1. DDS ADDA 测试工程文件一块板子上的数字波形闭环调一块新打的 ADDA 板最怕的是 DAC 有输出、ADC 也能采但两边对不上频率差一点、波形削顶、频谱上多出一排谐波。这份 DDS ADDA 测试工程文件就是把这个闭环拆开FPGA 内部用 DDS 产生正弦、三角或方波经 DAC 送出再被 ADC 采回最后通过在线抓数或串口把数据取出来做 FFT。它适合刚拿到 ADDA 板、需要快速验证收发链路的人也适合想把 DDS 频率控制字、LUT 量化、时钟域这些参数一次讲清的人。工程文件不是“点开就能跑”的演示里面每个参数都会影响回环结果下面按链路拆。2. 拆开工程看链路DDS 相位累加器、LUT 与 ADDA 回环工程文件拿到手先别急着编译。DDS ADDA 测试的链路一般分成三段数字侧由相位累加器和波形 LUT 产生采样点接口侧把并行或串行数据送到 DAC模拟侧再通过回环进入 ADC最后在 FPGA 内部被采集和缓存。三段里任何一段的位宽、时钟或数据编码对不上现象都可能表现成“波形不对”但根因不在同一个地方。先把这个结构看清楚后面改参数才不会靠猜。2.1 DDS 核心参数相位累加器位宽、频率控制字与 LUT 深度DDS 的核心是一个相位累加器加一张波形表。每个系统时钟相位累加器加上频率控制字累加器的高位作为 LUT 地址LUT 输出对应幅度的采样点。输出频率由三个量决定系统时钟f_clk、相位累加器位宽PHASE_W、频率控制字freq_word。三者关系是f_out freq_word * f_clk / 2^PHASE_W反过来配置时用freq_word round(f_out * 2^PHASE_W / f_clk)。这个公式看起来简单实际踩坑最多的是把 LUT 地址位宽当成累加器位宽来算结果频率差一个整数倍。一个最小化的 DDS 核心通常长这样module dds_core #( parameter PHASE_W 32, // 相位累加器位宽决定频率分辨率 parameter LUT_AW 12, // LUT 地址位宽深度 2^LUT_AW parameter DATA_W 12 // 输出数据位宽和 DAC 位数对齐 )( input wire clk, input wire rst_n, input wire [PHASE_W-1:0] freq_word, // 频率控制字 output reg [DATA_W-1:0] dac_data ); reg [PHASE_W-1:0] phase_acc; wire [LUT_AW-1:0] lut_addr phase_acc[PHASE_W-1 -: LUT_AW]; always (posedge clk or negedge rst_n) begin if (!rst_n) phase_acc {PHASE_W{1b0}}; else phase_acc phase_acc freq_word; // 每个时钟累加一次 end // 实际工程里用 LUT IP 或 case 表替换 always (posedge clk) begin dac_data sine_lut[lut_addr]; end endmodule这段代码里PHASE_W决定频率分辨率LUT_AW决定波形表深度DATA_W要和 DAC 的数据位宽对齐。lut_addr取的是相位累加器的高LUT_AW位也就是把相位圆等分成2^LUT_AW份。这样做的代价是相位截断会在频谱上产生杂散如果回环测试里发现底噪偏高先别怀疑 ADC把LUT_AW从 10 加到 12 或 14 再看一次很多时候杂散会明显下去。freq_word的位宽必须和PHASE_W一致综合工具不会替你检查这个位宽短了会静默截断现象就是输出频率和设定值对不上。2.2 ADDA 回环链路DAC 接口、ADC 采样与时钟域DAC 接口常见的有并行总线和串行总线两类。并行 DAC 一般把dac_data直接挂在数据脚上配合 DAC 时钟输出串行 DAC 则要通过 SPI 或类似时序先配置寄存器再按帧发送采样点。ADC 侧同样有并行和串行之分低速板常用并行 ADC高速板更常见 LVDS 或串行帧。工程文件里如果已经把接口时序写死先确认你的板子和工程里的接口类型一致别只看引脚数量对得上就下载。链路环节常见接口打开工程先看什么容易翻车的点DDS 到 DAC并行数据 DAC 时钟数据位宽、时钟极性、建立保持时间数据编码是偏移二进制还是补码DAC 输出到 ADC模拟回环、滤波网络是否有重建滤波器、阻抗匹配直接短接导致削顶或自激ADC 到 FPGA并行、LVDS、串行帧采样时钟来源、同步信号、位对齐采样时钟和系统时钟异步FIFO 读空采集到上位机在线抓数、串口、USB触发条件、采样深度、数据格式抓到的不是同一时间段的数据时钟域是回环测试里最容易被低估的部分。DDS 一般跑在系统时钟域ADC 采样时钟往往来自外部晶振或时钟芯片两者异步。如果工程里直接把 ADC 数据打进系统时钟域的寄存器综合可能过板子上却会随机出错。常见做法是加一个异步 FIFOADC 侧用采样时钟写系统侧用读时钟读中间用valid或空满标志控制。抓数时先看 FIFO 的empty和overflow这两个信号比波形本身更能说明问题。2.3 工程文件组成与打开方式这份工程文件通常包含顶层源码、DDS 核心、LUT 初始化文件、引脚和时序约束、仿真测试文件以及可能的抓数脚本。不同平台的工程格式不一样有的直接双击工程文件打开有的需要先新建工程再把源码和约束加进去。打开后不要立刻点编译先做三件事确认器件型号和板卡一致确认顶层模块没有被别的文件覆盖确认约束文件里的时钟引脚和复位引脚对得上原理图。文件或目录作用打开后先检查什么顶层源码连接 DDS、DAC 接口、ADC 采集和通信模块端口名是否和约束文件一致DDS 核心相位累加、LUT 查表、输出寄存PHASE_W、LUT_AW、DATA_W是否匹配LUT 初始化文件存放一个周期的波形采样点数据进制、深度、位宽是否和 IP 配置一致约束文件引脚分配、IO 电平、时钟周期时钟频率是否和f_clk参数一致仿真测试文件行为级验证 DDS 输出和接口时序是否给了freq_word和复位激励抓数或分析脚本导出 ADC 数据、做 FFT 或频点扫描数据格式是有符号还是无符号如果工程不是原生格式新建工程时把源码、LUT 文件和约束文件一起加入顶层模块手动指定避免工具自动把测试文件当成顶层。约束文件里最值得花时间看的是时钟周期约束DDS 的频率计算用的是实际时钟频率约束文件里写的是理想频率两者不一致时仿真和板级结果会对不上。先跑一遍行为仿真确认dac_data有波形、freq_word改变后周期跟着变再进综合实现这一步能省掉很多下载后的无效调试。3. 上手复现从工程导入到板级回环的四个步骤工程导入只是开始真正能让回环跑通的是参数配置、编译下载和抓数分析。下面按四个步骤走每一步都给出可抄的配置或脚本。不要跳步尤其是 LUT 生成和频率控制字计算这两步错了后面看波形只会越看越乱。3.1 导入工程与检查器件/约束先确认工程里的器件型号、封装和速度等级与手头板子一致。不一致时综合可能报错也可能勉强通过但引脚映射错位。接着看约束文件系统时钟引脚是不是板上的晶振输入复位按键是不是低有效DAC 和 ADC 的数据脚有没有按原理图逐位对应。IO 电平标准也要看1.8 V 的 bank 接 3.3 V 外设轻则不工作重则伤器件。检查完先跑一次综合不要直接实现综合报告里看顶层端口和时钟网络有没有异常。如果工程里没有现成的约束文件按原理图补一份最小约束时钟、复位、DAC 时钟输出、DAC 数据总线、ADC 采样时钟、ADC 数据总线。先只约束这些等回环通了再加调试端口。引脚约束不要一次写太多逐组加、逐组验证出问题时容易定位。3.2 配置 DDS 频率控制字与波形表波形表决定了输出幅度和杂散频率控制字决定了输出频率。先生成 LUT 文件再算频率控制字。下面这段 Python 生成的是偏移二进制正弦表深度 4096位宽 12适合大多数无符号 DAC 输入import math DEPTH 1 12 # LUT 深度和 LUT_AW12 对齐 WIDTH 12 # DAC 数据位宽 MID 1 (WIDTH - 1) with open(sine_lut.coe, w) as f: f.write(memory_initialization_radix10;\n) f.write(memory_initialization_vector\n) vals [] for i in range(DEPTH): s math.sin(2 * math.pi * i / DEPTH) # 偏移二进制中点 MID范围 0 ~ 2^WIDTH-1 q int(round(MID s * (MID - 1))) q max(0, min((1 WIDTH) - 1, q)) vals.append(str(q)) f.write(,\n.join(vals) ;\n)DEPTH要和 DDS 里的LUT_AW对齐WIDTH要和 DAC 数据位宽对齐MID是偏移二进制的中点。如果 DAC 吃的是二进制补码把MID s * (MID - 1)改成s * (MID - 1)再按位宽截断。生成后不要只看文件大小打开前几十行确认数据在0到4095之间且中点附近有2048左右的点。LUT 文件格式各平台不一样.coe、.mif、.hex都有工程里用哪种就生成哪种别直接改后缀。频率控制字用下面这段算算完把结果写进寄存器或参数f_clk 100_000_000 # 系统时钟按实际工程改 f_out 1_000_000 # 目标输出频率 PHASE_W 32 # 相位累加器位宽 freq_word round(f_out * (1 PHASE_W) / f_clk) actual freq_word * f_clk / (1 PHASE_W) print(freq_word , freq_word) print(actual f_out , actual)f_clk必须和约束文件里的时钟频率一致PHASE_W必须和 DDS 模块参数一致。round带来的频偏是有限的如果实际输出和目标的偏差超过应用允许范围增大PHASE_W比反复调freq_word更有效。算完后用仿真看一眼周期f_out为 1 MHz、f_clk为 100 MHz 时一个完整正弦周期大约 100 个时钟数一下dac_data的峰值间隔就能对上。3.3 编译、下载与在线抓取 ADC 数据编译前先确认 LUT 文件已经被工程识别综合日志里搜一下 LUT 文件名没有出现就是没加进去。实现完成后看时序报告重点看系统时钟和 ADC 采样时钟的建立保持余量。余量为负时不要抱着“先下载看看”的心态回环数据会时好时坏最后浪费更多时间。下载后先给一个低频单音比如 100 kHz用示波器看 DAC 输出有没有波形再用在线抓数看 ADC 数据是否有变化。抓数配置建议先保守采样深度取 4096 或 8192 点触发方式用自由运行或手动触发数据格式先按无符号 12 位导出。抓数时钟尽量用 ADC 采样时钟不要用系统时钟硬采否则看到的是跨时钟域后的数据不能直接拿来做 FFT。抓到的数据先看最小值、最大值和中点偏移二进制的中点在 2048 附近如果全在 0 或 4095先查 DAC 编码和 ADC 位对齐不要急着做频谱分析。抓数参数建议值说明采样深度4096 点或 8192 点太少频率分辨率不够太多导出慢触发方式自由运行或手动先保证能抓到连续数据数据格式无符号 12 位和 ADC 输出格式一致抓数时钟ADC 采样时钟避免跨时钟域引入假频谱分析工具Python NumPy方便做去偏置、加窗和 FFT3.4 用回环数据验证幅频特性抓到数据后先做时域检查再做频域检查。时域看峰峰值、均值、削顶比例频域看主频、谐波和底噪。下面这段脚本假设adc_data是从在线抓数导出的 12 位无符号数组fs是 ADC 实际采样率import numpy as np # adc_data: 从在线抓数导出的 12 bit 无符号整数数组 # fs: ADC 采样率单位 Hz x adc_data.astype(np.float64) x (x - 2048.0) / 2048.0 # 去偏置归一化到 -1 ~ 1 N len(x) w np.hanning(N) X np.fft.rfft(x * w) freq np.fft.rfftfreq(N, d1.0 / fs) mag 20 * np.log10(np.abs(X) / (np.sum(w) / 2) 1e-12) peak_f freq[np.argmax(mag[1:]) 1] # 跳过 DC print(peak frequency:, peak_f)x - 2048是去掉偏移二进制的中点hanning窗降低频谱泄漏np.sum(w) / 2是窗函数的幅度修正。mag[1:]跳过直流分量否则最大值永远在 0 Hz。主频峰值应该和设定的f_out接近偏差主要来自freq_word四舍五入和 ADC 采样时钟的实际误差。如果峰值旁边出现对称的杂散先查 LUT 深度和相位截断如果谐波明显查 DAC 输出后的重建滤波器和 ADC 输入端的抗混叠滤波器。4. 参数怎么设DDS 频率分辨率、SFDR 与 ADDA 采样率匹配DDS ADDA 回环测试里参数不是孤立调的。频率控制字影响输出频率LUT 位宽影响杂散ADC 采样率影响能看到的最高频率三者要一起看。很多“波形不对”的问题其实是参数之间不匹配输出频率接近采样率的整数分之一或者 LUT 深度和 DAC 位数差太多频谱上就会冒出解释不了的峰。4.1 频率控制字与输出频率的计算边界频率分辨率由f_clk / 2^PHASE_W决定。PHASE_W取 32、f_clk取 100 MHz 时分辨率大约是 0.023 Hz看起来够细但频率控制字是整数实际输出频率只能落在离散频点上。目标频率正好落在两个频点中间时四舍五入会带来频偏。频偏的绝对值是f_clk / 2^PHASE_W的一半以内大多数测试可以接受如果做频率扫描最好按频点步进而非随意给目标频率否则相邻频点的实际间隔不均匀。参数示例值作用调整后的影响f_clk100 MHz系统时钟变大则同频率控制字下输出频率变高PHASE_W32相位累加器位宽变大则频率分辨率更细资源略增freq_word42 949 673频率控制字直接决定输出频率f_out1 MHz目标输出受频率控制字量化影响频率分辨率0.023 Hz最小频率步进由前两项决定配置时先算freq_word再反算actual把两个值都记录下来。不要只写目标频率后面排查频偏时没有对照。做频点扫描时每个频点至少抓 4096 点频率间隔不要小于 FFT 的频率分辨率fs / N否则相邻频点会在同一个谱线里混在一起。4.2 LUT 量化位数、DAC 位数与 SFDR 的取舍LUT 数据位宽决定波形幅度量化精度DAC 位数决定最终模拟输出的理论动态范围。LUT 位宽低于 DAC 位宽时DAC 的低位基本在跳变SFDR 会被 LUT 量化台阶限制。经验上LUT 每增加 1 bit量化台阶减半理论信噪比改善约 6 dB。12 位 DAC 配 8 位 LUT频谱上很容易看到量化引起的杂散12 位 DAC 配 12 位或 14 位 LUT杂散会明显好一些。LUT 位宽量化级数相对 12 位 DAC 的匹配情况适用场景8 bit256偏低低位浪费只看波形有无不关注频谱10 bit1024勉强可用低频、低精度回环12 bit4096匹配常规 DDS ADDA 测试14 bit16384高于 DAC 位数需要更好 SFDR 或做插值LUT 深度和位宽不是一回事。深度决定一个周期采多少个点位宽决定每个点用多少 bit 表示。深度不够时相位截断杂散会变差位宽不够时幅度量化杂散会变差。回环测试里如果发现谐波固定出现在某些频点先加 LUT 深度如果发现底噪整体抬高先加 LUT 位宽。两者都加还不行再查 DAC 输出滤波和 ADC 输入调理。4.3 ADC 采样率、抗混叠与回环判据ADC 采样率必须满足奈奎斯特条件回环测试里最好让输出频率低于fs / 3给抗混叠滤波器留过渡带。输出频率接近fs / 2时哪怕滤波器阶数不低混叠分量也会落进有用频带。DAC 输出后的重建滤波器和 ADC 输入端的抗混叠滤波器要一起看DAC 侧滤掉镜像ADC 侧滤掉带外噪声只加一边频谱上仍会有多余分量。回环判据怎么算或怎么测合格线示例主频偏差实测峰值与设定f_out比较小于 FFT 分辨率的一半幅度削顶统计 ADC 数据中 0 和 4095 的比例削顶比例接近 0谐波抑制度二次、三次谐波与主频的 dB 差大于 40 dB按系统要求调整底噪远离主频处的平均谱线不随抓数次数大幅跳动混叠检查fs - f_out附近是否有强分量低于主频 30 dB 以上采样率不是越高越好。采样率提高后同样的抓数深度覆盖的时间变短FFT 频率分辨率变差采样率降低后抗混叠滤波器压力变大。常规做法是先按输出频率的 5 到 10 倍选采样率抓 8192 点看频谱再根据杂散位置微调。回环判据要写进测试记录不要只看一眼波形就下结论。5. 避坑与排查五个把回环测“翻车”的细节DDS ADDA 回环测试的现象很会骗人波形削顶可能被当成 DAC 坏了频率不对可能被当成时钟错了数据全零可能被当成 ADC 没起来。下面五条是血泪经验里出现频率最高的每条按“现象、原因、解决”写照着排查比盲目换板子快。5.1 波形削顶或贴底DAC 数据编码不匹配现象DAC 输出正弦波顶部和底部被削平ADC 抓回来的数据大量落在 0 或 4095。原因LUT 生成时用了偏移二进制DAC 接口却按二进制补码解释或者反过来。解决查 DAC 数据手册的输入编码确认中点对应的是0x800还是0x000在 LUT 生成脚本里改编码仿真时看中点码值是否正确再下载验证。5.2 输出频率差整数倍频率控制字用错位宽现象设置 1 kHz实测接近 1.2 kHz 或 0.8 kHz偏差不是随机小数而是固定比例。原因算freq_word时把 LUT 地址位宽LUT_AW当成了相位累加器位宽PHASE_W或者freq_word寄存器位宽不够被截断。解决统一用PHASE_W计算检查freq_word的位宽和模块端口一致用公式反算实际频率和仿真周期对照。5.3 ADC 数据全零或随机跳跨时钟域没处理现象在线抓数时数据全是 0或者偶尔跳几个随机值复位后有时正常有时不正常。原因ADC 采样时钟和系统时钟异步数据直接打进系统时钟域建立保持时间不满足或者异步 FIFO 的读写复位没有对齐。解决加异步 FIFOADC 侧用采样时钟写系统侧用读时钟读先看空满标志再读数据抓数时钟改用 ADC 采样时钟避免二次跨域。5.4 频谱底噪高、谐波多LUT 量化或模拟滤波不足现象时域波形看起来正常FFT 上主频旁边有一排谐波底噪比预期高。原因LUT 位宽低于 DAC 位宽幅度量化台阶大或者 DAC 输出直接进 ADC没有重建滤波和抗混叠滤波。解决先把 LUT 位宽加到不低于 DAC 位宽再看 LUT 深度是否足够模拟侧加低通滤波输出频率降到fs / 3以下再测。5.5 编译通过但板子无输出约束或电平标准错了现象综合实现都过下载后 DAC 没有波形ADC 也抓不到有效数据。原因约束文件里的引脚和原理图不一致或者 IO 电平标准与外部器件不匹配。解决对照原理图逐脚检查时钟、复位、DAC 时钟和数据总线先做一个单频点加 LED 指示的最小工程确认时钟和复位活着再逐步加回环链路。6. 进阶验证用 FFT 和频点扫描确认 DDS 输出质量回环能出波形只是第一步真正要确认的是 DDS 输出在目标频段内是否稳定。我一般会做一次频点扫描在f_out的范围内取 5 到 10 个频点每个频点抓 8192 点做 FFT 后记录主频幅度、二次谐波、三次谐波和底噪。扫描时保持f_clk、LUT、模拟滤波不变只改freq_word这样频谱上的变化才能归因到频率本身而不是参数乱动。设定频率实测主频主频幅度二次谐波差三次谐波差备注100 kHz99.98 kHz-1.2 dBFS-48 dB-52 dB基准点500 kHz499.91 kHz-1.3 dBFS-45 dB-49 dB观察滤波器带内平坦度1 MHz1.0002 MHz-1.5 dBFS-43 dB-47 dB接近常用工作点2 MHz1.9997 MHz-2.1 dBFS-40 dB-44 dB看高频段幅度跌落表格里的数值只是示例实际以你的板子和滤波器为准。重点看趋势频率升高时主频幅度是否明显跌落谐波差是否快速变差底噪是否在某几个频点突然抬起。如果 1 MHz 附近正常、2 MHz 附近谐波明显变差优先查 DAC 输出滤波器和 ADC 输入带宽而不是继续加 LUT 深度。频点扫描还有一个好处能提前发现“某些频率正好落在采样率整数分之一附近”的混叠问题这类问题在单频测试里很容易被漏掉。FFT 分析时我习惯把窗函数、去偏置和幅度修正写进固定脚本不每次手改。脚本里保留原始数据和参数记录后面对比不同频点时才不会把窗函数差异当成器件差异。还有一个小技巧抓数时同时记录 DAC 输出端的示波器波形哪怕只看一眼峰峰值也能快速判断削顶是数字侧编码问题还是模拟侧增益问题。数字侧和模拟侧一起看比单独盯 FFT 更可靠。从那以后我每次拿到新的 ADDA 板都强制先跑一遍“低频单音、中点检查、FFT 主频、频点扫描”这四步再动任何参数。看起来慢实际上比反复下载、反复怀疑器件快得多。这套 DDS ADDA 测试工程文件我放在项目简介对应位置拿到后先按第 3 章走一遍回环再按第 5 章对一遍现象基本能少走大半弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表