首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >TinyAWG 的 DDS 半字错位原来差了一拍

TinyAWG 的 DDS 半字错位原来差了一拍

原创
作者头像
不脱发的程序猿
发布于 2026-10-08 23:09:09
发布于 2026-10-08 23:09:09
30
举报

一个波形发生器,频率控制字算得对,读地址也对,送出的样本却可能读到了同一个字里的另一半。

这类问题容易藏在流水线里。拿正弦表看输出,相邻两点往往差得很小;换成陡峭边沿或自定义波形,错误才更值得警惕。排查时先把滤波器、放大器和示波器放到一边,在数字域问一个更直接的问题——这一次取出的,究竟是不是要求的那个样本?

下面围绕 TinyAWG 的 DDS 播放器,把一个 32 位 BRAM 字中的两个 16 位样本分清楚。实际源码、存储器配置和 EDA 图对应核查后,重新搭建了最小仿真:在工程配置的 1 拍 BRAM 读延迟下,半字选择链从 5 级改为 4 级,2364 次样本检查全部匹配。这个结论止于数字逻辑仿真,尚不能代替整机波形、频谱或带宽测量。

先看这半个字从哪里来

波形表放在 BRAM 里,PS 侧负责生成和写入,PL 侧根据相位连续读取。表容量是 128 KiB,对应 65536 个 16 位样本。存储器端口宽度为 32 位,写入时每两个样本合并成一个字。

图1 32位字中的两个样本和DDS读取路径
图1 32位字中的两个样本和DDS读取路径

图1是按工程重新整理的数据路径示意。低半字装偶数索引的样本,高半字装奇数索引的样本;它不是实测波形。

软件的打包规则可以写成 word[m] = {x[2m+1], x[2m]}。因此,PL 给定样本索引 k,要同时完成两件事:用 k >> 1 找到 32 位字,再用 k[0] 从这个字中选出正确的 16 位半字。字节地址则是 4 × (k >> 1),末尾两位恒为零。

这里的“同时”指的是两条路径最终在同一个时钟边沿相遇,而不是要求它们中间经过一样多的寄存器。读地址还要经过 BRAM,选择位不经过;对齐必须把这段读延迟算进去。

工程的相位累加器宽度为 32 位,查表索引取相位和的高 16 位。当表中恰好放一个周期时,理想数字输出频率为 fclk × pinc / 2^32。例如 pinc = 0x00010000 时,索引每拍前进一步,200 MHz 时钟模型下一个表周期需要 65536 拍。这个控制字也很适合拿来暴露奇偶选择错误。

把电路图和 RTL 对上

本次查看的是在线硬件 V1、板上标注 V1.0,软件来自 TinyAWG_V1.2.zip。原理图不是从项目文章里放大出来的,而是在嘉立创 EDA 编辑器中以 4 倍比例重新导出,再裁出相关局部。V1 的五页完整原理图和 PCB 顶、底面母图均已取得。

图2 V1核心板接口的DB0至DB5及CLKP
图2 V1核心板接口的DB0至DB5及CLKP

图2给出 CN3 上的数据低位与时钟连接。DB0 标为 V13,DB5 标为 T11,CLKP 标为 P14,与 41523.xdc 中的 DAC_out_0[0]、DAC_out_0[5] 和 clk_p_0 对应。

图3 V1核心板接口的DB6至DB13
图3 V1核心板接口的DB6至DB13

图3是 CN4 上的数据高位。DB6~DB13 的引脚标注依次为 K16、J16、K14、J14、K17、K18、H15、G15,与约束文件一致。这里核对的是这条 DAC 数据链,不能把核对结果延伸成整块板的电气验收。

图4 AD9744数字输入与供电局部
图4 AD9744数字输入与供电局部

图4可见 U7 AD9744 的 DB0~DB13、CLKP 和供电网络。输出 RTL 将有符号 16 位数据算术右移两位后送到 14 位 DAC,并把系统时钟引出为 clk_p。因此,本次检查点放在 dds_out,位于这个位宽转换之前;测试表里的数字是样本身份标签,不是输出电压。

AD9744 官方产品资料列出 14 位、210 MSPS 的器件能力。它说明器件的用途,不能据此认定这块板已经通过 200 MHz 的时序收敛和 DAC 建立、保持时间检查。当前专题也没有验证 FPGA Bank 电压或板级走线时延。

图5 V1 PCB顶层铜箔和顶层丝印
图5 V1 PCB顶层铜箔和顶层丝印

图5来自 V1 PCB 的独立顶层导出,核心板连接器 CN3、CN4 与 DAC U7 的位置可相互对应。图片只裁掉画布外围留白,电路和走线没有改动。

