DDS的RTL实现与部分说明
MEMORY
DDS至少需要存储正弦波的四分之一波形。在FPGA中,有几种实现形式:
- 使用库中的ROM IP对数据进行存储:优点是现有IP占用面积小,功耗较优;缺点是IP核不易移植。 2.代码中定义Memory然后在testbench中调用$readmemh。优点是可移植性高;缺点需要外置ROM。
1
reg [8:0] ROM_cos [0:64];
- 直接在代码中定义memory并赋值。优点是通用;缺点是需要对每个寄存器单独赋值,较为繁琐。
1
2
3
4
5
6
wire [8:0] ROM_cos [0:64];
assign ROM_cos[00]=9'd511;
assign ROM_cos[01]=9'd510;
...
...
assign ROM_cos[64]=9'd0 ;
为了方便移植,本文使用第三种方式对RAM赋值。
代码实现
为了验证初始相位是否有效,本文加了两路信号输出,初始相位的值分别定义为Phase1和Phase2,并赋值90deg和180deg。
DDS.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//phase:初始相位
//K_word:频率控制孿
module DDS(clk,rst_n,phase1,phase2,K_word,dout1,dout2);
input clk;
input rst_n;
input [7:0] phase1;
input [7:0] phase2;
input [7:0] K_word;
output reg [9:0] dout1;
output reg [9:0] dout2;
reg [7:0] phase_acc_r; //相位累加器结果
reg [7:0] mem_addr_r1;//相位累加器结果+初始相位结果
reg [7:0] mem_addr_r2;//相位累加器结果+初始相位结果
//四分之一余弦波形
wire [8:0] ROM_cos [0:64];
assign ROM_cos[00]=9'd511; assign ROM_cos[01]=9'd510; assign ROM_cos[02]=9'd510; assign ROM_cos[03]=9'd509; assign ROM_cos[04]=9'd508; assign ROM_cos[05]=9'd507; assign ROM_cos[06]=9'd505; assign ROM_cos[07]=9'd503;
assign ROM_cos[08]=9'd501; assign ROM_cos[09]=9'd498; assign ROM_cos[10]=9'd495; assign ROM_cos[11]=9'd492; assign ROM_cos[12]=9'd488; assign ROM_cos[13]=9'd485; assign ROM_cos[14]=9'd481; assign ROM_cos[15]=9'd476;
assign ROM_cos[16]=9'd472; assign ROM_cos[17]=9'd467; assign ROM_cos[18]=9'd461; assign ROM_cos[19]=9'd456; assign ROM_cos[20]=9'd450; assign ROM_cos[21]=9'd444; assign ROM_cos[22]=9'd438; assign ROM_cos[23]=9'd431;
assign ROM_cos[24]=9'd424; assign ROM_cos[25]=9'd417; assign ROM_cos[26]=9'd410; assign ROM_cos[27]=9'd402; assign ROM_cos[28]=9'd395; assign ROM_cos[29]=9'd386; assign ROM_cos[30]=9'd378; assign ROM_cos[31]=9'd370;
assign ROM_cos[32]=9'd361; assign ROM_cos[33]=9'd352; assign ROM_cos[34]=9'd343; assign ROM_cos[35]=9'd333; assign ROM_cos[36]=9'd324; assign ROM_cos[37]=9'd314; assign ROM_cos[38]=9'd304; assign ROM_cos[39]=9'd294;
assign ROM_cos[40]=9'd283; assign ROM_cos[41]=9'd273; assign ROM_cos[42]=9'd262; assign ROM_cos[43]=9'd251; assign ROM_cos[44]=9'd240; assign ROM_cos[45]=9'd229; assign ROM_cos[46]=9'd218; assign ROM_cos[47]=9'd207;
assign ROM_cos[48]=9'd195; assign ROM_cos[49]=9'd183; assign ROM_cos[50]=9'd172; assign ROM_cos[51]=9'd160; assign ROM_cos[52]=9'd148; assign ROM_cos[53]=9'd136; assign ROM_cos[54]=9'd124; assign ROM_cos[55]=9'd111;
assign ROM_cos[56]=9'd99 ; assign ROM_cos[57]=9'd87 ; assign ROM_cos[58]=9'd74 ; assign ROM_cos[59]=9'd62 ; assign ROM_cos[60]=9'd50 ; assign ROM_cos[61]=9'd37 ; assign ROM_cos[62]=9'd25 ; assign ROM_cos[63]=9'd12 ;
assign ROM_cos[64]=9'd0 ;
//reg [8:0] ROM_cos [0:64] = {
// 8'd511, 8'd510, 8'd510, 8'd509, 8'd508, 8'd507, 8'd505, 8'd503,
// 8'd501, 8'd498, 8'd495, 8'd492, 8'd488, 8'd485, 8'd481, 8'd476,
// 8'd472, 8'd467, 8'd461, 8'd456, 8'd450, 8'd444, 8'd438, 8'd431,
// 8'd424, 8'd417, 8'd410, 8'd402, 8'd395, 8'd386, 8'd378, 8'd370,
// 8'd361, 8'd352, 8'd343, 8'd333, 8'd324, 8'd314, 8'd304, 8'd294,
// 8'd283, 8'd273, 8'd262, 8'd251, 8'd240, 8'd229, 8'd218, 8'd207,
// 8'd195, 8'd183, 8'd172, 8'd160, 8'd148, 8'd136, 8'd124, 8'd111,
// 8'd99 , 8'd87 , 8'd74 , 8'd62 , 8'd50 , 8'd37 , 8'd25 , 8'd12 ,
// 8'd0 };
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
phase_acc_r <= 'b0 ;
end
else
phase_acc_r <= phase_acc_r + K_word ;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
mem_addr_r1 <= 'b0 ;
end
else
mem_addr_r1 <= phase_acc_r + phase1 ;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
mem_addr_r2 <= 'b0 ;
end
else
mem_addr_r2 <= phase_acc_r + phase2 ;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout1 <= 'b0 ;
end else
if (mem_addr_r1[7:6] == 2'b00 ) begin //1st, mem_addr_r[0, 63]
dout1 <= ROM_cos[mem_addr_r1[5:0]] + 10'd512 ; //上移
end
else if (mem_addr_r1[7:6] == 2'b01 ) begin //2nd, mem_addr_r[64, 127]
dout1 <= 10'd512 - ROM_cos[64-mem_addr_r1[5:0]] ; //两次翻转
end
else if (mem_addr_r1[7:6] == 2'b10 ) begin //3rd, mem_addr_r[128, 192]
dout1 <= 10'd512 - ROM_cos[mem_addr_r1[5:0]]; //翻转右移
end
else begin //4th, mem_addr_r [193, 256]
dout1 <= 10'd512 + ROM_cos[64-mem_addr_r1[5:0]]; //翻转上移
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout2 <= 'b0 ;
end else
if (mem_addr_r2[7:6] == 2'b00 ) begin //1st, mem_addr_r[0, 63]
dout2 <= ROM_cos[mem_addr_r2[5:0]] + 10'd512 ; //上移
end
else if (mem_addr_r2[7:6] == 2'b01 ) begin //2nd, mem_addr_r[64, 127]
dout2 <= 10'd512 - ROM_cos[64-mem_addr_r2[5:0]] ; //两次翻转
end
else if (mem_addr_r2[7:6] == 2'b10 ) begin //3rd, mem_addr_r[128, 192]
dout2 <= 10'd512 - ROM_cos[mem_addr_r2[5:0]]; //翻转右移
end
else begin //4th, mem_addr_r [193, 256]
dout2 <= 10'd512 + ROM_cos[64-mem_addr_r2[5:0]]; //翻转上移
end
end
endmodule
testbench.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
`timescale 1ns/1ns
module test_top();
reg clk;
reg rst_n;
reg [7:0] phase1;
reg [7:0] phase2;
reg [7:0] K_word;
wire [9:0] dout1;
wire [9:0] dout2;
DDS dut (.clk(clk),.rst_n(rst_n),.phase1(phase1),.phase2(phase2),.K_word(K_word),.dout1(dout1),.dout2(dout2));
initial begin
clk=1'd0;
rst_n=1'd0;
phase1=256/4; //90deg phase
phase2=256/2; //180deg phase
K_word=1<<4; //原始频率的1/16
#100
rst_n=1'd1;
#100000
$finish;
end
always #5 clk=~clk;
//initial begin
// $fsdbDumpfile("test_plus.fsdb");
// $fsdbDumpvars();
//end
endmodule
效果
时钟为10ns;频率控制字K_word=256»4,为16分频;dout1和dout2分别输出初始相位为90deg和180deg的正弦波。
本文由作者按照 CC BY 4.0 进行授权