回路検証を実際にやってみる
今まで学んだことを参考にして回路検証の実際を体験してみましょう。終了判定がたくさんありますが頑張りましょう。
(1)ここで学ぶ内容
- 回路検証の手順とゲートレベル検証の意義を理解する
- 修了判定を通じて回路検証の実際を体験する
- 検証手順を理解して説明できる
- ゲートレベル検証を理解して説明できる
- 検証仕様を与えられテストベンチを作成できる
- シミュレーション結果から回路の不具合を判断できる
| 修了判定 | 内容 |
|---|---|
| 修了判定1 | 論理合成してゲートレベルのシミュレーションを体験する |
| 修了判定2 | RTLとゲートレベルのシミュレーション結果の違いを体験する |
| 修了判定3 | シミュレーションして、回路の不具合を見つける |
| 修了判定4 | 不具合を修正してシミュレーションし、正しく動作することを確かめる (条件:修了判定3を先に実施する) |
| 修了判定5 | 回路検証時のトラブルを体験し、不具合の所在を判断する |
(2)回路検証の手順
HDL設計にはいくつもの工程があります。以下に整理して紹介します。
- RTL設計:HDLで回路を記述する
- RTL検証:検証対象の上位階層のテストベンチを用意して動作を確認する
- 論理合成:RTL検証でOKならゲートレベル回路を生成する
- ゲートレベル検証:ネットリストをテストベンチで検証する
(回路図ではなくVerilogHDLで記述された接続情報ネットリストが対象) - レイアウト:ゲートレベル検証でOKなら次工程へ
| 検証の種類 | 目的 |
|---|---|
| RTL検証 | 機能の確認 |
| ゲートレベル検証 | タイミングの確認 |
テストベンチの構造(RTL検証の場合):
module DUT( ck, ... );
input ck;
...
always @( posedge ck )
...
endmodule
(3)ゲートレベル検証の必要性1
ゲートレベル検証ではタイミングの確認を含め、いろいろな確認が行われます。
- 回路が設計仕様の周波数で動作するかどうかをチェックする
- 入力と出力の遅延量が仕様を満たしているかどうかもチェックする
構造:組み合わせ回路 → FF → 組み合わせ回路 → FF → 組み合わせ回路 の連鎖
遅延は理想状態) end subgraph ゲートレベルシミュレーション GATE[論理合成後の
ゲートレベルネットリスト] --> SIM2[論理シミュレータ] SDF[SDFファイル
配線遅延情報] --> SIM2 TB --> SIM2 SIM2 --> OUT2(実機のタイミングで
セットアップ/ホールド違反等を確認) end style RTL fill:#e3f2fd,stroke:#1976d2; style GATE fill:#fff3e0,stroke:#f57c00; style SDF fill:#f3e5f5,stroke:#9c27b0;
RTL検証で行われていた機能の確認をゲートレベル検証で補うこともある。
このカウンタ記述ではリセット信号RESが途中で不定値になってもelse以降を実行してカウントアップを続ける。このカウンタの記述には誤りはない。
reg [3:0] Q;
always @( posedge CK )
if ( RES==1'b1 )
Q <= 4'h0;
else
Q <= Q + 4'h1;
しかしRESを作成している回路に誤りがあっても、このカウンタの動作ではみつけられない。
→ ゲートレベルではRTLの不定値が伝搬して、出力全体が不定値となる。これにより回路の誤りを検出できる。
(4)ゲートレベル検証の必要性2
RTLで動作しても、論理合成に適さない記述がある。多くの場合論理合成ツールがエラーとして検出するが、記述によっては動作しない回路を生成してしまうことがある。
例:同一信号への複数alwaysブロック代入 → 論理合成に適さない記述
always @( posedge ck )
if ( RES==1'b1 )
Q <= 4'h0;
always @( posedge ck )
if ( EN==1'b1 )
Q <= Q + 4'h1;
RTL検証はOKでも、論理合成に適さない記述 → 動作しない回路を生成する
- ゲートレベルの回路はレイアウトと一対一に対応している
- したがって不具合が残ると、そのまま製造されてしまう
- 論理合成後のゲートレベル検証はレイアウト前の最終確認でもある
ゲートレベル → (論理合成)→ レイアウト へと一対一で対応している
(5)適切な検証入力
回路検証の目標はできるだけ少ない手順ですべての機能を検証することです。したがって次の点を考慮して検証入力を与えます。
- 設計仕様の機能をすべて確認する(検証もれがないようにする)
- 回路全体を動作させる
- 出力や設定値が0と1の両方の値となるように入力を与える
- 限界値(境界値)を入力する(設定値や出力の上限・下限をチェックする)
- 検証もれの「保険」として、できるだけ多くの値を入力する
(6)検証仕様からテストベンチを作成
検証仕様を作成し、これをもとにテストベンチを作成する例を紹介します。検証対象の回路は非同期リセット同期ロードつきの4ビットカウンタ(COUNT4LD)です。
ポート構成:RES(非同期リセット)、LD(同期ロード)、D[3:0](ロードデータ)、CK(クロック)、Q[3:0](出力 4bit)
| RES | LD | CK | Q |
|---|---|---|---|
| 1 | X | X | 0 |
| 0 | 1 | ↑ | D |
| 0 | 0 | ↑ | Q0+1 |
- カウンタの出力が 0, 1, 2, ···, 14, 15, 0, 1, ··· と進行すること
- 任意の値をロードできること
- クロックに同期せずにリセットできること
これらの項目にしたがってテストベンチを作成します。
- カウンタを初期化した後、20クロック分の遅延を与えカウンタを進める
- カウンタにいくつかの値をロードして、カウンタを進める
- カウント値が15になったらリセットして全ビットを0にする
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 * 9) // カウンタを9クロック進める
RES = 1; // リセット
#STEP RES = 0; // リセット解除
$finish; // シミュレーション終了
end
(7)修了判定1
ゲートレベル検証の体験
- 論理合成してネットリストを作成する。
回路記述:count4.v - ネットリストを用いて、ゲートレベル検証を行う。
テストベンチ:count4_test.v
ネットリスト:count4_net.v
COUNT4
RES→CK→>Q→/ 4回路仕様
RES CK Q 1 ↑ 0 0 ↑ Q0 + 1 - シミュレーション結果の波形を拡大して見ると、カウンタ値が順番に変化していない所がある。
値を調べて、下図の波形に記入する。
CK _____|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\ /‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
| |
Q ‾‾| 5 |‾‾‾| 4 |‾‾‾‾‾‾‾‾‾‾‾‾‾ 6 ‾‾‾‾‾‾/ \‾‾‾ 7 ‾‾‾|‾‾‾| 0 |‾‾‾‾‾‾‾ 8 ‾‾‾
(8)修了判定2
RTLとゲートレベルの違い
- テストベンチ(
count4_test2.v)では、リセット信号を途中で不定値にしている。 - このテストベンチを用いて、RTL(
count4.v)とゲートレベル(count4_net.v)で、
シミュレーションした場合、カウンタ出力Qはどうなるか確認する。
| RES | CK | Q |
|---|---|---|
| 1 | ↑ | 0 |
| 0 | ↑ | Q0 + 1 |
count4.vネットリスト:
count4_net.vテストベンチ:
count4_test2.v
initial begin
CK=0; RES=0;
#STEP RES = 1; // リセット
#STEP RES = 0; // リセット解除
#(STEP * 18) // カウンタを 18 クロック進める
RES = 1'bx; // リセット信号を不定値に
#(STEP * 8) // カウンタを 8 クロック進める
$finish; // シミュレーション終了
end
(9)修了判定3
回路検証の実際(誤りの発見)
- 回路記述(
count6.v)は、誤りがあって正しく動作しない。 - シミュレーションを実行して、不具合の症状を確認する。
- シミュレータのmoreコマンドで回路記述を表示し、誤っている部分を見つける。
(次の判定で誤っている部分を指摘する)
| RES | CK | Q |
|---|---|---|
| 1 | ↑ | 0 |
| 0 | ↑ | Q0 + 1 |
Q出力: 0, 1, 2, 3, 4, 5, 0, ..
count6.vテストベンチ:
count6_test.v
(10)修了判定4
回路検証の実際(修正後の再検証)
- 回路記述の主要部分を抜粋して下に示した。
- 次の3カ所のいずれか1カ所が誤っている。
- 3カ所のそれぞれを変更したファイルを用意してあるので、
シミュレーションして正しく動作しているか確認する。 - 確認した後、誤った行を指定して、採点する。
| RES | CK | Q |
|---|---|---|
| 1 | ↑ | 0 |
| 0 | ↑ | Q0 + 1 |
Q出力: 0, 1, 2, 3, 4, 5, 0, ..
| 修正後の回路記述: | count6_1.v |
count6_2.v |
|
count6_3.v |
|
| テストベンチ: | count6_test.v |
reg [2:0] Q;
always @( posedge CK ) begin // count6_1.v
if ( RES ) // count6_2.v
Q <= 3'h0;
× else if ( Q==3'h6 ) // count6_3.v
Q <= 3'h0;
else
Q <= Q + 3'h1;
end
(11)修了判定5(概要)
検証時のトラブル体験
- 検証対象は、4ビットのロード付きカウンタ。
- 検証入力と期待値を、すべて外部のファイルから与える
テストベンチを作成した。 - しかしテストベンチの途中で期待値の不一致が生じてしまった。
- シミュレーションを実行したり、各ファイルを参照するなど
して、どこに誤りがあるか調べる。
COUNT4LD_TEST
| RES | LD | CK | Q |
|---|---|---|---|
| 1 | X | X | 0 |
| 0 | 1 | ↑ | D |
| 0 | 0 | ↑ | Q0 +1 |
(12)修了判定5(検証内容)
検証時のトラブル体験(検証内容)
- (1)リセット後、カウンタ出力が、
0, 1, 2, .. , 14, 15, 0, 1
と進行することを確認。
- (2)4'd11をロードして、
11, 12, 13, 14, 15, 0, 1
と進行することを確認。
STEP
|←-→|
DELAY|←→|
: : : : : : :
CK: : ‾‾|__________|‾‾‾|__________|‾‾‾|__________|‾‾
: : : : : : :
RES__|‾‾‾‾‾‾‾‾‾‾‾‾‾‾|________________________________
: : : : : : :
LD___|______________|______________________|‾‾‾‾‾‾|__
: : : : : : : : :
D | | 0 | : : : : | 11 |
:‾‾‾‾‾‾‾: : : : :‾‾‾‾‾‾‾‾‾‾:
Q | | 0 | 1 | 2 | | 15| 0 | 1 | 11 | 12 |
期待値比較↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
確認してチェック
count4ld.v
count4ld_test.v
count4ld.dat
| テスト入力 | 期待値 | ||
|---|---|---|---|
| RES | LD | D | Q |
| 1 | 0 | 0000 | 0000 |
| 0 | 0 | 0000 | 0000 |
| 0 | 0 | 0000 | 0001 |
| 0 | 0 | 0000 | 0010 |
| 0 | 0 | 0000 | 0011 |
| ... | |||
| 0 | 0 | 0000 | 1101 |
| 0 | 0 | 0000 | 1110 |
| 0 | 0 | 0000 | 1111 |
| 0 | 0 | 0000 | 0000 |
| 0 | 1 | 1011 | 1011 |
| 0 | 0 | 1011 | 1011 |
| 0 | 0 | 1011 | 1100 |
| 0 | 0 | 1011 | 1101 |
| 0 | 0 | 1011 | 1110 |
| 0 | 0 | 1011 | 1111 |
| 0 | 0 | 1011 | 0000 |
| 0 | 0 | 1011 | 0001 |
この検証では期待値に誤りがありました。
回路記述やテストベンチをいくら確認しても間違いは見つからなかったと思います。
現実の回路検証ではこのようなこともよくあるので、あらゆる角度から疑ってみることが必要です。
回路を設計する際、最初から正確な期待値があることは、まれです。期待値は、回路検証のよりどころですので、作成に際しては細心の注意が必要です。
RES=1 LD=0 D=0000 Q=0000 PAT=0000
RES=0 LD=0 D=0000 Q=0000 PAT=0000
RES=0 LD=0 D=0000 Q=0001 PAT=0001
RES=0 LD=0 D=0000 Q=0010 PAT=0010
RES=0 LD=0 D=0000 Q=0011 PAT=0011
RES=0 LD=0 D=0000 Q=0100 PAT=0100
RES=0 LD=0 D=0000 Q=0101 PAT=0101
RES=0 LD=0 D=0000 Q=0110 PAT=0110
RES=0 LD=0 D=0000 Q=0111 PAT=0111
RES=0 LD=0 D=0000 Q=1000 PAT=1000
RES=0 LD=0 D=0000 Q=1001 PAT=1001
RES=0 LD=0 D=0000 Q=1010 PAT=1010
RES=0 LD=0 D=0000 Q=1011 PAT=1011
RES=0 LD=0 D=0000 Q=1100 PAT=1100
RES=0 LD=0 D=0000 Q=1101 PAT=1101
RES=0 LD=0 D=0000 Q=1110 PAT=1110
RES=0 LD=0 D=0000 Q=1111 PAT=1111
RES=0 LD=0 D=0000 Q=0000 PAT=0000
RES=0 LD=1 D=1011 Q=0001 PAT=1011 Error!
RES=0 LD=0 D=1011 Q=1011 PAT=1011
RES=0 LD=0 D=1011 Q=1100 PAT=1100
RES=0 LD=0 D=1011 Q=1101 PAT=1101
RES=0 LD=0 D=1011 Q=1110 PAT=1110
RES=0 LD=0 D=1011 Q=1111 PAT=1111
RES=0 LD=0 D=1011 Q=0000 PAT=0000
RES=0 LD=0 D=1011 Q=0001 PAT=0001
RES=0 LD=0 D=1011 Q=0010 PAT=0010
RES=0 LD=0 D=1011 Q=0011 PAT=0011
RES=0 LD=0 D=1011 Q=0100 PAT=0100
RES=0 LD=0 D=1011 Q=0101 PAT=0101
End of simulation 15/10/20 14:24:56
D:/exam/T_unit/T6_12>more count4ld.v
/* Copyright(C) 1999 by hdLab Inc. All rights reserved. */
/* 4ビットのリセット,ロード付きカウンタ */
module COUNT4LD( CK, RES, LD, D, Q );
input CK, RES, LD;
input [3:0] D;
output [3:0] Q;
reg [3:0] Q;
always @( posedge CK or posedge RES ) begin
if ( RES==1'b1 ) // RES .. 1で非同期リセット
Q <= 4'h0;
else if ( LD ) // LD .. 1で同期ロード
Q <= D;
else
Q <= Q + 4'h1;
end
endmodule
module COUNT4LD_TEST;
parameter STEP = 1000; // クロックの周期
parameter DELAY = 100; // 入力信号の遅延
parameter DELAY2 = 2; // 期待値取り込み位置
parameter NUM = 30; // カウンタの1周分+α
integer i; // ループ変数
reg [9:0] mem[0:NUM-1]; // 期待値格納配列
reg [9:0] tmp; // 作業用変数
reg CK, RES, LD;
reg [3:0] D;
wire [3:0] Q;
COUNT4LD C1( CK, RES, LD, D, Q );
always begin // クロックの記述
CK = 1; #(STEP/2) ;
CK = 0; #(STEP/2) ;
end
initial begin
$readmemb( "count4ld.dat", mem );
RES = 0;
#DELAY;
for ( i=0; i<NUM; i=i+1 ) begin
tmp = mem[i];
RES = tmp[9]; LD = tmp[8]; D = tmp[7:4]; // 検証対象への入力
#(STEP-DELAY-DELAY2);
$write( "RES=%b LD=%b D=%b Q=%b PAT=%b", RES, LD, D, Q, tmp[3:0] );
if ( Q !== tmp[3:0] ) // 期待値との比較
$write( " Error!\n" );
else
$write( "\n" );
#(DELAY+DELAY2); // 遅延量の帳尻あわせ
end
$finish;
end
endmodule
D:/exam/T_unit/T6_12>more count4ld.dat
/*
RES D
LD Q */
1_0_0000_0000
0_0_0000_0000
0_0_0000_0001
0_0_0000_0010
0_0_0000_0011
0_0_0000_0100
0_0_0000_0101
0_0_0000_0110
0_0_0000_0111
0_0_0000_1000
0_0_0000_1001
0_0_0000_1010
0_0_0000_1011
0_0_0000_1100
0_0_0000_1101
0_0_0000_1110
0_0_0000_1111
0_0_0000_0000
0_1_1011_1011
0_0_1011_1011
0_0_1011_1100
0_0_1011_1101
0_0_1011_1110
0_0_1011_1111
0_0_1011_0000
0_0_1011_0001
0_0_1011_0010
0_0_1011_0011
0_0_1011_0100
0_0_1011_0101
お疲れさま!回路検証の実際、どうだったかな?🐾
これで君もテストベンチマスター!学んだことを活かして、自分の回路をしっかり検証してみてね!
