FPGA设计案例--线性插值的DDS信号发生器

前言

        在项目中,有时需要用到DDS信号发生器来产生一些基本信号用于驱动AD,虽然,Vivado或Quartus都有提供DDS IP核,但是都只能产生正弦信号/或余弦信号,像三角波信号、方波信号是没有办法产生的,这时,就需要我们自己设计一款能够生成任意波形的DDS信号发生器了。


DDS原理

        DDS的作用是将一串脉冲转换成一个模拟波形,通常为一个正弦波、三角波或方波等,利用数字信号处理的方式来产生一个频率和相位可调的输出,该输出与一个参考时钟的频率相关。基本的DDS结构如图 1 所示,其主要部分为:相位累加器(产生输出波形相位角度的数据),相数转换器(将上述相位数据转换为瞬时输出幅度数据,通常是一个存储波形幅值的查找表),以及数模转换器(DAC)(将该幅度数据转换成采样模拟数据点)。

图1  DDS基本原理框图

        DDS输出信号的频率可以按照下面的表达式计算:

F_{out}=\frac{M}{2^N}F_{ref}

        其中,F_{out}表示输出信号的频率,M表示相位增量,N表示相位累加器位宽,F_{ref}表示参考时钟的频率。当NF_{ref}参数确定后,DDS所能输出的信号的最小频率也随之确定了,即DDS的频率分辨率为: F=\frac{F_{ref}}{2^N}。所以,根据所需要输出信号的最小频率即可计算出参数NF_{ref}的最佳值。

基于线性插值的DDS信号发生器

        在某预研测试系统中,需要产生一路1Hz~100KHz的信号正弦或三角波信号,用来作为测试源。根据输出信号频率范围,可以计算出\frac{F_{ref}}{2^N}\leqslant 1,当F_{ref}=10MHz时,N\geqslant 24。即,当DDS的参考时钟频率为10MHz时,要产生频率范围为1Hz~100KHz的信号,相位累加器的位宽至少需要24bit。也就表示,所需要的波形幅值的查找表的深度至少需要2^{24},即需要一个至少能够存储16M个采样点的查找表,对FPGA来说几乎不可能有16M*14bit这么大容量的存储器资源。

        那么有没有办法能够实现这样的一个DDS呢?当然是有办法的。那就是,使用一个采样点较少的查找表,然后结合线性插值思想,根据查找表中前后相邻的两个采样点,插值出中间的幅值。这样就可以使用较少的存储器资源实现相位累加器位宽如此之大的DDS系统了。

        多的就不说了,直接上代码:

module dds #(parameter PHIN_W = 32,DAT_W = 14)(
    input                       clk     ,
    input                       rst_n   ,
    input                       wave_sel,//0-sin 1-triangular     
    input   [PHIN_W - 1 : 0]    pha_inc ,//Phase increment
    output  [DAT_W - 1 : 0]     dout    
);    

//parameters declare
    localparam  LUT_D = 1024    ,//Number of Sample Point
                LUT_W = DAT_W   ;

//Signal declare
    reg     [PHIN_W - 1 : 0]            phase_acc   ;
    wire    [9 : 0]                     addr_0      ;
    wire    [9 : 0]                     addr_1      ;
    reg     [PHIN_W - 11 : 0]           weight      ;
    reg     [PHIN_W - 11 : 0]           weight_0    ;
    reg     [PHIN_W - 11 : 0]           weight_1    ;
    reg     [PHIN_W - 11 : 0]           weight_2    ;
    reg     [DAT_W - 1 : 0]             sub         ;
    reg     [PHIN_W - 11 + DAT_W : 0]   mult        ;//PHIN_W - 10 + DAT_W bit
    reg     [DAT_W - 1 : 0]             sum         ;
    wire    [DAT_W - 1 : 0]             sin_dout_a  ;
    wire    [DAT_W - 1 : 0]             sin_dout_b  ;
    wire    [DAT_W - 1 : 0]             tri_dout_a  ;
    wire    [DAT_W - 1 : 0]             tri_dout_b  ;

    wire    [DAT_W - 1 : 0]             dout_a      ;
    wire    [DAT_W - 1 : 0]             dout_b      ;
    reg     [DAT_W - 1 : 0]             dout_a_d0   ;
    reg     [DAT_W - 1 : 0]             dout_b_d0   ;
    reg     [DAT_W - 1 : 0]             dout_a_d1   ;
    reg     [DAT_W - 1 : 0]             dout_b_d1   ;

