verilog利用线性插值实现正弦波生成器(dds)

verilog实现线性插值实现正弦波生成器

​ 最近在项目上遇到一个需要在低资源FPGA上实现FFT逻辑的项目,而且要求实现窗函数。对于窗函数来说,莫非是实现正弦波生成器,正弦波生成器可以利用DDS模块,CORDIC模块,或者查找表的方式实现,以下主要讲解ROM核线性插值相结合的波形生成器,用于生成正弦波。

1.线性插值

​ 线性插值是一种数据估值算法,由于其拟合线是一条直线,所以叫做线性插值。即通过需要估值点的左右两个点的权重以及距离,对估值点的权重进行计算的一种算法。
(x1,y1) (x1,y1) (x0,y0) (x0,y0) (x,y) (x,y) Text is not SVG - cannot display

由于估值拟合线是直线那么,已知(x0,y0)(x1,y1),以及x到两点的距离,对y进行计算。

\[\begin{split} &\frac{y_1-y_0}{x_1-x_0} = \frac{y-y_0}{x-x_0} \\ &y = y_0 + \frac{(y_1-y_0)*(x-x_0)}{x_1-x_0} \end{split} \]

对正弦函数进行估值:
(x1,y1) (x1,y1) (x0,y0) (x0,y0) (x,y) (x,y) (x2,y2) (x2,y2) Text is not SVG - cannot display

其中(x,y)表示估算值,(x2,y2)表示真实值,误差为y2-y,即当x1-x0越小,估算值越准确。样本点越多越精确。

2.样本生成

以下matlab代码用于生成正弦函数样本值,用于进行数据估算。

matlab 复制代码
clc,clear,close all
%% 生成 rom 数据
Width=16;
Depth=256;
phi=linspace(0,2*pi,Depth+1);
phi=phi(1:end-1)';
cos_sig=cos(phi);
cos_sig=floor(cos_sig*(2^(Width-1)-1));
plot(cos_sig)
%% 生成.coe文件
filename='.\cos_rom.coe';
fid = fopen(filename,'w');
radix = 10;
fprintf(fid,"memory_initialization_radix=%d;\n",radix); %使用的进制
fprintf(fid,"memory_initialization_vector=");
for i=1:size(cos_sig,1)
    fprintf(fid,"\n%d",cos_sig(i));
end
fprintf(fid,";");
fclose(fid);

3.verilog实现线性插值

以下将使用参数:样本深度256,相位最大值65536进行讲解。

​ 对某一个点进行线性估值的时候,我们需要知道当前点在样本中对应相应点的邻近点。样本邻近两点相位差65536/256 = 256,假设插值相位位置为phase,则相邻点为floor(phase/256)floor(phase/256)+1floor表示向下取整,rom表示查找表数据。

那么

\[\begin{equation} y = y_0 + \frac{(y_1-y_0)*(x-x_0)}{x_1-x_0} = rom(floor(phase/256)) + \frac{(rom(floor(phase/256) + 1)-rom(floor(phase/256)))*(phase-floor(phase/256)*256)}{256} \end{equation} \]

以下为VERILOG代码实现:

verilog 复制代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2025/03/29 15:47:50
// Design Name: 
// Module Name: cos_gen_pipeline
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module cos_gen_pipeline(
    input			    clk     ,
    input               rst     ,
    input               valid   ,	
    input	    [15:0]	phase   ,	//相位,0~65535对应0~2pi)
    output              rdy     ,   
    output	reg [15:0]	cos_out
    );

    reg  [4:0]  valid_d;

    always @(posedge clk) begin
        if(rst)begin
            valid_d	<= 0;
        end else begin
            valid_d	<= {valid_d[3:0],valid};
        end
    end
    assign rdy = valid_d[4];

    
    wire	    [7:0]	addr1;
    wire	    [7:0]	addr2;
    wire signed	[15:0]	cos_dat1;
    wire signed	[15:0]	cos_dat2;
    wire 	    [15:0]	phase1;
    //-----------线性插值-----------------------------
    assign	addr1	= (phase>>8)    ;
    assign	addr2	= (phase>>8)+1  ;
    assign	phase1	= addr1<<8      ;
    cos_rom cos_rom_inst1(
        .clka	(clk        ),
        .addra	(addr1      ),
        .douta	(cos_dat1   )
    );
    cos_rom cos_rom_inst2(
        .clka	(clk        ),
        .addra	(addr2      ),
        .douta	(cos_dat2   )
    );
    reg		[15:0]	phase_d0    ;
    reg		[15:0]	phase_d1    ;	
    reg		[15:0]	phase1_d0   ;
    reg		[15:0]	phase1_d1   ;

    always @(posedge clk) begin
        if(rst)begin
            phase_d0	<= 0            ;
            phase_d1	<= 0            ;
            phase1_d0	<= 0            ;
            phase1_d1	<= 0            ;
        end else begin
            phase_d0	<= phase        ;
            phase_d1	<= phase_d0     ;
            phase1_d0	<= phase1       ;
            phase1_d1	<= phase1_d0    ;
        end
    end
    reg	[31:0]	multi;
    reg	[15:0]	delta_cos_data  ;
    reg	[15:0]	delta_phase     ;
    always @(posedge clk) begin
        if(rst)begin
            multi	<= 0;
        end else begin
            if(cos_dat2 > cos_dat1)begin
                delta_cos_data      <= (cos_dat2 - cos_dat1)        ;
                delta_phase         <= phase_d1 - phase1_d1         ;
                multi               <= delta_cos_data*delta_phase   ;
            end else begin
                delta_cos_data  <= (cos_dat1 - cos_dat2)            ;
                delta_phase     <= phase_d1 - phase1_d1             ;
                multi           <= delta_cos_data*delta_phase       ;
            end
        end
    end

    reg	signed  [15:0]	cos_dat1_d;
    reg	signed  [15:0]	cos_dat2_d;

    always @(posedge clk) begin
        if(rst)begin
            cos_dat1_d	<= 0;
            cos_dat2_d	<= 0;
        end else begin
            cos_dat1_d	<= cos_dat1;
            cos_dat2_d	<= cos_dat2;
        end
    end
    reg	signed  [15:0]	cos_dat1_d1;
    reg	signed  [15:0]	cos_dat2_d1;

    always @(posedge clk) begin
        if(rst)begin
            cos_dat1_d1	<= 0;
            cos_dat2_d1	<= 0;
        end else begin
            cos_dat1_d1	<= cos_dat1_d;
            cos_dat2_d1	<= cos_dat2_d;
        end
    end
    always @(posedge clk) begin
        if(rst)begin
            cos_out	<= 0;
        end else begin
            if(cos_dat2_d1 > cos_dat1_d1)begin
                cos_out	<= cos_dat1_d1 + (multi >> 8);
            end else begin
                cos_out	<= cos_dat1_d1 - (multi >> 8);
            end
        end
    end
