该正弦波参数为
- 周期T = 1ms * 32 = 32ms,
- 频率为 f = 1/T = 1/(1ms * (32/1))
那么读出完整正弦波的参数为
- 周期T = 1ms * 16 = 16ms
- 频率f = 1/T = 1/(1ms * 16) = 1/(1ms * (32/2))
读出正弦波的参数为
- 周期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
为什么地址是由相位控制字加频率控制字高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)
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/01/10 15:31:44
// Design Name:
// Module Name: DDS
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module DDS(
clk ,
ce ,
we,
data,
reset,
sine,
cose
);
//参数定义
parameter DATA_W = 32;
parameter DATA_W1 = 16;
//输入信号定义
input clk ;
input reset ;
input [DATA_W-1:0] data;
input we;
input ce;
//输出信号定义
output[DATA_W1-1:0] sine ;
output[DATA_W1-1:0] cose ;
//中间信号定义
reg [DATA_W-1:0] ADD_A ;
reg [DATA_W-1:0] ADD_B ;
reg [DATA_W1-1:0] cose_DR ;
reg [DATA_W1-1:0] sine_DR ;
wire [DATA_W-1:0] data;
wire [DATA_W1-1:0] cose_D;
wire [DATA_W1-1:0] sine_D;
wire [9:0] ROM_A;
assign cose=cose_DR;
assign sine=sine_DR;
assign ROM_A=ADD_B[31:22]; //这里比较关键,需要明白为什么是高位开始想加
//时序逻辑写法
always@(posedge clk or negedge reset)begin
if(reset)begin
ADD_A <=0;
end
else if(we) begin
ADD_A <=data;
end
end
always@(posedge clk or negedge reset)begin
if(reset)begin
ADD_B <=0;
end
else if(ce) begin
ADD_B <=ADD_B + ADD_A;
end
end
always@(posedge clk or negedge reset)begin
if(reset)begin
cose_DR <=0;
end
else if(ce) begin
cose_DR <=cose_D;
end
end
always@(posedge clk or negedge reset)begin
if(reset)begin
sine_DR <=0;
end
else if(ce) begin
sine_DR <=sine_D;
end
end
cos u1 (
.clka(clk), // input wire clka
.addra(ROM_A), // input wire [9 : 0] addra
.douta(cose_D) // output wire [15 : 0] douta
);
sine u2 (
.clka(clk), // input wire clka
.addra(ROM_A), // input wire [9 : 0] addra
.douta(sine_D) // output wire [15 : 0] douta
);
endmodule
仿真tb文件
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/01/10 15:57:17
// Design Name:
// Module Name: dds_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module dds_tb();
//时钟和复位
reg clk ;
reg rst_n;
reg we;
reg ce;
//uut的输入信号
reg[31:0] data ;
//uut的输出信号
wire[15:0] cos;
wire[15:0] sin;
//时钟周期,单位为ns,可在此修改时钟周期。
parameter CYCLE = 20;
//复位时间,此时表示复位3个时钟周期的时间。
parameter RST_TIME = 3 ;
//待测试的模块例化
DDS uut(
.clk (clk ),
.reset (rst_n ),
.we (we ),
.ce (ce ),
.data (data ),
.sine (sin ),
.cose (cos )
);
//生成本地时钟50M
initial begin
clk = 0;
forever
#(CYCLE/2)
clk=~clk;
end
//产生复位信号
initial begin
rst_n = 0;
#2;
rst_n = 1;
#(CYCLE*RST_TIME);
rst_n = 0;
end
//输入信号din0赋值方式
initial begin
#1;
//赋初值
we = 0;
#(5*CYCLE);
//开始赋值
we = 1;
end
//输入信号din1赋值方式
initial begin
#1;
//赋初值
ce = 0;
#(10*CYCLE);
//开始赋值
ce=1;
end
initial begin
#1;
//赋初值
data = 4194304;
end
endmodule
仿真结果