文章

DDS的RTL实现与部分说明

MEMORY

DDS至少需要存储正弦波的四分之一波形。在FPGA中,有几种实现形式:

  1. 使用库中的ROM IP对数据进行存储:优点是现有IP占用面积小,功耗较优;缺点是IP核不易移植。 2.代码中定义Memory然后在testbench中调用$readmemh。优点是可移植性高;缺点需要外置ROM。
1
reg [8:0]  ROM_cos  [0:64];
  1. 直接在代码中定义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

效果

image-20220116184512623

时钟为10ns;频率控制字K_word=256»4,为16分频;dout1和dout2分别输出初始相位为90deg和180deg的正弦波。

本文由作者按照 CC BY 4.0 进行授权

热门标签