Verilog入門と深堀 — HDL設計・検証・合成の実践ガイド

はじめに — Verilogとは何か

Verilogはハードウェア記述言語(Hardware Description Language: HDL)の一つで、デジタル回路の構造・振る舞い・並列性をテキストで記述し、シミュレーションや合成を可能にします。1984年にPhil MoorbyらがGateway Design Automationで開発し、後にIEEE標準(IEEE 1364)として整備されました。ASICやFPGAの設計現場で広く使われ、SystemVerilog(IEEE 1800)へと発展して高位の検証機能や言語機能が追加されています。

歴史と標準化の流れ

  • 1980年代:Gateway社でVerilogが開発され、商用ツールとともに普及。

  • 1995年:Verilog-95がIEEE 1364-1995として標準化。

  • 2001/2005年:言語の拡張が続き、機能追加が行われた(一般にはVerilog-2001/2005と呼ばれることが多い)。

  • SystemVerilog(機能拡張+検証機能)はAccelleraを経てIEEE 1800として標準化され、検証や制約ランダム化、クラスベースの記述などを導入。

Verilogの基本概念

Verilogは、ハードウェアの並列性を表現するための構文要素を多数持ちます。主要概念を整理すると以下の通りです。

  • モジュール(module): 設計ブロックの単位。入出力ポートを持ち、階層設計が可能。

  • ネットワイヤ(wire)とレジスタ(reg): wireは接続を、regは手続き的ブロック内で値を保持するために使われる。SystemVerilogでは'logic'が導入され、より明確な型付けが可能。

  • 連続代入(assign): combinationalな接続を表す。

  • 初期化・手続きブロック(initial, always): 時間経過や信号変化に応じた動作を記述する。

  • シミュレーションと合成: 記述できる文法のうち、合成ツールがサポートするサブセットが『合成可能(synthesizable)』である。

モジュールの構造とポート宣言

モジュールはモジュール名、ポートリスト、内部宣言と常に構造化されます。例:

<!-- 例示: モジュール定義(説明用。実際はコメントアウトしないで実装) -->

module fifo (clk, rst, din, dout, wr_en, rd_en);

input clk;

/* ... */

endmodule

Verilog-2001以降はANSIスタイルのポート宣言(型と幅を同時に記述)もサポートされ、可読性が向上します。

データ型と信号の扱い

代表的な型とその用途:

  • wire: 接続用。連続代入(assign)やポートに使う。

  • reg: proceduralブロック内で値を割り当てるための変数(ただし名前に'レジスタ'が付くが必ずしも物理レジスタとは限らない)。

  • integer, real: テストベンチや計算用。合成対象には不向き。

  • ベクタ([N:0]): ビット幅の指定。符号あり/無しを指定する'signed'修飾子はVerilog-2001で導入。

手続きブロック(initial, always)と感度リスト

alwaysブロックはシミュレーションにおけるイベント駆動モデルの中心です。感度リストによっていつブロックが評価されるかを決定します。例:

  • always @(posedge clk) ... : クロック立ち上がりで評価(同期回路の記述)。

  • always @(*) ... : combinationalロジックの推奨形(感度リストの自動化)。

常に合成可能な記述になるよう、ルールに従って書くことが重要です(例:combinational回路では全出力を必ず割り当てる)。

ブロッキング代入とノンブロッキング代入

Verilogの最も重要なポイントの一つが、"=" によるブロッキング代入と "<=" によるノンブロッキング代入の使い分けです。

  • ブロッキング代入(a = b;): 逐次的に評価され、次の文は代入完了後に実行される。組合せロジックの記述や初期化に使われるが、同期ロジックで誤用するとレースが発生する。

  • ノンブロッキング代入(a <= b;): 同一クロックエッジでの並列的な更新をモデル化。フリップフロップの挙動を正しく記述するために同期ブロック内では常に使用することが推奨される。

不適切な使い分けはシミュレーションと合成で異なる結果を生み、デバッグを非常に難しくします。

連続代入と結線

assign文は常に評価される連続割当を行い、combinationalな関係を表現します。wireと組み合わせ、論理演算や多重化を記述します。

遅延と時間制御

Verilogは遅延(#delay)や時間制御をサポートしますが、合成対象の記述に遅延を入れるべきではありません。遅延は主にテストベンチやモデリングで使い、合成ツールは合成用の遅延を無視します。タイミングは合成後のSTA(静的タイミング解析)で評価します。

生成(generate)とパラメータ化

generate文とパラメータ(parameter)を使うことで、汎用的なモジュールを作り、繰り返し構造や幅可変の回路を記述できます。これはIP作成やモジュール再利用で強力です。

合成可能な記述と非合成(シミュレーション専用)の違い

すべてのVerilog文が合成可能なわけではありません。ファイル入出力や$display、遅延、リアル型、振る舞いモデルなどは主にシミュレーション用です。合成を目的とする場合は、合成ツール(Vivado, Intel Quartus, Synopsys DC, Yosysなど)のドキュメントに従ってサブセットで記述する必要があります。

検証(テストベンチ)手法とSystemVerilogの関係

テストベンチではinitialブロック、$monitor、$display、ファイル入出力、ランダム刺激などを用いて設計を検証します。大規模な検証にはSystemVerilogが一般的で、クラスベースのUVM(Universal Verification Methodology)などを利用して、再利用性やスケーラビリティの高い検証環境を構築します。Verilog単独でも小〜中規模のテストベンチは作成できますが、近年はSystemVerilogが主流です。

シミュレーションのイベントモデルとデルタサイクル

Verilogのシミュレーションはイベント駆動で、"デルタサイクル"という概念によりゼロ時間の依存関係を解決します。これにより同一時刻での複数の更新順序が決まり、ブロッキング/ノンブロッキングの使い分けが意味を持ちます。デルタサイクルの理解は複雑なタイミングバグ(レース条件)を診断する際に不可欠です。

代表的なツールチェーン

  • シミュレータ: ModelSim/QuestaSim(Mentor/Siemens)、Synopsys VCS、Cadence Xcelium、オープンソースのIcarus Verilog。

  • 合成ツール: Synopsys Design Compiler、Xilinx Vivado、Intel Quartus、オープンソースではYosys(特にASICフローの一部として注目)。

  • 形式検証/静的解析: SVA(SystemVerilog Assertions)やLPV/CDCツールなど。

実務でのよくある落とし穴とベストプラクティス

  • 同期回路では常にノンブロッキング代入を使う。これにより意図しない順序依存を避ける。

  • combinationalブロックでは全ての出力が常に割当されるようにする(Latches生成を避ける)。

  • 感度リストを手動で書く場合、"always @(*)"を使って網羅的にする。

  • 合成可能かどうかを常に意識し、シミュレーション専用の記述はテストベンチに限定する。

  • モジュールのインターフェースは明確にし、パラメータで幅や深さを制御して再利用性を高める。

  • コードの可読性とドキュメント化を行い、設計レビューを定期的に実施する。

学習の進め方とおすすめの実践課題

入門後は以下の順で学習すると理解が深まります。

  • 基本構文と簡単な組合せ回路の設計(加算器、デコーダ、マルチプレクサ)。

  • 同期回路(フリップフロップ、レジスタファイル、カウンタ)。

  • FIFOやUARTなどのインターフェース設計で状態機械の実装と検証を体験する。

  • テストベンチ作成、波形確認、カバレッジ計測。

  • 合成してFPGAに書き込み、実機で動作確認を行う。

まとめ

Verilogはデジタル回路設計の基礎技術であり、シミュレーション、合成、検証のための重要な言語です。言語の歴史的背景や標準化、合成可能な記述ルール、ブロッキング/ノンブロッキング代入の意味、デルタサイクルなどの概念を理解することで、堅牢で再利用可能なハードウェア設計が可能になります。最近ではSystemVerilogやUVMの利用が増え、検証手法が高度化しているため、これらを併せて学ぶことが推奨されます。

参考文献