DDS学习心得

FPGA学习:DDS信号发生器

第一次写博客,多多见谅。
DDS是直接数字式频率吧合成器(Direct Digital Synthesizer)的英文缩写,是一项关键的数字化技术。

DDS的基本结构主要由相位累加器、相位调制器、波形数据表ROM、D/A转换器等四大结构组成,其中较多设计还会在数模转换器之后增加一个低通滤波器。

2506632-20220110225224223-675041483.png (1271×451)

系统时钟CLK为整个系统的工作时钟,频率为f_clk;频率输入字F_WORD,一般为整数,数字的大小控制着输出信号频率的大小,输出数字越大则输出的信号频率越大,反之,则输出的频率越小。相位输出字为P_WORD,为整数,数值大小控制输出信号的相位偏移,主要用于相位的信号调制,输出的信号为CLK_OUT,频率为f_out。

在图中所展示的四大结构中,相位累加器是整个DDS的核心,在这里完成相位累加,生成相位码。相位累加器的频率输入字为K,表示相位增量,其位宽为N,满足等式K=2^N*f_out/f_clk。其在输入相位累加器之前,在系统时钟同步下做数据寄存,数据改变时不会干扰相位累加器的正常工作。

相位调制器接收相位累加器输出的相位码, 在这里加上一个相位偏移值P,主要用于信号的相位调制,如应用于通信方面的相移键控等,不使用此部分时可以去掉,或者将其设为一个常数输入,同样相位字输入也要做寄存。

波形数据表ROM中存有一个完整周期的正弦波信号。假设波形数据ROM的地址位宽为12位,存储数据位宽为8位,即ROM有2^12 = 4096个存储空间,每个存储空间可存储1字节数据。将一个周期的正弦波信号,沿横轴等间隔采样212 = 4096次,每次采集的信号幅度用1字节数据表示,最大值为255,最小值为0。将4096次采样结果按顺序写入ROM的4096个存储单元,一个完整周期正弦波的数字幅度信号写入了波形数据表ROM中。波形数据表ROM以相位调制器传入的相位码为ROM读地址,将地址对应存储单元中的电压幅值数字量输出。

输出信号CLK_OUT的信号频率fOUT = K * fCLK / 2^N。当K = 1时,可得DDS最小分辨率为:fOUT = fCLK / 2N,此时输出信号频率最低。根据采样定理,K的最大值应小于2N/2。

所以,问题来了,相位累加器得到的相位码是如何进行寻址的呢?

对于N位的相位累加器,它对应的相位累加值为2N,如果正弦ROM中存储单元的个数也是2N的话,这个问题就很好解决,但是这对ROM的对存储容量的要求较高。在实际操作中,我们使用相位累加值的高几位对ROM进行寻址,也就是说并不是每个系统时钟都对ROM进行数据读取,而是多个时钟读取一次,因为这样能保证相位累加器溢出时,从正弦ROM表中取出正好一个正弦周期的样点。

因此,相位累加器每计数2^N次,对应一个正弦周期。而相位累加器1秒钟计数f_clk次,在k=1时,DDS输出的时钟频率就是频率分辨率。频率控制字K增加时,相位累加器溢出的频率增加,对应DDS输出信号clk_out频率变为K倍的DDS频率分辨率。

设:ROM存储单元个数为4096,每个存储数据用8位二进制表示。即,ROM地址线宽度为12,数据线宽度为8;相位累加器位宽N = 32。

根据上述条件可以知道,相位调制器位宽M = 12,那么根据DDS原理。那么在相位调制器中与相位控制字进行累加时,应用相位累加器的高12位累加。而相位累加器的低20位只与频率控制字累加。

我们以频率控制字K = 1为例,相位累加器的低20位一直会加1,直到低20位溢出向高12位进位,此时ROM为0,也就是说,ROM的0地址中的数据被读了220次,继续下去,ROM中的4096个点,每个点都将会被读220次,最终输出的波形频率应该是参考时钟频率的1 / 220,周期被扩大了220 倍。同样当频率控制字为100时,相位累加器的低20位一直会加100,那么,相位累加器的低20位溢出的时间比上面会快100倍,则ROM中的每个点相比于上面会少读100次,所以最终输出频率是上述的10倍。

为什么地址是由相位控制字加频率控制字高12位得到的?
1、本次实验使用的rom是宽度为14,深度为2^12 = 4096的数据,所以相位控制字根据rom的深度选择了12位宽
2、为什么ROM宽度是14,深度不取2^14?FPGA资源不够,没有这么多的寄存器存取这么多的数据
3、地址 = 相位 + 频率更迭,而相位宽度为12位,频率的宽度比相位多,所以频率控制字取高几位是由相位控制字的宽度决定的
4、取频率控制字高12位是如何完成频率变换的?
举例:
2^1 = 2’b10
2^2 = 3’b100
2^3 = 4’b1000

