FPGA——DDS原理及代碼實現


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

五、部分效果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM