1. はじめに
Verilogにおけるalways
文の役割とは?
デジタル回路の設計で広く使われているハードウェア記述言語「Verilog HDL」では、always
文が非常に重要な役割を果たします。Verilogでは、ハードウェアの動作をソフトウェアのように記述するのではなく、「どのような条件下で信号がどのように変化するか」を定義する形で回路を表現します。その中で、always
文は一定の条件が発生したときに特定の動作を行うという処理を記述するための基本的な構文です。
always
文はなぜ必要なのか?
Verilogには、主に2種類の回路の動作を記述する方法があります。
- 組み合わせ回路:入力が変化すると即座に出力も変化する回路
- 順序回路:クロック信号などのタイミングに合わせて出力が変化する回路
これらを記述する際、単なるassign
文だけでは複雑な条件分岐や状態の記憶を記述することができません。ここでalways
文が登場します。
たとえば、複数の条件分岐がある論理や、フリップフロップを用いた記憶動作を記述するには、always
文を使って制御構造(if
文やcase
文)を記述する必要があります。
よく使われるalways
文のパターン
always
文にはいくつかの代表的な使い方があり、それぞれ設計したい回路の種類によって使い分けられます。
always @(*)
→ 組み合わせ回路の記述に使われるalways @(posedge clk)
→ クロックの立ち上がりに同期する順序回路の記述always @(posedge clk or negedge rst)
→ 非同期リセット付きの順序回路など、より複雑な制御構造
このように、Verilogの中核をなす構文であるalways
文を理解することは、ハードウェア設計者としての第一歩と言っても過言ではありません。
本記事の目的
この記事では、Verilogにおけるalways
文について、構文の基礎から応用的な使い方、注意すべき落とし穴、SystemVerilogにおける拡張までを幅広く解説していきます。
always
文の正しい書き方が知りたい- 論理合成でエラーになる原因がわからない
=
と<=
の使い分けに困っている- よくある初学者のミスを防ぎたい
このような疑問や悩みを持つ方にとって、実用的で理解しやすいガイドとなることを目指します。
2. always
文の基本構文と種類
always
文の基本構文
Verilogのalways
文は、特定の条件(感知リスト)に基づいて処理を繰り返し実行するための記述方式です。基本的な構文は以下の通りです。
always @(感知リスト)
begin
// 実行する処理
end
この構文で重要なのは、「感知リスト」と呼ばれる部分です。これは、「どの信号が変化したときにこのブロックを実行するか」を定義する場所です。
always @(*)
の使い方(組み合わせ回路)
組み合わせ回路では、入力が変わるたびに出力も即座に変わる必要があります。このような場合には、感知リストとして @(*)
を使用します。
always @(*) begin
if (a == 1'b1)
y = b;
else
y = c;
end
このように記述することで、a
, b
, c
のいずれかが変化すると always
ブロックが実行され、出力 y
が再計算されます。
@(*)
を使うメリット
- 全ての入力信号を自動で感知リストに含めてくれる
- 感知リストの書き忘れによる論理シミュレーションと合成結果の不一致を防げる
always @(posedge clk)
の使い方(順序回路)
順序回路では、クロック信号に同期して状態が変化します。このときは、感知リストに posedge clk
を指定します。
always @(posedge clk) begin
q <= d;
end
この場合、クロックの立ち上がり(posedge)に合わせて、d
の値が q
にラッチされます。<=
はノンブロッキング代入で、順序回路では一般的にこの形式が使われます。
posedge
と negedge
posedge
:立ち上がりエッジで動作negedge
:立ち下がりエッジで動作
用途に応じて適切なエッジを選びましょう。
always @(posedge clk or negedge rst)
(非同期リセット付き)
複雑な回路では、リセット機能が必要なことがよくあります。非同期リセット付きの記述は以下のようになります。
always @(posedge clk or negedge rst) begin
if (!rst)
q <= 1'b0;
else
q <= d;
end
このように記述すると、リセット信号が「0」になると即座に q
がリセットされ、それ以外のときはクロックに同期して d
を保持します。
組み合わせ回路と順序回路の使い分け
回路の種類 | 使用するalways | 特徴 |
---|---|---|
組み合わせ回路 | always @(*) | 入力に応じて即座に出力が変化 |
順序回路 | always @(posedge clk) | クロックに同期して動作する |
3. always
文における代入の種類
Verilogには2種類の代入方法がある
Verilogのalways
文内では、2つの異なる代入演算子が使われます。
=
:ブロッキング代入(blocking assignment)<=
:ノンブロッキング代入(non-blocking assignment)
この違いを理解しないままコーディングを進めると、意図しない動作やシミュレーション結果と合成結果の不一致につながるため、非常に重要なポイントです。
ブロッキング代入(=
)とは?
ブロッキング代入は、1つのステートメントが完了してから次のステートメントに進むという「順番に処理される」代入方式です。ソフトウェア的な制御に近いイメージです。
always @(*) begin
a = b;
c = a;
end
この場合、a = b
が先に実行され、その結果を使って c = a
が実行されます。変数の代入順がロジックに直接影響するため、順番に気をつける必要があります。
主な用途
- 組み合わせ回路での制御構造(
if
,case
)内 - 状態を保持しない処理
ノンブロッキング代入(<=
)とは?
ノンブロッキング代入は、すべてのステートメントが同時に評価され、同時に反映されるという「並行的な動作」を表現する代入方式です。ハードウェアの並列性を意識した代入となります。
always @(posedge clk) begin
a <= b;
c <= a;
end
この場合、a <= b
と c <= a
は同時に評価され、クロックエッジ後に一括して反映されます。そのため、c
には前のクロック周期のa
の値が入ります。
主な用途
- 順序回路(レジスタ、フリップフロップ)
- 複数の状態を正確に保持・伝搬したい場合
ブロッキングとノンブロッキングの違いまとめ
特徴 | ブロッキング代入 (= ) | ノンブロッキング代入 (<= ) |
---|---|---|
実行順序 | 上から順に処理 | 全体を評価し、同時に反映 |
主な使用場面 | 組み合わせ回路 | 順序回路 |
代入結果の反映タイミング | すぐに反映される | クロックエッジ後に反映 |
よくあるミス | 意図しないラッチの生成 | 値が更新されない・伝搬されない |
混在させるとどうなる?
=
と <=
を同一ブロックや同一信号に対して混在させることは原則避けるべきです。以下のような記述は、シミュレーションでは正しく見えても、合成後のハードウェアではバグの原因になります。
always @(posedge clk) begin
a = b;
a <= c;
end
この例では、a
に対して2回代入しており、順序も代入方法も混在しています。これにより、どの値が最終的に記録されるか不明確になります。
使い分けの指針
- 組み合わせ回路では
=
を使う(always @(*)
の中) - 順序回路(クロック同期)では
<=
を使う(always @(posedge clk)
の中)
このルールに従って記述するだけでも、多くのミスを防ぐことができます。

4. always
文を使用する際の注意点とよくあるミス
センシティビティリストの記述ミス
感知すべき信号を正しく記述しないとバグの温床に
Verilogでは、always
文の感知リスト(@(...)
)にどの信号の変化に反応するかを明記する必要があります。以下はセンシティビティリストに一部の信号しか書かれていない例です。
always @(a) begin
if (b)
y = 1'b1;
else
y = 1'b0;
end
このコードでは、b
の変化には反応しません。そのため、b
が変わっても出力y
が更新されないというバグが発生します。
解決策:@(*)
を使う
センシティビティリストの書き忘れや漏れを防ぐためには、以下のように @(*)
を使うのが推奨されます。
always @(*) begin
if (b)
y = 1'b1;
else
y = 1'b0;
end
@(*)
は、文中で参照しているすべての信号を自動的に感知リストに含めてくれるため、保守性・安全性の面でも優れています。
意図しないラッチの生成
if文・case文の記述漏れがラッチを生む
以下のように、条件分岐の中ですべてのケースに代入が行われないと、合成ツールは「値を保持する必要がある」と判断し、ラッチ(Latch)が生成されます。
always @(*) begin
if (enable)
y = d; // enableが0のとき、yの値が未定義のまま
end
このコードは一見正しそうに見えますが、enable
が0
のときにy
の値が更新されないため、前回の値を保持するラッチが自動的に挿入されてしまいます。
解決策:すべての条件で代入を行う
always @(*) begin
if (enable)
y = d;
else
y = 1'b0; // 必ず代入される
end
このようにどの条件でもy
に明示的に値を与えることで、ラッチの生成を防ぐことができます。
条件分岐が複雑すぎる
複雑なif
文やcase
文を使って出力信号を制御している場合、条件が網羅されていないと未定義動作や論理抜けが発生する可能性があります。
よくあるケース:case文でdefaultがない
always @(*) begin
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
// 2'b11 の場合に値が未定義になる可能性
endcase
end
このように、case
文で全ての条件を網羅していないと、意図しない値が出力されることがあります。
解決策:default節の追加
always @(*) begin
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = 1'b0; // セーフティネットとして必須
endcase
end
default
節を用意することで、どのような入力が来ても出力が定義されるようになり、設計の安全性が向上します。
複数の信号を同時に制御するときの注意
1つのalways
文で複数の信号を制御する場合、代入の順序や記述漏れにより、意図しない依存関係が生まれることがあります。複雑な回路では、制御対象を明確に分けるために複数のalways
ブロックに分割することも検討すべきです。
よくある落とし穴のまとめ
問題 | 原因 | 解決策 |
---|---|---|
出力が更新されない | 感知リストに必要な信号が含まれていない | @(*) を使って自動感知にする |
ラッチが生成される | 一部の条件で代入が行われていない | else やdefault で必ず値を代入する |
未定義動作が起こる | case 文で全条件を網羅していない | default を必ず記述する |
制御が複雑になりすぎる | 複数の信号を1つのブロックで同時に扱っている | 信号ごとにalways ブロックを分けるなど整理 |
5. SystemVerilogにおけるalways
文の拡張
always_comb
:組み合わせ回路専用
概要
always_comb
は、従来の always @(*)
とほぼ同じ動作をしますが、明示的に「組み合わせ論理である」ことを示す構文です。
always_comb begin
y = a & b;
end
主なメリット
- 感知リストを自動生成
- 意図しないラッチ生成時にツールが警告を出してくれる
- 以前に定義された
同名変数
との干渉を防ぐ
使用例(Verilogとの比較)
// Verilog
always @(*) begin
y = a | b;
end
// SystemVerilog
always_comb begin
y = a | b;
end
always_ff
:順序回路専用(フリップフロップ)
概要
always_ff
は、クロック駆動の順序回路を記述するための構文で、posedge clk
や negedge rst
のようなトリガ条件を必須とします。
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
主なメリット
<=
ノンブロッキング代入のみ許可(=
はエラー)- センシティビティリストが正しいかツールがチェック
- 順序回路であることが一目でわかるため、保守性が高い
always_latch
:ラッチ回路専用
概要
always_latch
は、意図的にラッチ(レベルトリガ)を記述する場合に使う構文です。ただし、意図しないラッチ生成を防ぐためにも、使用は最小限にとどめるべきです。
always_latch begin
if (enable)
q = d;
end
注意点
- 条件分岐によって代入がスキップされるとき、ラッチ生成が明示される
- 設計上どうしても必要なとき以外は、極力使用を避ける方がよい
SystemVerilog構文の使い分け
構文 | 用途 | Verilogでの対応 | 特徴 |
---|---|---|---|
always_comb | 組み合わせ回路 | always @(*) | 感知リスト自動生成、ラッチ検出が可能 |
always_ff | フリップフロップ | always @(posedge clk) | クロック同期、代入ミスを防ぐ |
always_latch | ラッチ | always @(*) (条件分岐不備) | 意図的なラッチ記述に限定、誤用検出が可能 |
これからの開発ではSystemVerilogの利用が主流に
近年の開発現場では、可読性や安全性の観点からSystemVerilog構文が推奨されるケースが増えています。ツールによる構文解析機能も進化しており、always_ff
やalways_comb
を使うことで「書いたつもりだけど動かない」ミスを未然に防げるようになっています。
特に、チーム開発や大規模な設計案件では、構文によって回路の意図が明確になるため、コードレビューや保守の効率が飛躍的に高まります。
6. FAQ:always
文に関するよくある質問と回答
この章では、VerilogやSystemVerilogでのalways
文の使用に関して、実際によくある疑問や検索されやすいポイントに対して、簡潔かつ正確に答えていきます。初心者から中級者まで、設計現場で「あるある」な悩みをカバーします。
Q1. always
文内でif
文とcase
文、どちらを使えばいいの?
A. 基本的には、条件分岐のパターン数や複雑さによって使い分けます。
- 条件が2~3個程度 →
if
文が簡潔で読みやすい - 明確に分岐する複数の状態がある場合 →
case
文の方が見やすく、意図も伝わりやすい
また、case
文を使うとすべてのケースを網羅することが前提となるため、設計ミスを減らす効果もあります。
Q2. センシティビティリストを省略すると何が起きるの?
A. 感知リストを省略、または一部の信号しか含まれていない場合、一部の信号が変化してもalways
文が実行されず、出力が更新されない可能性があります。
これにより、シミュレーションと実機で動作が異なるという厄介な問題を引き起こします。
これを防ぐには、@(*)
または SystemVerilog の always_comb
を使うのがベストです。
Q3. always
文で意図しないラッチが生成されるのはなぜ?
A. 条件分岐(if
やcase
)の中ですべての条件で出力変数に代入していない場合、合成ツールは「値を保持する必要がある」と判断し、自動的にラッチを生成します。
例(NG):
always @(*) begin
if (en)
y = d; // en==0のとき、yは保持される
end
解決策:
always @(*) begin
if (en)
y = d;
else
y = 1'b0; // 必ず代入される
end
Q4. =
と<=
は混在させてはいけないの?
A. 基本的には同一のalways
ブロック内で混在させるべきではありません。
- 組み合わせ回路 →
=
(ブロッキング代入) - 順序回路 →
<=
(ノンブロッキング代入)
特に、同じ信号に対して=
と<=
の両方を使うとシミュレーションでは動くが、実機では予期せぬ動作をすることがあります。
原則:
1つの信号に対しては、一貫した代入方式を使うこと。
Q5. always_ff
と従来のalways @(posedge clk)
の違いは?
A. 動作的にはほぼ同じですが、コードの安全性と可読性の面でalways_ff
が優れています。
比較項目 | always @(posedge clk) | always_ff |
---|---|---|
センシティビティ | 自分で明示する必要あり | 自動でチェックされる |
誤った代入(=など) | コンパイルは通る可能性も | エラーとして検出される |
可読性 | 回路の意図が曖昧になる | 「順序回路」と明確に分かる |
Q6. always
文で複数の信号を制御しても大丈夫?
A. 原則としては可能ですが、制御対象が多くなると保守性やバグの発見が難しくなるため、必要に応じて複数のalways
ブロックに分けるのが望ましいです。
分割の目安:
- 出力ごとに動作が独立している場合
- 同期制御と非同期制御が混在している場合
Q7. 組み合わせ回路なのに<=
を使うとどうなる?
A. 通常はシミュレーション上では動作しますが、論理合成時に想定外の回路が生成されることがあります。基本的に、組み合わせ回路ではブロッキング代入(=
)を使いましょう。
7. まとめ
always
文はVerilog設計の基礎にして最重要構文
Verilogによるハードウェア設計において、always
文は組み合わせ回路と順序回路の両方を記述できる強力なツールです。設計の幅を広げるだけでなく、制御の流れやタイミングを明確に記述できるため、初心者からプロフェッショナルまで必須の知識と言えるでしょう。
本記事では、以下のポイントを中心に詳しく解説しました。
本記事の振り返り
always @(*)
とalways @(posedge clk)
の違いと使い分け=
(ブロッキング代入)と<=
(ノンブロッキング代入)の違いと正しい使用シーン- センシティビティリストの書き方やラッチ生成の回避など、よくあるミスへの対処法
- SystemVerilogで導入された
always_comb
、always_ff
、always_latch
による安全な設計スタイル - 実際の設計現場でよくある疑問をFAQ形式で整理し、実用的な回答を提示
記述の精度が品質を決める
ハードウェア記述は「書いた通りに回路ができる」ため、コード上の些細なミスがそのまま物理的な不具合に直結する可能性があります。特にalways
文は動作の中心を担う構文なので、記述の正確性、代入方法、条件分岐の網羅性など、細かい部分にまで注意を払うことが大切です。
今後のステップ:より高レベルな設計へ
always
文を正しく使えるようになれば、次は以下のようなステップに進むことができます。
- 状態遷移回路(FSM:Finite State Machine)の設計
- パイプライン構造やストリーミング処理の実装
- IPコアの作成やFPGAへの実装
また、SystemVerilogやVHDLといった他のHDL言語にも対応することで、より広い設計現場でも通用するスキルが身につきます。
最後に:設計者としての心構え
回路設計は「動けばOK」ではなく、「正確に動作し、将来の拡張や変更にも耐えられる構造」が求められます。
本記事を通して、always
文に関する基本的な知識だけでなく、安全で堅牢な設計のための考え方も感じ取っていただけたなら幸いです。