nao-milkの経験ブログ

25年間の半導体エンジニア経験で知り得た内容を記載したブログです。

シミュレーション環境とテストベンチ構成

 

f:id:nao-milk:20210319185948p:plain

設計仕様書を作成し、HDL(Verilog-HDL)で論理回路を記述した後にするのが、論理シミュレーションでのデバッグです。

 

私の場合のシミュレーション環境とテストベンチ構成をお知らせします。

参考にしていただければと思います。

 

考え方

私の場合のシミュレーション環境とテストベンチ内の構成の考え方についてご説明します。

基本的には、RTLシミュレーションから実遅延検証まで流用できるような構成で記述しています。

 

テストベンチのTOPは、1つのファイル。

テストベンチのTOP内でテストシナリオファイル*1をインクルードする構成にしています。

また、テストシナリオファイルはシミュレーション前に各テストシナリオファイルから1つをコピーしてテストシナリオファイルを作り、シミュレーションを開始します。

 

 下の図がファイル構成のイメージです。

例えば)

 シミュレーション開始前に「Scenario02.v」を「TestScenario.v」にコピーします。

 ※これは手動で行っても良いし、スクリプトファイルを作り、コマンドラインで番号(01とか02など)を指定することで自動的にコピーするようにしても構いません。

 そして、「TestBench.v」をコンパイルすることで、

 「TestScenario.v」がインクルードされます。

f:id:nao-milk:20210319170023p:plain

シミュレーション環境のファイル構成

 

このような構成にする理由ですが、

テストベンチのTOPは、ターゲット回路だけでなく、クロック生成、リセット生成、対向モデルなど、色々と配置します。

もし、テストシナリオ毎にターゲット回路を配置し、対向モデルの配置、クロックの生成など記述していたら、端子が変更になったりした場合に修正するファイルが増えるからです。

 

簡単な機能のLSIならいいですが、通信系や画像処理など、100シナリオ、1000シナリオになる場合は、大変な作業です。

なので、シミュレーション環境を構築する場合も、モジュールの切り分けと同様に、共通して使えるものはテストベンチTOPで記述し、シナリオ毎に変更するものはテストシナリオで記述するように、検証開始前の検証プラン時に検討する必要があります。

 

テストベンチTOP

テストベンチのTOP(TestBench.v)に何を記述(配置)するのか。。。

私の場合、ある程度基本構成ができています。

  1. ターゲット回路に必要な信号の宣言と入力信号の初期値記述
  2. クロックや入力遅延などのパラメータ宣言
  3. テストシナリオのインクルード
  4. クロックの生成
  5. ターゲット回路の配置(インスタンス)
  6. テストシナリオで共通で使用できるTaskやFunction記述
  7. 対向モデルの配置(インスタンス)

記述で重要となるのが、テストシナリオをどこでインクルードするか。

です。

 

コンパイラは、記述の上から順に解析していきます。

従って、

テストシナリオファイルで必要となる信号は、インクルードする前に宣言と初期値を設定しておきます。

尚、TaskやFunctionは、記述の順番には関係ないので、インクルード後に記述しても問題はないです。

 

では、簡単ですが、TestBench.v記述例を下に示します。

説明は、記述例の下に記載しています。

f:id:nao-milk:20210319180111p:plain

テストベンチ記述例

 テストベンチ記述例の説明

1行目

シミュレーションのタイムスケールを記述します。

ここでは、1ns/1psです。

#での時間の整数部が1ns単位、小数部が1psと示しているだけです。

3~7行目

 信号の宣言と初期値を同時に設定した書き方になっています。

尚、ターゲット回路の入力信号は、reg宣言で初期値を設定できるようにしておき、出力信号はwire宣言します。

9~11行目

テストシナリオ毎でクロックの周波数を変更できるように記述しています。

※テストシナリオで変更しない限り、この記述した時間でクロックが生成されます。

 (50MHzで良ければ、テストシナリオ内で記述しなければよいだけです)

