VerilogHDLが実行される順序
論理合成と違ってシミュレーションには文を実行する順序があるので、最初にこれを説明する。特に注意しなければならないのは、並行に実行される文同士でどちらが先に実行されるかによって結果が変わってしまう(レーシングracingという)書き方を絶対にしないことである。もしレーシングが起きる記述が内在していると、シミュレータを変更したり、文の順序を入れ替えたときに、シミュレーション結果が合わなくなる恐れがある。
◇
Verilogのモジュールの中は、かならずinitialかalwaysで始まる複数の文をbegin-endでくくったブロック(または1個の文)で書かれている。その実行順序の規則は次のとおり。
(1) 普通はモジュールの先頭から順に実行するが、initialやalwaysのブロック同士の実行順序は言語仕様上不確定である。(つまり並列に実行されるイメージ)
(2) initialブロックは1回だけ、alwaysブロックは無限に繰り返し実行される。
(3) ブロックはbegin-endでくくられ、この中の文は上から順に実行する。(fork-joinブロックもあるが、ややこしくなるのでここでは触れない)
(4) 遅延(#)かイベント待ち(@)があると、その条件が成立するまで次の文の実行を保留する。
例)#(10) 文; 10単位時間後に文を実行する。
@(posedge clk) 文; 信号clkが0→1に変化したら文を実行する。
(5) 実行する文がなくなるか、または$finishまで来たらそこで終了する。
initial文だけならば$finishがなくても終了するが、always文があると$finishを書かないと終了しないことに注意。
例えば次の記述は、右の欄にしめす順に実行される
module(input clk);
always begin @(posedge clk)【1】 @(posedge clk)【2】 end
always begin @(posedge clk)【3】 end
initial begin 【4】 @(posedge clk)【5】 @(posedge clk)【6】 @(posedge clk)【7】 #10 $finish; end
initial begin 【8】 end
endmodule |
実行順序: (【4】【8】) ↓clk立ち上がりエッジ (【1】【3】【5】) ↓clk立ち上がりエッジ (【2】【3】【6】) ↓clk立ち上がりエッジ (【1】【3】【7】) ↓10単位時間後 終了 注:()内の実行順序は不確定である |
論理合成向けのRTL記述はすべて次のように書いてあるので、シミュレーションではclkの立ち上がり毎に各alwaysのbegin-end内が繰り返し実行されることになる。
always @(posedge clk) begin ... end |
◇◇
initial,alwaysブロック同士の実行順序は不確定と言ったが、他ブロックの変数を自ブロックで代入することはあるので、代入結果がブロックの実行順序に依存しないようにノンブロッキング代入というものがある。
・ブロッキング代入(=)は現在の右辺の値を計算したらすぐ左辺に代入する
・ノンブロッキング代入(<=)は、現在の全always,initial文の右辺を計算した後に、左辺に代入する。
例えば次の記述で現在a=1,b=2とするとクロック立ち上がり後、ブロッキング代入ではb=aとc=bの実行順序に依存してb=1,c=2になるのかb=c=1になるのか不確定だが、ノンブロッキング代入ではb=1,c=2に確定する。
× // ブロッキング代入 always @(posedge clk) b = a; always @(posedge clk) c = b; |
○ // ノンブロッキング代入 always @(posedge clk) b <= a; always @(posedge clk) c <= b; |