今回から、UVMの検証環境構築について解説していきます。UVMは一見難しそうですが、一度環境を作ってみると、そんなに難しくありません。UVMは検証環境の再利用性向上を目的としていますので、FPGAの検証(シミュレーション)の生産性を向上させることができます。
目次
UVMはUniversal Verification Methodologyの略です。論理回路のシミュレーションを行うためのライブラリです。SystemVerilogで記述されています。UVMはIEEE Std 1800.2-2017となっており、論理回路の設計者や検証(シミュレーション)の担当者にとって、必要なものとなっています。
その特徴は、検証環境の再利用を可能にすることで、検証の生産性を向上するという点にあります。
検証環境の開発は、SystemVerilogのclassの定義(作成)によります。UVMには検証環境開発のためのベースとなるクラスが定義されています。ユーザーはそのクラスを継承してclassを定義します。
関連記事
UVMのテストベンチの構成を図1に、構成要素についての説明を表1に示します。
ブロック | 説明 |
Test bench | シミュレーションのトップモジュール module top(); です。 |
Test | uvm_testを継承して定義します。 UVM Testは、テストケースのトップレベルのクラスです。トップモジュールでこのクラスをインスタンス、またはコマンドライン引数によってテストケースを選択します。 |
Environment | uvm_envを継承して定義します。 UVM EnvironmentはUVM Agent, UVM Scoreboard, 別のUVM Environmentをインスタンスし、検証環境の階層をつくります。 今回の解説では、1つのUVM Agentと1つのUVM Scoreboardをインスタンスします。 |
Agent | uvm_agentを継承して定義します。 UVM Agentは、DUTを取り扱うコンポーネントで構成される、基本的な検証コンポーネントです。UVM Sequencer, UVM Driver, UVM Monitorなどで構成されます。 |
Sequencer | uvm_sequencerを継承して定義します。 UVM Sequencerは、シーケンスを定義・実行することで、トランザクションを生成します。そして、そのトランザクションをUVM Driverに渡します。 (役割は上記ですが、実際にユーザーが書くことはほぼありません。) |
Driver | uvm_driverを継承して定義します。 UVM Driverは、UVM Sequencerからトランザクションを取得し、シグナルレベルに変換します。そして、DUTをドライブします。 |
Collector | uvm_componentを継承して定義します。 Collectorは、シグナルレベルであるDUTのレスポンスをサンプリングし、トランザクションに変換します。そして、そのトランザクションをUVM Monitorに渡します。 UVMのマニュアルでは「UVM Collector」というのは定義されていません。レスポンスのサンプリングはUVM Monitorで行うこともできますが、Collectorとしてサンプリング機能を分離することで、再利用性が高まります。 |
Monitor | uvm_monitorを継承して定義します。 UVM Monitorは、Collectorからトランザクションを受け取り、DUTのレスポンスのチェックや、カバレッジ計算を行います。また、他の解析用コンポーネントにトランザクションを転送します。 |
Scoreboard | uvm_scoreboardを継承して定義します。 UVM ScoreboardはDUTの動作のチェックを行います。通常、UVM AgentからDUTの入力、出力の情報をトランザクションで取得します。その後、期待値の生成と比較などを行います。 |
Sequences | uvm_sequenceを継承して定義します。 UVM Sequenceはコンポーネントではなく、オブジェクトです。 DUTをドライブするためのトランザクションを生成する手順を保有します。 |
構築するUVM検証環境のDUTとして、Muti-Clock Multiplier (MC_MULT)を作成しました(図2)。FPGAのハードウェア乗算器(DSP)を使わずに、ロジックのみで乗算を行うモジュールです。筆算の原理を利用します(図3)。名前の通り、積を算出するまでに複数クロック必要です(図4)。
ソースコードは下記です。ちょっと長いです。パラメータ P_BW_A と P_BW_B で入出力データのビット幅を指定できます。STARTがアサートされてから積が出力されるまで、(P_BW_A+1)クロック必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: MC_MULT // Function: Multi clock multiplier ////////////////////////////////////////////////////////////////////////////////// module MC_MULT #( parameter P_BW_A = 8, /* Bit width of A_IN. P_BW_A+1 clocks needed for calculation */ parameter P_BW_B = 8 /* Bit width of B_IN */ ) ( input wire CLK, input wire RST, /* Active high */ input wire [P_BW_A - 1:0] A_IN, input wire [P_BW_B - 1:0] B_IN, input wire START, /* Caluculation start */ output wire [P_BW_A + P_BW_B - 1:0] MULT_OUT, /* Multiply result. This value is valid when VALID==1'b1 */ output reg VALID /* Result valid. 1 clock pulse */ ); /********************************************************************************/ /**** Functions *****************************************************************/ /********************************************************************************/ function integer min_bw (input integer n); /* Minimum required bit width for n */ integer cnt; integer n_shift; begin cnt = 0; n_shift = n; while (n_shift > 0) begin n_shift = n_shift >> 1; cnt = cnt + 1; end min_bw = cnt; end endfunction /********************************************************************************/ /**** Local parameters **********************************************************/ /********************************************************************************/ localparam P_BW_CNT = min_bw(P_BW_A - 1); localparam [P_BW_CNT - 1:0] P_CNT_MAX = P_BW_A - 1; /********************************************************************************/ /**** Signals *******************************************************************/ /********************************************************************************/ /* Rising edge of START*/ reg start_ff1; wire start_redg; /* Caluculation running */ reg calc_running; /* Counter */ reg [P_BW_CNT - 1:0] counter; /* Latch input data and shif */ reg [P_BW_A + P_BW_B - 1:0] a_lat; reg [P_BW_A + P_BW_B - 1:0] b_lat; /* Accumlator */ reg [P_BW_A + P_BW_B - 1:0] accumlator; /********************************************************************************/ /**** Behaviour *****************************************************************/ /********************************************************************************/ /* Rising edge of START*/ always @ (posedge CLK or posedge RST) begin if (RST) begin start_ff1 <= 1'b0; end else begin start_ff1 <= START; end end assign start_redg = START & (~start_ff1); /* Caluculation running */ always @ (posedge CLK or posedge RST) begin if (RST) begin calc_running <= 1'b0; end else begin if (start_redg == 1'b1) begin calc_running <= 1'b1; end else begin if (VALID == 1'b1) begin calc_running <= 1'b0; end else begin calc_running <= calc_running; end end end end /* Counter */ always @ (posedge CLK or posedge RST) begin if (RST) begin counter <= 'd0; end else begin if (calc_running == 1'b1) begin if (counter <= P_CNT_MAX) begin counter <= counter + 1'b1; end else begin counter <= 'd0; end end else begin counter <= 'd0; end end end /* Valid signal */ always @ (posedge CLK or posedge RST) begin if (RST) begin VALID <= 1'b0; end else begin if ((calc_running == 1'b1) && (counter == P_CNT_MAX)) begin VALID <= 1'b1; end else begin VALID <= 1'b0; end end end /* Latch input data A and shift */ always @ (posedge CLK or posedge RST) begin if (RST) begin a_lat <= 'd0; end else begin if (calc_running == 1'b1) begin a_lat <= a_lat >> 1; end else begin if (start_redg == 1'b1) begin a_lat <= A_IN; end else begin a_lat <= 'd0; end end end end /* Latch input data B and shift */ always @ (posedge CLK or posedge RST) begin if (RST) begin b_lat <= 'd0; end else begin if (calc_running == 1'b1) begin b_lat <= b_lat << 1; /* This is correct */ // b_lat <= b_lat >> 1; /* This is miss cording */ end else begin if (start_redg == 1'b1) begin b_lat <= B_IN; end else begin b_lat <= 'd0; end end end end /* Accumlator */ always @ (posedge CLK or posedge RST) begin if (RST) begin accumlator <= 'd0; end else begin if (calc_running == 1'b1) begin if (a_lat[0] == 1'b1) begin accumlator <= accumlator + b_lat; end else begin accumlator <= accumlator; end end else begin accumlator <= 'd0; end end end /* Result output */ assign MULT_OUT = accumlator; endmodule |
今回は、UVMの検証環境全体について解説しました。次回から各コンポーネントの作成方法について詳しく解説していきます。
ちょっと長くなっちゃいました (汗)