工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Industrial programming >> VHDL

制約付きランダム検証

制約付きランダム検証は、テスト対象デバイス (DUT) の疑似ランダム トランザクションの生成に依存するテストベンチ戦略です。目標は、DUT とのランダムな相互作用を通じて、多数の定義済みイベントの機能範囲に到達することです。

Open Source VHDL Verification Methodology (OSVVM) は、制約付きランダム テストベンチを作成するための便利なパッケージを多数含む無料の VHDL ライブラリです。この記事で使用する RandomPkg と CoveragePck に特に関心があります。このライブラリの機能の詳細については、OSVVM GitHub ページにアクセスすることをお勧めします。

被試験デバイス

制約付きランダム テストベンチと、有向テストを使用する従来のテストベンチとの違いをよりよく説明するために、例を詳しく説明します。このブログの前回の記事でリング バッファー FIFO を作成しましたが、モジュールの正確性を検証するためのセルフチェック テストベンチは作成しませんでした。

制約付きランダム検証を使用するリング バッファー FIFO 用の適切なテストベンチを作成します。

entity ring_buffer is
  generic (
    RAM_WIDTH : natural;
    RAM_DEPTH : natural
  );
  port (
    clk : in std_logic;
    rst : in std_logic;

    -- Write port
    wr_en : in std_logic;
    wr_data : in std_logic_vector(RAM_WIDTH - 1 downto 0);

    -- Read port
    rd_en : in std_logic;
    rd_valid : out std_logic;
    rd_data : out std_logic_vector(RAM_WIDTH - 1 downto 0);

    -- Flags
    empty : out std_logic;
    empty_next : out std_logic;
    full : out std_logic;
    full_next : out std_logic;

    -- The number of elements in the FIFO
    fill_count : out integer range RAM_DEPTH - 1 downto 0
  );
end ring_buffer;

リング バッファ モジュールのエンティティは、上記のコードに示されています。 DUT をブラック ボックスとして扱います。つまり、DUT がどのように実装されているかについての知識は想定していません。結局のところ、この記事はテストベンチに関するものであり、リング バッファー FIFO に関するものではありません。

エンティティ インスタンス化メソッドを使用して、テストベンチで DUT をインスタンス化します。インスタンス化は簡単なので、ここではコードを省略しますが、この記事の後半でダウンロードできます。

DUT ジェネリックは次の値にマップされます:

テストベンチ戦略

何かを実装する前に、テスト戦略を見てみましょう。下の画像は、これから作成するテストベンチの主な概念を示しています。

DUT の入力側でランダム書き込みトランザクションを実行します。入力データはクロック サイクルごとにランダムな値に設定され、書き込みイネーブル入力のストローブはランダムな期間になります。

同様に、ランダムに読み取りを実行します。乱数のクロック サイクルの間持続するバーストで読み取りイネーブル信号をアサートします。

DUT と並行して動作モデルがあります。これは、DUT で使用されるリング バッファとは異なる方法で実装されている FIFO ですが、同じインターフェイスを備えています。 DUT とは異なり、動作モデルは合成可能である必要はありません。これにより、高度な VHDL プログラミング機能を自由に使用して作成することができます。

別のプロセスで、DUT からの出力とビヘイビア モデルからの出力を比較します。このプロセスは、アサート ステートメントを使用して、すべてのクロック サイクルでこの比較を行う責任があります。 2 つの FIFO 実装の動作が常に異なる場合、アサーション エラーによりシミュレーションがエラーで終了します。

最後に、DUT に出入りするトランザクションを観察して、機能カバレッジ データを収集します。機能的なカバレッジ ポイントは、たとえば同時の読み取りと書き込み、または FIFO が少なくとも 1 回は満たされていることを意味します。これらのイベントは、メインのテストベンチ シーケンサー プロセスで監視します。監視しているすべての機能カバレッジ イベントが発生すると、シミュレーションは停止します。

OSVVM ライブラリのインポート

OSVVM ライブラリは、VHDL-2008 をサポートする任意のシミュレータで使用できます。シミュレーターに付属するデフォルトのライブラリーに既に含まれている場合があります。 ModelSim PE Student Edition に含まれており、Mentor Graphics から無料でダウンロードできます。

ModelSim には古いバージョンの OSVVM が付属していますが、必要なものはすべて揃っています。次のようにランダム パッケージとカバレッジ パッケージをインポートします。

library osvvm;
use osvvm.RandomPkg.all;
use osvvm.CoveragePkg.all;

OSVVM ライブラリの最新バージョンは、GitHub ページからいつでもダウンロードできます。シミュレーターに含まれていない場合、またはライブラリーの最新機能を使用したい場合は、これを行ってください。

OSSVM 変数の宣言

OSVVM ライブラリには、保護された型のパッケージが含まれています。これらから作成された変数は、それらが定義されたプロセスにスコープが限定されます。したがって、代わりに、以下のコードに示すように、テストベンチ アーキテクチャの宣言領域で変数を共有変数として宣言します。

 -- OSVVM variables
  shared variable rv : RandomPType;
  shared variable bin1, bin2, bin3, bin4, bin5, bin6 : CovPType;

rv RandomPType 型の変数 ランダムな値を生成するためのものです。ランダムな値を生成する必要があるすべてのプロセスで同じオブジェクトを使用できるため、必要なのはこれらのうちの 1 つだけです。最後のコード行では、CovPType 型の 6 つの変数を宣言しています。 .

6 つのカバレッジ ゴールがあるため、6 つのカバレッジ変数を宣言しました。これらのオブジェクトを「ビン」と呼びます。共有変数は、カバレッジ データの収集に使用する前に初期化する必要があります。 AddBins を呼び出すことでこれを行います CovPType のそれぞれの手順

    -- Set up coverage bins
    bin1.AddBins("Write while empty", ONE_BIN);
    bin2.AddBins("Read while full", ONE_BIN);
    bin3.AddBins("Read and write while almost empty", ONE_BIN);
    bin4.AddBins("Read and write while almost full", ONE_BIN);
    bin5.AddBins("Read without write when almost empty", ONE_BIN);
    bin6.AddBins("Write without read when almost full", ONE_BIN);

AddBins の最初のパラメーターとして、カバレッジ ビンの文字列の説明を提供します。 手順。この文字列は、シミュレーションの最後に各カバレッジ ビンの統計を出力するときに再び表示されます。テキストの説明からわかるように、非常に特殊なコーナー ケースが発生したかどうかを確認するためにビンを使用します。

AddBins ビン変数内に複数のスコアボードを作成するために使用できるオーバーロードされたプロシージャです。ただし、各ビンに関連付けられるスコアボードは 1 つだけです。したがって、便利な定数 ONE_BIN を提供します。 AddBins へのパラメータとして 手順。これは CovPType を初期化します それぞれ 1 つのビンを持つ変数。ビンによって表されるスコアボードは、監視するイベントが少なくとも 1 回発生した場合にカバーされていると見なされます。

ランダム入力の生成

DUT への入力データを生成するプロセスを作成することから始めましょう。リング バッファ FIFO は、試行された上書きおよび過剰読み取りを無視するように設計されています。したがって、ランダムな持続時間のバーストでランダムなデータを単純に書き込むことができます。 DUT が実際にデータを吸収する準備ができているかどうかを考える必要はありません。

  PROC_WRITE : process
  begin
    wr_en <= rv.RandSlv(1)(1) and not rst;

    for i in 0 to rv.RandInt(0, 2 * RAM_DEPTH) loop
      wr_data <= rv.RandSlv(RAM_WIDTH);
      wait until rising_edge(clk);
    end loop;
  end process;

このプロセスで考慮すべき唯一の点は、DUT がリセットされていないことです。このプロセスの最初の行でライト イネーブル信号をランダムに有効または無効にしますが、有効になるのは rst の場合のみです。 '0' です .

