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,足以满足大多数的使用情况了,如果需要更高的精度,可以更改样本的点数,为了提升频率精度,需要对相位控制字位宽进行扩展。

相关推荐
ytttr8731 天前
MATLAB基于LDA的人脸识别算法实现(ORL数据库)
数据库·算法·matlab
wuk9981 天前
matlab为地图进行四色着色
开发语言·matlab
元周民1 天前
matlab求两个具有共根的多项式的所有共根(未详细验证)
开发语言·matlab
代码小小只1 天前
考虑不确定性的电动汽车优化问题
matlab
yong99901 天前
基于小波分析与粒子群算法的电网潮流优化实现(MATLAB)
开发语言·算法·matlab
Dev7z1 天前
基于MATLAB HSI颜色空间的图像美颜系统设计与实现
开发语言·matlab
ghie90901 天前
MATLAB 高速公路裂缝检测
开发语言·matlab
天呐草莓1 天前
热传导方程
算法·matlab
hoiii1871 天前
基于MATLAB实现无监督数据建模
开发语言·matlab
kaikaile19951 天前
使用纯MATLAB M函数实现的无刷直流电机控制系统仿真
开发语言·matlab