基於FPGA的電子計算器設計(下)
今天給大俠帶來基於FPGA的電子計算器設計,由於篇幅較長,分三篇。今天帶來第三篇,下篇,話不多說,上貨。
導讀
本篇介紹了一個簡單計算器的設計,基於 FPGA 硬體描述語言 Verilog HDL,系統設計由計算部分、顯示部分和輸入部分四個部分組成,計算以及儲存主要用狀態機來實現。顯示部分由六個七段譯碼管組成,分別來顯示輸入數字,輸入部分採用4*4矩陣鍵盤,由0-9一共十個數字按鍵,加減乘除四個運算子按鍵,一個等號按鍵組成的。通過外部的按鍵可以完成加、減、乘、除四種功能運算,其結構簡單,易於實現。本篇為本人畢業設計部分整理,各位大俠可依據自己的需要進行閱讀,參考學習。
第三篇內容摘要:模擬測試驗證部分以及結論,包括ModelSim簡介、模組模擬驗證分析、矩陣按鍵模組、計算模擬舉例等相關內容。
六、模擬驗證設計
6.1 ModelSim簡介
在模擬設計時,用到了Mentor公司的Modelsim,這是一款硬體描述語言模擬軟體,該款軟體不單單能提供十分友好的模擬環境,而且它也是我們業界第一個也是僅此一個的單核心支援VHDL和Verilog語言混合模擬的軟體。它採用直接優化的編譯技術、Tcl/Tk技術、和單一核心模擬技術,從而達到令人編譯模擬速度快的效果,而且編譯程式碼和整個平臺沒有關係,這樣就更容易保護IP核,它是FPGA/ASIC設計的首選模擬軟體。
Modelsim有不同版本,例如:SE、PE、LE和OEM,其中最高階的版本是SE,而整合在 Actel、Atmel以及Lattice等FPGA廠商設計工具中的都是其OEM版本。
Modelsim SE支援PC、UNIX和LINUX的混合平臺;能給出十分全面到位以及高效能的驗證功能;全面支援業界設定的廣泛標準;同時Mentor Graphics公司提供了整個行業最出色的技術支援與服務。
Modelsim的主要特點有:
1)支援單核心的VHDL和Verilog混合在一起進行模擬處理;
2)具有原始碼模版、助手以及專案管理功能;
3)匯聚了效能考核、波形參考、程式碼覆蓋、資料流Chase X、Signal Spy、虛擬物件Virtual Object、Assertion視窗、Memory視窗、原始碼視窗顯示訊號值、訊號條件斷點等眾多除錯功能;
4)C和Tcl/Tk介面,C除錯;
5)能夠實現對System C的直接支援功能,同時可以和HDL任意混合使用;
6)能夠實現System Verilog的設計功能;
7)可以做到對系統級描述語言進行最全面的支援;
8)可以單獨或同時進行行為(behavioral)、RTL級、和門級(gate-level)的程式碼。
9)能夠實現RTL和門級優化,編譯模擬速率非常快,跨平臺跨版本的模擬。
6.2 模組模擬驗證分析
FPGA設計流程包括設計輸入,模擬,綜合,生成,板級驗證等很多階段。在整個設計流程中,完成設計輸入併成功進行編譯僅僅能說明設計符合一定的語法規範,並不能說明設計功能的正確性,這時,我們就需要通過模擬對設計進行驗證。我們主要進行的是功能模擬,又叫邏輯模擬,是指在不考慮器件延時和佈線延時的理想情況下對原始碼進行邏輯功能驗證;而時序模擬是在佈局佈線後進行。我們模擬是為了保證設計的正確性。
6.2.1 矩陣按鍵模組
矩陣鍵盤測試程式流程框圖如下:
圖6-1 按鍵測試程式流程圖
Figure 6-1 key testing program flow chart
首先,我們要先進行按鍵檢測,判斷是否有按鍵閉合,如果沒有說明沒有按鍵鍵入,那麼返回就是我們常說的消抖,重新進行按鍵檢測,直到有按鍵閉合。接下來會有10ms的延遲,儲存鍵值;再繼續判斷按鍵是否鬆開,如果是則又會產生10ms的延遲,否則返回判斷直到按鍵有鬆開為止,最後返回鍵值。
我們需要編譯一個模擬鍵盤定義data0-15,然後模擬輸入給FPGA一個行訊號,FPGA接收行訊號,同時輸出給模擬鍵盤一個列訊號,如果輸出的列訊號不存在低電平,那麼行訊號為4‘b1111,代表輸入的按鍵不在本列上,繼續掃描下一列直到找到相應的行訊號為止。部分程式碼如圖所示。
圖6-2 鍵盤掃描部分程式碼
Figure 6-2 keyboard scan code
圖6-3為鍵盤掃描模擬圖,當我們按下1時,資料顯示1,按下10顯示10,按下2顯示的是2,按下15顯示15,模擬結果有效,程式編譯正確。
圖6-3 鍵盤掃描模擬
Figure 6-3 keyboard scanning simulation
6.2.2 計算模擬舉例
加法計算舉例,首先pnumber輸入1,data_in輸入也為1,掃描結果為1;然後輸入10;pnumber輸入為2,data_in輸入為2,掃描結果為2;最後按鍵“=”,顯示結果即為3。模擬顯示結果正確,說明我們的編譯程式碼沒有問題,計算有效,計算器結果可信。
圖6-4 1+2=3程式模擬圖Figure 6-4 1+2=3 process simulation diagram
七、結論
本次電子計算器的設計是基於FPGA設計的,計算器基本上可以實現的加減乘除的功能。系統的計算部分、儲存部分、顯示部分和輸入部分四個部分都可以完成設計要求,輸入部分採用鍵盤矩陣原理,儲存部分用狀態機來實現,並進行了模擬。實現了防消抖的要求,計算結果較精確。達到了預期的要求目標。
附錄:設計主體原始碼
二進位制轉BCD程式碼:
module bin2bcd_12bit(bin, bcd);
input [19:0] bin;
output reg [23:0] bcd;
always @ (*)
begin
bcd[3:0] = bin%10;
bcd[7:4] = bin/10%10;
bcd[11:8] = bin/100%10;
bcd [15:12] = bin/1000%10;
bcd[19:16] = bin/10000%10;
bcd[23:20] = bin/100000%10;
end
endmodule
計算模組程式碼:
module calculator (clk, rst_n, flag, key_data, bin_data);
input clk;
input rst_n;
input flag;
input [3:0] key_data;
output reg [19:0] bin_data;
reg [1:0] state;
reg [19:0] num1;
reg [3:0] opcode;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
state <= 0;
num1 <= 0;
bin_data <= 0;
opcode <= 0;
end
else
begin
case (state)
0 : begin
if (flag)
begin
if (key_data < 10)
begin
bin_data <= bin_data * 10 + key_data;
end
else
begin
if (key_data == 14)
begin
state <= 0;
end
else
begin
opcode <= key_data;
state <= 1;
num1 <= bin_data;
bin_data <= 0;
end
end
end
else
begin
state <= 0;
end
end
1 : begin
if (flag)
begin
if (key_data < 10)
begin
bin_data <= bin_data * 10 + key_data;
end
else
begin
if (key_data == 14)
begin
case (opcode)
10 : begin bin_data <= num1 + bin_data; state <= 2; end
11 : begin bin_data <= num1 - bin_data; state <= 2; end
12 : begin bin_data <= num1 * bin_data; state <= 2; end
13 : begin bin_data <= num1 / bin_data; state <= 2; end
default : bin_data <= 0;
endcase
end
else
begin
state <= 1;
end
end
end
else
begin
state <= 1;
end
end
2 : begin
if (flag)
begin
if (key_data < 10)
begin
bin_data <= {16'd0,key_data};
state <= 0;
end
else
begin
if (key_data == 14)
begin
state <= 2;
end
else
begin
num1 <= bin_data;
opcode <= key_data;
bin_data <= 0;
state <=1;
end
end
end
else
begin
state <= 2;
end
end
endcase
end
end
endmodule
輸入部分程式碼:
module key_board (clk, rst_n, row, col, data, valid, clk_1k);
input clk;
input rst_n;
input [3:0] row;
output reg [3:0] col;
output reg [3:0] data;
output reg valid;
output reg clk_1k;
reg [14:0] cnt;
parameter T1ms = 24999;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
clk_1k <= 1'b1;
cnt <= 15'd0;
end
else
begin
if (cnt < T1ms)
begin
cnt <= cnt + 15'd1;
end
else
begin
cnt <= 15'd0;
clk_1k <= ~clk_1k;
end
end
end
reg [7:0] row_col;
reg [1:0] state;
reg [4:0] count;
always @ (posedge clk_1k or negedge rst_n)
begin
if (!rst_n)
begin
col <= 4'b0000;
row_col <= 8'd0;
state <= 0;
valid <= 0;
count <= 0;
end
else
begin
case (state)
0 : begin
if (row == 4'b1111)
begin
col <= 4'b0000;
end
else
begin
state <= 1;
end
end
1 : begin
if (row == 4'b1111)
begin
state <= 0;
count <= 0;
end
else
begin
if (count < 19)
begin
count <= count + 1;
end
else
begin
count <= 0;
state <= 2;
col <= 4'b0111;
end
end
end
2 : begin
if (row == 4'b1111)
begin
col <= {col[2:0],col[3]};
state <= 2;
end
else
begin
row_col <= {row,col};
state <= 3;
valid <= 1;
end
end
3 : begin
if (row == 4'b1111)
begin
state <= 0;
valid <= 0;
end
else
begin
valid <= 0;
state <= 3;
end
end
default : state <= 0;
endcase
end
end
always @ (*)
begin
case (row_col)
8'b0111_0111 : data = 4'hf;
8'b0111_1011 : data = 4'he;
8'b0111_1101 : data = 4'hd;
8'b0111_1110 : data = 4'hc;
8'b1011_0111 : data = 4'hb;
8'b1011_1011 : data = 4'ha;
8'b1011_1101 : data = 4'h9;
8'b1011_1110 : data = 4'h8;
8'b1101_0111 : data = 4'h7;
8'b1101_1011 : data = 4'h6;
8'b1101_1101 : data = 4'h5;
8'b1101_1110 : data = 4'h4;
8'b1110_0111 : data = 4'h3;
8'b1110_1011 : data = 4'h2;
8'b1110_1101 : data = 4'h1;
8'b1110_1110 : data = 4'h0;
default : data = 4'h0;
endcase
end
endmodule
數碼管頂層程式碼:
module seven_seg (clk, rst_n, data_in, sel, seg);
input clk;
input rst_n;
input [23:0] data_in;
output [2:0] sel;
output [7:0] seg;
wire clk_1k;
freq freq_dut(
.clk(clk),
.rst_n(rst_n),
.clk_1k(clk_1k)
);
sel_seg_encode sel_seg_encode_dut(
.clk(clk_1k),
.rst_n(rst_n),
.data_in(data_in),
.sel(sel),
.seg(seg)
);
endmodule
分頻部分程式碼:
module freq (clk, rst_n, clk_1k);
input clk;
input rst_n;
output reg clk_1k;
reg [14:0] cnt;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
clk_1k <= 1;
cnt <= 0;
end
else
begin
if (cnt < 24_999)
begin
cnt <= cnt + 1;
end
else
begin
cnt <= 0;
clk_1k <= ~clk_1k;
end
end
end
endmodule
位選段選連線程式碼:
module sel_seg_encode (clk, rst_n, data_in, sel, seg);
input clk;
input rst_n;
input [23:0] data_in;
output [2:0] sel;
output [7:0] seg;
wire [3:0] num;
seg_encode seg_encode_dut(
.rst_n(rst_n),
.num(num),
.seg(seg)
);
sel_encode sel_encode_dut(
.clk(clk),
.rst_n(rst_n),
.data_in(data_in),
.num(num),
.sel(sel)
);
endmodule
段選部分程式碼:
module seg_encode (rst_n, num, seg);
input rst_n;
input [3:0] num;
output reg [7:0] seg;
always @ (*)
begin
if (!rst_n)
begin
seg = 8'b0000_0000;
end
else
begin
case (num)
0 : seg = 8'b1100_0000;
1 : seg = 8'b1111_1001;
2 : seg = 8'b1010_0100;
3 : seg = 8'b1011_0000;
4 : seg = 8'b1001_1001;
5 : seg = 8'b1001_0010;
6 : seg = 8'b1000_0010;
7 : seg = 8'b1111_1000;
8 : seg = 8'b1000_0000;
9 : seg = 8'b1001_0000;
default : seg = 8'b0000_0000;
endcase
end
end
endmodule
位選部分程式碼:
module sel_encode (clk, rst_n, data_in, num, sel);
input clk;
input rst_n;
input [23:0] data_in;
output reg [3:0] num;
output reg [2:0] sel;
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
sel <= 0;
end
else
begin
if (sel < 5)
begin
sel <= sel + 1;
end
else
begin
sel <= 0;
end
end
end
always @ (*)
begin
if (!rst_n)
begin
num = 0;
end
else
begin
case (sel)
0 : num = data_in[23:20];
1 : num = data_in[19:16];
2 : num = data_in[15:12];
3 : num = data_in[11:8];
4 : num = data_in[7:4];
5 : num = data_in[3:0];
default : num = 0;
endcase
end
end
endmodule
本篇到此結束,各位大俠,有緣再見!
END
後續會持續更新,帶來Vivado、 ISE、Quartus II 、candence等安裝相關設計教程,學習資源、專案資源、好文推薦等,希望大俠持續關注。大俠們,江湖偌大,繼續闖蕩,願一切安好,有緣再見!
精彩推薦
直接擴頻通訊(下)模擬
Signal tap 邏輯分析儀使用教程
基於FPGA的實時影象邊緣檢測系統設計(下)
“FPGA產品設計與研發 ” 零基礎入門及就業