後続の for ループは、イネーブル信号がアクティブでない場合でも、ランダムな数のクロック サイクルの間、ランダム データを DUT に書き込みます。これができるのは、DUT が wr_data を無視するはずだからです。 wr_en 以外のポート 信号は '1' です . for ループの後、プログラムはプロセスの最初にループバックし、別のランダムな書き込みトランザクションをトリガーします。

ランダム読み取りの実行

DUT からデータを読み取るプロセスは、書き込みプロセスと似ています。 rd_en をランダムに起動できます これは、DUT が空のときに読み取り試行を無視するように設計されているためです。 rd_data に表示されるデータ ポートは実際にはチェックされません。このプロセスは、読み取りイネーブル信号のみを制御します。

  PROC_READ : process
  begin
    rd_en <= rv.RandSlv(1)(1) and not rst;

    for i in 0 to rv.RandInt(0, 2 * RAM_DEPTH) loop
      wait until rising_edge(clk);
    end loop;
  end process;

行動検証

テストベンチ内で DUT の動作モデルを構築し、その動作を検証します。これはよく知られたテストベンチ戦略です。まず、動作モデルに DUT と同じ入力を同時に供給します。次に、2 つの出力を比較して、DUT の動作が正しいかどうかを確認します。

上の画像は、このようなテストベンチの基本的なレイアウトを示しています。動作モデルは DUT と並行して動作します。これを青写真として使用して、DUT からの出力をチェックします。

テストベンチ FIFO

リンク リストを使用して動作モデルを実装します。リンク リストは合成できませんが、テストベンチには最適です。 VHDL でリンク リストを作成する方法を思い出してください。 あなたがこのブログの定期的な読者なら記事。そのコードを使用して、リング バッファー FIFO の動作モデルを実装します。

package DataStructures is
   type LinkedList is protected
 
      procedure Push(constant Data : in integer);
      impure function Pop return integer;
      impure function IsEmpty return boolean;
 
   end protected;
end package DataStructures;

リンク リスト FIFO のパッケージ宣言は、上記のコードに示されています。これは、3 つの機能を持つ保護された型です。プッシュ、ポップ、および IsEmpty。これらは、FIFO の要素の追加と削除、および FIFO に要素が残っていないかどうかのチェックに使用されます。

  -- Testbench FIFO that emulates the DUT
  shared variable fifo : LinkedList;

保護された型は、VHDL のクラスのような構造です。上記のコードに示すように、テストベンチの宣言領域で共有変数を宣言することにより、リンク リストのオブジェクトを作成します。

行動モデル

リング バッファ FIFO の動作を完全にエミュレートするために、DUT の出力信号を反映する 2 つの新しい信号を宣言します。最初の信号には動作モデルからの出力データが含まれ、2 番目の信号は関連する有効な信号です。

  -- Testbench FIFO signals
  signal fifo_out : integer;
  signal fifo_out_valid : std_logic := '0';

上記のコードは、動作モデルからの 2 つの出力信号の宣言を示しています。 DUT に接続されているものと同じであるため、動作モデル専用の入力信号は必要ありません。この記事の後半で説明するように、シグナルを使用してカバレッジ データを簡単に収集できるため、DUT 出力をエミュレートしています。

PROC_BEHAVIORAL_MODEL : process
begin
  wait until rising_edge(clk) and rst = '0';

  -- Emulate a write
  if wr_en = '1' and full = '0' then
    fifo.Push(to_integer(unsigned(wr_data)));
    report "Push " & integer'image(to_integer(unsigned(wr_data)));
  end if;
    
  -- Emulate a read
  if rd_en = '1' and empty = '0' then
    fifo_out <= fifo.Pop;
    fifo_out_valid <= '1';
  else
    fifo_out_valid <= '0';
  end if;
  
end process;

上記のコードは、リング バッファ FIFO の動作モデルを実装するプロセスを示しています。このプロセスは、リセット信号がアクティブでない場合、クロックの立ち上がりエッジごとにトリガーされます。

