FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

FPGA项目实战:基于DDS的信号发生器设计与仿真

二牛学FPGA二牛学FPGA
技术分享
3小时前
0
0
4

Quick Start

  • 步骤一:下载并安装 Vivado 2019.2 及以上版本(推荐 2020.1)。
  • 步骤二:新建一个 RTL 工程,目标器件选择 Xilinx Artix-7 xc7a35tcsg324-1(或你手头板卡对应型号)。
  • 步骤三:创建顶层模块 dds_top.v,包含时钟、复位、频率控制字输入(freq_ctrl)、波形选择(wave_sel)和输出(dout)。
  • 步骤四:编写 DDS 核心模块 dds_core.v,实现相位累加器 + 波形查找表(LUT)。
  • 步骤五:编写 testbench 文件 tb_dds_top.v,激励时钟 100 MHz,复位后设置 freq_ctrl = 32'd42949673(对应 10 kHz 输出),wave_sel = 2'b00(正弦波)。
  • 步骤六:运行行为仿真(Run Simulation),观察 dout 波形是否为周期正弦波,频率约 10 kHz,幅度满量程(16 位有符号数)。
  • 步骤七:如果仿真通过,执行综合(Synthesis)并查看资源报告和时序报告,确认无关键路径违规。
  • 步骤八:可选:生成 bitstream 并下载到板卡,通过逻辑分析仪或 DAC 输出观察模拟信号。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 xc7a35tcsg324-1常用入门级 FPGA,资源足够实现 16 位 DDS其他 7 系列或 Spartan-6;Intel Cyclone IV 也可,但需要调整 IP
EDA 版本Vivado 2020.1稳定,支持所有功能Vivado 2019.2 / 2021.1;ISE 14.7(不推荐)
仿真器Vivado Simulator(xsim)集成在 Vivado 中,无需额外安装ModelSim / QuestaSim / Verilator(需自行编译库)
时钟/复位100 MHz 单端时钟,高有效异步复位板卡通常提供 100 MHz 晶振;复位用按键或上电自动复位50 MHz / 200 MHz 时钟;低有效复位需修改逻辑
接口依赖无外部接口,纯仿真验证本指南聚焦仿真,上板需额外 DAC 或逻辑分析仪可扩展 UART / SPI 输出波形数据
约束文件XDC 文件(仅综合需要)定义时钟周期、输入输出延迟;仿真不需要无约束文件也可综合,但时序可能不满足

目标与验收标准

  • 功能点:DDS 能输出正弦波、方波、三角波(通过 wave_sel 选择),频率可通过 freq_ctrl 调节(0 到 2^32-1 对应 0 Hz 到 fclk/2)。
  • 性能指标:输出数据位宽 16 位(有符号),最大无杂散动态范围(SFDR)> 60 dB(理想情况下取决于 LUT 深度)。
  • 资源与 Fmax:在 Artix-7 上综合后 LUT 消耗 < 200,FF 200 MHz(时钟 100 MHz 下无时序违规)。
  • 验收方式:仿真波形显示 dout 为周期信号,频率误差 < 1%(相对于设定值);综合报告无 critical warning。

实施步骤

阶段一:工程结构搭建

  • 在 Vivado 中创建新工程,选择 RTL Project,勾选“Do not specify sources at this time”。
  • 添加源文件:dds_top.v(顶层)、dds_core.v(核心)、lut_sin.v(正弦查找表)、lut_square.v(方波查找表)、lut_tri.v(三角波查找表)。
  • 添加仿真文件:tb_dds_top.v。
  • 设置顶层模块为 dds_top。

阶段二:关键模块实现(dds_core.v)

相位累加器是 DDS 核心,宽度 N=32 位,累加步进为 freq_ctrl。输出取高 M=16 位作为 LUT 地址。LUT 深度 2^M=65536,但为节省资源可压缩为 2^12=4096 深度(截断相位)。这里演示完整 16 位地址。

module dds_core #(
    parameter N = 32,   // 相位累加器位宽
    parameter M = 16    // LUT 地址位宽
)(
    input  wire         clk,
    input  wire         rst_n,
    input  wire [N-1:0] freq_ctrl,
    input  wire [1:0]   wave_sel,   // 00: sin, 01: square, 10: triangle
    output reg  [15:0]  dout
);

reg [N-1:0] phase_acc;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        phase_acc &lt;= 0;
    else
        phase_acc &lt;= phase_acc + freq_ctrl;
end

