• 用verilog模拟DDS产生正弦波信号


    前言:

    DDS:直接数字频率合成,正弦波0-2pi周期内,相位到幅度是一一对应的(这里我们使用放大后的整数幅度)。

    主要思路:

    个人理解,FPGA不擅长直接做数字信号计算,那样太占用片上逻辑资源,所以需要事先建立 正弦波相位-幅度 表,然后在时钟下,通过相位累加并用相位作为地址索引来查询正弦波信号表。

    正弦波相位-幅度 表:
    存储的是量化的正弦波在一个周期的幅度信息(幅度的地址即相位)。
    幅度的地址数目决定了相位量化的误差。
    而存储每一个幅度的比特数决定了幅度的量化误差。
    可以通过matlab以及Xilinx的IP核向导创建。

    Verilog编写的DDS模块主要由三部分组成,

    • 相位累加器,用于决定输出信号频率的范围和精度;
    • 正弦函数模块,用于存储经量化和离散后的正弦函数的幅值;
    • 查表模块,对相位累加器的输出地址查表。

    两种方法可以改变输出信号的频率:

    • 改变查表寻址的时钟频率,从而改变输出波形的频率。
    • 改变寻址的步长来改变输出信号的频率。
      步长即为相位增量。
      由累加器对相位增量进行累加,
      累加器的值作为查表地址。

    相位累加器是 DDS 的核心所在,前面在低于时钟频率的任意频率生成(相位累加器)中我们已经进行了叙述。
    正弦函数模块包含一个周期正弦波的数字幅度信息,每个地址对应正弦波中0-2pi范围的一个相位点。查表模块把输入的地址相位信息映射成正弦波幅度的数字量信号。相位寄存器每经过 2^N/K 个时钟后回到初始状态,相应地正弦查询表经过一个循环回到初始位置,输出一个正弦波。

    输出正弦波周期为fo=fc* K/2^N ,最小分辨率为f=fc/2^N。(通过fc和K控制正弦波频率精度) 其中,N 为累加器位宽,K 为步长,fc 为时钟频率。计数模(最大值):M=2^N。

    一般正弦波表幅度地址位宽与累加的查表地址位宽不同,按前者位宽取后者对应高位的位宽即可。(具体见实例)

    先用matlab生成1024点的正弦波数据:

    clc;clear;
    N = 10;                     %储存单元地址线
    depth=2^N;                 %存储单元;
    widths=N;                    %数据宽度为8位;
    index = linspace(0,pi*2,depth);              
    sin_value = sin(index);                
    sin_value = sin_value * (depth/2 -1);  %扩大正弦幅度值    
    sin_value = fix((sin_value)+0.5);
    plot(sin_value);
    number = [0:depth];
    fid=fopen('sin_table.coe','w+');
    fprintf(fid,'memory_initialization_radix=10;
    ');
    fprintf(fid,'memory_initialization_vector=
    ');
    for i = 1 : depth - 1  
        fprintf(fid, '%d,
    ', sin_value(i));
    end
    fprintf(fid, '%d;', sin_value(depth));
    fclose(fid);
    

    Verilog程序

    1、adder.v文件,相位累加模块

    `timescale 1ns/1ps
    /***************************************
    晶振频率 fc = 100MHz
    输出频率 fo = 1kHz(根据需要可以设为任意值)
    控制参数 K  = (fo*2^N)/fc = 42950
    参数 N = 2^32,(32为计数器的位宽)
    ****************************************/
    module PHASE_ADDER(
        input clk,
        input rst,
        output reg [31:0] cnt,
        output reg clk_out
        );
    
    always @(posedge clk or posedge rst) 
        if(rst)
            cnt <= 0;
        else
            cnt <= cnt + 32'd42950;  //计数器步长K
    
    always @(posedge clk or posedge rst)
        if(rst)
            clk_out <= 1'b0;
        else if(cnt < 32'h7FFF_FFFF)
            clk_out <= 1'b0;
        else
            clk_out <= 1'b1;
    
    endmodule
    

    2、dds_top.v顶层设计

    `timescale 10ns /1ns
    
    module dds_top(
        input rst,
        input clk,
        output signed [15:0] sine_o
        );
    
        wire [31:0] phase;   //32bit内部连接线,传递相位增量
        wire clk_out;
        wire [9:0] addr;   //10bit相位信息
    
        PHASE_ADDER U_PHASE_ADDER(
            .clk    (clk    ),
            .rst    (rst    ),
            .cnt    (phase  ),
            .clk_out(clk_out)
            );
         
        assign addr = phase[31:22];//addr 10bit
        
        DDS_Table U_DDS_Table(
            .clka(clk),    // input wire clka
            .addra(addr),  // input wire [9 : 0] addra
            .douta(sine_o)  // output wire [15 : 0] douta
            );
    endmodule
    

    3、仿真测试文件

    `timescale 1ns/1ps
    
    module TB;
        
        reg clk;
        reg rst;
        wire clk_out;
        dds_top U_dds_top(
            .clk    (clk    ),
            .rst    (rst    )
        );
    
        initial begin
            clk = 0;
            rst = 0;
            #4 rst = 1;
            #3 rst = 0;
        end
    
        always #5 clk = ~clk;
    endmodule 
    

    matlab生成正弦数据:

    sin_table

    vivado和Modelsim联合仿真结果:

    仿真结果

  • 相关阅读:
    从电视剧《清平乐》聊聊宋仁宗和宋词
    也读《白鹿原》:望关中平原,窥民族秘史
    听说你在做数字化转型,了解中台一下不?
    刘润《商业洞察力30讲》学习总结
    《容器化.NET应用架构指南》脑图学习笔记(一)
    也聊春节:漫天红色与春晚变迁
    我的2019年终回顾:行道迟迟,载饥载渴,而立之年,持续刷新
    ASP.NET Core on K8S深入学习(11)K8S网络知多少
    ASP.NET Core on K8S深入学习(10)K8S包管理器Helm
    【译】gRPC vs HTTP APIs
  • 原文地址:https://www.cnblogs.com/christsong/p/5536995.html
Copyright © 2020-2023  润新知