Menu

Verilog always block:組み合わせと順序ロジック

alwaysブロックの動作、組み合わせalways @(*)とクロック同期always @(posedge clk)の違い、各々がどんなハードウェアを生成するかを決めるルール。

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

振る舞いVerilogの主役

assignは単一式の組み合わせロジックを記述します。if/elsecase、メモリが必要になると、alwaysに手を伸ばします。alwaysブロックは特定のsignalが変わるたびに再実行される手続きコードです。再実行のトリガとなるsignalが 感応リスト です。

最もよく見る2つのalwaysの形:

  1. always @(*) - ブロック内で読まれる 任意の signalが変わるたびに再実行。組み合わせロジックを構築。
  2. always @(posedge clk) - clkの立ち上がりエッジのみで再実行。クロック同期の順序ロジック(flip-flop)を構築。

他の形(@(a or b)@(negedge clk)@(posedge clk or negedge reset_n))もありますが、上の2つが書くことになる合成可能ブロックのほとんどを占めます。

組み合わせalways @(*)

3つの注目点:

  • outreg always内で代入されるものはすべてregでなければなりません。このキーワードは「これはflip-flop」を意味するわけではなく、ここでは単に「手続きブロックから書き込まれる」を意味するだけです。
  • always @(*) *は「読まれるものが変わったら起きろ」と言います。シミュレータが感応リストを自動的に把握します。リストを手書きできます(always @(sel))が、@(*)の方が安全です。signalを見落とすことが古典的なバグ源だからです。
  • クロックなし。 このブロックは組み合わせロジックを記述します。合成器はselから直接outを計算するロジックを生成します。flip-flopもクロックピンも不要です。

defaultケースは構文上はオプションですが、意図上はオプションではありません。省略すると、未指定の入力値でoutが前の値を保持することになり、意図しない ラッチ が合成されます。常にdefaultを入れてください。

順序always @(posedge clk)

組み合わせ版との重要な違い:

  • always @(posedge clk) ブロックはclkの立ち上がりエッジでのみ再実行します。エッジの間は何も起きません。
  • 非blocking代入<= クロック同期ブロック内では、これが正しい演算子です。「countをtime stepの終わりに新しい値にスケジュールする」という意味で、flip-flopの振る舞いそのものです。理由と代替はBlocking vs Non-blockingで扱います。
  • defaultは不要。 ifは両方の分岐(resetとnot-reset)をカバーします。ラッチのリスクなし。

合成器はこの形(クロック同期、非blocking代入)を見て、4ビットregister(4個のflip-flop)に加えて、count + 1を計算する組み合わせロジックと、resetかインクリメントかを選ぶmuxを生成します。

合成上の違い

同じmoduleソースが、alwaysブロックの形によってまったく異なる2種類のハードウェアを記述できます:

ブロックハードウェア
always @(*) y = expr;純粋な組み合わせロジック。メモリなし。
always @(posedge clk) y <= expr;flip-flop。クロックサイクル毎にexprを捕捉。
always @(*) if (en) y = expr;ラッチ - 通常はバグ。「else」のケースで古い値を保つ。
always @(posedge clk) if (en) y <= expr;イネーブル付きflip-flop。enが高のときだけ捕捉。

3つ目がラッチの罠です。ラッチは入力がアサートされていないときに出力を保持する透過型のメモリセルで、特定の設計では有用ですが、偶然作ると大抵バグです。ほとんどの合成ツールは、要求していないラッチを推論したときに大きく警告します。警告はエラーとして扱ってください。

感応リストのバリエーション

あまり一般的でない感応リストもいくつか目にします:

  • always @(a or b or c) - 明示的なリスト。Verilog-2001は,セパレータを追加:always @(a, b, c)。どちらも動きます。
  • always @(posedge clk or negedge reset_n) - 非同期reset。ブロックはクロック立ち上がりエッジ または resetの立ち下がりエッジで動きます。resetを即時に効かせる必要があるとき、次のクロックを待たないために使います。
  • always @(negedge clk) - 立ち下がりエッジクロック。稀。立ち上がりではなく立ち下がりで捕捉する「立ち下がりトリガ」flip-flop用に一部の設計が使います。

新しい設計では、組み合わせにはalways @(*)、順序にはalways @(posedge clk)を優先してください。非同期resetは、設計が本当に必要とするときだけ使います。

2つのブロックは2つのハードウェア

同じmodule内の複数のalwaysブロックは独立で、各々が自分のハードウェアになります:

クロック同期ブロックはflip-flopレジスタを生成します。組み合わせブロックはXORゲートを生成します。横に並んで暮らし、互いを知りません。2つの出力はまったく異なるスケジュールで変化します。

alwaysブロックができないこと

魅力的に見えるが許されないこと:

  • wireへの代入:ターゲットはregでなければなりません。コンパイラが強制します。
  • 2つの別のalwaysブロックから同じregへの代入:シミュレーションで未定義動作になり、合成もされません。1つのsignalに1つのドライバ。
  • 同じ組み合わせブロック内でフィードバックループを作る形で同じsignalを読み書きalways @(*) x = x + 1;はシミュレータが解決できない遅延0ループです。

最初の2つはコンパイラがキャッチします。3つ目はシミュレーション時にハングとしてだけ現れることがあります。

次に読むもの

次のドキュメントInitial Blockでは、alwaysの兄弟、シミュレーション開始時に1度だけ実行されるブロックを扱います。testbenchの主役です。その後、クロック同期ブロックが意図通りに動くかどうかを決めるblocking vs 非blocking代入のルールを扱います。

よくある質問

Verilogのalways blockとは?

alwaysは感応リスト内のsignalが変わるたびに再実行される手続きブロックを導入します。2つのフレーバーがあります:always @(*)は組み合わせロジックを構築し(読まれた入力が変わるたびに再実行)、always @(posedge clk)は順序ロジックを構築します(clkの立ち上がりエッジごとに再実行)。alwaysブロックのbodyはifcasefor、手続き的代入を含めます。

always @(*)とalways @(posedge clk)の違いは?

always @(*)はブロック内で読まれる任意のsignalに感応し、メモリなしの組み合わせロジックを生成します。always @(posedge clk)clkの立ち上がりエッジのみに感応し、クロックサイクル毎に1回stateを捕捉するflip-flopを生成します。前者はクロックもregisterもなく、後者は両方を持ちます。

Verilogの感応リストとは?

@の後に置く、alwaysブロックがいつ再実行されるかを決めるsignalのリストです。@(*)は「ブロック内で読まれるすべてのsignal」の略です。@(posedge clk)clkの立ち上がりエッジのみで実行されます。@(posedge clk or negedge reset_n)はどちらかのイベントで実行され、非同期resetに使います。感応リストを間違えるのは、シミュレーション/合成の不一致の最も一般的な原因の1つです。

always block内でwireに代入できますか?

いいえ。alwaysブロックはreg(SystemVerilogではlogic)にしか代入できません。コンパイラがこれを強制します。手続きロジックの出力としてwireを使いたい場合は、中間regを宣言し、always内で駆動し、外側でassignでwireをそれから駆動するか、wireをregに変えてください。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める