-
Notifications
You must be signed in to change notification settings - Fork 0
verilog
第零組:環境確認(動手前先做)
在終端機執行以下步驟,確認工具鏈正常。
建立 hello.v:
module hello;
initial begin
$display("Hello, Verilog!");
$finish;
end
endmodule執行:
iverilog -o hello hello.v
./hello
看到 Hello, Verilog! 代表 iverilog 正常。GTKWave 之後在第三組才需要。
第一組:認識 module、assign、基本語法
示範:HDL Example 4.1(書頁 173),COMBINATIONAL LOGIC
這是書上第一個 Verilog 範例。打開書,把這段程式碼完整打一遍,不要複製貼上:
module sillyfunction(input logic a, b, c,
output logic y);
assign y = ~a & ~b & ~c |
a & ~b & ~c |
a & ~b & c;
endmodule存成 sillyfunction.v,執行 iverilog -o silly sillyfunction.v,只要沒有 error 訊息就代表語法正確。這個階段還不需要 testbench。
練習:Exercise 4.1(書頁 224)
書上給了一段已寫好的 Verilog,題目要你畫出它對應的電路圖,再把電路化簡。做法是先把書上那段程式碼打出來,用 iverilog 確認語法沒問題,然後在紙上畫出電路,最後化簡布林式。
題目的模組長這樣:
module exercise1(input logic a, b, c,
output logic y, z);
assign y = a & b & c | a & b & ~c | a & ~b & c;
assign z = a & b | ~a & ~b;
endmodule紙上化簡後,把化簡後的式子也用 assign 改寫,確認 iverilog 能編譯。
第二組:向量、Mux、always_comb
示範:HDL Example 4.5(書頁附近),2:1 MULTIPLEXER
書上的程式碼:
module mux2(input logic [3:0] d0, d1,
input logic s,
output logic [3:0] y);
assign y = s ? d1 : d0;
endmodule打進去,執行 iverilog 確認語法。注意 [3:0] 這個向量宣告,以及三元運算子 ? :。
接著看 HDL Example 4.6,4:1 MULTIPLEXER,用 always_comb 和 case 寫的版本,把它也打一遍。
練習:Exercise 4.8
題目要你自己寫一個 8:1 multiplexer,模組名稱叫 mux8,輸入是 s[2:0]、d0 到 d7,輸出是 y。
從 mux2 和 mux4 的模式推導,自己想出 8:1 的寫法,不要看答案。存成 mux8.v,用 iverilog 確認。
第三組:第一個 testbench,加上 GTKWave 波形
示範:HDL Example 4.37(書頁 219),TESTBENCH
書上這個 testbench 測試的是 sillyfunction,也就是第一組的示範題。把 testbench 打出來:
module testbench1();
logic a, b, c, y;
sillyfunction dut(a, b, c, y);
initial begin
a = 0; b = 0; c = 0; #10;
c = 1; #10;
b = 1; c = 0; #10;
c = 1; #10;
a = 1; b = 0; c = 0;#10;
c = 1; #10;
b = 1; c = 0; #10;
c = 1; #10;
end
endmodule存成 testbench1.v。加上 GTKWave 的 dump 指令,修改 initial begin 區塊,在最前面加兩行:
$dumpfile("silly.vcd");
$dumpvars(0, testbench1);在最後 c = 1; #10; 後面加:
$finish;編譯並執行:
iverilog -o silly_sim sillyfunction.v testbench1.v
./silly_sim
gtkwave silly.vcd
GTKWave 開啟後,在左側 SST 欄位展開 testbench1,把 a、b、c、y 四個訊號拖進中間的波形區,確認 y 在 a=0,b=0,c=0、a=1,b=0,c=0、a=1,b=0,c=1 時為 1,其餘為 0。
練習:Exercise 4.3 加上練習 4.4
Exercise 4.3 要你寫一個四輸入 XOR 模組,輸入是 a[3:0],輸出是 y:
module xor4(input logic [3:0] a,
output logic y);
assign y = a[0] ^ a[1] ^ a[2] ^ a[3];
endmodule存成 xor4.v。
Exercise 4.4 要你幫這個模組寫 testbench,並且加上 GTKWave dump。把 16 個可能的輸入(0000 到 1111)全部測一遍,每個都用 #10 間隔,最後加 $finish。存成 xor4_tb.v,編譯執行,GTKWave 看波形確認每個輸入對應的 y 正確。
第四組:self-checking testbench,自動判斷對錯
示範:HDL Example 4.38(書頁 220),SELF-CHECKING TESTBENCH
這個 testbench 在每次設定輸入之後,用 assert 自動判斷輸出是否正確:
module testbench2();
logic a, b, c, y;
sillyfunction dut(a, b, c, y);
initial begin
a = 0; b = 0; c = 0; #10;
assert (y === 1) else $error("000 failed.");
c = 1; #10;
assert (y === 0) else $error("001 failed.");
b = 1; c = 0; #10;
assert (y === 0) else $error("010 failed.");
c = 1; #10;
assert (y === 0) else $error("011 failed.");
a = 1; b = 0; c = 0;#10;
assert (y === 1) else $error("100 failed.");
c = 1; #10;
assert (y === 1) else $error("101 failed.");
b = 1; c = 0; #10;
assert (y === 0) else $error("110 failed.");
c = 1; #10;
assert (y === 0) else $error("111 failed.");
$display("All tests done.");
$finish;
end
endmodule編譯執行後,如果什麼都沒印出(除了 All tests done.),代表全部通過。故意把 sillyfunction 裡的某個 & 改成 |,再跑一次,確認 assert 會印出 error 訊息。改回來。
練習:用 Exercise 4.5 搭配自己寫 self-checking testbench
Exercise 4.5 要你寫一個 minority 模組,三個輸入 a、b、c,輸出 y 在至少兩個輸入為 0 時為 1:
module minority(input logic a, b, c,
output logic y);
assign y = ~a & ~b | ~a & ~c | ~b & ~c;
endmodule存成 minority.v。然後自己寫 minority_tb.v,把八種輸入組合全部列出來,每個都加 assert 判斷。有故意寫錯過一次,看 assert 的回饋,再改回正確的。
第五組:sequential logic,flip-flop 與 always_ff
示範:HDL Example 4.17 和 4.18(書頁附近),REGISTER 和 RESETTABLE REGISTER
先看 4.17,最基本的 D flip-flop:
module flop(input logic clk,
input logic [3:0] d,
output logic [3:0] q);
always_ff @(posedge clk)
q <= d;
endmodule注意 always_ff 和 <=(nonblocking assignment)。再看 4.18,加上 reset:
module flopr(input logic clk,
input logic reset,
input logic [3:0] d,
output logic [3:0] q);
always_ff @(posedge clk, posedge reset)
if (reset) q <= 4'b0;
else q <= d;
endmodule把兩個都打出來,各自建一個 testbench,加上 clock 產生器:
initial clk = 0;
always #5 clk = ~clk;這樣每 5 個 time unit 切換一次,週期是 10。加上 GTKWave dump,執行後看波形確認 q 在 posedge clk 後更新,reset 時歸零。
練習:Exercise 4.29
題目要你用 HDL 實作書中 Section 3.4.1 的 traffic light controller FSM。這個 FSM 有兩個燈號 La 和 Lb,各自有 green、yellow、red 三種狀態,Moore machine。自己畫狀態圖(從書上 Figure 3.25 對照),再用 always_ff 和 always_comb 兩個 block 的 FSM 模板寫出來,加上 testbench 和 GTKWave,確認狀態在每個 posedge clk 正確轉換。
第六組:blocking vs nonblocking 的陷阱
示範:HDL Example 4.28 和 4.29(書頁附近),FULL ADDER USING NONBLOCKING 和 BAD SYNCHRONIZER WITH BLOCKING
這兩個 Example 放在一起讀。4.28 是正確的寫法,用 <=。4.29 是用 blocking = 寫 sequential logic 的錯誤示範。把兩個都打出來,各自加 testbench 跑,用 GTKWave 看兩者波形的差異。
練習:Exercise 4.48
書上給了以下兩個模組,題目問它們的硬體行為是否相同:
module circuit1(input logic clk,
input logic d,
output logic q);
logic n1;
always_ff @(posedge clk) begin
n1 <= d;
q <= n1;
end
endmodulemodule circuit2(input logic clk,
input logic d,
output logic q);
logic n1;
always_ff @(posedge clk) begin
n1 = d;
q = n1;
end
endmodule兩個都打出來,各自加 testbench,同樣的輸入序列打進去,GTKWave 把兩者的波形並排看,找出差異,自己解釋為什麼會不同。
第七組:testbench with test vector file,$readmemb
示範:HDL Example 4.39(書頁 221),TESTBENCH WITH TEST VECTOR FILE
這是最完整的 testbench 模式。先建立 example.txt 文字檔:
000_1
001_0
010_0
011_0
100_1
101_1
110_0
111_0
再打出書上的 testbench3 程式碼,這個 testbench 會自動讀取 example.txt,逐筆送入 DUT,並在 falling edge 自動比對結果。整組編譯執行,確認結果是 8 tests completed with 0 errors。故意把 example.txt 裡某一行的預期輸出從 1 改成 0,再跑一次,確認 $display 會印出 Error。
練習:Exercise 4.7
Exercise 4.6 要你寫一個十六進位七段顯示解碼器(0–9 和 A–F),Exercise 4.7 要你用 test vector file 的方式幫它寫 testbench。
自己先寫 hex7seg.v,輸入是 a[3:0],輸出是 seg[6:0],對應七個燈段。再建立 hex7seg.tv 文字檔,16 行,每行是 4-bit 輸入加底線加 7-bit 預期輸出。最後把 HDL Example 4.39 的 testbench 改寫成適合這個模組的版本,$readmemb 讀 hex7seg.tv,GTKWave 看波形確認每個輸入對應的 seg 輸出正確。