2^19 = 20’b1000_0000_0000_0000_0000
2^20 = 21’b1_0000_0000_0000_0000_0000
2^21 = 22’b10_0000_0000_0000_0000_0000

f = 1/T,N = 32
频率控制字为:2^20
fword_acc[31:0] + 2^20 相当于 (fword_acc[31:20] + 1)此时就是按照地址+1的速度寻址,假如Fclk = 50MHz(系统时钟),Tclk = 20ns,输出波形的周期就为:To = 20ns * 4096

频率控制字为:2^19
fword_acc[31:0] + (2^19 + 2^19) 相当于 (fword_acc[31:20] + 1),也就是要加两次频率控制字,才能实现一次地址+1,Tclk = 20ns,输出完整波形就要输出2次*4096个数据,输出的波形周期为:To = 20ns * (2 * 4096)

频率控制字为:2^21
fword_acc[31:0] + (2^21) 相当于 (fword_add[31:20] + 2’b10),加一次频率控制字,实现一次地址+2,Tclk = 20ns,因为是跳过一位地址取的数据,所以数据量减半,输出完整波形只需要输出4096/2个数据,输出的波形周期为:To = 20ns * (4096/2)

代码相关

DDS模块,目前只用了正弦波进行测试,当然,余弦波也是类似的哈。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/08/07 20:31:52
// Design Name: 
// Module Name: DDS_SIN_COS
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module DDS_SIN_COS(
    sys_clk,
    sys_rst,
    clk_out,
    signal_out
    );
    input sys_clk,sys_rst;
    output clk_out;
    output [11:0] signal_out;
    
    wire  [31:0]F_word;//频率控制字,可以对频率进行控制
    wire [11:0]P_word;//相位控制字
    assign F_word =32'd85899;
    assign P_word = 12'b0;
    
    wire [11:0] wave_data;
    reg [11:0] wave_data_r;
    reg [31:0] Fcnt;
    wire [11:0] rom_addr;
    
    assign signal_out = wave_data_r;
    
    always@(posedge sys_clk or negedge sys_rst)
        begin
            if(!sys_rst)
                Fcnt <= 32'd0;
            else
                Fcnt <= Fcnt + F_word;
        end
        
    assign rom_addr = Fcnt[31:20]+P_word;
    assign clk_out = sys_clk;
    
    rom_sin uut
    (
  .clka(clk_out),    // input wire clka
  //.wea(wea),      // input wire [0 : 0] wea
  .addra(rom_addr),  // input wire [10 : 0] addra
  //.dina(dina),    // input wire [15 : 0] dina
  .douta(wave_data)  // output wire [15 : 0] douta
    );
    
    always @(posedge clk_out)
    begin
	   wave_data_r <= wave_data;
    end
endmodule

testbench:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2024/08/07 20:37:52
// Design Name: 
// Module Name: tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
module tb(

    );
 reg clk;
 reg reset_n;
 wire [11:0] sine_out;
 
 initial
     begin
         clk=0;
         reset_n=1;
         #20 reset_n=0;
         #20 reset_n=1;
     end
 
 always 
    begin
        #10 clk=~clk;//需要获得50M时钟,周期20ps,所以延时为10
    end
 
 DDS_SIN_COS    dds_uut(
        .sys_clk(clk),
        .sys_rst(reset_n),
        //.clk_out(),//这个dac时钟是为了配合外部dac模块进行的输出,测试时不需要
        .signal_out(sine_out)
         );
 
endmodule

在这里插入图片描述

涉及ROM等ip核调用就不过多解释了,可以在网上查找波形ceo文件生成的方式。

引用来源:FPGA之DDS信号发生器(个人学习参考) - 快乐气氛组阿宇 - 博客园 (cnblogs.com)

第1篇:基于ROM的直接数字式频率合成器(DDS)的FPGA实现-CSDN博客

直接数字式频率合成法原理(DDS)_直接数字频率合成dds-CSDN博客

用FPGA实现dds的方案详解(保姆级入门教学)(VIVADO18.3、quartus13.1)_fpga dds-CSDN博客

用FPGA实现dds的方案详解(保姆级入门教学)(VIVADO18.3、quartus13.1)_fpga dds-CSDN博客

多谢这些博主的知识。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值