ビヘイビア モデルは、wr_en full の間、信号がアサートされます 信号は '0' です .同様に、動作モデル プロセスの読み取りロジックは、rd_en をリッスンすることで機能します。 と empty 信号。後者は DUT によって制御されますが、作成する次のプロセスで動作することを確認します。

出力の確認

DUT の出力をチェックするすべてのロジックは、«PROC_VERIFY» という名前のプロセスに集められます。 DUT の出力が動作モデルの出力と一致することを確認するために assert ステートメントを使用しています。また、FIFO が空のときに DUT と動作モデルが一致することも確認します。

検証プロセスのコードを以下に示します。

PROC_VERIFY : process
begin
  wait until rising_edge(clk) and rst = '0';
  
  -- Check that DUT and TB FIFO are reporting empty simultaneously
  assert (empty = '1' and fifo.IsEmpty) or
         (empty = '0' and not fifo.IsEmpty)
    report "empty=" & std_logic'image(empty) 
      & " while fifo.IsEmpty=" & boolean'image(fifo.IsEmpty)
    severity failure;

  -- Check that the valid signals are matching
  assert rd_valid = fifo_out_valid
    report "rd_valid=" & std_logic'image(rd_valid) 
      & " while fifo_out_valid=" & std_logic'image(fifo_out_valid)
    severity failure;

  -- Check that the output from the DUT matches the TB FIFO
  if rd_valid then
    assert fifo_out = to_integer(unsigned(rd_data))
      report "rd_data=" & integer'image(to_integer(unsigned(rd_data)))
        & " while fifo_out=" & integer'image(fifo_out)
      severity failure;
      report "Pop " & integer'image(fifo_out);
  end if;

end process;

最初のコード行からわかるように、プロセスはクロックの立ち上がりエッジによってトリガーされます。 DUT はクロック プロセスであり、DUT に接続されているダウンストリーム ロジックもクロック信号に同期している必要があります。したがって、クロックの立ち上がりエッジで出力をチェックすることは理にかなっています。

コードの 2 番目のブロックは、empty が DUT からの信号は、テストベンチ FIFO が空の場合にのみアサートされます。このテストに合格するには、DUT とビヘイビア モデルの両方が、FIFO が空であるかどうかに同意する必要があります。

次に、読み取りデータ有効信号の比較が続きます。 DUT と動作モデルは同時にデータを出力する必要があります。それ以外の場合は、何か問題があります。

最後に、DUT からの出力データが、テストベンチ FIFO から取り出した次の要素と一致することを確認します。もちろん、これは rd_valid 信号がアサートされます。つまり、rd_data 信号をサンプリングできます。

カバレッジ データの収集

テストベンチのメイン フローを制御するために、シーケンサー プロセスを作成します。このプロセスは、カバレッジ ビンを初期化し、テストを実行し、すべてのカバレッジ目標が達成されたときにテストベンチを停止します。以下のコードは、完全な «PROC_SEQUENCER» プロセスを示しています。

PROC_SEQUENCER : process
begin

  -- Set up coverage bins
  bin1.AddBins("Write while empty", ONE_BIN);
  bin2.AddBins("Read while full", ONE_BIN);
  bin3.AddBins("Read and write while almost empty", ONE_BIN);
  bin4.AddBins("Read and write while almost full", ONE_BIN);
  bin5.AddBins("Read without write when almost empty", ONE_BIN);
  bin6.AddBins("Write without read when almost full", ONE_BIN);

  wait until rising_edge(clk);
  wait until rising_edge(clk);
  rst <= '0';
  wait until rising_edge(clk);

  loop
    wait until rising_edge(clk);

    -- Collect coverage data
    bin1.ICover(to_integer(wr_en = '1' and empty = '1'));
    bin2.ICover(to_integer(rd_en = '1' and full = '1'));
    bin3.ICover(to_integer(rd_en = '1' and wr_en = '1' and
                           empty = '0' and empty_next = '1'));
    bin4.ICover(to_integer(rd_en = '1' and wr_en = '1' and
                           full = '0' and full_next = '1'));
    bin5.ICover(to_integer(rd_en = '1' and wr_en = '0' and
                           empty = '0' and empty_next = '1'));
    bin6.ICover(to_integer(rd_en = '0' and wr_en = '1' and
                           full = '0' and full_next = '1'));

    -- Stop the test when all coverage goals have been met
    exit when
      bin1.IsCovered and
      bin2.IsCovered and
      bin3.IsCovered and
      bin4.IsCovered and
      bin5.IsCovered and
      bin6.IsCovered;
  end loop;
  
  report("Coverage goals met");

  -- Make sure that the DUT is empty before terminating the test
  wr_en <= force '0';
  rd_en <= force '1';
  loop
    wait until rising_edge(clk);
    exit when empty = '1';
  end loop;

  -- Print coverage data
  bin1.WriteBin;
  bin2.WriteBin;
  bin3.WriteBin;
  bin4.WriteBin;
  bin5.WriteBin;
  bin6.WriteBin;
  
  finish;
