パラメタライズで回路を汎用化する

(1)ここで学ぶ内容

概要
  • HDLライブラリ構築に有効な、回路のパラメータ化を学ぶ
  • パラメータ化した回路の呼び出しや論理合成を学ぶ

目標
  • parameter宣言を用いて回路をパラメータ化できる
  • パラメタライズを用いて、ライブラリを構築できる
  • パラメータ化した回路を呼び出して利用できる
  • パラメータ化した回路の論理合成ができる

修了判定1

パラメータ化した回路をシミュレーションして動作確認する


修了判定2

パラメータ化した回路を異なる2つの手順で論理合成し、生成された回路の違いを確認する

HDLを使って回路記述に使えるライブラリをつくれます。ライブラリにはビット幅や上限値などを設定できるようにしておくと便利です。これを実現できるのがパラメタライズです。

(2)回路のパラメータ化

graph TD subgraph パラメタライズされた加算器モジュール ADDER[module ADDER
parameter WIDTH = 8;] end ADDER -.->|インスタンス化 #\(8)| INST1[ADDER 8bit版] ADDER -.->|インスタンス化 #\(16)| INST2[ADDER 16bit版] ADDER -.->|インスタンス化 #\(32)| INST3[ADDER 32bit版] style ADDER fill:#e3f2fd,stroke:#1976d2,stroke-width:2px; style INST1 fill:#e8f5e9,stroke:#388e3c; style INST2 fill:#e8f5e9,stroke:#388e3c; style INST3 fill:#e8f5e9,stroke:#388e3c;

HDLライブラリによる再利用

         HDLライブラリ(DB)
               │  │
        ┌──────┴──┴──────┐
        │    チップ全体   │
        │ ┌──────┐┌─────┐│
        │ │ライブ ││ライブ││
        │ │ラリの ││ラリの││
        │ │回路   ││回路  ││
        │ └──────┘└─────┘│
        │ ┌──────────────┐│
        │ │  新規設計の  ││
        │ │  回路ブロック││
        │ └──────────────┘│
        └────────────────┘
  • 一度設計した回路をライブラリとして登録・再利用
  • 新規設計する部分を最小化して開発期間を短縮

ライブラリの回路ブロック(汎用化)

     ┌─────────────────────┐
     │   ライブラリの回路  │
     │  ─/ ─┬──[+]──┬─ /─ │
     │  ─/ ─┘       └─ /─ │
     │  ─/ ─────────── /─ │
     │                     │
     │  ─ [カウンタ]─[=MAX]─│
     └─────────────────────┘
  • ビット幅・上限値などをパラメータ化
  • 任意の設定で呼び出せる汎用ライブラリを構築
パラメタライズとは

回路をパラメータ化して汎用性を持たせることをパラメタライズと呼ぶ。パラメータ化できる代表的な値:

  • 入出力のビット幅・本数
  • 演算のビット幅
  • 回路動作時の上限値・下限値

大規模な回路の設計ではHDLを使っても多くの期間を必要とします。そこで一度設計した回路をHDLライブラリとして登録しておき、再利用して設計期間を短縮することもできます。

(3)パラメータによるライブラリの定義

回路をパラメータ化する例を紹介します。ここではビット幅をパラメータ化した2to1のセレクタを記述してみます。

このセレクタは選択信号SELが0のときDLを、1のときDHを選択し、Qに出力します。

Nビット 2to1 セレクタ
SEL2TO1
  N   ┌─────────┐  N
 ─/─>│DL       │─/─>
  N  │      Q  │
 ─/─>│DH       │
     │SEL      │
  ───┤         │
     └─────────┘

SEL=0 → Q=DL
SEL=1 → Q=DH

module SEL2TO1( DL, DH, SEL, Q );
//パラメータ宣言でビット幅をパラメータ化
parameter        N = 4;
input    [N-1:0] DL, DH;
input            SEL;
output   [N-1:0] Q;
reg      [N-1:0] Q;

