みねっちょのマイコン関係ブログ

組込開発系フリーソフトやハードの情報発信ブログ

効率的な Verilog HDL のコーディング手法

サイト内 Google 検索:


最終更新: 2021-05-30

本記事では、効率的な Verilog HDL のコーディング手法を、具体的なパーツ毎にルールを紹介します。

目次:

効率的な Verilog HDL とは:

効率的=生産性の高い HDL ソースコードとは、次の様な事を指します。

  • 少ない労力で多くの機能を実現出来る: 行数は少ない
  • 読み易くデバッグ効率が良い: パターンに則っており、行数は少ない
  • 行数 (コメントを除く) が可読性良く少なく、デザインレビューが容易
    (同じ機能の場合に、1万行と10万行のどちらがレビュー容易か考えよ。
     10万行のソースコード レビューは実質的に不可能。)
  • コメントはきちんと記載されている
  • 論理合成の時間が短い: 推定はさせない、部分的にボトムアップ
  • 周波数と電力のトレードオフが容易: 論理の平坦化が必要な部分の module 化
  • ASIC と FPGA モックアップの切り替えが容易で、かつバグが入り込まない:
    = DesignWare 部や、テーブルルックアップ ROM 化可能部の module 化

具体的なパーツ毎のルール:

フリップ フロップ:

  • 全て事前パーツ化し、本体 HDL 内では FF に always 文は使用しない
  • レジスタは全てパーツの module を呼び出して1本1行に記載
  • ノーマル、セット付、リセット付、イネーブル付毎に全ビット数を予め用意
    (論理合成ツールが parameter 文を理解出来る場合はビットはパラメタライズ可)
  • FF パーツは全て事前合成し、トップダウン合成時に FF 推定させてはならない
    (トップダウン合成で FF / ラッチ推定が有る場合は HDL のバグと認識する)
    (always_comb でも else if 文を放置するとラッチが合成されるので注意)
  • always_ff 記述を使用すると、Icarus Verilog Ver. 11 以降が必要となるので避ける

代入:

  • assign 文を使用し1行で記載 (発生箇所毎に wire 宣言して記載する方針も有る)
  • wire 宣言や reg 宣言内での代入はしない (可読性の向上と、不具合の防止)
    (C 言語の変数宣言部で代入を行うのは行儀作法が悪いのと同じ考え方)
  • reg 宣言部で定数を初期値として代入して良いのは、テストベンチのみ
  • wire 宣言は文中の発生箇所毎に宣言して回路も記述するという設計方針も有るが、全体として方針を統一すること。

マルチプレクサ:

  • 2入力の場合は条件 (三項) 演算子「?  :」を使用して assign 文1行で記載
  • 3入力以上の場合は always 文に caseZ 記述と ? を使用して真理値表で記載
  • caseZ は必ず人間が見て One Hot となるよう記述し、2行マッチさせない
  • caseZ 内の最終行には、フェイルセーフ用の「default:」を記載する
  • 3入力以上の場合には、条件 (三項) 演算子「?  :」のネストで記載してはならない
  • 3入力以上の場合には、if 記述のネストで記載してはならない
  • always 文のセンシティビティ リストには「*」(Verilog 2001形式) を使用
    (労力の削減と、記載漏れによる FF 合成不具合の防止)
  • always @* 記述内での右辺値の function は評価されないので注意すること
  • always_comb 内の else if 文を放置するとラッチが生成される場合があるので注意

算術演算ユニット:

  • ビット数が少ない場合には、assign 文と算術演算子で1行に記載
  • ビット数が多い場合には、事前に DesignWare 単位にパーツ module 化して1行で記載
  • パーツ module 内は ASIC では DesignWare で、FPGA ではそれと等価な LPM で実現
    (LPM: Library of Parameterized Modules、メガファンクションの1種)

    www.synopsys.com


    www.pldworld.com

  • パーツ内の DesignWare のアーキテクチャは人間が選びインスタンシエートする
    (CLA の方式や Booth のアルゴリズムを合成ツールに検討させない)
  • マルチサイクル化は事前に人間が判断しておくこと
  • 各パーツは事前に論理合成しておく
  • トップダウン論理合成時に DesignWare や メガファンクションの推定をさせない
    (計算機能力の無駄になるだけでなく、良質な回路が合成されない事が多い)

シフタ、ビット拡張、アライナ:

  • assign 文とシフト演算子で1行に記載
  • 連接演算子「{ }」の入れ子と繰り返し記述を利用して効率的に記載する
  • バレルシフタの場合は、繰り返し記述で2つ連接して右シフトし上位は切り捨てる
  • 算術シフトの場合は、MSBを繰り返し記号で拡張してから右シフトする
  • 符号拡張は MSB を繰り返し記述で拡張した物と下位を連接する

大規模デコーダ

  • always 文と caseZ 記述で and-or 論理に記載
  • フィールド毎に「`define」定義を使用して可読性を向上する
  • 合成時に「論理の平坦化 (set_flatten) 」を指定可能な様にmodule 化しておく
  • 平坦化が適切に行われないと、動作周波数が上がらない
  • 複数行マッチが有る場合は、module の事前の個別合成で真理値表圧縮を検討する
    (and-or に平坦化した状態を維持した論理 (真理値表) 圧縮は、PLA圧縮とも呼ばれる)
    (PLA: Programmable Logic Array)
  • FPGA 専用設計の場合は ROM 化可能に記載する

大規模な組み合わせ論理回路:

  • FPGA の場合にテーブル ルックアップ ROM 化出来る部分は module 化しておく

シーケンサ

  • フリップ フロップ1個 (モジュール化) と、前記のデコーダ1個 (caseZ) で実現

マイクロコード:

  • 逐次実行型か Next Address 指定型かを事前に検討しておく
  • 逐次実行型の飛び込みアドレスは opcode (の一部) を左シフトして生成する
  • 「`define」定義を使用して記述するのが楽ではあるが、移植性は下がる
  • 汎用 ROM 化可能にすれば移植性は上がるが、労力は掛かる

小規模 ROM:

  • ASIC では論理回路に合成してしまうが、FPGA ではメモリに割り当てる
    (高周波数を求めない場合は、FPGA でも論理回路に合成して良い)

【論理回路設計の目次へ戻る】