這篇文章的目標就是實作正反器,但是在開始之前我們要先學習新的 Verilog 語法,這可以有效幫助我們簡化程式碼。
條件判斷在軟體撰寫共分成兩種:if / else
, switch / case
。這兩者在 Verilog 中都有支援,只不過 switch / case
會被改為 case
而已。
不論是 if / else
或是 case
都必須在 always block 或 initial block 中使用,因此需要搭配條件判斷的變數都是 reg
型態。
先從 if / else
開始介紹吧!
always @ (*) begin
if ( <cond 1> ) begin
// some code
end
else if ( <cond 2> ) begin
// some code
end
else begin
// some code
end
end
內部架構和 C 語言的是大同小異,如果 if / else
內部的程式碼只有一行,begin
, end
這兩個關鍵字都可以省略。<cond> 代表的是判斷式,我們可以搭配邏輯運算符號來組成判斷式,如:==
, ||
, &&
, !
等。
再來就是 case
的語法介紹。
always @(*) begin
case ( <vector> )
<pattern 1>: <code>;
<pattern 2>: <code>;
...
default: <code>;
endcase
end
其中,我覺得最彈性的地方是 <vector> 的指定,我們可以透過接合運算子 { }
將數個 vector 結合,這樣可以減少使用 case
的次數。
在 Verilog 中,使用條件判斷要非常仔細,我們必須要考慮所有可能性。如果有一個可能性沒有被包含進去,那麼 Verilog 在合成電路時,有可能會以 latch 去處理個漏洞,電路的功能也許會和我們所學的不太一樣。
先附上 DFF 的真值表,這樣比較方便程式的撰寫!
Clock | D | Q | Q' |
---|---|---|---|
Low | x | Q 不變 | Q' 不變 |
High | 0 | 1 | 0 |
High | 1 | 0 | 1 |
分別使用 if / else
和 case
來完成 DFF 吧。
// Using if / else
module Dff(
input clk, D,
output reg Q
);
always @ (posedge clk) begin
if (D == 1'b1) Q = 1'b1;
else Q = 1'b0;
end
endmodule
// Using case
module Dff(
input clk, D,
output reg Q
);
always @ (posedge clk) begin
case (D)
1'b1: Q = 1'b1;
1'b0: Q = 1'b0;
endcase
end
endmodule
DFF 內部邏輯其實很容易完成,但是若要再圖上同時看到時脈和 D, Q ,我覺得就沒有那麼簡單了!
我們可以從兩個方向切入:
我綜合了上述兩點產生了一些測資:
module Dff_tb;
reg clk, D;
wire Q;
Dff dff(clk, D, Q);
initial begin
$dumpfile("DFF.vcd");
$dumpvars(0, Dff_tb);
clk = 1'b0;
#5 clk = 1'b0; D = 1'b1;
#5 clk = 1'b1; D = 1'b1;
#5 clk = 1'b1; D = 1'b0;
#5 clk = 1'b0; D = 1'b0;
#5 clk = 1'b1; D = 1'b0;
#5 clk = 1'b0; D = 1'b1;
#5 clk = 1'b1; D = 1'b1;
#5 clk = 1'b0; D = 1'b0;
#5 $finish;
end
endmodule
結果如附圖: (可以檢查兩份檔案輸出的波形圖是否都相同)
時脈正緣共出現在 3 個時間點:10, 25, 35 sec ,可以看到在這些時間點,Q 都有跟隨 D 做出改變。
其他時間點,如:15 sec (時脈高位) D 雖然變為低位,但是 Q 仍維持高位。