なぜreal宣言としているかというと、parameter宣言だとテストシナリオ内で変更できない為です。

尚、小数を含まない場合は、integerで良いです。

 13行目

ターゲット回路へ入力する信号の入力遅延です。

(クロック及びリセットを除いた入力信号です)

これは、実遅延検証を意図した時の入力遅延になります。

この値も、このままで良ければ、テストシナリオ内で記述しなければよいだけです。

このようにしている理由は、クロックの周波数と同じ考えです。

15行目

テストシナリオのインクルードです。

20~28行目

クロックの生成となります。

ここは出荷試験用パターンのクロック波形指定が色濃く残っています。

周期の変数を示すPERIODがあり、PERIODの変化を基準にクロックの立ち上がり時間、立ち下がり時間を設定しています。

 尚、シミュレーション終了前にPERIODの値を表示することで、パターン数がわかります。

33行目

入力遅延です。

宣言と遅延回路を1行で記述しています。

尚、InDIの初期値は、6行目のDIの初期値と一緒にする必要があります。

遅延なので、assign文で記述したい所ですが、assignで記述するとシミュレーション開始時に不定値から始まります。

 

テストシナリオ

テストシナリオでは、テストベンチTOPでインクルードされることを意図して記述します。

尚、テストシナリオ内だけ使用する信号は、わざわざテストベンチTOPで宣言する必要は無く、テストシナリオ内で宣言、初期値設定を行っても良いです。

 

では、簡単ですが、TestScenario.v記述例を下に示します。

説明は、記述例の下に記載しています。

f:id:nao-milk:20210319181739p:plain

テストシナリオ記述例

テストシナリオ記述例の説明

10~12行目

システムクロック周期と波形を変更しています。

ここでは、すでにテストベンチで宣言された変数の為、initial文で変更しています。

17行目

入力遅延を変更しています。

ここでは、すでにテストベンチで宣言された変数の為、initial文で変更しています。

23~25行目

パワーオンリセットの動作記述になります。

リセット信号は、PERIODの変化点で変化するようにしています。

もし、PERIODの変化から数ns遅延したい場合は、#で遅延したら良いだけです。

27~29行目

ターゲット回路への入力値を生成しています。

これは、クロックの立ち上がりに同期させて入力させています。

尚、テストベンチであろうと、クロックと同期させる場合は、<=(ノンブロッキング代入)で記述してください。

もし、=(ブロッキング代入)で記述し、入力遅延を0nsとした場合、シミュレーターによっては次のクロックの立ち上がりでラッチせず、同時にラッチすることがあります。

※入力遅延の記述が無くても、同じです。気を付けてください。

 

繰り返しの場合、for文を使いたいですが、私は変数宣言が面倒なので、repeat文をよく使用します。

 

シミュレーション

上記TestBench.vとTestScenario.vを使って、シミュレーションするとどのような波形になるのか、以下に示します。

f:id:nao-milk:20210319183404p:plain

テストシナリオ内で、クロック周波数(波形)と入力遅延が変更されるので、上図のようになります。

尚、テストベンチ内でDI[7:0]及びInDI[7:0]に-1を記述しているので、DI[7:0]では255になります。

これは、8'hFFや255など記述が面倒だった為です。

テストベンチは静的解析ツールを通さないので、固定数にbitを示す記述はしません。

次の製品でbit幅が変わった場合、修正するのが面倒な為です。

尚、-1は32bitとまでとなる為、32bitを超える場合は、{33{1'b1}}と記述したりします。

 

最後に

Verilogの参考書にはテストベンチの記述について、書かれているものは、無いと思っています。(実務に沿っていない教科書)

上記説明は、私が色々な案件を受け持ち(掛け持ちもありました)、シミュレーション環境を構築する上で、施行錯誤して整ってきた環境と構成になります。

 

色々な機能のLSIがあるので、これが正解というものはありませんが、上記説明がこれからのシミュレーション環境や構成を検討する上で利用して頂ければと思います。

 

*1:テストパターンやテストスイートなど言ったりしますが。。。