一、前言
會FPGA硬件描述語言、設計思想和接口協議,掌握些基本的算法是非常重要的,因此開設本專題探討些基於AD DA數字信號處理系統的一些簡單算法,在數字通信 信號分析與檢測等領域都會或多或少有應用。我們還是從老生常談的DDS函數發生器開始,講解DAC ADC基本使用以及DDS算法原理與設計方式。
二、設計預期
功能:基於ROM的頻率可調DDS正弦函數發生器
DAC ADC型號與設計參數:DAC為AD9708,更新速率125MSPS,精度8bit;ADC為AD9280,采樣率32MSPS,精度8bit。由於ADC采樣率限制,設計使用32MHZ頻率時鍾更新與采樣數據,並將ROM深度定義為1024.
驗證手段:MATLAB產生正弦函數,經過8bit量化后存儲在ROM中,數據經過DAC 電纜 ADC環回到FPGA,ILA抓取ADC接收數據波形,觀察對比發送與接收數據是否相近。
三、DDS原理
這里只介紹DDS的基本思想,關於詳細原理,請參考:【圖文】DDS原理_百度文庫 https://wenku.baidu.com/view/11cfbf85a0116c175f0e4818.html
實際上,DDS的核心就是將正弦或余弦函數存儲在ROM中,利用相位累加特性通過采用不同的步長對ROM尋址的方式產生頻率可調正弦波。另外需要注意產生信號的頻率范圍要滿足奈奎斯特采樣定理,該定理支持若想無失真恢復原始信號,采樣頻率必須大於等於信號最高頻率成分的2倍。反過來說:產生信號的最高頻率小於等於采樣率的1/2.采樣頻率即為FPGA是時鍾頻率。為防止信號混疊,一般取最高頻率成分的1/3.
四、MATLAB產生正弦序列.coe文件及ROM初始化
MATLAB產生頻率為1/2*pi標准正弦序列。驗證無誤后,在VIVADO中調用Block Memory Generator IP核,配置為單口ROM,使用剛才產生的系數文件初始化ROM地址數據。
五、DAC ADC驅動
該設計使用的DAC ADC均為為低速並口轉換芯片,無需配置,只要FPGA給出時鍾信號,並輸出/入並行數據即可。根據AD9708 datasheet時序圖,其在時鍾上升沿采樣,故FPGA在輸出時鍾下降沿更新數據可滿足建立與保持時間要求。ADC同樣上升沿開始更新數據,接收端在時鍾是上升沿采集數據,這樣每一時鍾周期可以采到上一拍送出的數據。
六、函數發生器及測試工程設計

1 `timescale 1ns / 1ps 2 3 module sin_generator#(parameter FCW_W = 16, 4 DAC_W = 8) 5 ( 6 input clk,//DAC采樣時鍾 由PLL產生 7 input rst_n, 8 9 input [FCW_W-1:0] fcw, 10 output [DAC_W-1:0] dac_data, 11 output dac_clk 12 ); 13 14 reg [ (FCW_W-1):0] sum ; 15 wire [9:0] addra; 16 //reg [9:0] addra;//地址測試信號 17 wire ena; 18 wire [7:0] douta; 19 20 //相位累加器 21 //時鍾下降沿產生數據 DAC上升沿采樣 22 always @(negedge clk or negedge rst_n )begin 23 if(rst_n==0) begin 24 sum <= (0) ; 25 end 26 else begin 27 sum <= (sum+fcw) ; 28 end 29 end 30 31 assign addra = sum[FCW_W-1-:10]; 32 33 //rom地址測試 34 /*always @(posedge clk or negedge rst_n )begin 35 if(rst_n==0) begin 36 addra <= (0) ; 37 end 38 else begin 39 addra <= (addra+1) ; 40 end 41 end*/ 42 43 44 blk_mem_gen_0 u_bram ( 45 .clka(clk), // input wire clka 46 .ena(ena), // input wire ena 47 .addra(addra), // input wire [9 : 0] addra 48 .douta(douta) // output wire [7 : 0] douta 49 ); 50 assign ena = 1'b1; 51 //輸出信號 52 assign dac_data = douta; 53 assign dac_clk = ~clk; 54 55 endmodule
函數發生器模塊由輸入端口fcw數值確定頻率控制字。測試工程頂層包括差分時鍾轉單端時鍾原語,用於產生DAC ADC時鍾的PLL 函數發生器模塊,生成特定頻率控制字的VIO IP核,還有接收端ADC數據采樣邏輯以及ILA 調試IP核。

1 `timescale 1ns / 1ps 2 3 4 module DDS_Demo_top 5 #(parameter AD_DA_W = 8) 6 ( 7 input sys_clk_p, 8 input sys_clk_n, 9 input rst_n, 10 11 output [AD_DA_W-1:0] DAC_data, 12 output DAC_clk, 13 14 input [AD_DA_W-1:0] ADC_data, 15 output ADC_clk 16 ); 17 18 localparam FCW_W = 16; 19 20 wire sys_clk_ibufg; 21 wire clk_dac,clk_adc; 22 reg [ (AD_DA_W-1):0] data_ad ; 23 24 wire [FCW_W-1 : 0] probe_out0; 25 wire [AD_DA_W*2-1:0] probe0; 26 27 //ADC接口信號 28 //ADC在時鍾上升沿后送出數據,FPGA下一個上升沿采樣 29 assign ADC_clk = clk_adc; 30 31 always @(posedge clk_adc or negedge rst_n )begin 32 if(rst_n==0) begin 33 data_ad <= (0) ; 34 end 35 else begin 36 data_ad <= (ADC_data) ; 37 end 38 end 39 40 41 /***************************************子模塊例化***************************************/ 42 IBUFGDS # 43 ( 44 .DIFF_TERM ("FALSE"), 45 .IBUF_LOW_PWR ("FALSE") 46 ) 47 u_ibufg_sys_clk 48 ( 49 .I (sys_clk_p), 50 .IB (sys_clk_n), 51 .O (sys_clk_ibufg) 52 ); 53 54 clk_wiz_0 u_pll 55 ( 56 // Clock out ports 57 .clk_out1(clk_dac), // output clk_out1 58 .clk_out2(clk_adc), // output clk_out2 59 // Status and control signals 60 .resetn(rst_n), // input resetn 61 .locked(), // output locked 62 // Clock in ports 63 .clk_in1(sys_clk_ibufg)); // input clk_in1 64 65 sin_generator#(.FCW_W(FCW_W), 66 .DAC_W(AD_DA_W)) 67 u_sin_gen 68 ( 69 .clk (clk_dac) ,//DAC采樣時鍾 由PLL產生 70 .rst_n (rst_n) , 71 .fcw (probe_out0) , 72 .dac_data (DAC_data) , 73 .dac_clk (DAC_clk) //由clk_dac產生 74 ); 75 76 //debug cores 77 vio_0 u_vio ( 78 .clk(clk_dac), // input wire clk 79 .probe_out0(probe_out0) // output wire [15 : 0] probe_out0 80 ); 81 82 ila_0 u_ila ( 83 .clk(clk_adc), // input wire clk 84 .probe0(probe0) // input wire [15:0] probe0 85 ); 86 87 assign probe0[7:0] = DAC_data; 88 assign probe0[15:8] = ADC_data; 89 90 endmodule
最后添加引腳約束文件:

