1. 程式人生 > >基於FPGA的訊號發生器的設計

基於FPGA的訊號發生器的設計

基於FPGA的訊號發生器的設計

首先先要介紹的是本設計的主晶片是ALTERA的一款晶片,晶片名稱是EP2C5T144C8N。該晶片的LE單元有4608個,PLL鎖相環具有2個,IO口管腳具有142個,差分通道55個,嵌入式乘法器26個,RAM的儲存容量大小是119808bits.該FPGA晶片的功耗也比較低。對於訊號發生器的設計,不論晶片的型別,只要所設計的邏輯單元夠用即可,還有就是記憶體要夠。好了,話不多說。直接來開始FPGA的訊號發生器設計吧。

我當時用的是Quartus-II是13.0的版本作為開發環境,設計的通訊方式是最常見的SPI通訊方式,該方式通訊比較簡單。當時我為了調這個通訊的時序也花了一天時間才搞定,哎!自認為還是比較笨了。

直接上圖:

    該圖是quartus的開發環境,大家看下,感覺和你們的也不會有太大的區別。儘管軟體的版本一直在升級,可是感覺變化也不會太大。

    繼續上圖:

    這個是我所建立的檔案,大概看了一下,其中.v檔案是我建立的一些verilog檔案,有幾個特殊的檔案,比如.bdf檔案是頂層檔案。因為採用的思想是自頂向下的設計思想。先模組化,然後頂層直接呼叫即可。.qip檔案是呼叫的是quatus自帶的IP核,主要是用來存放波形資料的。.stp檔案是signaltep檔案,主要是用來硬體模擬觀察資料用的,這個很有用哦,和modelsim差不多的功能,主要區別是,一個是硬體模擬,一個是軟體模擬。都可以用來分析時序。

    直接上程式碼:

module SPI_SLAVE

#(

    parameterrom_len_width=9,//ROM標的深度,同時也代表了相位控制字的位數

    parameterftw_width=21,//頻率控制字的位數

    parameter N=8

)

(

input rst,    //復位埠

input [N-1:0] txdata,    //N位傳送資料,CS下降沿把資料存入模組

input sclk,       //spi時鐘

input cs,     //spi片選

input mosi,       //從接收端

input ftw_en, //頻率輸入控制端使能

input ptw_en, //相位控制埠使能

input clk,   //頻率>>sclk

 

output reg miso,     //從輸出端

output [ftw_width-1:0]    ftw_out, //頻率控制字的位數

output [rom_len_width-1:0] ptw_out//相位控制字輸出

//output [5:0] count,//測試用

//output [23:0] data_temp_done //測試用

);

 

reg [9-1:0] temp_rx,temp_tx;

reg negedge_cs,temp_cs;

reg data_done;

reg [5:0] count_spi;

reg [24-1:0] data_temp;//24位資料暫存暫存器

reg [ftw_width-1:0] ftw_last;

reg [rom_len_width-1:0] ptw_last;

reg [4:0] i;

 

always @(negedge rst or posedge sclk)//mosi receive logic

begin

if(!rst)

    begin

    count_spi<=0;

    temp_rx<=8'd0;

    data_temp<=0;

    ftw_last<=0;

    ptw_last<=0;

    i<=0;

    end

    else if(cs==0)

    begin

    case(i)

    5'd0:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d1

    end

    5'd1:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d2

    end

    5'd2:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d3

    end

    5'd3:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d4

    end

    5'd4:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d5

    end

    5'd5:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d6

    end

    5'd6:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d7

    end

    5'd7:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d8

    end

    5'd8:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d9

    end

    5'd9:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d10

    end

    5'd10:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d11

    end

    5'd11:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d12

    end

    5'd12:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d13

    end

    5'd13:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d14

    end

    5'd14:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d15

    end

    5'd15:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d16

    end

    5'd16:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d17

    end

    5'd17:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d18

    end

    5'd18:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d19

    end

    5'd19:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d20

    end

    5'd20:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d21

    end

    5'd21:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d22

    end

    5'd22:

    begin

    data_temp[24-1:0]<={data_temp[24-2:0],mosi};//序列資料左移輸入

    i<=i+1'b1;//翻轉到下一個狀態5’d23

    end

    5'd23:

    begin

    data_temp[24-1:0]={data_temp[24-2:0],mosi};//序列資料左移輸入

    if(!ftw_en)

           begin

           ftw_last[21-1:0]<=data_temp[24-4:0];

           data_temp<=24'd0;

           end

    else if(!ptw_en)

           begin

           ptw_last<=data_temp[8:0];

           data_temp<=24'd0;

           end

    i<=0;//翻轉到下一個狀態5’d0

    end

endcase

end

