分享

FPGA——DDS原理及代码实现

 西北望msm66g9f 2024-05-24 发布于山东

一、DDS各参数意义

图片

* 如图,一个量化的32点的正弦波,也就是说一个ROM里存了32个这样的数据,每次读出一个数据要1ms,分别读出1,2,3...30,31,32,共32个点,读取完整的正弦波需要1ms * 32 = 32ms的时间

该正弦波参数为 > 周期T = 1ms * 32 = 32ms, > 频率为 f = 1/T = 1/(1ms * (32/1))
图片
  • 在读出一个数据时间不变(1ms)的情况下,想要让读出的正弦波频率增加一倍,那就要间隔读取,分别读出2,4,6,8,10...28,30,32,此时只需要读16个点 那么读出完整正弦波的参数为 > 周期T = 1ms * 16 = 16ms > 频率f = 1/T = 1/(1ms * 16) = 1/(1ms * (32/2))

图片
  • 想要读出的正弦波频率减少一倍,那就要插值读取,分别读出0.5,1,1.5,2,2.5,3...30.5,31,31.5,32,此时要读64个点 读出正弦波的参数为 > 周期T = 1ms * 64 = 64ms > 频率f = 1/T = 1/(1ms * 64) = 1/(1ms * (32/0.5))

图片

这里,1ms即为Tclk,fclk = 1/Tclk = 1/1ms;32 = 2^5即为N=5,而32除以的数(1,2,0.5)即为频率控制字fword,那么fo = (fclk * fword)/(2^N)

图片

通常,FPGA并不擅长浮点运算,第三种情况,上式的(32/0.5)是很难实现的,因此在正弦波周期一样的情况下,将精度N调高一位,N=6,(2^5 * 2)/(0.5 * 2),此时fword就不用为0.5,而是1

  • 相位控制字pword的参数解释,如果从x轴为8的数据开始取,那么相当于正弦波相移了90°,pword = 8,这就是相位控制字

图片
图片

二、DDS的verilog代码实现

  • 如何写代码?

    • 照着DDS结构图书写即可


  • 对于相位调制器代码的解释

//相位调制器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_addr <= 0;
else
data_addr <= fword_acc[31:20] + syn_pword;
end

为什么地址是由相位控制字加频率控制字高12位得到的? 1、本次实验使用的rom是宽度为14,深度为2^12 = 4096的数据,所以相位控制字根据rom的深度选择了12位宽 2、为什么ROM宽度是14,深度不取2^14?FPGA资源不够,没有这么多的寄存器存取这么多的数据 3、地址 = 相位 + 频率更迭,而相位宽度为12位,频率的宽度比相位多,所以频率控制字取高几位是由相位控制字的宽度决定的 4、取频率控制字高12位是如何完成频率变换的? 举例: 2^1 = 2'b10 2^2 = 3'b100 2^3 = 4'b1000 ...... 2^19 = 20'b1000_0000_0000_0000_0000 2^20 = 21'b1_0000_0000_0000_0000_0000 2^21 = 22'b10_0000_0000_0000_0000_0000

f = 1/T,N = 32 频率控制字为:2^20 fword_acc[31:0] + 2^20 相当于 (fword_acc[31:20] + 1)此时就是按照地址+1的速度寻址,假如Fclk = 50MHz(系统时钟),Tclk = 20ns,输出波形的周期就为:To = 20ns * 4096

频率控制字为:2^19 fword_acc[31:0] + (2^19 + 2^19) 相当于 (fword_acc[31:20] + 1),也就是要加两次频率控制字,才能实现一次地址+1,Tclk = 20ns,输出完整波形就要输出2次*4096个数据,输出的波形周期为:To = 20ns * (2 * 4096)

