最近在项目上遇到一个需要在低资源FPGA上实现FFT逻辑的项目,而且要求实现窗函数。对于窗函数来说,莫非是实现正弦波生成器,正弦波生成器可以利用DDS模块,CORDIC模块,或者查找表的方式实现,以下主要讲解ROM核线性插值相结合的波形生成器,用于生成正弦波。
线性插值是一种数据估值算法,由于其拟合线是一条直线,所以叫做线性插值。即通过需要估值点的左右两个点的权重以及距离,对估值点的权重进行计算的一种算法。
(x1,y1)
(x0,y0)
(x,y)Text is not SVG - cannot display
由于估值拟合线是直线那么,已知(x0,y0)和(x1,y1),以及x到两点的距离,对y进行计算。
(x0,y0)
(x1,y1)
x
y
对正弦函数进行估值:
(x,y)
(x2,y2)Text is not SVG - cannot display
其中(x,y)表示估算值,(x2,y2)表示真实值,误差为y2-y,即当x1-x0越小,估算值越准确。样本点越多越精确。
(x,y)
(x2,y2)
y2-y
x1-x0
以下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);
以下将使用参数:样本深度256,相位最大值65536进行讲解。
对某一个点进行线性估值的时候,我们需要知道当前点在样本中对应相应点的邻近点。样本邻近两点相位差65536/256 = 256,假设插值相位位置为phase,则相邻点为floor(phase/256)和floor(phase/256)+1,floor表示向下取整,rom表示查找表数据。
65536/256 = 256
floor(phase/256)
floor(phase/256)+1
floor
那么
以下为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
仿真结果:
这样一个正弦波生成器就完成了,SNR=91db,足以满足大多数的使用情况了,如果需要更高的精度,可以更改样本的点数,为了提升频率精度,需要对相位控制字位宽进行扩展。
您必须 [ 登录 ] 才能发表留言!