一、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;