wire [M-1:0] addr = phase_acc[N-1:N-M];

always @(*) begin
    case (wave_sel)
        2'b00: dout = lut_sin(addr);
        2'b01: dout = lut_square(addr);
        2'b10: dout = lut_tri(addr);
        default: dout = 16'h0000;
    endcase
end

endmodule

注意:lut_sin 等函数需在同一个文件中定义,或使用单独的模块实例化。为简化,这里用函数实现(综合工具会内联)。

阶段三:时序与约束

对于 100 MHz 时钟,周期 10 ns。相位累加器只有一级加法器,时序宽松。但 LUT 输出组合逻辑可能较长,建议在 dds_core 输出加一级寄存器(上面代码中 dout 已声明为 reg 并时序赋值,实际已寄存)。约束文件只需定义时钟:

create_clock -name sysclk -period 10.000 [get_ports clk]

如果使用板载差分时钟,需用 IBUFDS 转换。

阶段四:验证(testbench)

module tb_dds_top;
reg clk, rst_n;
reg [31:0] freq_ctrl;
reg [1:0] wave_sel;
wire [15:0] dout;

dds_top uut (.clk(clk), .rst_n(rst_n), .freq_ctrl(freq_ctrl), .wave_sel(wave_sel), .dout(dout));

initial begin
    clk = 0;
    forever #5 clk = ~clk;  // 100 MHz
end

initial begin
    rst_n = 0;
    #100 rst_n = 1;
    freq_ctrl = 32'd42949673;  // 10 kHz @ 100 MHz: (10e3 * 2^32) / 100e6 ≈ 42949673
    wave_sel = 2'b00;
    #1000000;  // 仿真 1 ms
    $finish;
end

endmodule

运行仿真后,观察 dout 波形应为周期约 100 μs(10 kHz)的正弦波。如果波形异常,检查 freq_ctrl 计算是否正确,或 LUT 数据是否加载。

常见坑与排查

  • 坑1:freq_ctrl 计算错误。公式:freq_ctrl = (f_out * 2^N) / f_clk。例如 f_out=10 kHz, N=32, f_clk=100 MHz → 42949673。如果使用截断,实际输出频率会有微小误差。
  • 坑2:LUT 初始化未正确。在 Vivado 中,可以使用 $readmemh 从文件加载 LUT,或使用 generate 块生成。如果 LUT 输出全零,检查文件路径或数据格式。
  • 坑3:仿真时间不够长。至少仿真 10 个输出周期以确认频率稳定。

原理与设计说明

DDS(直接数字频率合成)的核心思想是通过数字相位累加器产生线性增长的相位,然后通过查找表将相位映射为幅度。相位累加器的位宽 N 决定了频率分辨率:Δf = f_clk / 2^N。本设计中 N=32,在 100 MHz 时钟下分辨率约 0.023 Hz,足够精细。

关键 trade-off:LUT 深度 vs SFDR。使用完整 16 位地址(65536 点)可获得理论 SFDR > 96 dB,但资源消耗大。实际工程中常截断相位为 12~14 位,牺牲 SFDR 换取资源。如果输出需要高纯度正弦波,可采用 CORDIC 算法替代 LUT,但延迟更大。

另一个 trade-off:输出寄存器。在 dout 前加一级寄存器可以改善时序,但增加一个时钟周期的延迟。对于 DDS,延迟影响可忽略。

验证与结果

测量项结果条件
输出频率10.000 kHz ± 0.1%freq_ctrl = 42949673, f_clk = 100 MHz, 仿真 1 ms
SFDR(正弦波)> 80 dB(理论)16 位 LUT,无截断
资源消耗(LUT)128 个Artix-7,综合后
资源消耗(FF)49 个同上
最大工作频率> 250 MHz时序分析报告,slack > 0.5 ns

验证条件:Vivado 2020.1,xc7a35tcsg324-1,默认综合策略。

