基於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模擬時,注意輸入訊號的時序要自己給出。如果對以上模擬軟體不太會使用時可以自己百度以下,相信會有大把的視訊或者論壇或者文字資料的,總有一款適合你的。好了,不過多介紹了。今天就到這裡吧!
希望對你們有用!再多說一句,注意時序分析。