
- 如圖,一個量化的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


為什么地址是由相位控制字加頻率控制字高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
`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