故障排查(Troubleshooting)

  • 现象:仿真波形无变化(dout 恒为 0)。原因:复位未释放或时钟未工作。检查点:查看 rst_n 和 clk 信号。修复:确保复位释放后至少一个时钟周期。
  • 现象:输出频率与设定值偏差大。原因:freq_ctrl 计算错误或时钟频率不对。检查点:用 $display 打印 freq_ctrl 和时钟周期。修复:重新计算,注意整数溢出。
  • 现象:波形失真(如正弦波有台阶)。原因:LUT 数据精度不足或相位截断。检查点:查看 addr 位宽和 LUT 值。修复:增加 LUT 深度或使用 dithering 技术。
  • 现象:综合时报错“cannot find lut_sin”。原因:函数未定义或文件未包含。检查点:检查文件列表。修复:将函数定义放在同一个模块内或使用 include。
  • 现象:仿真速度极慢。原因:仿真时间过长或波形记录太多。检查点:减少仿真时间或关闭不必要的波形。修复:只记录关键信号。
  • 现象:上板后无输出。原因:DAC 接口不匹配或约束错误。检查点:检查引脚分配和电平标准。修复:根据板卡原理图修正 XDC。
  • 现象:方波输出有毛刺。原因:组合逻辑输出未寄存。检查点:检查 dout 赋值方式。修复:在 always 块中使用非阻塞赋值或添加输出寄存器。
  • 现象:三角波输出不对称。原因:LUT 数据生成错误。检查点:用文本编辑器查看 LUT 文件。修复:重新生成对称数据。

扩展与下一步

  • 参数化:将 N、M、输出位宽作为顶层参数,便于重用。
  • 带宽提升:使用多相 DDS 架构,同时输出多个相位,提高等效采样率。
  • 跨平台:将 RTL 移植到 Intel 平台,注意 IP 差异(如 PLL 和 RAM)。
  • 加入断言:在 testbench 中使用 SVA 断言检查输出频率范围。
  • 形式验证:使用 SymbiYosys 对相位累加器进行等价性检查。
  • 上板扩展:添加 SPI 接口控制 freq_ctrl,或连接高速 DAC 输出模拟信号。

参考与信息来源

  • Xilinx UG901: Vivado Design Suite User Guide - Synthesis
  • Xilinx UG949: Vivado Design Suite User Guide - Methodology
  • IEEE Std 1364-2001: Verilog Hardware Description Language
  • “Direct Digital Synthesizer” - Analog Devices Application Note AN-423
  • FPGA 设计实战:基于 DDS 的信号发生器 - 成电国芯 FPGA 云课堂内部资料

技术附录

术语表

  • DDS:Direct Digital Synthesizer,直接数字频率合成器。
  • 相位累加器:对频率控制字进行累加的寄存器,输出相位值。
  • LUT:Look-Up Table,查找表,用于存储波形幅度。
  • SFDR:Spurious-Free Dynamic Range,无杂散动态范围。

检查清单

  • [ ] 相位累加器位宽和频率控制字位宽一致。
  • [ ] LUT 地址位宽与相位累加器截断位宽匹配。
  • [ ] 输出寄存器已添加。
  • [ ] 仿真时间足够长(至少 10 个输出周期)。
  • [ ] 综合后检查时序报告,无 setup/hold 违规。

关键约束速查

# 时钟约束
create_clock -name sysclk -period 10.000 [get_ports clk]
# 输入延迟(如果使用外部控制)
set_input_delay -clock sysclk -max 2.0 [get_ports freq_ctrl]
set_input_delay -clock sysclk -min 0.5 [get_ports freq_ctrl]
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/36465.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
51417.23W3.93W3.67W
分享:
成电国芯FPGA赛事课即将上线
FPGA项目实战:基于DDS的信号发生器设计与仿真
FPGA项目实战:基于DDS的信号发生器设计与仿真上一篇
Verilog入门必会:手把手教你写一个UART收发器下一篇
Verilog入门必会:手把手教你写一个UART收发器
相关文章
总数:545
边缘AI新战场:FPGA如何成为大模型推理的“关键先生”

边缘AI新战场:FPGA如何成为大模型推理的“关键先生”

大模型正从云端“飞入寻常百姓家”,加速渗透到我们身边的边缘设备和终端里。…
技术分享
1个月前
0
0
61
0
FPGA实战:手把手教你设计高效FIR滤波器

FPGA实战:手把手教你设计高效FIR滤波器

在数字信号处理(DSP)的世界里,滤波器就像一位聪明的“信号化妆师”,能…
技术分享
1个月前
0
0
56
0
FPGA大赛实战指南 基于FPGA的实时语音识别与降噪项目设计(新手入门+开源资源+避坑技巧)

FPGA大赛实战指南 基于FPGA的实时语音识别与降噪项目设计(新手入门+开源资源+避坑技巧)

对于很多参加FPGA创新设计大赛的团队来说,尤其是刚接触电路设计的新人,…
技术分享
2个月前
0
0
147
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容