Menu

Verilog forループ:コンパイル時に展開される

Verilogのforループがソフトウェアの兄弟分とどう違うか。実行時に反復実行されるのではなく、合成ツールによって並列ハードウェアに展開される。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

ソフトウェアそっくり

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コピーを作る」と言っている、振る舞い的ではありません。

クイックな区別:

  • 手続き的foralways内):単一の振る舞いブロック内の文を展開。
  • Generate foralways外):構造的構造(インスタンス、assign文、名前付きブロック)全体を複製。

複製しているものに合うものを使ってください。

forが光る場面:vector演算

ループは、vectorの各ビットに同じ演算を行うときに最高です。population count、パリティ、バイト反転、ルックアップテーブル生成:

32反復、各々1ビット代入を行います。手書きの32本のwire代入を書き出すよりずっと読みやすいです。合成ツールはきれいに展開します。

whilerepeatforever

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

whilerepeatforeverは狭いケース(特に定数カウントで時計付き本体のrepeat)でのみ合成可能です。testbenchでは有用なツールです。合成可能RTLでは、カウント付きforと明示的な状態機械を優先してください。

testbenchでの手続き的for

testbenchでは、forループはソフトウェアと同じように振る舞います。自由に使ってください:

ネストされたループは2つの2ビット入力のすべての組み合わせを掃引します。シミュレータは反復を順次実行します。展開の心配なし。testbenchは合成されません。

よくある間違い

非定数境界で合成可能コードにforループを使う。 合成ツールは拒否します。境界がランタイムなら、カウンタと状態機械を構築してください。

ループ本体が並列ハードウェアになることを忘れる。 本体に乗算器がある64反復ループは64個の並列乗算器です。おそらく望むものではありません。広いデータパスでは、1つの乗算器を作って順次に供給してください。

integer iiという名の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ループを使ってください。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める