
1. 项目概述用System Generator实现一个简易DDS在数字信号处理、通信系统或者测试测量领域我们经常需要一个能产生特定频率和波形的信号源。过去这通常意味着要搬出一台笨重的函数发生器。但现在随着FPGA技术的发展我们完全可以把一个高度灵活、可编程的信号发生器“塞”进芯片里。DDS也就是直接数字频率合成技术就是实现这个想法的核心。它不像传统的模拟振荡器那样依赖电容电感而是通过数字计算和查表的方式“合成”出我们想要的波形精度高、切换快相位还能连续可调。这次我们不写一行传统的HDL代码而是用Xilinx System Generator这个图形化设计工具来快速搭建一个简易的DDS。对于算法工程师、系统架构师或者那些更习惯在Simulink/Matlab环境下思考的硬件开发者来说System Generator就像一座桥梁让你能用拖拽模块、连线的方式直观地构建出最终能在FPGA上运行的硬件系统。我们这次的目标很明确从原理出发设计一个能产生正弦波的DDS然后在System Generator里把它实现出来最后看看它实际跑起来的效果。你会发现用这种方式入门FPGA上的DSP设计门槛低得多也直观得多。2. DDS核心原理与System Generator设计思路2.1 DDS是如何“合成”频率的要理解如何在System Generator里搭建DDS首先得吃透它的工作原理。你可以把DDS想象成一个在圆形跑道上匀速跑步的人这个跑道就是正弦波的一个完整周期360度或2π弧度。这个人手里拿着一张巨大的表格正弦函数表表格上按顺序写满了跑道每个位置对应的高度值正弦波的幅度。这个人的跑步速度由两个因素决定他的步长频率控制字和时钟频率。假设时钟每“滴答”一下他就向前走一步。如果步长是1那么他需要跑完整整一圈比如跑完表格的1024个点才能输出一个完整的正弦波这时输出频率最低。如果他把步长加大到10那么时钟每响一下他就向前蹦10个点只用原来1/10的时间就跑完了一圈输出的正弦波频率也就变成了原来的10倍。这个步长就是DDS的核心参数——频率控制字Frequency Tuning Word, FTW。输出频率的计算公式非常直接F_out (FTW * F_clk) / 2^N其中N是相位累加器的位宽。相位累加器就是这个“跑步的人”它在一个固定的时钟节拍下不断地把FTW加到自己的当前位置上。累加的结果相位值作为地址去查询那张巨大的波形表格查找表LUT表格里存储的对应幅值被读取出来经过数模转换器DAC就变成了我们想要的模拟正弦波。在System Generator的纯数字仿真环境里我们暂时不涉及DAC关注的是数字幅值序列的正确性。2.2 为什么选择System Generator来实现你可能会问用VHDL或Verilog写一个DDS也不难为什么还要用System Generator这主要取决于你的背景和项目阶段。对于算法验证和系统级建模来说System Generator的优势非常明显。首先直观性。整个数据流、相位累加、查表过程都以图形化模块和连线的形式呈现一目了然。你不需要先去理解“进程”、“always块”这些HDL概念可以直接关注算法本身。其次与Matlab/Simulink的无缝集成。你可以在同一个环境中用Simulink丰富的数学和信号处理模块来生成激励、分析输出频谱、计算信噪比实现算法仿真和硬件实现的闭环验证效率极高。最后快速迭代。调整一个参数比如FTW或者LUT深度只需要双击模块修改属性然后重新生成远比修改和调试HDL代码来得快。当然它也有其适用边界。对于追求极致资源利用率、需要复杂控制逻辑、或者要进行芯片级综合优化的最终产品手写HDL代码可能仍是更优选择。但对于原型验证、教学演示、以及快速构建一个可工作的DDS核心来说System Generator是一个非常高效的起点。我们这个“简易DDS”项目正是要利用这些优势快速走通从原理到实现的完整流程。2.3 简易DDS的系统架构规划基于上述原理我们规划在System Generator中构建如下核心模块链相位累加器一个在时钟驱动下不断累加FTW的寄存器。这是DDS的“发动机”。相位截断与查找表地址相位累加器的输出位宽通常很大如32位但我们的波形表不可能有2^32那么大。因此需要截取高几位例如高10位作为查找表的地址。这里涉及到一个关键的精度权衡。正弦波形查找表一个只读存储器存储了一个或多个完整周期的正弦波数字幅值。地址输入幅值输出。频率控制字输入提供一个可配置的常数或端口用于输入FTW控制输出频率。时钟与复位整个系统的同步驱动源。我们的设计目标是在System Generator中实例化这些模块正确连接并配置合理的位宽和精度参数最终在仿真中观察到一个纯净的正弦波数字序列。3. 在System Generator中的详细实现步骤3.1 环境搭建与基本设置首先确保你的机器上安装了Vivado Design Suite包含System Generator以及MATLAB/Simulink。建议使用较新的版本以获得更好的兼容性例如Vivado 2022.1与MATLAB R2022a。启动MATLAB在命令窗口输入sysgen启动System Generator组件浏览器。然后新建一个Simulink模型.slx文件。在开始拖模块之前有一项关键设置定点数Fixed-Point数据类型。FPGA内部处理的是定点数而非浮点数。System Generator默认会使用定点数我们需要理解其配置。定点数格式通常表示为Fix_N_M其中N是总位宽M是整数部分位宽对于有符号数包含一位符号位。例如Fix_16_14表示一个16位的数其中1位符号位13位整数位2位小数位不更常见的解读是Fix_N_M的M指的是二进制小数点左边的位数包括符号位。Fix_16_14意味着总位宽16位其中14位在二进制小数点左边1位符号位 13位整数位那么小数部分位宽就是16-142位。理解这个对控制数据精度和动态范围至关重要。注意在System Generator中当你从Simulink库拖入一个常数或运算模块时务必双击模块在“基本”或“数据类型”选项卡中明确指定其定点数类型。盲目使用默认的“继承”类型后期很容易出现位宽不匹配或精度丢失的诡异错误。3.2 核心模块的选型与配置现在我们开始搭建核心链路。从System Generator工具库Xilinx Blockset中寻找并拖入以下模块Gateway In这是信号从Simulink浮点世界进入System Generator定点硬件世界的“门户”。我们至少需要两个一个用于输入时钟clk一个用于输入频率控制字ftw。对于clk数据类型通常设为布尔型Boolean即可。对于ftw我们需要根据设计确定其位宽。假设我们相位累加器位宽N_acc为32位那么ftw也应为32位无符号整数UFix_32_0。累加器Accumulator模块在“Basic Elements”库里可以找到。这是实现相位累加的核心。配置双击模块。在“基本”选项卡设置“复位类型”为“上升沿”这样我们可以用复位信号清零。“累加器位宽”设为32N_acc。在“实现”选项卡确保“流水线”选项被勾选这会在累加器输出插入寄存器提高系统时序性能是FPGA设计的好习惯。连接它的一个输入接ftw的Gateway In另一个add端口悬空默认为加运算。clk和rst端口连接到对应的全局时钟和复位源。切片器Slice模块在“Basic Elements”库里。用于从32位的相位累加值中截取出高若干位作为查找表地址。配置假设我们的正弦查找表深度为1024点地址位宽10位。我们截取累加器输出的最高位吗不应该截取最高有效位。对于相位累加其输出可以看作一个整数数值越大代表相位越大。我们取高10位[31:22]作为查找表地址。在Slice模块中设置“位宽”为10“偏移量”为22从第22位开始并选择“从最高位开始偏移”。为什么取高位因为高位代表了相位累加值中变化最慢、但决定大范围相位的部分这正是我们需要的粗粒度相位信息。低位代表了相位细小的、快速变化的部分它们贡献了频率的细微分辨率但用于查表地址会产生巨大的、不必要的ROM容量开销。舍弃低位的过程称为“相位截断”它会引入一定的相位噪声这是DDS输出频谱中杂散成分的主要来源之一。正弦波ROMROM模块在“Memory”库里找到“Dual Port ROM”或“Single Port ROM”。我们使用单口ROM即可。关键配置——初始化这是重中之重。双击ROM模块在“基本”选项卡设置“地址宽度”为10对应1024深度。在“初始化”部分点击“编辑”按钮会弹出初始化向量设置。我们需要填入一个包含1024个点的、一个完整周期的正弦波数据向量。生成初始化数据最方便的方法是使用MATLAB命令预先生成。在MATLAB命令窗口输入% 生成一个周期的正弦波样本幅度归一化到定点数范围 LUT_DEPTH 1024; n 0:LUT_DEPTH-1; sin_wave sin(2*pi*n/LUT_DEPTH); % 浮点数范围[-1, 1] % 量化为14位有符号定点数假设输出位宽为14位 OUTPUT_BITS 14; % 将[-1, 1]映射到有符号定点数范围[-2^(OUTPUT_BITS-1), 2^(OUTPUT_BITS-1)-1] scale_factor 2^(OUTPUT_BITS-1) - 1; % 例如14位则为8191 sin_wave_fixed round(sin_wave * scale_factor); % 确保数值在范围内 sin_wave_fixed min(max(sin_wave_fixed, -scale_factor-1), scale_factor); % 转换为System Generator需要的格式十进制整数用空格或逗号分隔 init_vector mat2str(sin_wave_fixed); % 或者手动复制这个数组然后将生成的sin_wave_fixed数组例如一个1x1024的矩阵复制粘贴到ROM的初始化向量框中。同时在ROM的“输出”选项卡中将“数据类型”设置为有符号定点数例如Fix_14_1314位总宽13位二进制小数点左侧即1位符号位12位整数位1位小数位这里需要根据缩放因子调整。更简单的做法是设为Fix_14_0然后通过Gateway Out后的增益模块来调整幅度。连接地址端口addr接Slice模块的输出。Gateway Out这是信号从System Generator定点世界返回到Simulink进行观察和分析的“门户”。将ROM的输出连接到Gateway Out模块。配置在Gateway Out的属性中你可以选择“将定点数转换为双精度浮点数”这样在Simulink Scope里看到的波形就是熟悉的浮点值了。系统时钟与复位还需要一个System Generator令牌Token。从Xilinx Blockset顶层拖入“System Generator”模块到你的模型中。双击它进行系统级配置。配置在“编译”选项卡选择你的目标FPGA器件型号例如xc7z020clg400-1。设置“综合工具”为Vivado Synthesis“硬件描述语言”为Verilog或VHDL。在“时钟”选项卡设置“仿真周期”为你的时钟周期例如如果时钟频率是100MHz周期就是10ns。这个令牌会自动为你的设计提供全局时钟sysgen_clk和复位sysgen_rst信号。你需要将这些信号连接到累加器、ROM等模块的时钟和复位引脚。3.3 连接与参数化将上述模块按照“FTW输入 - 累加器 - 切片器 - ROM - 输出”的顺序连接起来。别忘了连接所有的时钟和复位线。一个良好的习惯是使用信号线命名和Goto/From标签来保持图纸清晰。例如将全局时钟线命名为clk复位线命名为rst。为了灵活测试我们可以将FTW输入模块Gateway In的常数值设置为一个参数。具体做法是在MATLAB工作区定义一个变量比如FTW_VALUE 42949673;这个值对应输出频率约为(FTW_VALUE * 100e6) / 2^32 ≈ 1.0 MHz假设时钟为100MHz。然后在Gateway In的常量值栏中填入FTW_VALUE。这样只需在命令窗口改变FTW_VALUE并重新运行仿真就能快速改变输出频率。4. 仿真验证与频谱分析4.1 搭建仿真测试平台在System Generator设计外围我们需要用Simulink搭建一个测试环境。添加Scope从Simulink库Sinks拖入一个“Scope”模块连接到DDS输出的Gateway Out用于观察时域波形。添加频谱分析仪为了更专业地分析输出信号质量从DSP System Toolbox库中拖入“Spectrum Analyzer”模块。将DDS输出也连接到此模块。设置仿真参数在Simulink菜单栏点击“仿真 - 模型配置参数”。在“求解器”选项中设置“仿真时间”为足够长以观察多个波形周期例如0.001秒 “步长”可以设为“自动”。对于定点仿真建议将“数据类型覆盖”设置为“双精度”以避免Simulink自身定点运算可能带来的混淆。运行仿真点击运行按钮。你应该能在Scope中看到一个光滑的数字正弦波。在Spectrum Analyzer中将“频谱单位”设为“dBm”分辨率带宽RBW设得小一些如100Hz应该能看到一个清晰的主频谱峰以及由于相位截断和幅度量化产生的杂散频率分量。4.2 关键测试案例与结果分析让我们进行几组测试并解读结果测试1基础频率输出。设置FTW_VALUE 1073741824即2^30。根据公式输出频率应为(2^30 * 100e6) / 2^32 25 MHz。在Scope中测量周期应为40ns。频谱分析仪应显示一个干净的25MHz单频信号其信噪比SNR和杂散动态范围SFDR取决于我们设计的位宽。测试2频率切换动态性能。我们可以用Simulink的“Signal Builder”或“Repeating Sequence”模块产生一个在仿真中途变化的FTW信号代替常数输入。观察Scope你会看到输出正弦波的频率在切换瞬间是相位连续的这是DDS相比传统PLL的一大优点。测试3微小频率输出。设置一个很小的FTW例如FTW_VALUE 42950输出频率约为1kHz。观察时域波形一个周期内应包含大量采样点波形非常光滑。这展示了DDS极高的频率分辨率。实操心得仿真时如果发现输出波形是“阶梯状”而非光滑正弦波首先检查ROM的初始化数据是否正确是否是一个完整的、平滑的正弦周期。其次检查Gateway Out的采样时间设置是否正确应设置为-1继承输入采样时间。最后确保Simulink Scope的采样时间足够高能正确显示信号。4.3 性能评估与优化方向通过频谱分析仪我们可以定量评估这个简易DDS的性能。主要关注两个指标信噪比主要由输出幅度的量化位数即ROM输出位宽决定。理论SNR约为6.02 * N 1.76dB其中N是幅度量化位数。我们用了14位理论SNR约86dB。实测值会略低因为还有相位截断噪声。无杂散动态范围指主信号幅度与最大杂散信号幅度的差值。这主要由相位截断的位数决定。我们截断了低22位SFDR的理论值大约在6.02 * PdB左右其中P是保留的地址位宽10位但这是一个简化模型实际会更复杂。优化方向提高SFDR可以增加查找表深度用更多地址位或者采用正弦相位差算法、角度旋转算法等更高级的技术用较小的ROM实现高精度输出。System Generator库中甚至有现成的“DDS Compiler”IP核它内部就集成了这些优化算法。提高频率纯度在ROM输出后添加一个数字滤波器如FIR滤波器滤除由于采样和量化产生的高频镜像噪声。这可以在System Generator中直接使用FIR Compiler模块实现。多通道与调制基于这个核心可以扩展为多通道DDS用于I/Q信号生成或者在相位累加器前加入调制模块实现调频或调相。5. 常见问题、调试技巧与硬件协同仿真5.1 搭建与仿真中的典型问题即使按照步骤操作你也可能会遇到一些坑。这里记录几个常见问题及其解决方法问题现象可能原因排查步骤与解决方案仿真时报错提示“端口数据类型不匹配”模块间连线数据类型定点格式不一致。1. 检查每个模块输入输出端口的数据类型右键点击信号线 - 信号属性。2. 确保上游模块的输出类型与下游模块的输入类型兼容。System Generator的“Gateway In”是源头其输出类型决定了后续链路的类型。可以在需要转换的地方插入“Convert”类型转换模块。输出波形为一条直线零值ROM未正确初始化或地址输入始终为固定值。1. 双击ROM模块确认初始化向量已正确载入并且不是全零。2. 用Scope探头同时观察Slice模块的输出地址在仿真运行时看地址是否在循环递增。如果地址不变检查累加器的clk和rst连接以及FTW输入是否非零。输出波形幅度异常饱和或太小ROM输出数据的定点格式与后续处理或显示不匹配。1. 检查ROM输出数据类型。如果设为Fix_14_13其数值范围约为[-4096, 4095]。如果实际正弦波数据范围是[-8191,8191]就会饱和。2. 调整ROM输出的定点数格式或者在后级Gateway Out之前添加一个“Scale”模块进行归一化。仿真速度极慢仿真时间或步长设置不当或者模型中存在高采样率的模块。1. 在“配置参数”中将求解器类型改为“定步长”并指定一个合适的步长如1e-8秒即10ns时钟周期。2. 确保System Generator令牌中的“仿真周期”与Simulink主步长一致。无法生成HDL代码System Generator令牌配置错误或设计中有不支持的Simulink模块。1. 双击System Generator令牌确认“编译”选项卡中的FPGA部件、综合工具等设置正确。2. 确保设计中只使用了Xilinx Blockset和少数支持的Simulink模块如Scope、Constant。避免使用复杂的Simulink函数模块。5.2 进阶从仿真到硬件仿真成功只是第一步。要让DDS在真实的FPGA板上跑起来还需要以下步骤生成HDL代码在System Generator令牌中点击“生成”按钮。System Generator会自动将你的图形化设计转换为VHDL或Verilog代码并创建一个Vivado工程。添加I/O约束在生成的Vivado工程中你需要为时钟输入、复位输入如果有、以及DDS的数字输出信号分配具体的FPGA管脚。这通过编写XDC约束文件完成。输出信号处理我们的DDS输出是14位数字信号。要变成真正的模拟波形你需要连接一个外部的DAC芯片。在FPGA端可能需要根据DAC的接口如并行、SPI、I2S编写一个接口控制器模块。你也可以利用FPGA开发板上的音频编解码器如果支持来输出低频音频信号进行验证。硬件协同仿真这是更高级的验证手段。通过JTAG可以将FPGA硬件纳入Simulink仿真环路。你可以在Simulink中实时改变FTW并直接从FPGA抓取输出数据回Simulink分析。这需要在System Generator中配置“硬件协同仿真”选项并连接好硬件平台。5.3 扩展思考从简易DDS到实用系统这个简易DDS是一个完美的教学原型但离一个 robust 的实用信号源还有距离。在实际项目中你可能需要考虑动态配置通过微处理器如FPGA内部的MicroBlaze软核或者外部的ARM Cortex通过AXI总线动态配置FTW、相位偏移等参数。波形存储与切换将ROM扩展为双端口RAM并设计一个波形加载机制实现任意波形的发生。自动幅度控制在输出级添加一个数字乘法器充当数字衰减器/放大器实现输出幅度的可编程控制。抖动注入技术为了进一步改善SFDR可以在相位累加器或ROM地址输入端有规律地加入一个很小的随机抖动信号将确定性杂散能量打散成底噪声。通过System Generator实现这个简易DDS你收获的不仅仅是一个能用的信号源模块更重要的是一套在Simulink环境下进行FPGA DSP系统级设计和验证的方法论。它让你能更专注于算法和系统架构而将繁琐的HDL编码和底层时序问题交给工具去处理极大地加速了从概念到原型的进程。当你下次需要快速验证一个数字滤波器、一个调制解调方案时不妨试试把System Generator作为你的第一站。