/*if(flag==0)

    begin

    count_spi<=0;

    temp_rx<=8'd0;

    data_temp<=0;

    ftw_last<=0;

    ptw_last<=0;

    flag<=1;

    end

    else if(cs==0&&flag==1)

    begin

    count_spi<=count_spi+1'd1; 

    temp_rx[N-1:0]<={temp_rx[N-2:0],mosi};//序列資料左移輸入

    case(count_spi)

    6'd8:

       begin

       data_temp[24-1:16]<=temp_rx[7:0];//高位在前

       //temp_rx=0;

       end

    6'd16:

       begin

       data_temp[16-1:8]<=temp_rx[7:0];

       //temp_rx=0;

       end

    6'd24:

       begin

       data_temp[7:0]<=temp_rx[7:0];

       //temp_rx=0;

       if(!ftw_en)

           begin

           ftw_last[21-1:0]<=data_temp[24-1:3];

           data_temp<=24'd0;

           count_spi<=0;

           end

       if(!ptw_en)

              begin

              ptw_last<=data_temp[8:0];

              data_temp<=24'd0;

              end

       flag<=0;

       end

    endcase

    end*/

end

/*always @(posedge clk)//cs negedge test

begin

    if((cs==0)&&(temp_cs==1))

    negedge_cs<=1;

    else

    negedge_cs<=0;

    temp_cs<=cs;

end*/

/*always @(negedge sclk or posedge negedge_cs)//miso transmitlogic

begin

if(negedge_cs==1)

temp_tx<=txdata;

else

temp_tx[N-2:0]<=temp_tx[N-1:1];

miso<=temp_tx[0];

end*/

assign ftw_out=ftw_last;

assign ptw_out=ptw_last;

//assign count=count_spi;//測試用

//assign data_temp_done=data_temp;//測試用

Endmodule

    上面的模組程式碼是用來完成SPI資料的接收,接收資料長度是24位的,這個可以根據自己的需要進行必要的調整。

module phase_accumulator

#(

    parameterrom_len_width=9,//ROM標的深度

    parameterphase_acc_width=26,//相位累加器的位數

    parameterftw_width=21//頻率控制字的位數

)

(

input rst,

input [ftw_width-1:0] ftw,

input [rom_len_width-1:0] ptw,

input clk,

output[rom_len_width-1:0] q

);

`define ptw_enable 1

`ifdef ptw_enable

reg [rom_len_width-1:0] phase;

reg [phase_acc_width-1:0] phase1;

always @(negedge rst or posedge clk)

begin

    if(!rst)

       begin

           phase=1'd0;

           phase1=1'd0;

       end

    else

       begin

           phase1<=phase1+ftw;

           phase<=phase1[phase_acc_width-1:phase_acc_width-rom_len_width]+ptw;

       end

end

`else

`endif

assign q   =   phase;

endmodule

這部分的模組是完成相位累加器的設計,採用的是當今比較流行的DDS設計原理,

輸出的q是rom表的地址,該地址根據相位累加器的累加來驅動地址的遞增。不同DDS的建議可以去看下DDS的設計原理。在此,我也不嫌麻煩,將它給貼出來給你們看看。如下

令DDS時鐘為,相位累加器位數為n,頻率控制字的位數為m,ROM表的深度的位寬為D,相位控制字的位數為p

再令,,。

當要設計一個DDS時,應該根據具體的效能指標來設計,從而避免不必要的資源浪費,一般設計給出的具體指標如下:

指標:頻率解析度為,相位解析度為,要求產生訊號的最高頻率為,且最高相位為,每個週期點數不少於N個。

                   根據上面的指標,我們要求得具體的DDS時鐘為,相位累加器位數為n,頻率控制字的位數為m,ROM表的深度的位寬為d

(1)其中、n、m是和頻率有關的引數,可以先進行求解:

根據要求可以列出以下不等式:






好了,我們繼續:

module div_clk

#(

parameter div =2//設定分頻係數

)

(

input clk,

output signal_clk

);

/*reg [8:0] flag;

reg clk_flag;

/*always @(posedge clk)

begin

flag<=flag+1'd1;

if(flag==div)

         begin

         clk_flag<=~clk_flag;

         flag<=0;

         end

end*/

assign signal_clk=clk;

endmodule

這個是用來提供時鐘分頻的,分頻大小可以自行設定。這裡的時鐘主要是用來提供給DA晶片的時鐘,我用的是50M的時鐘所以就沒有分頻了。

這個是頂層檔案的設計圖。

下面來讓我們看看模擬圖:注意,模擬時只能是針對某個模組設計進行模擬,用軟體模擬時,要用tesbench檔案和一個.v的例項檔案。硬體模擬時,主要是用到下載器裡的邏輯分析儀對資料的抓取。

硬體模擬

這個是我之前模擬的時序,記錄被儲存了,現在拿出來,可以看到右上角有未連線硬體的顯示提醒。

軟體模擬時序:

在這裡說以下,用外部modlsim模擬時,注意輸入訊號的時序要自己給出。如果對以上模擬軟體不太會使用時可以自己百度以下,相信會有大把的視訊或者論壇或者文字資料的,總有一款適合你的。好了,不過多介紹了。今天就到這裡吧!

希望對你們有用!再多說一句,注意時序分析。