end process;

まず、AddBins を呼び出してカバレッジ ビン オブジェクトを初期化します。 この記事の前半で説明したように、それらの手順。その後、リセットが解除された後、カバレッジ データの収集に進みます。クロックの立ち上がりエッジごとに、ループ構造内のコードが実行されます。

ループ内の最初のコード ブロックは、カバレッジ データを収集するためのものです。 ICover を呼び出すことができます ビンに手順を実行して、それが表すカバレッジ ポイントでのヒットを記録します。整数パラメータ 0 を指定した場合 、呼び出しは効果がありません。整数パラメータ 1 を使用する場合 、ヒットとしてカウントされます。

ONE_BIN を使用して初期化したため、各カバレッジ ビン オブジェクト内には 1 つの «bin» しかありません 絶え間ない。 ICover(1) を呼び出すと、この単一のビンに到達できます。 .ブール式を整数 1 に変換することで、カバレッジ ポイントのヒットまたはミスを登録できます。 または 0 to_integer を使用 関数

カバレッジ データが記録された後、IsCovered を呼び出して、すべてのカバレッジ目標が達成されたかどうかを確認します。 すべてのビンで機能します。次に、すべてのカバレッジ目標が達成された場合、ループを終了します。

テストを終了する前に、DUT が空であることを確認します。これを実現するために、wr_en を強制することで、ライター プロセスとリーダー プロセスから制御を引き継ぎます。 '0' への合図 そして rd_en '1' への合図 .

最後に、WriteBin を呼び出して、各カバレッジ目標が達成された回数の統計を出力します。 各カバレッジ ビンで機能します。 finish プロセスの最後にあるキーワードにより、シミュレーターはシミュレーションを停止します。

テストベンチの実行

以下のフォームを使用して、すべての VHDL ファイルを含む ModelSim プロジェクト全体をダウンロードできます。

Zip に含まれている do ファイルを実行してプロジェクトをロードした後、ModelSim コンソールで «runtb» と入力するだけでテストベンチを実行できます。カバレッジの目標がランダムに達成されるため、テストベンチの実行時間はランダムになります。ただし、実際に使用される疑似乱数シーケンスであるため、テスト結果は再現可能です。

コードではシードを初期化しませんでした。つまり、デフォルトのシード値が疑似乱数ジェネレーターに使用されます。 InitSeed を呼び出して別のシードを設定することができます RandomPType の手順 オブジェクト、これは別のランダム シーケンスを生成します。

コンソール出力

«runtb» コマンドを実行した後に ModelSim コンソールに表示される出力を以下に示します。シミュレーションの実行中に、テストベンチ FIFO へのランダムなプッシュとポップが大量に発生します。

