回路検証を実際にやってみる

今まで学んだことを参考にして回路検証の実際を体験してみましょう。終了判定がたくさんありますが頑張りましょう。

(1)ここで学ぶ内容

概要
  • 回路検証の手順とゲートレベル検証の意義を理解する
  • 修了判定を通じて回路検証の実際を体験する
目標
  • 検証手順を理解して説明できる
  • ゲートレベル検証を理解して説明できる
  • 検証仕様を与えられテストベンチを作成できる
  • シミュレーション結果から回路の不具合を判断できる
修了判定一覧
修了判定内容
修了判定1論理合成してゲートレベルのシミュレーションを体験する
修了判定2RTLとゲートレベルのシミュレーション結果の違いを体験する
修了判定3シミュレーションして、回路の不具合を見つける
修了判定4不具合を修正してシミュレーションし、正しく動作することを確かめる
(条件:修了判定3を先に実施する)
修了判定5回路検証時のトラブルを体験し、不具合の所在を判断する

(2)回路検証の手順

HDL設計にはいくつもの工程があります。以下に整理して紹介します。

HDL設計フロー
  1. RTL設計:HDLで回路を記述する
  2. RTL検証:検証対象の上位階層のテストベンチを用意して動作を確認する
  3. 論理合成:RTL検証でOKならゲートレベル回路を生成する
  4. ゲートレベル検証:ネットリストをテストベンチで検証する
    (回路図ではなくVerilogHDLで記述された接続情報ネットリストが対象)
  5. レイアウト:ゲートレベル検証でOKなら次工程へ
RTL検証とゲートレベル検証の目的の違い
検証の種類目的
RTL検証機能の確認
ゲートレベル検証タイミングの確認

テストベンチの構造(RTL検証の場合):

module DUT( ck, ... );
input ck;
...
always @( posedge ck )
...
endmodule

(3)ゲートレベル検証の必要性1

ゲートレベル検証ではタイミングの確認を含め、いろいろな確認が行われます。

タイミングのチェック(組み合わせ回路の連鎖確認)
  • 回路が設計仕様の周波数で動作するかどうかをチェックする
  • 入力と出力の遅延量が仕様を満たしているかどうかもチェックする

構造:組み合わせ回路 → FF → 組み合わせ回路 → FF → 組み合わせ回路 の連鎖

graph TD subgraph RTLシミュレーション RTL[RTLコード] --> SIM1[論理シミュレータ] TB[テストベンチ] --> SIM1 SIM1 --> OUT1(論理の正しさを確認
遅延は理想状態) 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検証の補完(特に不定値が関係したもの)

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)です。

COUNT4LD 回路仕様

ポート構成:RES(非同期リセット)、LD(同期ロード)、D[3:0](ロードデータ)、CK(クロック)、Q[3:0](出力 4bit)

RESLDCKQ
1XX0
01D
00Q0+1
gantt title Modulo-6 カウンタのバグ波形 (5でクリアされない) dateFormat s axisFormat %S section CLK クロック : 0, 8s section カウント値 (バグあり) 0 : 0, 1s 1 : 1, 2s 2 : 2, 3s 3 : 3, 4s 4 : 4, 5s 5 : 5, 6s 6 (不正な値!) : crit, 6, 7s 0 : 7, 8s
検証仕様
  1. カウンタの出力が 0, 1, 2, ···, 14, 15, 0, 1, ··· と進行すること
  2. 任意の値をロードできること
  3. クロックに同期せずにリセットできること

これらの項目にしたがってテストベンチを作成します。

  • カウンタを初期化した後、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

設問

ゲートレベル検証の体験

  1. 論理合成してネットリストを作成する。
    回路記述: count4.v
  2. ネットリストを用いて、ゲートレベル検証を行う。
    テストベンチ: count4_test.v
    ネットリスト: count4_net.v

    COUNT4
    RES
    CK
    →>
    Q
    / 4
    回路仕様
    RESCKQ
    10
    0Q0 + 1
  3. シミュレーション結果の波形を拡大して見ると、カウンタ値が順番に変化していない所がある。
    値を調べて、下図の波形に記入する。
CK  _____|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\    /‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
                                             |  |
Q   ‾‾|  5  |‾‾‾|  4  |‾‾‾‾‾‾‾‾‾‾‾‾‾  6  ‾‾‾‾‾‾/    \‾‾‾  7  ‾‾‾|‾‾‾|  0  |‾‾‾‾‾‾‾  8  ‾‾‾
      
たい へん よく できました。

(8)修了判定2

設問

RTLとゲートレベルの違い

  • テストベンチ( count4_test2.v )では、リセット信号を途中で不定値にしている。
  • このテストベンチを用いて、RTL( count4.v )とゲートレベル( count4_net.v )で、
    シミュレーションした場合、カウンタ出力Qはどうなるか確認する。
COUNT4
RES
CK
→>
Q
/ 4
回路仕様
RESCKQ
10
0Q0 + 1
回路記述  : count4.v
ネットリスト: count4_net.v
テストベンチ: count4_test2.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コマンドで回路記述を表示し、誤っている部分を見つける。
    (次の判定で誤っている部分を指摘する)
COUNT6
RES
CK
→>
Q
/ 3
6進カウンタ
RESCKQ
10
0Q0 + 1

Q出力: 0, 1, 2, 3, 4, 5, 0, ..

回路記述  : count6.v
テストベンチ: count6_test.v

(10)修了判定4

設問

回路検証の実際(修正後の再検証)

  • 回路記述の主要部分を抜粋して下に示した。
  • 次の3カ所のいずれか1カ所が誤っている。
  • 3カ所のそれぞれを変更したファイルを用意してあるので、
    シミュレーションして正しく動作しているか確認する。
  • 確認した後、誤った行を指定して、採点する。
たい へん よく できました。
COUNT6
RES
CK
→>
Q
/ 3
6進カウンタ
RESCKQ
10
0Q0 + 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

count4ld.dat
COUNT4LD
RES
LD
4
/
D
CK
→>
Q
/ 4
RESLDCKQ
1XX0
01D
00Q0 +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
1000000000
0000000000
0000000001
0000000010
0000000011
...
0000001101
0000001110
0000001111
0000000000
0110111011
0010111011
0010111100
0010111101
0010111110
0010111111
0010110000
0010110001
正しくは
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
      
もふねこ

お疲れさま!回路検証の実際、どうだったかな?🐾
これで君もテストベンチマスター!学んだことを活かして、自分の回路をしっかり検証してみてね!