Skip to content

verilog

Tingwei edited this page May 3, 2026 · 1 revision

第零組:環境確認(動手前先做)

在終端機執行以下步驟,確認工具鏈正常。

建立 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
endmodule
module 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 輸出正確。

Clone this wiki locally