频率控制字为:2^21 fword_acc[31:0] + (2^21) 相当于 (fword_add[31:20] + 2'b10),加一次频率控制字,实现一次地址+2,Tclk = 20ns,因为是跳过一位地址取的数据,所以数据量减半,输出完整波形只需要输出4096/2个数据,输出的波形周期为:To = 20ns * (4096/2)

图片
module dds_module(
clk ,
rst_n ,
fword ,
pword ,
data_out
);
parameter FWORD_W = 32; //频率控制字位宽
parameter PWORD_W = 12; //相位控制字位宽
parameter DATAO_W = 14; //输出数据位宽

input clk;
input rst_n;
input [FWORD_W-1:0] fword;
input [PWORD_W-1:0] pword;
output [DATAO_W-1:0] data_out;

wire [DATAO_W-1:0] data_out;

reg [FWORD_W-1:0] syn_fword;
reg [PWORD_W-1:0] syn_pword;
reg [FWORD_W-1:0] fword_acc;
reg [PWORD_W-1:0] data_addr;

//同步寄存器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
syn_fword <= 0;
syn_pword <= 0;
end
else begin
syn_fword <= fword;
syn_pword <= pword;
end
end

//相位累加器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
fword_acc <= 0;
else
fword_acc <= syn_fword + fword_acc;
end

//相位调制器,不需要用寄存器,但是用了性能更好
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_addr <= 0;
else
data_addr <= fword_acc[31:20] + syn_pword;
end

dds_rom dds_rom(
.clka (clk),
.addra (data_addr),
.douta (data_out)
);

endmodule

四、仿真文件

`timescale 1ns / 1ns
module dds_module_tb();
parameter CYCLE = 20;

parameter FWORD_W = 32; //频率控制字位宽
parameter PWORD_W = 12; //相位控制字位宽
parameter DATAO_W = 14; //输出数据位宽

reg clk;
reg rst_n;
reg [FWORD_W-1:0] fword;
reg [PWORD_W-1:0] pword;
wire [DATAO_W-1:0] data_out;

reg [FWORD_W-1:0] fword1;
reg [PWORD_W-1:0] pword1;
wire [DATAO_W-1:0] data_out1;

dds_module dds_module(
.clk (clk),
.rst_n (rst_n),
.fword (fword),
.pword (pword),
.data_out (data_out)
);

dds_module dds_module1(
.clk (clk),
.rst_n (rst_n),
.fword (fword1),
.pword (pword1),
.data_out (data_out1)
);

initial clk = 0;
always #(CYCLE/2) clk = ~clk;

initial begin
rst_n = 1;
#3;
rst_n = 0;
#(10*CYCLE);
rst_n = 1;
end

initial begin
fword = 0;
@(posedge rst_n);
#(15*CYCLE);
fword = 32'h200000;
#(100000)
fword = 32'h800000;
end

initial begin
fword1 = 0;
@(posedge rst_n);
#(15*CYCLE);
fword1 = 32'h200000;
end

initial begin
pword = 0;
end

initial begin
pword1 = 0;
@(posedge rst_n)
#(15*CYCLE)
pword1 = 1024;
end

initial begin
#100000;
#100000;
$stop;
end

endmodule

五、多种波形的实现

module dds_module(

clk ,

rst_n ,
fword ,
pword ,
cft_model,
data_out
);
parameter FWORD_W = 32; //频率控制字位宽
parameter PWORD_W = 12; //相位控制字位宽
parameter MOD_W = 2;
parameter DATAO_W = 14; //输出数据位宽

input clk;
input rst_n;
input [FWORD_W-1:0] fword;
input [PWORD_W-1:0] pword;
input [MOD_W-1:0] cft_model;

output [DATAO_W-1:0] data_out;

reg [DATAO_W-1:0] data_out;

//中间变量
wire [DATAO_W-1:0] sine_data_out;

reg [FWORD_W-1:0] syn_fword;
reg [PWORD_W-1:0] syn_pword;
reg [FWORD_W-1:0] fword_acc;
reg [PWORD_W-1:0] data_addr;

//同步寄存器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
syn_fword <= 0;
syn_pword <= 0;
end
else begin
syn_fword <= fword;
syn_pword <= pword;
end
end

//相位累加器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
fword_acc <= 0;
else
fword_acc <= syn_fword + fword_acc;
end

//相位调制器,不需要用寄存器,但是用了性能更好
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_addr <= 0;
else
data_addr <= fword_acc[31:20] + syn_pword;
end

//正弦波
dds_rom dds_rom(
.clka (clk),
.addra (data_addr),
.douta (sine_data_out)
);

always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_out <= 0;
else if(cft_model == 2'd0)//正弦波
data_out <= sine_data_out;
else if(cft_model == 2'd1)//三角波
data_out[DATAO_W - 1 -: 12] <= data_addr;
else if(cft_model == 2'd2)//0直流
data_out[DATAO_W - 1 -: 12] <= 8192 - 1;
else if(cft_model == 2'd3)//方波
data_out <= (data_addr >= 2048) ? 14'd0 : 14'd16383;
end

endmodule

注: ( :- ) 12 ,向下取12位 data_out共有14位 取高12位,相当于左移两位,乘以4,每个输入的值放大4倍

四、matlab代码

clc;
clear;

f = 1;%取样点,1,2,0.5
x=0:f:32;

y=sin((2*pi/32)*x);

stem(x,y);
axis([0,32,-1.5,1.5]);

grid on;

五、部分效果

图片

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多