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代码用于生成正弦函数样本值,用于进行数据估算。

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代码实现:

`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  

仿真代码:

`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, "%dn", $signed(cos_out));             if(data_cnt == 4096*4-1)begin                 $fclose(file);                 $finish;             end         end             end endmodule  

仿真结果:

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

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

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

发表评论

您必须 [ 登录 ] 才能发表留言!

相关文章