前面的篇幅大致說明了基本的 Verilog 語法,如果想要認識更多內容可以參考筆者寫的 數位IC起手式 ,裡面記載我在學習 Verilog 的學習過程和思路,相信可以幫到你們!
當我們設計了一個電路,我們要檢測這個電路的邏輯是否正確。測試的方式有兩種,第一種是透過模擬器生成波形圖檔案,再透過波形圖檢查電路運作過程是否正確,第二種是將電路燒錄到 FPGA 中,使用真的硬體來去檢測電路運作。
使用 FPGA 固然可以更準確的檢測電路,但是這個板子並不便宜,也不是隨手可得的資源,因此使用波形圖模擬 (simulation) 電路,在電路設計中扮演很重要的角色。
在這一篇文章中,我們會學著怎麼使用 Icarus Verilog (簡稱 Iverilog) 搭配 GTKWave 來模擬並檢測電路。其中,Iverilog 是一套模擬器,透過這個工具,我們可以把 Verilog 轉換成特定格式,並模擬電路。通常我們會再把相關資料輸出成波形圖格式 (.vcd) ,並用 GTKWave 開啟。
假設我們設計了一個加法器,程式碼如下:
module FullAdder(
output Cout, Sum,
input A, B, Cin
);
assign Cout = (A & B) | (B & Cin) | (A & Cin);
assign Sum = A ^ B ^ Cin;
endmodule
為了要檢查電路是否正確,我們要設計一個測試檔案(testbench) ,這個檔案說明了測資該怎麼被傳入模組,還有我們傳入了哪些側資。除此之外,我們也可以在其中加上「輸出成波形圖」的相關敘述,如此就可以更直覺地看出電路的運作過程。
module tb;
wire Cout, Sum;
reg A, B, Cin;
FullAdder FA(Cout, Sum, A, B, Cin);
initial begin
$dumpfile("FAdder.vcd");
$dumpvars(0, tb);
end
initial begin
A = 1'b1; B = 1'b1; Cin = 1'b1;
#2
A = 1'b0; B = 1'b1; Cin = 1'b1;
#2
A = 1'b1; B = 1'b0; Cin = 1'b0;
#2
A = 1'b0; B = 1'b1; Cin = 1'b0;
#2
$finish();
end
endmodule
說明上述的語法:
FAdder.vcd
代表波形圖的檔案名稱,可以根據自己的需求更改檔名。另一個要注意的是 tb
代表 testbench 的模組名稱 (module name) ,因此這需要根據當前情況作更動。initial begin
$dumpfile("FAdder.vcd");
$dumpvars(0, tb);
end
reg
,否則使用 wire
。因為我們會不斷的更動輸入資料,所以輸入端的接口需要設定為 reg
,也就是暫存器。wire Cout, Sum;
reg A, B, Cin;
#n
):延遲僅僅是讓我們看波形圖時,能清楚知道各個時間段的電路表現為何。若不加上延遲,電路運作的速度極快,波形圖完全無法表現出來。延遲通常加在兩組測資之間。使用 Iverilog 模擬我們需要下達兩組指令。
iverilog -o FAdder tb_FAdder.v
vvp FAdder
gtkwave FAdder.vcd
波形圖結果如下圖:
到這邊就是完整的模擬過程,當然大家也可以使用 $display()
輸出某個變數在不同時間的數值,過程大致上就和在 C 程式設計時,使用 printf()
輸出目標變數相同。差別在於 $display()
要放在 always
或是 initial
中,也因為這是電路設計,因此 $display()
被呼叫的次數可能大於一遍,畢竟電路是不間斷的。