图6 V1 PCB底层铜箔和底层丝印
图6 V1 PCB底层铜箔和底层丝印

图6单独显示底层铜箔及底层丝印,观察方向仍是从顶面透视,没有镜像。它用于配合顶层辨认结构,不是可直接印刷、转印或判断全部回流路径的制板文件。本次没有做 ERC、DRC 或模拟电路仿真。

输出等五拍,选择链为什么只需四级

源码里最容易误导人的,是把“输出等了多少拍”和“选择位需要多少级寄存器”当成一回事。

先定义起点:在边沿 N 到来之前,sample_idx_r 已经保存样本索引 k。从 N 开始,字地址和奇偶选择位各自往前走。所有相关赋值都是非阻塞赋值,同一边沿中右侧读到的是寄存器旧值。

图7 BRAM读数据与奇偶选择位的对齐时序
图7 BRAM读数据与奇偶选择位的对齐时序

图7按本工程的一拍同步 BRAM 推导。N 更新字地址,N+1 更新 BRAM 字节地址,N+2 存储器输出目标字,N+3 用 doutb_r 锁存。到了 N+4,半字 Mux 消费的是边沿之前已经存在的 doutb_r;对应的 k[0] 此时也必须已经存在于选择链的最高位。

奇偶位在 N 进入 bit0,N+1 到 bit1,N+2 到 bit2,N+3 到 bit3。N+4 的 Mux 读旧 bit3,四级链就够了。 Mux 后面还有一个输出寄存器,所以最终 dds_out 在 N+5 才出现。

原实现 CONTROL_DELAY = 5,Mux 读的是旧 bit4。在这个配置下,它属于更早的样本索引,数据字却已经到了当前样本对应的字。地址看起来完全正常,错误发生在字内部。

这里“一拍 BRAM”也不是凭印象假设的。工程的 Block Memory Generator XCI 同时给出 B 口读延迟 1,且存储器原语输出寄存器、核输出寄存器均未启用。Block Design 中 B 口与 DDS 使用同一 FCLK_CLK0,时钟配置为 200 MHz。换 IP 设置时,必须重新数这条路径。

修改很小,证据要够具体

功能修改只有一项,把半字选择链长度从 5 改为 4。相位累加、偏移、地址生成和最终输出寄存器均保留。下面摘出修正后的关键逻辑,端口及其余寄存器声明见附件完整模块。

代码语言:txt
复制
localparam CONTROL_DELAY = 4;
reg [CONTROL_DELAY-1:0] odd_shift_reg;

always @(posedge clk) begin
    odd_shift_reg <= {
        odd_shift_reg[CONTROL_DELAY-2:0], sample_idx_r[0]
    };
end

wire is_odd_final = odd_shift_reg[CONTROL_DELAY-1];
reg [15:0] dds_out_internal;

always @(posedge clk) begin
    dds_out_internal <= is_odd_final
        ? doutb_r[31:16] : doutb_r[15:0];
end

always @(posedge clk) begin
    dds_out <= dds_out_internal;
end

不要先用正弦表检验它。最省事的办法是让每个样本存自己的索引:样本 31 的值就是 31,样本 32 的值就是 32。读错了半字,输出会直接变成同一字里邻居的编号。下面这段函数就是测试台使用的打包存储器模型。

