initial / always, blocking vs non-blocking, procedural continuous assignment, timing controls, intra-assignment delay, scheduling, flow control and fork/join — a self-study guide to behavioral descriptionsinitial / always、阻擋式與非阻擋式賦值、程序式連續賦值、時序控制、賦值內延遲、模擬排程、流程控制與 fork/join — 行為描述自學指南initial / always、ブロッキング代入とノンブロッキング代入、手続き型連続代入、タイミング制御、代入内遅延、スケジューリング、フロー制御と fork/join — 動作記述の自学ガイド
A single Verilog module may freely mix every description level: continuous assignments (dataflow), primitive and UDP instances (gate level), sub-module instances (structural) and procedural blocks (behavioral).同一個 Verilog 模組可以自由混用所有描述層次:連續賦值(資料流)、基本閘與 UDP 的實體化(閘層次)、子模組的實體化(結構式),以及程序區塊(行為式)。一つの Verilog モジュールの中で、あらゆる記述レベルを自由に混在させてよい。連続代入(データフロー)、プリミティブと UDP のインスタンス(ゲートレベル)、サブモジュールのインスタンス(構造記述)、そして手続きブロック(動作記述)である。
module my_module (...); ... assign ...; // continuous assignment and (...); // instantiation of a primitive my_udp (...); // instantiation of a UDP adder_16bits M1 (...); // instantiation of a module always @ (...) begin // "always" block ... end endmodule
begin ... end block.本體是單一敘述,或是一個 begin ... end 區塊。本体は単一の文、または begin ... end ブロックである。reg, integer, ...). The only exception is force ... release, which can also drive nets.程序式敘述只能對暫存器型別變數(reg、integer 等)賦值。唯一的例外是 force ... release,它也可以驅動 net。手続き文が値を代入できるのはレジスタ型変数(reg、integer など)だけである。唯一の例外は force ... release で、これは net も駆動できる。initial — one-shot activity flow. Runs once from time 0. Not synthesized, but essential for test-benches.initial — 一次性的活動流程,從時間 0 起只執行一次。不可合成,但在測試平台(test-bench)中不可或缺。initial — 一回限りの動作フロー。時刻 0 から一度だけ実行される。合成はできないが、テストベンチには不可欠である。always — cyclic activity flow. An infinite loop until simulation stops; with an event list it re-evaluates whenever a signal in the sensitivity list changes.always — 循環的活動流程,在模擬結束前都是無窮迴圈;加上事件列表後,只要敏感列表中的訊號改變就重新計算一次。always — 周期的な動作フロー。シミュレーションが止まるまで無限ループする。イベントリストを付ければ、感度リスト中の信号が変化するたびに再評価される。initial // an "initial" behavior begin sig_a = 0; // procedural assignments sig_b = 1; // execute sequentially sig_c = 0; end
always @ (b or c or d) begin
if (b == 1'b1) begin
a = c;
end
else begin
a = d;
end
endinitial: runs exactly once, starting at time 0; not synthesizable, used in a test-bench to generate stimulus.initial:只執行一次,從時間 0 開始;不可合成,用在 test-bench 產生激勵。initial:時刻 0 から一度だけ実行される。合成はできず、テストベンチで刺激を与えるために使う。always: an infinite loop; combined with an @(...) sensitivity list it re-evaluates every time a listed signal changes.always:無窮迴圈;配合 @(...) 敏感列表,每當列表中訊號改變就重新計算。always:無限ループ。@(...) の感度リストと組み合わせると、リスト中の信号が変化するたびに再評価される。initial / always blocks run in parallel with one another, while the statements inside a block execute in order.多個 initial / always 區塊之間是並行的,區塊內的敘述則依序執行。複数の initial / always ブロックは互いに並行に動作し、ブロック内の文は順番に実行される。always @(b or c or d) above is a 2-to-1 multiplexer: the sensitivity list must contain every input that is read, otherwise the synthesized result and the simulation disagree.上例 always @(b or c or d) 是一個 2 對 1 多工器:敏感列表必須包含所有讀到的輸入,否則合成結果與模擬不一致。上の例 always @(b or c or d) は 2 対 1 のマルチプレクサである。感度リストには読み出すすべての入力を含めなければならず、さもないと合成結果とシミュレーションが一致しない。Blocking =阻擋式 =ブロッキング = | Non-blocking <=非阻擋式 <=ノンブロッキング <= | |
|---|---|---|
| Semantics語意意味 | Assignment completes before the next statement starts賦值完成之後,下一個敘述才開始執行代入が完了してから次の文が実行される | All RHS sampled first; LHS updated together at the end of the time step ("in parallel")先取樣所有右手邊(RHS);左手邊(LHS)在時間步結束時一起更新(「同時」發生)まずすべての右辺を採取し、時間ステップの終わりに左辺をまとめて更新する(「並行」に起こる) |
| Example範例例 | a = 0; a = 1; c = a; → c = 1 | a = 0; a <= 1; c = a; → c = 0 |
| Typical use典型用途典型的な用途 | Combinational logic, test-bench sequencing組合邏輯、測試平台(test-bench)的流程安排組合せ論理、テストベンチの手順記述 | Sequential (clocked) logic序向(時脈同步)邏輯順序(クロック同期)論理 |
d <= 0; d <= 1; in the same time step — the lecture calls this ambiguous and treats it as illegal style. (The IEEE standard actually guarantees the last one wins within a single block, so d = 1, but relying on this is poor practice and some tools warn.)在同一個時間步內寫 d <= 0; d <= 1; — 課堂上認為這是模稜兩可的,視為不合法的寫法。(IEEE 標準其實保證同一區塊內最後一次生效,所以 d = 1;但依賴這一點並不是好習慣,有些工具會發出警告。)同一の時間ステップで d <= 0; d <= 1; と書く — 講義ではこれを曖昧とみなし、許されない書き方として扱う。(IEEE 規格では実際には同一ブロック内の最後の代入が勝つと保証されているので d = 1 になるが、これに頼るのは悪い習慣であり、警告を出すツールもある。)
Start from a = 1; b = 0;.起始值為 a = 1; b = 0;。初期値を a = 1; b = 0; とする。
| Code程式碼コード | Values used實際讀到的值実際に読まれる値 | Result結果結果 |
|---|---|---|
a = b; b = a; | b=0, then the new a=0b=0,接著讀到的是新的 a=0b=0、次に新しい a=0 | a=0, b=0 (no swap)a=0, b=0(沒有交換)a=0, b=0(交換されない) |
b = a; a = b; | a=1, then the new b=1a=1,接著讀到的是新的 b=1a=1、次に新しい b=1 | a=1, b=1 (no swap)a=1, b=1(沒有交換)a=1, b=1(交換されない) |
a <= b; b <= a; | old b=0, old a=1舊的 b=0、舊的 a=1古い b=0、古い a=1 | a=0, b=1 (swapped)a=0, b=1(成功交換)a=0, b=1(交換される) |
b <= a; a <= b; | old a=1, old b=0舊的 a=1、舊的 b=0古い a=1、古い b=0 | a=0, b=1 (swapped; order irrelevant)a=0, b=1(成功交換;順序無關)a=0, b=1(交換される。順序は無関係) |
With non-blocking assignments every RHS sees the values from before the time step, so the statement order does not matter. This is exactly how a bank of flip-flops sharing one clock behaves.使用非阻擋式賦值時,每一個 RHS 讀到的都是這個時間步開始之前的值,所以敘述的先後順序無關緊要。這正是一組共用同一時脈的正反器(flip-flop)的行為。ノンブロッキング代入では、どの右辺も時間ステップの直前の値を読む。したがって文の順序は結果に影響しない。これは一つのクロックを共有するフリップフロップ群の振る舞いそのものである。
module blocking_nonblocking(); reg a, b, c, d; initial begin // (A) blocking, inter-statement delay #10 a = 0; #11 a = 1; #12 a = 0; #13 a = 1; end initial begin // (B) non-blocking, inter-statement delay #10 b <= 0; #11 b <= 1; #12 b <= 0; #13 b <= 1; end initial begin // (C) blocking, intra-assignment delay c = #10 0; c = #11 1; c = #12 0; c = #13 1; end initial begin // (D) non-blocking, intra-assignment delay d <= #10 0; d <= #11 1; d <= #12 0; d <= #13 1; end initial begin $monitor("TIME = %d A = %b B = %b C = %b D = %b", $time, a, b, c, d); #50 $finish; end endmodule
| Block區塊ブロック | Why原因理由 | Change times變化時刻変化する時刻 |
|---|---|---|
A (#d a = v) | Each delay suspends the block, delays accumulate每個延遲都會暫停整個區塊,所以延遲會累加各遅延がブロック全体を停止させるため、遅延が累積する | 10, 21, 33, 46 |
B (#d b <= v) | Same — the # precedes the statement and still suspends the block同上 — # 放在敘述之前,一樣會暫停區塊同上 — # は文の前に置かれており、やはりブロックを停止させる | 10, 21, 33, 46 |
C (c = #d v) | Blocking: block waits for each assignment to complete, so delays accumulate阻擋式:區塊要等每一次賦值完成才往下走,所以延遲累加ブロッキング:ブロックは各代入の完了を待つので、遅延が累積する | 10, 21, 33, 46 |
D (d <= #d v) | Non-blocking: all four are scheduled at time 0 and the block does not wait非阻擋式:四行都在時間 0 被排程,區塊不等待ノンブロッキング:四つとも時刻 0 でスケジュールされ、ブロックは待たない | 10, 11, 12, 13 |
TIME = 0 A = x B = x C = x D = x TIME = 10 A = 0 B = 0 C = 0 D = 0 TIME = 11 A = 0 B = 0 C = 0 D = 1 TIME = 12 A = 0 B = 0 C = 0 D = 0 TIME = 13 A = 0 B = 0 C = 0 D = 1 TIME = 21 A = 1 B = 1 C = 1 D = 1 TIME = 33 A = 0 B = 0 C = 0 D = 1 TIME = 46 A = 1 B = 1 C = 1 D = 1
#10 a = 0;: suspend the whole block for 10 units first, then perform the assignment → the delays accumulate.#10 a = 0;:先暫停整個區塊 10 個單位,再執行賦值 → 延遲會累加。#10 a = 0;:まずブロック全体を 10 単位停止させ、その後に代入を実行する → 遅延が累積する。#10 b <= 0;: the # again sits in front of the statement and still suspends the block → same as a.#10 b <= 0;:# 一樣放在敘述前面,仍然是暫停區塊 → 與 a 相同。#10 b <= 0;:# はやはり文の前にあり、ブロックを停止させる → a と同じになる。c = #10 0;: the RHS is evaluated at once but the write is postponed by 10; because it is blocking, the block waits for that write to finish before moving on → the delays accumulate too.c = #10 0;:RHS 立即計算,但「賦值」延後 10;因為是阻擋式,區塊要等這次賦值完成才往下走 → 也累加。c = #10 0;:右辺はただちに評価されるが、「書き込み」が 10 だけ先送りされる。ブロッキングなのでブロックはその書き込みの完了を待つ → やはり累積する。d <= #10 0;: a non-blocking assignment only schedules a future update and the block does not wait, so all four lines are scheduled at time 0 and take effect at 10, 11, 12 and 13 respectively.d <= #10 0;:非阻擋式只「排程」未來的更新,區塊不等待,四行都在時間 0 被排程,分別在 10、11、12、13 生效。d <= #10 0;:ノンブロッキング代入は将来の更新を「スケジュール」するだけでブロックは待たない。四行とも時刻 0 でスケジュールされ、それぞれ 10、11、12、13 で有効になる。Problem: Inside always @(posedge clk), which of the following swap x and y every clock? (a) x = y; y = x; (b) x <= y; y <= x; (c) tmp = x; x = y; y = tmp; (d) y <= x; x = y;題目:在 always @(posedge clk) 之中,下列哪些寫法會在每個時脈交換 x 與 y?(a) x = y; y = x; (b) x <= y; y <= x; (c) tmp = x; x = y; y = tmp; (d) y <= x; x = y;問題:always @(posedge clk) の中で、次のうちクロックごとに x と y を交換するのはどれか。(a) x = y; y = x; (b) x <= y; y <= x; (c) tmp = x; x = y; y = tmp; (d) y <= x; x = y;
(a) No — both end up equal to the old y.(a) 不行 — 兩者最後都等於舊的 y。(a) 不可 — どちらも古い y と等しくなってしまう。
(b) Yes — both RHS sample the old values.(b) 可以 — 兩個 RHS 取樣到的都是舊值。(b) 可 — 両方の右辺が古い値を採取する。
(c) Yes in simulation; synthesizes to the same two flip-flops (tmp becomes a wire).(c) 在模擬上可以;合成後同樣是兩個正反器(tmp 變成一條 wire)。(c) シミュレーション上は可。合成すると同じ二つのフリップフロップになる(tmp は配線になる)。
(d) Yes numerically (x = y reads the old y since the non-blocking update has not happened yet), but mixing = and <= in one clocked block is bad style and invites simulation/synthesis mismatch. Use (b).(d) 數值上可以(非阻擋式的更新還沒發生,所以 x = y 讀到的是舊的 y),但在同一個時脈區塊內混用 = 與 <= 是不良寫法,容易造成模擬與合成不一致。請用 (b)。(d) 数値上は交換される(ノンブロッキングの更新はまだ起きていないので x = y は古い y を読む)。しかし一つのクロックブロック内で = と <= を混ぜるのは悪い書き方であり、シミュレーションと合成の不一致を招く。(b) を使うこと。
A PCA is executed from inside a procedural block, but once executed it behaves like a continuous assignment: the target keeps following the RHS expression until the binding is removed.PCA 是在程序區塊內執行的,但一旦執行,它的行為就像連續賦值:在綁定被解除之前,目標會持續跟著 RHS 運算式變化。PCA は手続きブロックの内部で実行されるが、いったん実行されると連続代入のように振る舞う。すなわち、束縛が解除されるまで対象は右辺の式に追従し続ける。
deassign or until a new PCA to the same register is established; deassign is optional.綁定會持續到 deassign,或直到對同一暫存器建立新的 PCA 為止;deassign 並非必要。束縛は deassign まで、あるいは同じレジスタに新しい PCA が設定されるまで続く。deassign は必須ではない。module mux4_PCA (a, b, c, d, select, y_out); input a, b, c, d; input [1:0] select; output y_out; reg y_out; always @ (select) begin // Caution!! if (select == 0) assign y_out = a; else if (select == 1) assign y_out = b; else if (select == 2) assign y_out = c; else if (select == 3) assign y_out = d; else assign y_out = 1'b0; end endmodule
Only select is in the list, yet y_out still follows changes on a–d, because the PCA binding is continuous. Replace assign y_out = a with a plain y_out = a and the model breaks: data changes without a select change would be ignored (and synthesis would build a mux anyway, giving a sim/synth mismatch).列表裡只有 select,但 y_out 仍然會跟著 a–d 的變化,因為 PCA 的綁定是連續的。若把 assign y_out = a 改成單純的 y_out = a,模型就壞了:select 沒變而資料改變時會被忽略(而合成仍舊會做出一個多工器,造成模擬與合成不一致)。リストには select しか入っていないが、PCA の束縛が連続的であるため y_out は a〜d の変化にも追従する。assign y_out = a を単なる y_out = a に置き換えるとモデルは壊れる。select が変わらずにデータだけが変化した場合、それが無視されてしまうからである(それでも合成はマルチプレクサを作るので、シミュレーションと合成が食い違う)。
module Flop_PCA (preset, clear, q, qbar, clock, data);
input preset, clear, clock, data;
output q, qbar;
reg q;
assign qbar = ~q;
always @ (negedge clock)
q = data;
always @ (clear or preset)
begin
if (!clear) assign q = 0; // async clear dominates
else if (!preset) assign q = 1;
else deassign q; // clock regains control
end
endmodule
While clear or preset is active the PCA overrides the clocked assignment q = data; after deassign, q keeps its value until the next falling clock edge.clear 或 preset 有效期間,PCA 會蓋過時脈同步的賦值 q = data;執行 deassign 之後,q 會保持原值直到下一個時脈下降緣。clear または preset が有効な間、PCA はクロック同期の代入 q = data を上書きする。deassign の後、q は次のクロック立ち下がりまで値を保持する。
assign PCAs.用在 net 上時,它會蓋過基本閘的輸出與連續賦值;用在暫存器上時,它會蓋過程序式賦值以及 assign 形式的 PCA。net に対してはプリミティブの出力と連続代入を上書きし、レジスタに対しては手続き型代入および assign 形式の PCA を上書きする。release.綁定在 release 之前無法解除。束縛は release まで解除できない。... force sig_a = 1; // internal nodes of the DUT force sig_b = 1; force sig_c = 0; sig_in1 = 0; #5 sig_in1 = 1; #5 sig_in1 = 0; // insert code to construct tests release sig_a; release sig_b; release sig_c; ...
In the lecture diagram sig_a, sig_b come from mod_abc and sig_c from mod_xyz; forcing them isolates the AND/OR gates feeding mod_def so they can be tested independently of the upstream modules.在課堂的圖中,sig_a、sig_b 來自 mod_abc,sig_c 來自 mod_xyz;把它們 force 住就能把餵給 mod_def 的 AND/OR 閘隔離出來,獨立於上游模組進行測試。講義の図では sig_a と sig_b は mod_abc から、sig_c は mod_xyz から来ている。これらを force すると、mod_def へ入力される AND/OR ゲートを切り離し、上流のモジュールとは独立に検証できる。 Non-synthesized不可合成合成不可
| Variable type變數型別変数の型 | Output of primitive基本閘的輸出プリミティブの出力 | Continuous assignment連續賦值連続代入 | Procedural assignment (always / initial)程序式賦值 (always / initial)手続き型代入 (always / initial) | assign ... deassign (PCA) | force ... release (PCA) |
|---|---|---|---|---|---|
| Net | Yes可可 | Yes可可 | No不可不可 | No不可不可 | Yes可可 |
| Register | Comb — No Seq (UDP) — Yes組合邏輯 — 不可 序向(UDP)— 可組合せ — 不可 順序(UDP)— 可 | No不可不可 | Yes可可 | Yes可可 | Yes可可 |
assign/deassign: registers only. force/release: nets and registers alike, and it has the highest priority.assign/deassign:只能對 reg;force/release:net 與 reg 都可以,優先權最高。assign/deassign:reg のみ。force/release:net にも reg にも使え、優先度が最も高い。force/release is mainly for test-bench debugging and can drive internal nodes through hierarchical names.兩者都不建議用於可合成設計;force/release 主要用在 test-bench 除錯,可搭配階層式名稱強制內部節點。どちらも合成対象の設計には勧められない。force/release は主にテストベンチのデバッグ用で、階層名を使って内部ノードを強制できる。Five mechanisms: delay control #, event control @, event or, named events, and the wait construct.共有五種機制:延遲控制 #、事件控制 @、事件 or、具名事件,以及 wait 敘述。機構は五つある。遅延制御 #、イベント制御 @、イベントの or、名前付きイベント、そして wait 構文である。
Suspends the activity flow at the location of the operator. Typical use: clock generators in test-benches.在運算子所在的位置暫停活動流程。典型用途:測試平台中的時脈產生器。演算子が置かれた位置で動作フローを停止させる。典型的な用途はテストベンチのクロック生成器である。
always
begin
#0 clock = 0;
#50 clock = 1;
#50;
end // period 100always
begin
#clock_period/2;
clock = ~clock;
end // clock must be initialised!The right-hand version needs initial clock = 0;; otherwise ~x = x and the clock stays unknown forever. Also, an always block with no delay and no event control is a zero-delay infinite loop that hangs the simulator.右邊那個版本必須搭配 initial clock = 0;,否則 ~x = x,時脈會永遠停在未知值。另外,沒有延遲也沒有事件控制的 always 區塊是一個零延遲無窮迴圈,會讓模擬器卡死。右側の書き方には initial clock = 0; が必要である。さもないと ~x = x となり、クロックは不定のままになる。また、遅延もイベント制御も持たない always ブロックはゼロ遅延の無限ループとなり、シミュレータを停止させてしまう。
Synchronizes execution to an event: the flow waits until the event occurs.把執行與某個事件同步:流程會等到該事件發生為止。実行をイベントに同期させる。フローはそのイベントが起こるまで待つ。
...
@ signal_1 a = b; // wait for any change on signal_1
......
@ (event_a) begin
...
@ (event_b) begin
...
end
end
...| Edge邊緣エッジ | Transitions that count算數的轉態有効とみなされる遷移 |
|---|---|
posedge | 0→1, 0→x, x→1 (also 0→z, z→1)0→1、0→x、x→1 (也包含 0→z、z→1)0→1、0→x、x→1 (0→z、z→1 も含む) |
negedge | 1→0, 1→x, x→0 (also 1→z, z→0)1→0、1→x、x→0 (也包含 1→z、z→0)1→0、1→x、x→0 (1→z、z→0 も含む) |
Usually used for clock and reset signals in the sensitivity list:通常用在敏感列表中的時脈與重置訊號:通常は感度リスト中のクロックやリセット信号に用いる。
always @ (posedge clock) begin
#10 b = a; // samples a 10 units AFTER the edge
end
Positive-edge D flip-flop with asynchronous active-low set and reset:具非同步低態有效 set 與 reset 的正緣觸發 D 型正反器:非同期かつ負論理の set と reset をもつ立ち上がりエッジ D フリップフロップ:
always @ (posedge clk or negedge set or negedge rst) begin
if (rst == 0)
q = 1'b0; // reset has highest priority
else if (set == 0)
q = 1'b1;
else if (clk == 1)
q = data_in; // woke up by the clock edge
else
q = q;
end
When the block is woken by negedge rst or negedge set, the matching rst == 0 / set == 0 branch is taken first. But a negedge also covers 1→x: then rst == 0 evaluates to x (treated as false) and control falls through to a later branch. Adding if (clk == 1) confirms that the trigger really was a rising clock edge before data_in is taken; otherwise q holds its value. Synthesizable style normally just writes else q <= data_in; and switches to non-blocking assignment.由 negedge rst 或 negedge set 喚醒時,對應的 rst == 0 / set == 0 分支會先成立。但 negedge 也包含 1→x:此時 rst == 0 為 x(視為假),會落到後面的分支;加上 if (clk == 1) 確認觸發源真的是時脈上升緣,才取 data_in,否則保持 q。可合成風格通常直接寫 else q <= data_in; 並改用非阻擋式賦值。negedge rst や negedge set で起動された場合は、対応する rst == 0 / set == 0 の分岐がまず成立する。しかし negedge には 1→x も含まれ、そのとき rst == 0 は x(偽とみなされる)となって後ろの分岐に落ちてしまう。if (clk == 1) を加えることで、起動要因が本当にクロックの立ち上がりであることを確かめてから data_in を取り込み、そうでなければ q を保持する。合成可能な書き方では通常 else q <= data_in; と書き、ノンブロッキング代入に変える。
event; referenced directly in that module, or in other modules by hierarchical dereferencing.只能在模組內用關鍵字 event 宣告;在該模組內可直接引用,其他模組則需透過階層式名稱取用。モジュール内でのみキーワード event により宣言する。そのモジュール内では直接参照でき、他のモジュールからは階層名で参照する。->.必須用事件觸發運算子 -> 明確觸發。イベントトリガ演算子 -> によって明示的に起動する。module top (clk, data, q);
input clk, data;
output q;
talker M1 (clk);
receiver M2 (q, data);
endmodule
module talker (clk);
input clk;
event do_it;
always @ (posedge clk)
-> do_it; // trigger
endmodulemodule receiver (q, data);
input data;
output q;
reg q;
always @ (top.M1.do_it) // hierarchical
q = data;
endmodule
The receiver has no clock port at all — it is synchronized to talker only through the hierarchical event name top.M1.do_it.receiver 根本沒有時脈埠 — 它只透過階層式事件名稱 top.M1.do_it 與 talker 同步。receiver にはクロック端子が一切ない。階層イベント名 top.M1.do_it だけで talker と同期している。
Suspends the activity flow until the expression is true (level-sensitive, unlike the edge-sensitive @). If the condition is already true, execution continues immediately.暫停活動流程直到運算式為真(屬於位準敏感,與邊緣敏感的 @ 不同)。若條件當下已經為真,則立即繼續執行。式が真になるまで動作フローを停止させる(エッジセンシティブな @ と異なり、レベルセンシティブである)。条件がすでに真であれば、ただちに実行を続ける。
data_out = 0; wait (enable) data_out = data_in;
In lhs = #d rhs; or lhs = @(ev) rhs; the RHS is evaluated immediately; only the update of the LHS waits.在 lhs = #d rhs; 或 lhs = @(ev) rhs; 之中,RHS 會立刻被計算;只有 LHS 的更新要等待。lhs = #d rhs; や lhs = @(ev) rhs; では、右辺はただちに評価される。待たされるのは左辺の更新だけである。
a = #5 b; // sample b now, write a at +5 c = d; // runs at +5 (blocked) e = @(bus) f; // sample f now, write e at next bus change g = h; // runs after e is written
initial begin
@(posedge clk)
g <= @(bus) k; // sample k, schedule
p <= q; // runs immediately
end
always begin
@(posedge clock)
g <= @(bus) k;
endThe equivalent of a = #5 b; is begin temp = b; #5 a = temp; end.a = #5 b; 等價於 begin temp = b; #5 a = temp; end。a = #5 b; は begin temp = b; #5 a = temp; end と等価である。
| Code程式碼コード | Behavior行為振る舞い |
|---|---|
assign y = a | b; | Zero-delay combinational.零延遲的組合邏輯。遅延ゼロの組合せ論理。 |
always @(a or b) y = a | b; | Same as above (complete sensitivity list).與上一列相同(敏感列表完整)。上と同じ(感度リストが完全な場合)。 |
assign #5 y = a | b; | Inertial delay: output follows after 5; pulses shorter than 5 are swallowed.慣性延遲:輸出延後 5 跟上;寬度小於 5 的脈衝會被吞掉。慣性遅延:出力は 5 遅れて追従し、幅が 5 未満のパルスは飲み込まれる。 |
always @(a or b) #5 y = a | b; | Waits 5, then evaluates with the values at that moment. Input changes during the wait are missed (the block is not at the @).先等 5,再用那一刻的值計算。等待期間的輸入變化會被漏掉(因為區塊不在 @ 上)。まず 5 だけ待ち、その時点の値で評価する。待っている間の入力変化は取りこぼす(ブロックが @ にいないため)。 |
always @(a or b) y = #5 a | b; | Evaluates at the change, writes 5 later. Changes during the wait are missed → output can get stuck at a stale value.在變化當下計算,5 之後才寫入。等待期間的變化會被漏掉 → 輸出可能卡在過時的值。変化した時点で評価し、5 後に書き込む。待機中の変化は取りこぼすため、出力が古い値のまま固定されることがある。 |
always @(a or b) y <= #5 a | b; | Transport delay: every change is scheduled and the block returns to @ immediately, so every pulse is reproduced 5 later.傳輸延遲:每一次變化都被排程,而且區塊立刻回到 @,所以每個脈衝都會在 5 之後忠實重現。伝送遅延:変化のたびにスケジュールされ、ブロックはただちに @ へ戻る。そのためすべてのパルスが 5 遅れて忠実に再現される。 |
To model a delayed combinational output, use assign #d (inertial) or y <= #d expr (transport). Delays inside a blocking always silently lose events.要描述帶延遲的組合輸出,請用 assign #d(慣性)或 y <= #d expr(傳輸)。把延遲寫在阻擋式的 always 裡會無聲無息地漏掉事件。遅延付きの組合せ出力をモデル化するには assign #d(慣性)か y <= #d expr(伝送)を使う。ブロッキングの always 内に遅延を書くと、警告もなくイベントを取りこぼす。
Problem: b = 0 throughout; all outputs have settled to 0 before t = 0. Input a goes 0→1 at t = 2 and 1→0 at t = 4. Sketch y for each of the six descriptions of §5.2.題目:b = 0 始終不變;所有輸出在 t = 0 之前都已穩定為 0。輸入 a 在 t = 2 由 0→1,在 t = 4 由 1→0。請畫出 §5.2 六種描述各自的 y 波形。問題:b = 0 は終始変わらない。すべての出力は t = 0 以前に 0 に落ち着いている。入力 a は t = 2 で 0→1、t = 4 で 1→0 と変化する。§5.2 の六つの記述それぞれについて y の波形を描け。
always @(a or b) y = a | b 這種寫法與第一列的 assign 完全相同。always @(a or b) y = a | b の形は、最初の assign の行とまったく同じである。reg_a = repeat (5) @ (negedge clock) reg_b;
begin // equivalent
temp = reg_b;
@ (negedge clock);
@ (negedge clock);
@ (negedge clock);
@ (negedge clock);
@ (negedge clock);
reg_a = temp;
endTest-bench example:測試平台範例:テストベンチの例:
module repeater;
reg clock;
reg reg_a, reg_b;
initial clock = 0;
always #5 clock = ~clock;
initial begin
#5 reg_a = 1;
#10 reg_a = 0;
#5 reg_a = 1;
#20 reg_a = 0;
end
initial #100 $finish;
initial begin
#10 reg_b = repeat (5) @ (posedge clock) reg_a;
end
endmodule
Problem: For the repeater module above, when does reg_b leave x, and to what value?題目:對於上面的 repeater 模組,reg_b 在什麼時候離開 x?變成什麼值?問題:上の repeater モジュールにおいて、reg_b はいつ x を脱し、どの値になるか。
reg_b becomes 1 at t = 55 — even though reg_a is 0 by then. The value was captured at t = 10.reg_b 在 t = 55 變成 1 — 即使此時 reg_a 已經是 0。因為值是在 t = 10 就被擷取下來的。reg_b は t = 55 で 1 になる。そのとき reg_a はすでに 0 であるが、値は t = 10 の時点で取り込まれているからである。
$monitor. ($display executes when it is encountered, i.e. before the non-blocking updates.)執行 $monitor。($display 則是在被執行到的當下就印出,也就是在非阻擋式更新之前。)$monitor を実行する。($display は出会った時点で実行される。つまりノンブロッキング更新の前である。)initial begin
a = 1;
b = 0;
a <= b;
b <= a;
$display("a=%b b=%b", a, b);
end
// display: a=1 b=0initial begin
a = 1;
b = 0;
a <= b;
b <= a;
$monitor("a=%b b=%b", a, b);
end
// monitor: a=0 b=1$display prints immediately, before the non-blocking swap takes effect. $monitor prints at the end of the time step (and whenever an argument changes afterwards), so it sees the swapped values. $strobe behaves like a one-shot $monitor.$display 當下就印出,此時非阻擋式的交換尚未生效。$monitor 在時間步結束時才印(之後只要引數改變也會再印),所以看到的是交換後的值。$strobe 的行為就像只觸發一次的 $monitor。$display はその場でただちに出力するため、ノンブロッキングの交換はまだ反映されていない。$monitor は時間ステップの終わりに出力し(以降は引数が変化するたびに出力する)、交換後の値を見る。$strobe は一回限りの $monitor のように振る舞う。
initial begin
x = 0;
x <= 1;
$display("D x=%b", x);
$strobe ("S x=%b", x);
#1 $display("D1 x=%b", x);
end
D x=0 (before the NBA update), S x=1 (end of time step 0), D1 x=1 (time 1).D x=0(非阻擋式更新之前)、S x=1(時間步 0 的結束)、D1 x=1(時間 1)。D x=0(ノンブロッキング更新の前)、S x=1(時間ステップ 0 の終わり)、D1 x=1(時刻 1)。
Constructs: conditional operator ?:, case / casex / casez, if ... else, loops (repeat, for, while, forever), disable, fork ... join.可用的建構:條件運算子 ?:、case / casex / casez、if ... else、迴圈(repeat, for, while, forever)、disable、fork ... join。使える構文:条件演算子 ?:、case / casex / casez、if ... else、ループ(repeat, for, while, forever)、disable、fork ... join。
Besides continuous assignments, ?: can be used in a procedural statement to assign a register:除了連續賦值之外,?: 也可以寫在程序式敘述中,對暫存器賦值:連続代入のほか、?: は手続き文の中でレジスタへの代入にも使える。
always @ (posedge clock) q = s ? a : b;
case requires a complete bit-wise match (including x and z), so the expression and items should have the same bit length.case 要求逐位元完全相符(包含 x 與 z),所以運算式與各個項目的位元寬度應該相同。case はビットごとの完全一致(x と z を含む)を要求するので、式と各項目のビット幅は揃えるべきである。default, not all values covered) in combinational logic synthesizes to a latch — avoid it.在組合邏輯中,不完整的 case 列表(沒有 default、也沒有涵蓋所有值)合成後會產生閂鎖(latch) — 應該避免。組合せ論理において不完全な case リスト(default がなく、すべての値も網羅していない)は、合成するとラッチになる — 避けること。always @ (a or b or c or d or select)
begin
case (select)
0: y = a;
1: y = b;
2: y = c;
3: y = d;
default: y = 1'bx;
endcase
end
| Statement敘述文 | Bits treated as don't care被當成 don't care 的位元ドントケアとして扱われるビット |
|---|---|
case | none — exact 4-value match沒有 — 四值完全比對なし — 4 値の完全一致 |
casez | z (and ?)z(以及 ?)z(および ?) |
casex | x, z (and ?)x、z(以及 ?)x、z(および ?) |
always @ (opcode or a or b or c) casex (opcode) 4'b1zzx : begin // don't care bits 2:0 out = a; end 4'b01?? : begin // bits 1:0 are don't care out = b; end 4'b001? : begin // bit 0 is don't care out = c; end default : begin out = 4'b0; end endcase
Don't-care also applies to x/z in the expression. An unknown opcode 4'bxxxx matches the first item and hides the bug in simulation. Prefer casez with ?.don't care 也會套用到運算式中的 x/z。一個未知的 opcode 4'bxxxx 會比中第一個項目,在模擬中把錯誤掩蓋掉。建議改用 casez 搭配 ?。ドントケアは式の側の x/z にも適用される。未知のオペコード 4'bxxxx が最初の項目に一致してしまい、シミュレーション上でバグが隠れる。? を用いた casez を使うほうがよい。
Problem: For the casex above, what is out when opcode = (a) 4'b1010 (b) 4'b0110 (c) 4'b0011 (d) 4'b0001 (e) 4'b0x10?題目:對於上面的 casex,當 opcode = (a) 4'b1010 (b) 4'b0110 (c) 4'b0011 (d) 4'b0001 (e) 4'b0x10 時,out 各是什麼?問題:上の casex について、opcode = (a) 4'b1010 (b) 4'b0110 (c) 4'b0011 (d) 4'b0001 (e) 4'b0x10 のとき、out はそれぞれ何になるか。
(a) a — matches 1zzx. (b) b — 01??. (c) c — 001?. (d) 0 — default. (e) b — bit 2 is x and treated as don't care, so 0x10 first matches 01?? (items are checked in order).(a) a — 比中 1zzx。(b) b — 01??。(c) c — 001?。(d) 0 — 落到 default。(e) b — 第 2 位元是 x,被當成 don't care,所以 0x10 會先比中 01??(項目是依序比對的)。(a) a — 1zzx に一致する。(b) b — 01??。(c) c — 001?。(d) 0 — default に落ちる。(e) b — ビット 2 が x でドントケア扱いになるため、0x10 はまず 01?? に一致する(項目は順に照合される)。
x or z.真:數值為 1。假:0、x 或 z。真:数値が 1。偽:0、x、z。else pairs with the nearest if; use begin ... end in nesting to clarify.else 會配對到最近的 if;巢狀時請用 begin ... end 明確界定。else は最も近い if と対応する。入れ子にするときは begin ... end で範囲を明示すること。always @ (posedge clk) begin // if reset is asserted if (reset == 1'b0) begin counter <= 4'b0000; // if counter is enabled and up count is asserted end else if (enable == 1'b1 && up_en == 1'b1) begin counter <= counter + 1'b1; // if counter is enabled and down count is asserted end else if (enable == 1'b1 && down_en == 1'b1) begin counter <= counter - 1'b1; // if counting is disabled end else begin counter <= counter; // redundant code end end
The last branch is redundant in a clocked block: a register holds its value anyway. (In a combinational block, the missing else would create a latch.) Note the priority: up-count wins if both up_en and down_en are asserted.在時脈同步的區塊中,最後一個分支是多餘的:暫存器本來就會保持原值。(但在組合區塊中,少了 else 反而會產生閂鎖。)另外注意優先權:若 up_en 與 down_en 同時有效,上數會贏。クロック同期ブロックでは最後の分岐は冗長である。レジスタはもともと値を保持するからである。(ただし組合せブロックでは else が欠けるとラッチが生じる。)優先順位にも注意する。up_en と down_en が同時に有効なら、アップカウントが勝つ。
parameter memory_size = 1024;
...
word_address = 0;
repeat (memory_size)
begin
memory[word_address] = 0;
word_address = word_address + 1;
endbegin
reg [7:0] temp;
counter = 0;
temp = a;
while (temp)
begin
if (temp[0]) counter = counter + 1;
// or: counter = counter + temp[0];
temp = temp >> 1;
end
endreg [15:0] data;
integer k;
for (k = 4; k > 0; k = k - 1)
begin
data[k+10] = 0;
data[k+2] = 1;
endreg [3:0] k; for (k = 0; k <= 15; k = k + 1) ...
(a) Which bits of data does the left for loop write? (b) What happens with the right-hand loop? (c) With a = 8'b1011_0010, what does the while loop leave in counter, and how many iterations does it run?(a) 左邊的 for 迴圈會寫到 data 的哪些位元?(b) 右邊的迴圈會發生什麼事?(c) 若 a = 8'b1011_0010,while 迴圈最後留在 counter 的值是多少?總共跑了幾次?(a) 左の for ループは data のどのビットに書き込むか。(b) 右のループでは何が起こるか。(c) a = 8'b1011_0010 のとき、while ループが counter に残す値はいくつか。また何回繰り返すか。
(a) k = 4, 3, 2, 1 → data[14:11] = 0, data[6:3] = 1; other bits untouched.(a) k = 4、3、2、1 → data[14:11] = 0、data[6:3] = 1;其餘位元不受影響。(a) k = 4、3、2、1 → data[14:11] = 0、data[6:3] = 1。他のビットは変化しない。
(b) Infinite loop: a 4-bit k wraps from 15 to 0, so k <= 15 is always true. Use integer k or a 5-bit index.(b) 無窮迴圈:4 位元的 k 會從 15 繞回 0,所以 k <= 15 永遠成立。請改用 integer k 或 5 位元的索引。(b) 無限ループになる。4 ビットの k は 15 から 0 へ折り返すため、k <= 15 が常に真になる。integer k か 5 ビットのインデックスを使うこと。
(c) Four 1-bits → counter = 4. The loop stops when temp becomes 0, i.e. after 8 shifts (the MSB is 1).(c) 共有四個 1 → counter = 4。迴圈在 temp 變成 0 時停止,也就是移位 8 次之後(因為 MSB 是 1)。(c) 1 のビットは四つ → counter = 4。ループは temp が 0 になった時点、すなわち 8 回シフトした後に止まる(MSB が 1 のため)。
module Add_prop_gen (sum, c_out, a, b, c_in); // behavioral model output [3:0] sum; output c_out; input [3:0] a, b; input c_in; reg [3:0] carrychain; wire [3:0] g = a & b; // carry generate (continuous assignment) wire [3:0] p = a ^ b; // carry propagate (continuous assignment) always @ (a or b or c_in) // event "or" begin : carry_generation // named block integer i; #0 carrychain[0] = g[0] + (p[0] & c_in); // #0 needed for simulation for (i = 1; i <= 3; i = i + 1) begin carrychain[i] = g[i] | (p[i] & carrychain[i-1]); end end wire [4:0] shiftedcarry = {carrychain, c_in}; // concatenation wire [3:0] sum = p ^ shiftedcarry; // summation (low 4 bits) wire c_out = shiftedcarry[4]; // carry out, bit select endmodule
g, p, sum and c_out use continuous assignments; only the carry chain is computed in an event-triggered always block — exactly the "mix every level" example.結構:g、p 與 sum、c_out 用連續賦值;只有進位鏈用事件觸發的 always 計算 — 正是「混合各種層次」的範例。構成:g、p と sum、c_out は連続代入で書き、桁上げ連鎖だけをイベント起動の always で計算している — まさに「各レベルを混在させる」例である。shiftedcarry = {carrychain, c_in} is 5 bits wide: [0]=c_in, [i]=c_i. sum = p ^ shiftedcarry keeps only the low 4 bits, so $s_i = p_i \oplus c_i$.shiftedcarry = {carrychain, c_in} 是 5 位元:[0]=c_in、[i]=c_i。sum = p ^ shiftedcarry 只取低 4 位元,所以 $s_i = p_i \oplus c_i$。shiftedcarry = {carrychain, c_in} は 5 ビット幅で、[0]=c_in、[i]=c_i である。sum = p ^ shiftedcarry は下位 4 ビットだけを取るので $s_i = p_i \oplus c_i$ となる。g[0] + (...) instead of |: the result is only one bit wide, and $g_i$ and $p_i$ can never both be 1, so the addition is equivalent to an OR.第一行寫 g[0] + (...) 而非 |:結果只有 1 位元,而 $g_i$ 與 $p_i$ 不可能同時為 1,所以「加」等於「OR」。最初の行で | ではなく g[0] + (...) と書いている。結果は 1 ビットしかなく、$g_i$ と $p_i$ が同時に 1 になることはないので、「加算」は「OR」と等価である。#0 pushes this block slightly later within the same time step, making sure the continuous assignments to g and p have already been updated.#0 讓這個區塊在同一時間步內延後一點執行,確保 g、p 的連續賦值已先更新。#0 によってこのブロックは同じ時間ステップ内で少し後ろにずれ、g と p への連続代入が先に更新されることが保証される。integer i may only be declared inside a named block.在 named block 內才可以宣告區域變數 integer i。integer i のようなローカル変数は、名前付きブロックの中でしか宣言できない。initial
begin : clock_loop
clock = 0;
forever
#10 clock = ~clock;
end
initial
#100 disable clock_loop;module find_first_one (a, trigger, i);
input [15:0] a;
input trigger;
output [4:0] i;
reg [4:0] i;
always @ trigger
begin : search
for (i = 0; i < 16; i = i + 1)
if (a[i] == 1) disable search;
end
endmoduleThe slide writes a bare disable; — disable must name a block or task, so the loop is wrapped in the named block search. If no bit is set, i ends at 16 (which is why it is 5 bits wide).投影片上只寫了一個光禿禿的 disable; — disable 必須指名一個區塊或任務,所以這裡把迴圈包在具名區塊 search 內。若沒有任何位元為 1,i 最後會停在 16(這就是它要宣告成 5 位元的原因)。スライドには disable; とだけ書かれている — disable はブロックまたはタスクを名指ししなければならないので、ここではループを名前付きブロック search で囲んでいる。どのビットも 1 でなければ i は 16 で終わる(5 ビット幅にしてある理由がこれである)。
| Question問題問い | Answer解答答え |
|---|---|
always vs forever? | always is a module-level construct that starts at time 0 and can never be stopped. forever is a statement placed inside a procedural block; it can be started later (after initialisation) and terminated with disable.always 是模組層級的建構,從時間 0 開始,而且永遠停不下來。forever 則是放在程序區塊內的敘述;它可以晚一點才開始(例如初始化之後),也可以用 disable 終止。always はモジュールレベルの構文で、時刻 0 から始まり決して止められない。forever は手続きブロック内に置く文であり、初期化の後など遅れて開始でき、disable で終了させられる。 |
disable vs $finish? | disable terminates only a named block or task; execution continues after it and the rest of the simulation goes on. $finish ends the entire simulation.disable 只終止一個具名區塊或任務;之後的敘述會繼續執行,整個模擬也照常進行。$finish 則是結束整個模擬。disable は名前付きブロックかタスクを終了させるだけで、その後の実行は続き、シミュレーション全体も続行する。$finish はシミュレーション全体を終了させる。 |
join waits for completion of all threads.join 會等到所有執行緒都完成。join はすべてのスレッドの完了を待つ。fork #50 a = 1; #100 a = 0; #150 a = 1; #200 a = 0; join
begin #50 a = 1; #50 a = 0; #50 a = 1; #50 a = 0; end
Both produce the same waveform: in fork all delays are measured from the fork start (absolute), in begin ... end they accumulate (relative).兩者產生相同的波形:在 fork 中所有延遲都是從 fork 起點算起(絕對時間),在 begin ... end 中則會累加(相對時間)。どちらも同じ波形になる。fork では遅延がすべて fork の開始時点から数えられ(絶対時間)、begin ... end では累積する(相対時間)。
A race: the target value is ambiguous because different assignments occur in the same time step in no defined order.競爭(race):由於不同的賦值在同一個時間步內以未定義的順序發生,目標的值因此模稜兩可。競合(race)とは、異なる代入が同一の時間ステップ内で順序の定まらないまま起こるために、対象の値が曖昧になることをいう。
fork #150 a = b; #150 c = a; join
fork a = #150 b; c = #150 a; join
Left: both statements execute at t = 150; c gets either the old or the new a depending on thread order. Right: both RHS are sampled at t = 0 (intra-assignment delay), so c deterministically receives the original a — the pair behaves like a shift.左邊:兩個敘述都在 t = 150 執行;c 拿到舊的還是新的 a,取決於執行緒的順序。右邊:兩個 RHS 都在 t = 0 取樣(賦值內延遲),所以 c 必定拿到原本的 a — 這一對敘述的行為就像一次移位。左:どちらの文も t = 150 で実行される。c が古い a を得るか新しい a を得るかはスレッドの順序次第である。右:どちらの右辺も t = 0 で採取される(代入内遅延)ため、c は必ず元の a を受け取る — この二文はシフト動作のように振る舞う。
(a) When does each of the two blocks in §8.1 finish? (b) Rewrite the racing fork using non-blocking assignments. (c) Given(a) §8.1 的兩個區塊各在什麼時候結束?(b) 請用非阻擋式賦值改寫有競爭的那段 fork。(c) 給定(a) §8.1 の二つのブロックはそれぞれいつ終了するか。(b) 競合する fork をノンブロッキング代入で書き直せ。(c) 次のコードについて、
fork #10 x = 1; begin #5 y = 2; #10 y = 3; end #20 z = x + y; join
at what time does the join complete and what is z?join 在什麼時刻完成?z 又是多少?join は何時刻で完了し、z はいくつになるか。
(a) Both finish at t = 200 (fork: longest thread; begin: 50×4).(a) 兩者都在 t = 200 結束(fork:取最長的執行緒;begin:50×4 累加)。(a) どちらも t = 200 で終了する(fork は最長スレッド、begin は 50×4 の累積)。
(b) fork #150 a <= b; #150 c <= a; join — both RHS read in the same time step before any update, so c gets the old a.(b) fork #150 a <= b; #150 c <= a; join — 兩個 RHS 都在同一時間步、任何更新之前讀取,所以 c 拿到的是舊的 a。(b) fork #150 a <= b; #150 c <= a; join — どちらの右辺も同一時間ステップ内で、更新が起こる前に読まれるので、c は古い a を得る。
(c) x = 1 at 10, y = 2 at 5 and 3 at 15, z evaluated at 20 → z = 4; join completes at t = 20.(c) x 在 10 變成 1,y 在 5 變成 2、在 15 變成 3,z 在 20 計算 → z = 4;join 在 t = 20 完成。(c) x は 10 で 1、y は 5 で 2、15 で 3 になり、z は 20 で評価される → z = 4。join は t = 20 で完了する。
= sequential; <= sample-all-then-update.= 依序執行;<= 先全部取樣再一起更新。= は逐次実行。<= は全部採取してから一括更新。<=; combinational → =.時脈同步邏輯用 <=;組合邏輯用 =。クロック同期論理は <=、組合せ論理は =。assign/deassign: reg only.assign/deassign:只能用於 reg。assign/deassign:reg のみ。force/release: net or reg, highest priority.force/release:net 或 reg 都可以,優先權最高。force/release:net でも reg でも可、優先度は最高。# delay, @ edge/event, wait level.# 是延遲,@ 是邊緣/事件,wait 是位準。# は遅延、@ はエッジ/イベント、wait はレベル。event + ->.具名事件:用 event 宣告,用 -> 觸發。名前付きイベント:event で宣言し -> で起動。y <= #d = transport; assign #d = inertial.y <= #d 是傳輸延遲;assign #d 是慣性延遲。y <= #d は伝送遅延、assign #d は慣性遅延。casez safer than casex.casez 比 casex 安全。casez は casex より安全。$display before NBA update.$display 在非阻擋式更新之前印出。$display はノンブロッキング更新より前に出力。$monitor/$strobe at end of step.$monitor/$strobe 在時間步結束時印出。$monitor/$strobe は時間ステップの終わりに出力。initial, # delays, named events, wait, assign/deassign and force/release (tool dependent), repeat intra-assignment delays, forever with delays, fork ... join.initial、# 延遲、具名事件、wait、assign/deassign 與 force/release(視工具而定)、repeat 形式的賦值內延遲、帶延遲的 forever、fork ... join。initial、# 遅延、名前付きイベント、wait、assign/deassign と force/release(ツール依存)、repeat 形式の代入内遅延、遅延付きの forever、fork ... join。