パラメタライズで回路を汎用化する
(1)ここで学ぶ内容
- HDLライブラリ構築に有効な、回路のパラメータ化を学ぶ
- パラメータ化した回路の呼び出しや論理合成を学ぶ
目標
- parameter宣言を用いて回路をパラメータ化できる
- パラメタライズを用いて、ライブラリを構築できる
- パラメータ化した回路を呼び出して利用できる
- パラメータ化した回路の論理合成ができる
修了判定1
パラメータ化した回路をシミュレーションして動作確認する
修了判定2
パラメータ化した回路を異なる2つの手順で論理合成し、生成された回路の違いを確認する
HDLを使って回路記述に使えるライブラリをつくれます。ライブラリにはビット幅や上限値などを設定できるようにしておくと便利です。これを実現できるのがパラメタライズです。
(2)回路のパラメータ化
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に出力します。
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ビットのセレクタを記述します。
SEL8
8 ┌─────────┐ 8
─/─>│DL │─/─>
8 │ Q │
─/─>│DH │
│SEL │
───┤ │
└─────────┘
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ビット 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進のカウンタを記述してみます。出力のビット数はカウント値に依存しますのでこれもパラメータ化します。
(0, 1, ‥, N-1)
N_COUNT
┌─────────┐
│RES │ L
───> │ Q ├─/─>
│ │
───> │>CK │
└─────────┘
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
// N=24(24進カウンタ), L=5(5ビット)
N_COUNT #(24, 5) C24( CK, RES, Q );
#のあとの括弧内にコンマで区切って複数の数値を記述します。この値はそれぞれ記述した順番にパラメータN、パラメータLの値になります。
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進カウンタを論理合成する場合を考えてみます。
このカウンタのパラメータ宣言のデフォルト設定は「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進カウンタになります。
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 |
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
|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進カウンタを論理合成する。以下の手順で行い結果を確認する。
- N進カウンタ(
n_count.v)を論理合成し、デフォルト設定の4ビットカウンタになることを、回路図のFFの数で判断する。 - カウンタの呼び出し部(
count24.v)を合成スクリプト(count24.scr)を用いて論理合成する。まず合成スクリプトを参照してパラメタライズに対応した論理合成になっていることを確認する。 count24.vを論理合成して、5ビット、24進カウンタになっていることを、回路図のFFの数で判断する。
| ファイル名 | |
|---|---|
| N進カウンタ | n_count.v |
| カウンタの呼び出し部 | count24.v |
| カウンタの合成スクリプト | count24.scr |
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]