ソフトウェアそっくり
VerilogのforループはCのコピーです:
for (i = 0; i < 8; i = i + 1) begin
// 本体
end
3つの同じ部分:初期化、条件、増分。条件が成り立つ限り本体が再実行されます。
testbenchでは、ソフトウェアから期待する通りに振る舞います。シミュレータは各反復を順に進めます:
4反復、4行の出力。驚きなし。
驚きは、合成可能コードにforループを入れたときに来ます。
展開
合成可能alwaysブロック内のforループは、ハードウェアでランタイムループに なりません 。合成ツールはelaboration時にそれを 展開(unroll) します。本体のN個のコピーに展開します。Nは反復数です:
これはループのように見えます。シミュレーションでは、シミュレータは確かに8反復をループします。合成では、ループはdata[0]からdata[7]の8並列チェックに展開され、すべて同時に起こります。合成ツールが見るのは:
count = 0;
if (data[0]) count = count + 1;
if (data[1]) count = count + 1;
if (data[2]) count = count + 1;
...
if (data[7]) count = count + 1;
…そしてその列をadder treeに変えます。ランタイムの振る舞いは「8ビットすべてを同時に見て1の数を数える」、1回の組み合わせスイープです。
含意:合成可能Verilogのforループは無料ではない。64反復ループはハードウェアで本体の64コピーになります。本体が複雑なら、大きな組み合わせブロックを作ったことになります。Nが小さい(数個から数十個)ときにループを使ってください。より大きなカウントでは、通常クロック付きカウンタと状態機械が欲しいでしょう。
定数境界が必要
合成ツールはNがelaboration時に既知のときだけループを展開できます。つまりループ境界は定数でなければなりません:
// 動く - 境界は定数
for (i = 0; i < 8; i = i + 1) ...
// 動く - 境界はparameter
for (i = 0; i < WIDTH; i = i + 1) ...
// 合成されない - 境界がランタイムsignalに依存
for (i = 0; i < dynamic_count; i = i + 1) ...
最後の形はシミュレーションでは動くかもしれませんが、合成ツールは拒否します。本当にランタイムカウントのループが必要なら、クロック同期状態機械とカウンタregisterで構築します。ハードウェアにはソフトウェアのような可変回数ループはありません。
generate for vs 手続き的for
別だが関連する構造がgenerate forで、genvarを使いalwaysブロックの外側に置かれます:
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : g
bit_inverter inv(.x(in[i]), .y(out[i]));
end
endgenerate
これはbit_inverterの8 インスタンス を刻印します(Module Instantiationで扱いました)。構造的です。「このサブmoduleの8コピーを作る」と言っている、振る舞い的ではありません。
クイックな区別:
- 手続き的
for(always内):単一の振る舞いブロック内の文を展開。 - Generate
for(always外):構造的構造(インスタンス、assign文、名前付きブロック)全体を複製。
複製しているものに合うものを使ってください。
forが光る場面:vector演算
ループは、vectorの各ビットに同じ演算を行うときに最高です。population count、パリティ、バイト反転、ルックアップテーブル生成:
32反復、各々1ビット代入を行います。手書きの32本のwire代入を書き出すよりずっと読みやすいです。合成ツールはきれいに展開します。
while、repeat、forever
forを超えて、Verilogには3つの他のループ構造があります。主にtestbench用:
// 条件が偽になるまで実行
while (~done) begin
@(posedge clk);
cycles = cycles + 1;
end
// N回実行 - カウンタが不要のときforよりシンプル
repeat (8) @(posedge clk);
// 永遠に実行 - クロックジェネレータ、モニタリングループ
always #5 clk = ~clk;
forever begin
@(posedge clk);
$display("count=%0d", count);
end
while、repeat、foreverは狭いケース(特に定数カウントで時計付き本体のrepeat)でのみ合成可能です。testbenchでは有用なツールです。合成可能RTLでは、カウント付きforと明示的な状態機械を優先してください。
testbenchでの手続き的for
testbenchでは、forループはソフトウェアと同じように振る舞います。自由に使ってください:
ネストされたループは2つの2ビット入力のすべての組み合わせを掃引します。シミュレータは反復を順次実行します。展開の心配なし。testbenchは合成されません。
よくある間違い
非定数境界で合成可能コードにforループを使う。 合成ツールは拒否します。境界がランタイムなら、カウンタと状態機械を構築してください。
ループ本体が並列ハードウェアになることを忘れる。 本体に乗算器がある64反復ループは64個の並列乗算器です。おそらく望むものではありません。広いデータパスでは、1つの乗算器を作って順次に供給してください。
integer iとiという名のregを混在させる。 2つは異なるスコープで、ループ内ではintegerが勝ちます。混乱を避けるためにわかりやすい名前を選んでください。
次に読むもの
これでVerilogが提供するすべての手続き的構造が揃いました。次の章は、デジタル設計者が実際に出荷するパターンに統合します。Clocked Logicはflip-flop、register、パイプライン、Finite State Machinesは複数の動作モードを持つコントローラの標準慣用句です。
よくある質問
Verilogのforループはどう動きますか?
構文的にはCのように見えます:for (i = 0; i < N; i = i + 1) statement;。しかし合成可能コードでは、ループはelaboration時に展開されます。合成ツールは本体のNコピーに展開します。ハードウェアにランタイムループカウンタもループもありません。testbenchではforループはソフトウェアの兄弟分のように振る舞います。シミュレータが順次ステップできるからです。
VerilogのforループはISO合成可能ですか?
はい、ただしループ境界がelaboration時に既知の定数の場合のみです。合成ツールはループをNコピーに展開します。境界がランタイムsignalに依存すると、ループは合成できません。クロック同期の順序設計に変換しなければなりません。
Verilogのforとgenerate forの違いは?
alwaysブロック内のforループは手続き的構造で、展開で合成されます。generate forループ(genvar付き)は明示的なelaboration時の構造で、構造的ハードウェアを刻印します。複数のmoduleインスタンス、複数のwire、複数のassign文。手続きブロック内ではforを、外ではgenerate forを使って構造を複製してください。
VerilogにwhileループはありますE?
あります - while (condition) statement;です。境界された反復回数で終了することを合成ツールが証明できる場合のみ合成可能です。実際にはまれなので、whileは主にtestbenchとシミュレーション専用コードに現れます。合成可能な反復にはカウント付きforループを使ってください。