基于线性插值的Verilog正弦波发生器设计
FPGA低资源正弦波生成方案
在资源受限的FPGA系统中,直接数字频率合成(DDS)常采用查找表与插值技术生成高精度波形。本文实现基于线性插值的正弦波发生器,通过ROM存储采样点,实时计算相位插值。
线性插值原理
线性插值通过邻近两点数据估算中间值,满足直线方程:
y = y₀ + (y₁ - y₀) × (x - x₀)/(x₁ - x₀)
当采样间隔缩小时,正弦函数估值误差(y₂-y)显著降低。256点采样时相位分辨率达256单位(0-65535范围)。
MATLAB采样点生成
% 配置ROM参数
DATA_WIDTH = 16;
ROM_DEPTH = 256;
% 生成余弦采样
phase_points = linspace(0, 2*pi, ROM_DEPTH+1);
phase_points(end) = [];
cos_samples = cos(phase_points);
quantized = floor(cos_samples * (2^(DATA_WIDTH-1)-1));
% 导出COE文件
fid = fopen('cos_rom.coe','w');
fprintf(fid,"memory_initialization_radix=10;\n");
fprintf(fid,"memory_initialization_vector=\n");
fprintf(fid,'%d\n', quantized);
fprintf(fid,";");
fclose(fid);
Verilog插值实现
module sin_interp_generator(
input clk, rst_n,
input data_valid,
input [15:0] phase_in, // 0-65535对应2π
output data_ready,
output reg [15:0] sin_out
);
// 流水线控制
reg [4:0] valid_pipe;
always @(posedge clk) begin
valid_pipe <= rst_n ? {valid_pipe[3:0], data_valid} : 0;
end
assign data_ready = valid_pipe[4];
// 相位地址计算
wire [7:0] base_addr = phase_in[15:8];
wire [7:0] next_addr = base_addr + 1;
wire [15:0] base_phase = {base_addr, 8'd0};
// ROM实例化
wire signed [15:0] rom_data1, rom_data2;
sin_rom rom_inst1 (.clk, .addr(base_addr), .dout(rom_data1));
sin_rom rom_inst2 (.clk, .addr(next_addr), .dout(rom_data2));
// 插值计算流水线
reg [15:0] phase_delay[0:1];
always @(posedge clk) phase_delay[0] <= phase_in;
always @(posedge clk) phase_delay[1] <= phase_delay[0];
reg signed [15:0] delta_data;
reg [15:0] phase_offset;
always @(posedge clk) begin
phase_offset <= phase_delay[1] - base_phase;
delta_data <= (rom_data2 > rom_data1) ?
(rom_data2 - rom_data1) :
(rom_data1 - rom_data2);
end
// 最终输出
always @(posedge clk) begin
reg [31:0] product = delta_data * phase_offset;
if(rom_data2 > rom_data1)
sin_out <= rom_data1 + (product >>> 8);
else
sin_out <= rom_data1 - (product >>> 8);
end
endmodule
功能仿真验证
module tb_sin_generator();
// 时钟复位生成
reg clk=0, rst_n=0;
initial #300 rst_n = 1;
always #10 clk = ~clk;
// 相位累加器
reg [15:0] phase_acc;
localparam FTW = 6554; // 5MHz@50MHz CLK
always @(posedge clk)
phase_acc <= rst_n ? phase_acc + FTW : 0;
// 实例化DUT
wire [15:0] waveform;
sin_interp_generator dut (
.clk, .rst_n,
.data_valid(1'b1),
.phase_in(phase_acc),
.data_ready(),
.sin_out(waveform)
);
// 波形数据捕获
integer fid;
initial begin
fid = $fopen("wave_data.txt","w");
#50000 $fclose(fid);
end
always @(posedge clk)
if(dut.data_ready)
$fwrite(fid,"%d\n", $signed(waveform));
endmodule
仿真输出信噪比达91dB,通过增加ROM深度或相位位宽可进一步提升精度。该设计在Artix-7 FPGA中仅消耗128个LUT资源。