verilog利用线性插值实现正弦波生成器(dds)
verilog实现线性插值实现正弦波生成器
最近在项目上遇到一个需要在低资源FPGA上实现FFT逻辑的项目,而且要求实现窗函数。对于窗函数来说,莫非是实现正弦波生成器,正弦波生成器可以利用DDS模块,CORDIC模块,或者查找表的方式实现,以下主要讲解ROM核线性插值相结合的波形生成器,用于生成正弦波。
1.线性插值
线性插值是一种数据估值算法,由于其拟合线是一条直线,所以叫做线性插值。即通过需要估值点的左右两个点的权重以及距离,对估值点的权重进行计算的一种算法。
由于估值拟合线是直线那么,已知(x0,y0)和(x1,y1),以及x到两点的距离,对y进行计算。
&\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}
\]
对正弦函数进行估值:
其中(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)+1,floor表示向下取整,rom表示查找表数据。
那么
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, "%d\n", $signed(cos_out));
if(data_cnt == 4096*4-1)begin
$fclose(file);
$finish;
end
end
end
endmodule
仿真结果:


这样一个正弦波生成器就完成了,SNR=91db,足以满足大多数的使用情况了,如果需要更高的精度,可以更改样本的点数,为了提升频率精度,需要对相位控制字位宽进行扩展。
verilog利用线性插值实现正弦波生成器(dds)的更多相关文章
- mysql利用LAST_INSERT_ID实现id生成器
首先了解 LAST_INSERT_ID LAST_INSERT_ID 有自己的存储空间,能存一个数字 不带参数时返回最近insert的那行记录的自增字段值.带参数时会将自己存储的数字刷成参数给定的值 ...
- [FPGA]Verilog利用PWM调制巧妙完成RGB三色彩虹呼吸灯(给简约的题目以美妙的解答)
概述 实现彩虹呼吸灯 题目就是这么简短,但这是目前我碰到的最有意思的一道题目,因为他有无数种解决方法,并且每一种都是那么高级或者巧妙,比如 可以利用3路不同初相的PWM调制信号驱动三颗RGB灯重叠呼吸 ...
- Verilog利用$fdisplay命令往文件中写入数据
最近在做的事情是,用FPGA生成一些满足特定分布的序列.因此为了验证我生成的序列是否拥有预期的性质,我需要将生成的数据提取出来并且放到MATLAB中做数据分析. 但是网上的程序很乱,表示看不懂==其实 ...
- 基于FPGA的DDS设计(一)
最近在学习基于FPGA的DDS设计,借此机会把学习过程记录下来,当作自己的学习笔记也希望能够帮助到学习DDS的小伙伴. DDS(Direct Digital Synthesizer)直接数字合成器,这 ...
- Python小白学习之路(二十三)—【生成器补充】
生成器的一些补充 接着下鸡蛋和吃包子! 补充一:生成器只能遍历一次 (总是把生成器比喻成母鸡下鸡蛋,需要一个下一个,首先是下出来的鸡蛋不能塞回母鸡肚子里,其次是一个母鸡一生只能下一定数量的鸡蛋,下完了 ...
- python里的生成器
author:headsen chen date:2018-03-22 10:59:46 notice:This article created by headsen chen himself and ...
- FFMpeg ver 20160219-git-98a0053 滤镜中英文对照 2016.02.21 by 1CM
FFMpeg ver 20160219-git-98a0053 滤镜中英文对照 2016.02.21 by 1CM T.. = Timeline support 支持时间轴 .S. = Slice t ...
- FFMpeg ver 20160213-git-588e2e3 滤镜中英文对照
1 FFMpeg ver 20160213-git-588e2e3 滤镜中英文对照 2016.02.18 by 1CM 2 T.. = Timeline support 3 支持时间轴 4 .S. = ...
- FFMpeg 滤镜中英文对照
FFMpeg ver 20160213-git-588e2e3 滤镜中英文对照 2016.02.17 by 1CM T.. = Timeline support 支持时间轴 .S. = Slice t ...
- shell脚本生成限定范围的随机数
#!/bin/bash +)) 这串代码实现了随机生成从1~50之间是数 这串代码特别简单,就是利用RANDOM这个随机数生成器进行取余就能够实现,至于为什么取余时需要+1是因为在取余时如果被整除那么 ...
随机推荐
- Html5移动应用性能优化笔记
前景描述:最近一直在学习html5移动开发,找了很多资料,做了很多的页面.奈何作为一个程序猿,没有前端攻城狮那般专业,处处碰壁,想遍各种方法,经历各种尝试,最终的效果自己都能看醉.其中最大的问题就是 ...
- Linux系统设置用户密码规则(复杂密码策略)方法
Linux系统下的用户密码的有效期 可以修改密码可以通过login.defs文件控制.设置密码过期期限(默认情况下,用户的密码永不过期.) 编辑 /etc/login.defs 文件,可以设置当前密码 ...
- ChatGpt怎么玩
注册 梯子 先找好梯子, 归属地最好是米国之类的, 否则提示 OpenAI's services are not available in your country. 注: 最好用固定代理, 如tro ...
- Jetpack架构组件学习(6)——使用Glance实现桌面小组件
原文地址: Jetpack架构组件学习(6)--使用Glance实现桌面小组件-Stars-One的杂货小窝 公司陆续整了几个Compose写的app,有个小组件的功能,顺便试了下Jetpack库里的 ...
- 从整理扑克牌到字母异位词分组:一道巧妙的排序应用题 |LeetCode 49 字母异位词分组
LeetCode 49 字母异位词分组 点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中) 生活中的算法 你有没有玩过扑克牌?打完一局之后,我们通常会把散落的牌收 ...
- 2025年值得推荐的 8 款 WPF UI 控件库
前言 今天大姚给大家分享 8 款开源.美观.功能强大.简单易用的WPF UI控件库,希望可以帮助到有需要的同学. WPF介绍 WPF 是一个强大的桌面应用程序框架,用于构建具有丰富用户界面的 Wind ...
- StarUML画类图
1.Classes说明 [1]Class 类 [2]Interface 接口 [8]Generalization A与B的泛化关系,A继承B.继承非抽象类 [9]Interface Realizat ...
- 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
在AI技术飞速发展的今天,大语言模型(LLM)的应用越来越广泛,但高昂的使用成本常常让个人开发者和小型团队望而却步.今天,我要为大家介绍一个非常实用的开源项目--DeepSeek-Free-API,它 ...
- 你还不会使用Pycham Remote development 打开远程主机工作目录吗?这篇文章帮你解决!
前言 必备: 本地开发机与远程主机都要安装Pycharm专业版!!! 废话不多说直接开始!! 1.打开pycharm 2.依次点击File.Remote Development 3.依次点击SSH.N ...
- Maomi.MQ 2.0 | 功能强大的 .NET 消息队列通讯模型框架
说明 作者:痴者工良 文档地址:https://mmq.whuanle.cn 仓库地址:https://github.com/whuanle/Maomi.MQ 作者博客: https://www.wh ...