always @( DL or DH or SEL ) begin
    if ( SEL==1'b1 )
        Q <= DH;
    else
        Q <= DL;
end

endmodule

parameter宣言を使ってビット幅をパラメータ化します。ここでは入出力のビット幅を N とし、デフォルト値として N = 4(4ビット)を設定しています。ビット幅を宣言している部分を N を使って記述します。

セレクタの機能はalways文で記述しています。回路をパラメータ化してもこの部分はごく普通の記述になります。

(4)ライブラリの呼び出し

パラメータに値を設定するのは、ライブラリを呼び出す側の記述で行います。ここではNビットの2to1セレクタを呼び出して、8ビットのセレクタを記述します。

8ビット 2to1 セレクタ
SEL8
  8   ┌─────────┐  8
 ─/─>│DL       │─/─>
  8  │      Q  │
 ─/─>│DH       │
     │SEL      │
  ───┤         │
     └─────────┘
呼び出し側(SEL8)の記述
module SEL8( DL, DH, SEL, Q );
input  [7:0]  DL, DH;
input         SEL;
output [7:0]  Q;

// '#'でパラメータに値を設定
SEL2TO1 #(8) S1( DL, DH, SEL, Q );

endmodule
ライブラリ側(N=8に展開された状態)
//Nビット 2to1 セレクタのライブラリ
module SEL2TO1( DL, DH, SEL, Q );
//パラメータ宣言でビット幅をパラメータ化
parameter        N = 8;  // ← 8が渡される
input    [N-1:0] DL, DH; // [7:0]
input            SEL;
output   [N-1:0] Q;       // [7:0]
reg      [N-1:0] Q;
    // その他の宣言や初期化処理
パラメータ値の設定方法

モジュール名とインスタンス名の間に #(値) を記述し、ライブラリのパラメータに値を与えます。# は遅延を示す記号ですが、モジュール接続で用いるとパラメータへの値付けになります。

モジュール名 #(パラメータ値) インスタンス名( ポート接続 );
もふねこ

もふねこ:
パラメタライズ(parameter)を使えるようになると、同じコードを何度も書かなくてよくなるから、一気に「プロの設計者」っぽくなるよ🐾!
#(8)みたいに呼び出す時に値を渡すだけで、8ビット版や16ビット版が簡単に作れちゃうなんて魔法みたいだよね!

(5)複数パラメータのライブラリ

パラメータを複数もつライブラリを記述することができます。ここでは0からN-1までの値をとるN進のカウンタを記述してみます。出力のビット数はカウント値に依存しますのでこれもパラメータ化します。

N進カウンタ
(0, 1, ‥, N-1)
N_COUNT
     ┌─────────┐
     │RES      │  L
───> │      Q  ├─/─>
     │         │
───> │>CK      │
     └─────────┘
N進カウンタの定義部
module N_COUNT( CK, RES, Q );
parameter    N = 16; // N進
parameter    L =  4; // ビット幅
input        CK;
input        RES;
output [L-1:0] Q;
reg    [L-1:0] Q;

always @( posedge CK or posedge RES ) begin
    if ( RES )
        Q <= 0;
    else if ( Q== N-1 )
        Q <= 0;
    else
        Q <= Q + 1;
end

endmodule
24進カウンタとして呼び出す
// N=24(24進カウンタ), L=5(5ビット)
N_COUNT #(24, 5) C24( CK, RES, Q );

#のあとの括弧内にコンマで区切って複数の数値を記述します。この値はそれぞれ記述した順番にパラメータN、パラメータLの値になります。

24進カウンタに展開された定義部(N=24, L=5の場合)
module N_COUNT( CK, RES, Q );
parameter    N = 24; // N進
parameter    L =  5; // ビット幅
input        CK;
input        RES;
output [4:0] Q;
reg    [4:0] Q;

always @( posedge CK or posedge RES ) begin
    if ( RES )
        Q <= 0;
    else if ( Q== 23 )
        Q <= 0;
    else
        Q <= Q + 1;
end

endmodule

(6)論理合成時の対応

パラメータ化したライブラリを論理合成する場合には特別な対応が必要です。前ページで紹介したN進カウンタを論理合成する場合を考えてみます。

⚠️ 直接 compile すると問題が起きる

このカウンタのパラメータ宣言のデフォルト設定は「4ビット、16進」です。そのまま論理合成ツールの compile コマンドを実行すると、生成したネットリストは4ビット16進カウンタになってしまします。

❌ 直接 compile(NG)

パラメータ化したライブラリ
    n_count.v
        │
    compile
        │
      ネットリスト
   ⚠️ 4ビット、16進のまま

✅ analyze → compile(OK)

n_count.v    count24.v(呼び出し側)
    │               │
  analyze         compile
    │               │
  WORKライブラリ ←─┘
                    │
                  ネットリスト
               ✅ 5ビット、24進

パラメータ化したライブラリを論理合成する場合には、analyze コマンドを用います。analyze すると記述はWORKライブラリに格納されます。これは論理合成ツール上に構築された概念上のライブラリです。

analyze した後、呼び出し側の記述を論理合成します。呼び出し側はモジュール接続の記述になっています。この場合、下の階層であるN進カウンタはWORKライブラリから読み出され、呼び出し側と共に論理合成されます。生成されたネットリストは記述通りの5ビット24進カウンタになります。

合成スクリプトの例(count24.scr)
define_design_lib WORK -path "./WORK"
    ‥ ライブラリを保存するディレクトリを設定しています
analyze -library WORK -f verilog n_count.v
    ‥ n_count.vをライブラリとして読み込みます
current_design COUNT24
    ‥ 論理合成対象をCOUNT24にして
compile
    ‥ 論理合成します

最初に論理合成したn_count.vは、FFが4つの、ただの16進カウンタになってしまいました。パラメータ化した回路をそのまま論理合成しても、デフォルト値として宣言した値を用いて、回路を生成してしまいます。
そこで、以上のような合成スクリプトを用いて、「HDLライブラリ」として論理合成すればパラメタライズの論理合成がうまくいきます。

(7)修了判定 1

設問

本文の中で紹介したN進カウンタをシミュレーションして動作確認する。

ファイル名
N進カウンタn_count.v
カウンタの呼び出し部count24.v
テストベンチcount24_test.v
COUNT24(5ビット、24進カウンタ)
         N_COUNT
RES ───>│RES      │
        │      Q  ├──5──> Q
CK ────>│>CK      │
        └─────────┘

シミュレーション結果(24進カウンタ Q の遷移)

     0  CK=0  RES=0  Q= x
   500  CK=1  RES=0  Q= x
  1000  CK=0  RES=1  Q= 0
  1500  CK=1  RES=0  Q= 0
  2000  CK=0  RES=0  Q= 0
  2500  CK=1  RES=0  Q= 1
  3000  CK=0  RES=0  Q= 1
  3500  CK=1  RES=0  Q= 2
  4000  CK=0  RES=0  Q= 2
  4500  CK=1  RES=0  Q= 3
  
  
 19000  CK=0  RES=0  Q=17
 19500  CK=1  RES=0  Q=17  (0x11)
 20000  CK=0  RES=0  Q=18  (0x12)
 20500  CK=1  RES=0  Q=18
 
 25000  CK=0  RES=0  Q=23  (0x17)
 25500  CK=1  RES=0  Q= 0  ← 24でリセット(24進)
 26000  CK=0  RES=0  Q= 0
 26500  CK=1  RES=0  Q= 1
波形図(24進カウンタ)
        |0     |5000  |10000 |15000 |20000 |25000
CK  ───┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─
RES ────┐   └──────────────────────────────────
Q   xx│00│01│02│03│‥│0e│0f│10│11│12│‥│17│00│01

0x00〜0x17(0〜23)を一周して0x00に戻る(24進動作を確認)

(8)修了判定 2

設問

本文の中で紹介したN進カウンタを論理合成する。以下の手順で行い結果を確認する。

  1. N進カウンタ(n_count.v)を論理合成し、デフォルト設定の4ビットカウンタになることを、回路図のFFの数で判断する。
  2. カウンタの呼び出し部(count24.v)を合成スクリプト(count24.scr)を用いて論理合成する。まず合成スクリプトを参照してパラメタライズに対応した論理合成になっていることを確認する。
  3. count24.vを論理合成して、5ビット、24進カウンタになっていることを、回路図のFFの数で判断する。
ファイル名
N進カウンタn_count.v
カウンタの呼び出し部count24.v
カウンタの合成スクリプトcount24.scr
COUNT24(5ビット、24進カウンタ)
         N_COUNT
RES ───>│RES      │
        │      Q  ├──5──> Q
CK ────>│>CK      │
        └─────────┘

n_count.v を直接 compile(デフォルト値)

  • FF数:4個(4ビット)
  • カウント数:16進(0〜15)
  • パラメータのデフォルト値 N=16, L=4 が使われる
  • 出力 Q[3:0]

count24.scr で analyze → compile

  • FF数:5個(5ビット)
  • カウント数:24進(0〜23)
  • 呼び出し側の #(24, 5) が反映される
  • 出力 Q[4:0]