endmodule

仿真代码:

verilog 复制代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2025/04/05 00:00:38
// Design Name: 
// Module Name: tb_cos_gen_pipeline
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module tb_cos_gen_pipeline();
    reg clk;
    reg rst;

    initial begin
        clk <=0;
        rst <=1;

        #300
        rst <=0;
    end

    always #10 clk <= ~clk;

    reg                 valid   ;
    reg	    [15:0]	    phase   ;

    localparam FREQ_FTW = 6554; // 频率控制字 生成5Mhz的正弦波,采样率50M,round((5/50)*(2^16))

    always @(posedge clk)begin
        if(rst)begin
            valid <= 0;
            phase <= 0;
        end
        else begin
            valid <= 1;
            phase <= phase + FREQ_FTW;
        end
    end

    wire                rdy     ;   
    wire    [15:0]	    cos_out ;

    cos_gen_pipeline    cos_gen_pipeline(
        .clk     (clk    ),
        .rst     (rst    ),
        .valid   (valid  ),	//使能信号  
        .phase   (phase  ),	//相位,0~65535对应[0~2pi)
        .rdy     (rdy    ),   //输出准备好信号
        .cos_out (cos_out)
    );

    integer file = 0;

    initial begin
        file = $fopen("cos_gen_pipeline.txt", "w");
        if (file == 0) begin
            $display("Error opening file");
            $finish;
        end
    end
    reg [15:0]  data_cnt = 0;
    always @(posedge clk)begin
        if(rdy)begin
            data_cnt <= data_cnt + 1;
            $fwrite(file, "%d\n", $signed(cos_out));
            if(data_cnt == 4096*4-1)begin
                $fclose(file);
                $finish;
            end
        end        
    end
endmodule

仿真结果:

​ 这样一个正弦波生成器就完成了,SNR=91db,足以满足大多数的使用情况了,如果需要更高的精度,可以更改样本的点数,为了提升频率精度,需要对相位控制字位宽进行扩展。

相关推荐
Mason Lin1 天前
2025年3月29日(matlab -ss -lti)
开发语言·matlab
Qian_ShouYi2 天前
MATLAB 代码学习
学习·算法·matlab
斯汤雷2 天前
Matlab绘图案例,设置图片大小,坐标轴比例为黄金比
数据库·人工智能·算法·matlab·信息可视化
鹿屿二向箔2 天前
阀门流量控制系统MATLAB仿真PID
开发语言·matlab
没有黑科技2 天前
0.雷达信号
matlab
QQ__17646198243 天前
Matlab安装tdms插件
开发语言·matlab·tdms插件
天`南3 天前
【三维异构Dvhop定位】基于灰狼优化算法的三维异构Dvhop定位算法【Matlab代码#93】
matlab·dvhop·异构无线传感器网络
小白狮ww3 天前
Retinex 算法 + MATLAB 软件,高效率完成图像去雾处理
开发语言·人工智能·算法·matlab·自然语言处理·图像识别·去雾处理
机器学习之心3 天前
区间预测 | QRTCN时间卷积神经网络分位数回归时间序列区间预测模型(Matlab完整源码和数据)
matlab·回归·cnn·分位数回归·时间卷积神经网络·qrtcn·区间预测模型