VSIM 2> runtb
# ** Warning: Design size of 15929 statements or 2 leaf instances exceeds
#             ModelSim PE Student Edition recommended capacity.
# Expect performance to be quite adversely affected.
# ** Note: Push 34910
#    Time: 790 ns  Iteration: 0  Instance: /ring_buffer_tb
...
# ** Note: Pop 37937
#    Time: 83100 ns  Iteration: 0  Instance: /ring_buffer_tb
# ** Note: Pop 13898
#    Time: 83110 ns  Iteration: 0  Instance: /ring_buffer_tb
# %% WriteBin: 
# %% Write while empty  Bin:(1)   Count = 2  AtLeast = 1
# 
# %% WriteBin: 
# %% Read while full  Bin:(1)   Count = 3  AtLeast = 1
# 
# %% WriteBin: 
# %% Read and write while almost empty  Bin:(1)   Count = 106  AtLeast = 1
# 
# %% WriteBin: 
# %% Read and write while almost full  Bin:(1)   Count = 1  AtLeast = 1
# 
# %% WriteBin: 
# %% Read without write when almost empty  Bin:(1)   Count = 1  AtLeast = 1
# 
# %% WriteBin: 
# %% Write without read when almost full  Bin:(1)   Count = 3  AtLeast = 1
#
# Break in Process PROC_SEQUENCER at C:/crv/ring_buffer_tb.vhd line 127

すべてのカバレッジ目標が達成されると、すべてのカバレッジ ビンの統計が出力されます。 1 回だけヒットしたビンもあれば、106 回ヒットしたビンもあります。しかし、最終的に、各ビンは少なくとも 1 回はヒットしています。したがって、カバレッジ ビンを定義したすべてのイベントがテストおよび検証されたことを知ることができます。

波形

波形を調べて、テストベンチが何をしてきたかを理解しましょう。下の画像は fill_count の波形を示しています アナログ値として表される信号。この信号のトレースが一番上にある場合、FIFO はいっぱいであり、一番下にある場合は空です。

波形からわかるように、リング バッファはランダムに満たされたり空になったりしています。それでも、これらの傾斜と充填レベルの低下を明示的にプログラムしませんでした。それでも、DUT との相互作用の有機的なパターンが見られます。

制約付きランダム検証の詳細

制約付きランダム検証は、テスト ベクトルの順列が多すぎて網羅的テストが実用的でない場合に適したテストベンチ戦略です。ランダムな相互作用は、精度を犠牲にすることなく、有向コーナー ケース テストよりも自然な動作を示します。

カバレッジ データ コレクションを正しく設定している限り、すべてのコーナー ケースが満たされていることを確認できます。追加の利点は、ランダム化されたテストでは、特にテストしていない DUT の弱点が明らかになる可能性が高いことです。すべてのコーナー ケースを把握している限り、それらの有向テストを作成できます。ただし、まれなケースは見過ごされがちです。そのような場合に、制約付きランダム検証方法の恩恵を受けることができます。

この記事は、制約付きランダム検証でできることの表面をなぞっただけです。 OSVVM GitHub ページのドキュメントを読んで、主題をより深く掘り下げることをお勧めします。

また、私が所属していない SynthWorks の Advanced VHDL Testbenches and Verification コースもお勧めします。しかし、私はこのフィジカル コースの 5 日間バージョンに参加しました。このコースは、VHDL 分析および標準化グループ (VASG) の議長である Jim Lewis によって教えられます。全体として、VHDL テストベンチを次のレベルに引き上げたいと考えているすべての企業にとって、大きな投資となります。


VHDL

  1. ケイデンスは10億ゲートのSoC検証をスピードアップ
  2. シーメンスは、シームレスなハードウェア支援検証のためにVeloceに追加します
  3. プロジェクトは、IoTセキュリティの信頼できる設計と検証のフローを調査します
  4. Synopsysは、HBM3IPと検証を使用したマルチダイ設計を可能にします
  5. QR、RFID、温度検証によるアクセス制御
  6. メンテナンスの不快で予測不可能でランダムな側面
  7. Java で乱数を生成する方法
  8. Java 8-ストリーム
  9. 検証グラフィックスによる傾斜ベッド旋盤機能制御
  10. 高速 PCB 設計の差動アイソメ処理とシミュレーション検証
  11. ロータリー コンプレッサーの CAGI 性能検証プログラム