1 #################################clock && reset############################################### 2 create_clock -period 5 [get_ports sys_clk_p] 3 set_property PACKAGE_PIN R4 [get_ports {sys_clk_p}] 4 set_property IOSTANDARD DIFF_SSTL15 [get_ports {sys_clk_p}] 5 6 set_property PACKAGE_PIN T6 [get_ports rst_n] 7 set_property IOSTANDARD LVCMOS15 [get_ports rst_n] 8 9 #####################DAC PIN connect J4 expansion interface########################## 10 set_property PACKAGE_PIN H14 [get_ports {DAC_clk}] 11 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_clk}] 12 13 set_property PACKAGE_PIN J14 [get_ports {DAC_data[7]}] 14 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[7]}] 15 set_property PACKAGE_PIN H15 [get_ports {DAC_data[6]}] 16 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[6]}] 17 set_property PACKAGE_PIN J15 [get_ports {DAC_data[5]}] 18 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[5]}] 19 set_property PACKAGE_PIN G13 [get_ports {DAC_data[4]}] 20 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[4]}] 21 set_property PACKAGE_PIN H13 [get_ports {DAC_data[3]}] 22 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[3]}] 23 set_property PACKAGE_PIN J21 [get_ports {DAC_data[2]}] 24 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[2]}] 25 set_property PACKAGE_PIN J20 [get_ports {DAC_data[1]}] 26 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[1]}] 27 set_property PACKAGE_PIN G16 [get_ports {DAC_data[0]}] 28 set_property IOSTANDARD LVCMOS33 [get_ports {DAC_data[0]}] 29 30 #####################ADC PIN connect J4 expansion interface########################## 31 32 set_property PACKAGE_PIN D22 [get_ports {ADC_clk}] 33 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_clk}] 34 35 set_property PACKAGE_PIN G21 [get_ports {ADC_data[7]}] 36 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[7]}] 37 set_property PACKAGE_PIN G22 [get_ports {ADC_data[6]}] 38 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[6]}] 39 set_property PACKAGE_PIN H20 [get_ports {ADC_data[5]}] 40 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[5]}] 41 set_property PACKAGE_PIN G20 [get_ports {ADC_data[4]}] 42 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[4]}] 43 set_property PACKAGE_PIN J22 [get_ports {ADC_data[3]}] 44 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[3]}] 45 set_property PACKAGE_PIN H22 [get_ports {ADC_data[2]}] 46 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[2]}] 47 set_property PACKAGE_PIN K21 [get_ports {ADC_data[1]}] 48 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[1]}] 49 set_property PACKAGE_PIN K22 [get_ports {ADC_data[0]}] 50 set_property IOSTANDARD LVCMOS33 [get_ports {ADC_data[0]}]
七、實驗結果分析
依據之前的參數和DDS信號頻率公式,所生成正弦函數頻率最好在32/2^16~32/3MHZ之內,使用VIO改變頻率控制字數值,觀察ILA抓取的發送與接收數據模擬形式波形。任意給出三組頻率范圍內波形,頻率依次由低到高。
總體來講還是比較簡單。搭建好DAC ADC環路,后面可以驗證些濾波 同步算法,或者做些數字頻率計、示波器之類的實用設計。