テストベンチの基本構造を知る
(1) ここで学ぶ内容
概要
- 動作確認に適したテスト入力について学ぶ
- テストベンチのモジュール構造と記述を理解する
目標
- テストベンチの構造を理解し説明できる
- 回路の動作確認に必要なテスト入力を用意できる
- テストベンチを用いて回路の検証ができる
TESTBENCH] STIMULUS(テスト入力生成
initial / always) DUT[設計した回路モジュール
DUT: Device Under Test] MONITOR(出力確認・波形ダンプ
, ) STIMULUS -->|入力信号| DUT DUT -->|出力信号| MONITOR TB_TOP -.-> STIMULUS TB_TOP -.-> DUT TB_TOP -.-> MONITOR end style TB_TOP fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px; style DUT fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; style STIMULUS fill:#e3f2fd,stroke:#1976d2; style MONITOR fill:#fff3e0,stroke:#f57c00;
ここからはテストベンチの勉強をします。テストベンチは動作確認のために用意する記述のことです。初めに動作確認をするとき、どのような入力を与えるのがよいか考えてみましょう。
(2) 動作確認のためのテスト入力1
組み合わせ回路
graph LR
A((A)) --> AND1[AND]
B((B)) --> AND1
AND1 --> OR1[OR]
C((C)) --> OR1
OR1 --> Q((Q))
真理値表
| C | B | A | Q |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 |
設計した回路の動作を確認するためには、適切な入力を与える必要があります。むやみに入力を与えても動作の半分も確認できていないことがあります。
ここではどのような入力が適切なのかを示します。
まず組み合わせ回路の場合を考えてみます。
簡単な例としてANDとORで構成された3入力1出力の回路を示します。入力は3本なので真理値表に示す8通りの入力が考えられます。
この回路を検証するためにはこの8通りの入力を与えて、出力が真理値表のようになっていることを確認します。
4ビット加算回路
4
4
4
16ビット加算回路
16
16
16
- 入力の取り得る値をすべて確認する必要はない
- 部分的な値の確認にとどめる
次に加算回路について考えます。
図に示す4ビット加算回路では、4ビットの入力が2つありますので、全入力は8ビットです。
したがって2の8乗、つまり256通りの入力を与えれば完全な動作確認ができます。
では16ビットの加算回路ではどうなるでしょう。
16ビット入力が2つなので全入力は32ビットです。2の32乗は約42億 9000万です。これらをすべて入力するのは現実的ではありません。
したがって取り得るすべての組み合わせを入力するのではなく、基本的な動作を確認するための部分的な値を入力するだけでよいのです。
組み合わせ回路の検証では、取り得る値をすべて入力するのが原則です。
しかし現実的には部分的な値の入力にとどめています。
(3) 動作確認のためのテスト入力2
順序回路
graph LR
DIN((DIN)) --> FF1[FF1
D Q]
FF1 --> FF2[FF2
D Q]
FF1 --> AND1[AND]
FF2 --> AND1
AND1 --> Q((Q))
CK((CK)) --> FF1
CK --> FF2
gantt
title タイミングチャート
dateFormat s
axisFormat %S
section CK
step1: 0, 1s
step2: 1, 1s
step3: 2, 1s
step4: 3, 1s
step5: 4, 1s
step6: 5, 1s
step7: 6, 1s
step8: 7, 1s
section DIN
step9: 0, 2s
step10: active, 2, 5s
step11: 5, 8s
section FF1
step12: 0, 3s
step13: active, 3, 6s
step14: 6, 8s
section FF2
step15: 0, 4s
step16: active, 4, 7s
step17: 7, 8s
section Q
step18: 0, 4s
step19: active, 4, 6s
step20: 6, 8s
今度は順序回路の場合を考えてみます。順序回路では、保持した値を考慮して入力を与えます。
まず図の回路について考えます。この回路はFF2個とANDゲートから構成されています。最初にクロックを連続的に与えます。
次に2入力ANDゲートの入力が00、10、11、01となるようにDINに入力を与えます。
2クロックの間DINに1を与えることでこの状態をつくりだせます。FF1とFF2の両方が1のとき、出力Qが1となります。
カウンタの場合
4
gantt
title カウンタの波形
dateFormat s
axisFormat %S
section ck
step21: 0, 1s
step22: 1, 1s
step23: 2, 1s
step24: 3, 1s
step25: 4, 1s
step26: 5, 1s
step27: 6, 1s
step28: 7, 1s
section reset
step29: 0, 2s
step30: active, 2, 4s
step31: 4, 8s
section q
11 : 0, 1s
12 : 1, 2s
0 : 2, 4.5s
1 : 4.5, 6s
2 : 6, 7.5s
3 : 7.5, 8s
section q[0]
step32: active, 0, 1s
step33: 1, 4.5s
step34: active, 4.5, 6s
step35: 6, 7.5s
step36: active, 7.5, 8s
section q[1]
step37: active, 0, 1s
step38: 1, 6s
step39: active, 6, 8s
section q[2]
step40: 0, 1s
step41: active, 1, 2s
step42: 2, 8s
section q[3]
step43: active, 0, 2s
step44: 2, 8s
次に4ビットカウンタの場合です。カウンタの基本動作はクロックを入れ続けることで確認できます。2の4乗、16クロック以上入力すればカウンタが一周します。これで取り得るすべての値を確認したことになります。
リセット入力の動作を厳密に確認するには、カウンタ出力が1になっているビットがリセット信号により0になることを確かめる必要があります。
ここではカウント値が3のときと、12のときにリセットしています。
順序回路の検証では使用するFFが取り得るすべての値をつくりだすのが原則です。しかし現実的には機能的なチェックにとどめています。
(4) 回路規模に応じたテストベンチ
→ ▷ ←
回路を記述したらテストベンチを作成して動作確認をするのが基本です。ではどんな回路でも記述するたびに検証しなければならないのでしょうか。小さな回路、例えばカウンタやセレクタの単位で検証していては効率的ではありません。
数行の回路記述に、数十行のテストベンチを用意していては非効率です。
一般に機能的にまとまた単位でテストベンチを作成し検証します。
カウンタやセレクタなどが数個まとまって一つの処理を構築しているブロックの単位で検証します。
場合によってはより上位の大きなブロックから一気に検証してもかまいません。
各機能ブロックで検証したのち、これらの回路を接続したチップ全体でも専用のテストベンチを作成して検証します。
(5) テストベンチの構造
検証対象
(1) 検証対象の上位階層でテスト入力を与える
→
=D ▷
(2) テストベンチをブロック化してテスト入力を与える
テストベンチ
(3) 検証対象の出力をチェックして良否を判別する
テストベンチの記述にはいくつかのスタイルがあります。
最初は検証対象の上位階層を用意して信号を接続する方法です。接続した信号の値を書き換えることで、検証対象の入力を変化しています。今までに紹介してきたのはこのスタイルで、実際の設計にもよく使われます。
次はテストベンチを一つのブロックとして記述したものです。
上位階層で検証対象と接続します。実装時に接続される別のチップを表現する場合などに有効です。
3番目は検証対象の出力をチェックする構造です。
この構造は今まで紹介した2通りの構造のいずれでも記述できます。あらかじめ出力の期待値を用意しておき、検証対象の出力をチェックして、良否を判別する方法です。
テストベンチは確認する機能にあわせて何種類か用意することがあります。
1つのテストベンチですべて確認するのではなく、分割して検証することでテストベンチの作成を効率的に行うことができます。
(6) テストベンチのHDL記述構造
module テストベンチ名;
┌────────────────────────────┐
│ 各種宣言 │
│ ・検証対象への入力は reg 宣言 │
│ ・検証対象の出力は wire 宣言 │
│ ・定数は parameter 宣言 │
└────────────────────────────┘
┌────────────────────────────┐
│ 検証対象の記述 │
└────────────────────────────┘
┌────────────────────────────┐
│ クロックの記述 │
└────────────────────────────┘
┌────────────────────────────┐
│ テスト入力の記述 │
│ initial begin │
│ ... │
│ end │
└────────────────────────────┘
┌────────────────────────────┐
│ 出力信号などの画面表示記述 │
└────────────────────────────┘
endmodule
module FF_BLK_test;
reg CK, DIN;
wire Q;
parameter STEP=1000;
FF_BLK F1( CK, DIN, Q );
always begin
CK=0; #(STEP/2) ;
CK=1; #(STEP/2) ;
end
initial begin
DIN=0;
#STEP DIN=1;
#(STEP*2) DIN=0;
#(STEP*2) $finish;
end
endmodule
前ページの最初に紹介した、検証対処の上位階層として作成したテストベンチについて記述構造を紹介します。
記述例として3ページで紹介した順序回路のテスト入力を用います。
テストベンチもモジュール構造で記述します。
moduleに続き、テストベンチの名前を記述します。
続いて接続する信号を宣言します。
検証対象への入力はレジスタ宣言、検証対象の出力はワイヤ宣言、定数はパラメータ宣言となります。
次に検証対象を記述します。下位層を含む回路記述で、下の下位層を接続するのと同じ方法です。
まず接続する<モジュール名> <インスタンス名>(<信号名>、<信号名>、…);を記述します。ここでは順番による接続を行っていますが、名前による接続も可能です。
テストベンチの場合、検証対象のインスタンス名はモジュール名と同じにしてもかまいません。同じ検証対象のモジュールを何個も接続することはないので、区別する必要がないからです。
検証対象の記述に続いて、クロックを記述します。
テスト入力はinitial文のbegin~endの中で記述します。
シミュレーション結果は文字列で画面に表示したり、波形表示でみることができます。
画面に文字で表示する場合には、そのための記述が必要です。
波形表示はシミュレータごとに方法がことなります。特別な記述を必要としないものや数行の記述を追加するだけで波形表示可能なものがあります。
ここで紹介した記述構造はよく用いられるものです。
verilogHDLの文法の範囲内であれば、いろいろなスタイルで記述することができます。
(7) ワンポイント・アドバイス
テストベンチはプログラミング
-
・条件分岐
if文
case文 -
・ループ構造
for文
while文 etc -
・タスク(サブルーチンに相当)
task -
・システムタスク(組み込みルーチンに相当)
$finish
$fopen etc
graph TD
A1[ ] --> B1{ }
B1 --> C1[ ]
B1 --> C2[ ]
A2[ ] --> B2{ }
B2 --> C3[ ]
B2 --> C4[ ]
B2 --> C5[ ]
B2 --> C6[ ]
graph TD
A[ ] --> B{ }
B --> C[ ]
C --> B
B --> D[ ]
テストベンチはプログラミングです。つまりC言語などでプログラムを書くのと同じように自由に記述できます。
例えば条件分岐のif文やcase文、ループ構造のfor文やwhile文を使うことができます。
また、サブルーチンに相当するtask、組み込みルーチンのシステムタスクを使うことでより高度なテストベンチを記述することができます。
テストベンチはプログラミングです。詳しくは検証実験2からのユニットで紹介します。
🐾 もふねこの現場メモ:
実習でステートマシンがデッドロックして、シミュレーションが永遠に終わらなくなった学生がいたんだ。タイムアウト処理($finishを一定時間後に呼ぶ「番犬」)を入れていなかったから、「どこが壊れているか」すら分からない状態で顔面蒼白に…。
テストベンチを書くときは、必ず#100_000; $finish; のような番犬を入れておこうね!
(8) 修了判定1
- ・4ビットのリセット,ロード付きカウンタのテストベンチを記述する
- ・以下の項目を確認できること
- (1) カウンタの基本動作
カウンタ出力が,0,1,2,..,14,15,0,1,..と進行する - (2) 任意の値のロード
- (3) クロックに非同期なリセット動作
4
4
- ・4ビットのリセット,ロード付きカウンタ
- ・CKの立ち上がりでカウントアップ
- ・RESは1で非同期リセット
- ・LDは1で同期ロード
- ・Dはロード用データ入力
module COUNT4LD_TEST;
reg CK, RES, LD; // 入力はreg宣言
reg [3:0] D; // 入力はreg宣言
wire [3:0] Q; // 出力はwire宣言
parameter⭕ STEP=1000; // 1クロックの周期を宣言
COUNT4LD⭕ C1( CK, RES, LD, D, Q ); // 検証対象接続
always⭕ begin // クロックの記述
CK = 0; #(STEP/2) ;
CK = 1; #(STEP/2) ;
end
initial⭕ begin // テスト入力の記述
CK=0; RES=0; LD=0; D=4'h0;
#STEP RES = 1⭕; // リセット
#STEP RES = 0; // リセット解除
#(STEP * 20⭕ ) // 20クロック進める
D = 4'h9; LD = 1⭕ ; // 9をロード
#STEP LD = 0;
#(STEP * 8⭕ ) // 8クロック進める
D = 4'h6; LD = 1;// 6をロード
#STEP LD = 0;
#(STEP * 12⭕) // 12クロック進める
$finish; // シミュレーション終了
end
endmodule
(9) 修了判定2
- ・シミュレータを用いて,4ビットカウンタの動作確認を行う。
- ・以下の項目を,波形により確認する。
- (1) カウンタの基本動作
カウンタ出力が,0,1,2,..,14,15,0,1,..と進行する - (2) 任意の値のロード
- (3) クロックに非同期なリセット動作
4
4
テストベンチ・ファイル: count4ld_test.v
シミュレーション波形 (Tiny-Waves)
Tiny-Waves count4ld.log time 0 1 2 3 4 5 6 7 8 ... 22 23 ... 31 32 ... 45 CK _|?|_|?|_|?|_|?|_|?|_|?|_|?|_|?|_ ... _|?|_ ... _|?|_ ... RES 0 1 0 0 0 0 0 0 0 ... 0 0 ... 0 0 ... LD 0 0 0 0 0 0 0 0 0 ... 1 0 ... 1 0 ... D[3:0] 0 0 0 0 0 0 0 0 0 ... 9 9 ... 6 6 ... Q[3:0] x 0 1 2 3 4 5 6 7 ... 9 a ... 6 7 ... 2 load at 22s : D=9 ???? load at 31s : D=6 ???? ???????????????????
Verilog-HDL シミュレータ出力
D:/exam/T_unit/T1_09>verilog count4ld.v count4ld_test.v
Verilog-HDL (faked) simulator
Copyright 1999-2002 hdLab, Inc.
Compiling source file "count4ld.v"
Compiling source file "count4ld_test.v"
0 CK=0 RES=0 LD=0 D=0 Q=x
500 CK=1 RES=0 LD=0 D=0 Q=x
1000 CK=0 RES=1 LD=0 D=0 Q=0
1500 CK=1 RES=1 LD=0 D=0 Q=0
2000 CK=0 RES=0 LD=0 D=0 Q=0
2500 CK=1 RES=0 LD=0 D=0 Q=1
3000 CK=0 RES=0 LD=0 D=0 Q=1
3500 CK=1 RES=0 LD=0 D=0 Q=2
4000 CK=0 RES=0 LD=0 D=0 Q=2
4500 CK=1 RES=0 LD=0 D=0 Q=3
5000 CK=0 RES=0 LD=0 D=0 Q=3
5500 CK=1 RES=0 LD=0 D=0 Q=4
6000 CK=0 RES=0 LD=0 D=0 Q=4
6500 CK=1 RES=0 LD=0 D=0 Q=5
7000 CK=0 RES=0 LD=0 D=0 Q=5
7500 CK=1 RES=0 LD=0 D=0 Q=6
8000 CK=0 RES=0 LD=0 D=0 Q=6
8500 CK=1 RES=0 LD=0 D=0 Q=7
9000 CK=0 RES=0 LD=0 D=0 Q=7
9500 CK=1 RES=0 LD=0 D=0 Q=8
10000 CK=0 RES=0 LD=0 D=0 Q=8
10500 CK=1 RES=0 LD=0 D=0 Q=9
11000 CK=0 RES=0 LD=0 D=0 Q=9
11500 CK=1 RES=0 LD=0 D=0 Q=a
12000 CK=0 RES=0 LD=0 D=0 Q=a
12500 CK=1 RES=0 LD=0 D=0 Q=b
13000 CK=0 RES=0 LD=0 D=0 Q=b
13500 CK=1 RES=0 LD=0 D=0 Q=c
14000 CK=0 RES=0 LD=0 D=0 Q=c
14500 CK=1 RES=0 LD=0 D=0 Q=d
15000 CK=0 RES=0 LD=0 D=0 Q=d
15500 CK=1 RES=0 LD=0 D=0 Q=e
16000 CK=0 RES=0 LD=0 D=0 Q=e
16500 CK=1 RES=0 LD=0 D=0 Q=f
17000 CK=0 RES=0 LD=0 D=0 Q=f
17500 CK=1 RES=0 LD=0 D=0 Q=0
18000 CK=0 RES=0 LD=0 D=0 Q=0
18500 CK=1 RES=0 LD=0 D=0 Q=1
19000 CK=0 RES=0 LD=0 D=0 Q=1
19500 CK=1 RES=0 LD=0 D=0 Q=2
20000 CK=0 RES=0 LD=0 D=0 Q=2
20500 CK=1 RES=0 LD=0 D=0 Q=3
21000 CK=0 RES=0 LD=0 D=0 Q=3
21500 CK=1 RES=0 LD=0 D=0 Q=4
22000 CK=0 RES=0 LD=1 D=9 Q=4
22500 CK=1 RES=0 LD=1 D=9 Q=9
23000 CK=0 RES=0 LD=0 D=9 Q=9
23500 CK=1 RES=0 LD=0 D=9 Q=a
24000 CK=0 RES=0 LD=0 D=9 Q=a
24500 CK=1 RES=0 LD=0 D=9 Q=b
25000 CK=0 RES=0 LD=0 D=9 Q=b
25500 CK=1 RES=0 LD=0 D=9 Q=c
26000 CK=0 RES=0 LD=0 D=9 Q=c
26500 CK=1 RES=0 LD=0 D=9 Q=d
27000 CK=0 RES=0 LD=0 D=9 Q=d
27500 CK=1 RES=0 LD=0 D=9 Q=e
28000 CK=0 RES=0 LD=0 D=9 Q=e
28500 CK=1 RES=0 LD=0 D=9 Q=f
29000 CK=0 RES=0 LD=0 D=9 Q=f
29500 CK=1 RES=0 LD=0 D=9 Q=0
30000 CK=0 RES=0 LD=0 D=9 Q=0
30500 CK=1 RES=0 LD=0 D=9 Q=1
31000 CK=0 RES=0 LD=1 D=6 Q=1
31500 CK=1 RES=0 LD=1 D=6 Q=6
32000 CK=0 RES=0 LD=0 D=6 Q=6
32500 CK=1 RES=0 LD=0 D=6 Q=7
33000 CK=0 RES=0 LD=0 D=6 Q=7
33500 CK=1 RES=0 LD=0 D=6 Q=8
34000 CK=0 RES=0 LD=0 D=6 Q=8
34500 CK=1 RES=0 LD=0 D=6 Q=9
35000 CK=0 RES=0 LD=0 D=6 Q=9
35500 CK=1 RES=0 LD=0 D=6 Q=a
36000 CK=0 RES=0 LD=0 D=6 Q=a
36500 CK=1 RES=0 LD=0 D=6 Q=b
37000 CK=0 RES=0 LD=0 D=6 Q=b
37500 CK=1 RES=0 LD=0 D=6 Q=c
38000 CK=0 RES=0 LD=0 D=6 Q=c
38500 CK=1 RES=0 LD=0 D=6 Q=d
39000 CK=0 RES=0 LD=0 D=6 Q=d
39500 CK=1 RES=0 LD=0 D=6 Q=e
40000 CK=0 RES=0 LD=0 D=6 Q=e
40500 CK=1 RES=0 LD=0 D=6 Q=f
41000 CK=0 RES=0 LD=0 D=6 Q=f
41500 CK=1 RES=0 LD=0 D=6 Q=0
42000 CK=0 RES=0 LD=0 D=6 Q=0
42500 CK=1 RES=0 LD=0 D=6 Q=1
43000 CK=0 RES=0 LD=0 D=6 Q=1
43500 CK=1 RES=0 LD=0 D=6 Q=2
End of simulation 15/09/29 14:02:06
お疲れさま!テストベンチの基本構造は理解できたかな?🐾
初期化ブロックやクロックの生成など、検証のための最初のステップはこれでバッチリだね!
🐾 もふねこの実話つまずきコラム(外部note)
このユニットでは、シミュレーション検証に不可欠な「テストベンチ」の役割と基本構造を学んだけど、実際の実務や実習では「RTLとの違いがよくわからない…」と混乱したり、「シミュレーションでは完璧に動いたのに、実機に書き込んだら全く動作しない!」という深刻なトラブルがよく起きるんだ。その原因と実話エピソードをnoteで解説しているから、ぜひ読んでみてね!