代码语言:txt
复制
function [31:0] packed_word(input [31:0] byte_addr);
    reg [15:0] even_idx;
    begin
        even_idx = {byte_addr[16:2], 1'b0};
        packed_word = {even_idx + 16'd1, even_idx};
    end
endfunction

测试台并排运行原版和修正版,用一拍同步读模型返回这个 32 位字,再把 N 消费的样本索引排队,到 N+5 与 dds_out 比较。这样既查样本身份,也查约定的输出时刻。测试还检查两版相位索引、字节地址保持一致,B 口一直使能、写使能为零且地址四字节对齐。

图8 实际仿真CSV中的原版错位与修正版输出
图8 实际仿真CSV中的原版错位与修正版输出

图8由实际 CSV 的前 16 次有效检查绘制。第一点期望 31,原版却输出 30;下一点期望 32,原版输出 33。同一个字中的两个半字被错选,表现为邻居样本交替出现。修正版与期望曲线重合。横轴是仿真周期,纵轴是身份标签,不能当成示波器上的幅度测量。

六组控制字把容易藏住的情况也跑一遍

仿真采用 200 MHz 时钟模型,启动阶段预热 40 拍。随后连续改变相位增量和偏移,切换参数时不清空整条流水线。结果如下;表中错误数指样本或约定时刻不匹配的次数。

场景

pinc / poff,十六进制

检查次数

原版错误

修正版错误

连续相邻索引

00010000 / 00000000

264

264

0

隔一个索引读取

00020000 / 00010000

300

9

0

步进与接近回绕的偏移

00030000 / FFFF0000

300

290

0

半个索引步进

00008000 / FFFE0000

300

154

0

负向模增量

FFFF0000 / 12340000

300

295

0

大步进与混合偏移

9E3779B9 / 13579BDF

900

476

0

合计

1 拍 BRAM 读延迟

2364

1488

0

第二组尤其值得留意。每次跨过两个样本,稳态中的奇偶位不变,错误的选择延迟可能被掩盖;表里的 9 次错误来自连续参数切换期间。这也是不能拿“某个频率看着正常”证明整条流水线正确的原因。

还做了一个反例:把存储器模型改成两拍读,仍按原来 N+5 的契约检查。修正版出现 1852 次不匹配,测试能明确检出配置改变。这次日志的 REGRESSION_PASS 表示成功检出了不适用条件,不是两拍 BRAM 也正确。如果实际 IP 增加输出寄存器,控制链长度和最终输出期望都要重新推导,不能直接套用数字 4。

不拿启动假设冒充硬件结论

原模块的 phase_acc_r 没有显式复位或 INIT。本次测试台在启动时明确把相位累加器与输入流水寄存器置零,目的在于隔离并验证半字对齐。模块中的 rstn 只经过同步释放电路控制 BRAM 复位,不能据此认为相位状态也被复位。

这个初始状态是仿真前提,不是 FPGA 上电行为的证明。准备改入整机工程时,应先设计确定的相位初始化及输出有效窗口,再重新检查综合结果和时序。这里没有执行目标综合、布局布线、烧录或实体测试,也没有验证电源、输出保护、GUI、模拟带宽与失真。

不需要板子也能复现

最小源码包 TinyAWG-DDS-lane-audit-v1.zip 给出未改动的原模块、修正版、差异补丁、完整测试台、运行入口、实际 CSV/VCD 和 LGPL/GPL 许可文本。它是最小仿真工程,不是可直接烧录的固件。

使用 Node.js 18.15.0 或更高版本,在解压目录执行 npm install --ignore-scripts,再执行 node run.mjs。依赖固定为 @veriflow/iverilog-wasm 0.1.4,本篇实际采用的就是这条 WASM 仿真路径。运行结果写到 results/;verified-results/ 保留成稿时的原始结果,方便对照。

先确认一拍模型得到 2364 次检查、原版 1488 次错误、修正版零错误;再确认两拍反例确实被检出。之后换自己的控制字、波形标签或 BRAM 配置,继续沿着同一个样本索引追地址、数据和控制位。半字之外,字节使能、通道编号和帧标记也可以用这个办法检查。

来源与验证范围

项目为 greentor 的《35M带宽200M采样率14位任意波发生器 TinyAWG》,完整地址 https://oshwhub.com/greentor/tinyawg-signal-source 。项目页与对应 EDA 标注 LGPL 3.0;复用代码保留许可,附件包含源文件及修改记录。

本文独立讲解与自绘示意采用 CC BY;项目电路图、PCB 及复用代码遵循原 LGPL 3.0,页面标签不改变其许可。

上游源码从项目制作文档 https://ai.feishu.cn/docx/BGycd4URIot8Aoxs3vLcBdq2nFc 获取 TinyAWG_V1.2.zip。硬件图取自同项目在线 V1/V1.0 工程,五页原理图及 PCB 顶、底面重新导出;正文局部仅裁剪、保真缩放并适度锐化,未生成或改写连线、丝印与数值。底面图未镜像。数据路径图、时序图为本文重绘示意,结果图来自实际仿真 CSV。

器件参考为 Analog Devices 的 AD9744 产品资料 https://www.analog.com/en/products/ad9744.html 及所链接 Rev. C 数据手册。本文新增内容是 DDS 半字选择对齐推导、4 级选择链修改、六组回归及两拍读延迟反例;验证范围为指定启动状态和存储器模型下的 RTL 仿真,无整机实测。最小源码包可从文末完整网址下载。

最小源码下载地址 https://img.dfrobot.com.cn/mc-front/5caab1791804207b131ae7df/cca7f7dde71835d47d10ae16ea49ef2c.zip,文件名为 TinyAWG-DDS-lane-audit-v1.zip。下载包包含本文对应的完整最小仿真工程、实际 CSV/VCD、修改记录及 LGPL/GPL 许可文本。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 先看这半个字从哪里来
  • 把电路图和 RTL 对上
  • 输出等五拍,选择链为什么只需四级
  • 修改很小,证据要够具体
  • 六组控制字把容易藏住的情况也跑一遍
  • 不拿启动假设冒充硬件结论
  • 不需要板子也能复现
  • 来源与验证范围
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档