//phase accumulator 
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            phase_acc <= 'd0;
        end
        else begin
            phase_acc <= phase_acc + pha_inc;
        end
    end
    assign addr_0 = phase_acc[PHIN_W - 1 -: 10];
    assign addr_1 = phase_acc[PHIN_W - 1 -: 10] + 1;

//Pipeline 1
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            weight   <= 'd0;
            weight_0 <= 'd0;
            weight_1 <= 'd0;
            weight_2 <= 'd0;
        end
        else begin
            weight   <= phase_acc[PHIN_W - 1 - 10 : 0];
            weight_0 <= weight;
            weight_1 <= weight_0;
            weight_2 <= weight_1;
        end
    end

//Pipeline 1    
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            sub <= 'd0;
        end
        else begin
            sub <= (dout_a >= dout_b) ? (dout_a - dout_b) : (dout_b - dout_a);
        end
    end
    assign dout_a = (wave_sel == 1'b0) ? sin_dout_a : tri_dout_a;
    assign dout_b = (wave_sel == 1'b0) ? sin_dout_b : tri_dout_b;

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            dout_a_d0 <= 'd0;
            dout_b_d0 <= 'd0;
            dout_a_d1 <= 'd0;
            dout_b_d1 <= 'd0;
        end
        else begin
            dout_a_d0 <= dout_a;
            dout_b_d0 <= dout_b;
            dout_a_d1 <= dout_a_d0;
            dout_b_d1 <= dout_b_d0;
        end
    end

//Pipeline 2    
    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            mult <= 'd0;
        end
        else begin
            mult <= sub * weight_2;
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            sum <= 'd0;
        end
        else begin
            sum <= (dout_a_d1 >= dout_b_d1) ? dout_a_d1 - mult[PHIN_W + 3 -: DAT_W] : 
                                              dout_a_d1 + mult[PHIN_W + 3 -: DAT_W];
        end
    end

//output
    assign dout = sum;

    
//ROM instantiate

    xilinx_dpram #(.RAM_DEPTH   (LUT_D      ),
                   .RAM_WIDTH   (LUT_W      ),
                   .RAM_MODE    ("SYNC"     ),
                   .RAM_INIT    ("sin.mem"  )

    )u_sin_rom(
    .clk     (clk           ),//clk_a
    .clk_b   (clk           ),//clk_b
    .rst_n   (rst_n         ),

    //port a
    .wren_a  (1'b0          ),
    .rden_a  (1'b1          ),
    .addr_a  (addr_0        ),
    .din_a   ({LUT_W{1'b0}} ),
    .dout_a  (sin_dout_a    ),
    
    //port b
    .wren_b  (1'b0          ),
    .rden_b  (1'b1          ),
    .addr_b  (addr_1        ),
    .din_b   ({LUT_W{1'b0}} ),
    .dout_b  (sin_dout_b    )
    );

    xilinx_dpram #(.RAM_DEPTH   (LUT_D      ),
                   .RAM_WIDTH   (LUT_W      ),
                   .RAM_MODE    ("SYNC"     ),
                   .RAM_INIT    ("tri.mem"  )
    )u_tri_rom(
    .clk     (clk           ),//clk_a
    .clk_b   (clk           ),//clk_b
    .rst_n   (rst_n         ),

    //port a
    .wren_a  (1'b0          ),
    .rden_a  (1'b1          ),
    .addr_a  (addr_0        ),
    .din_a   ({LUT_W{1'b0}} ),
    .dout_a  (tri_dout_a),
    
    //port b
    .wren_b  (1'b0          ),
    .rden_b  (1'b1          ),
    .addr_b  (addr_1        ),
    .din_b   ({LUT_W{1'b0}} ),
    .dout_b  (tri_dout_b    )
    );

endmodule 

图2 仿真波形图

       当参考时钟频率为10MHz、相位增量为100时,输出信号的频率为:F_{out}=\frac{10*10^6*100}{2^{24}}\approx 59.6Hz,在Questasim仿真波形中测量得到的频率大约为59.481Hz,频率误差小于1Hz,说明使用插值思想实现的DDS信号发生器基本是符合所提指标的。这样,我们只需要一个深度为1024的查找表,即可实现这样高分辨率的DDS信号发生器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值