Verilog テストベンチとは?
VerilogまたはVHDLのテストベンチ(test bench)とは、FPGAやLSI/ASIC開発において、VerilogまたはVHDLで記述した回路が期待通りの動作をしているか確認するための、一連のファイルのことです。テストベンチファイルには、入力信号、クロック、リセット信号などを記述し、それらの信号を回路記述したデザインファイルに送り、出力を期待値と照合することで設計の検証を行います。
![テストベンチとHDLシミュレーター、デザインファイル 図1. テストベンチとHDLシミュレーター、デザインファイル](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image.adapt.full.medium.png/1732770301923.png)
図1. テストベンチとHDLシミュレーター、デザインファイル
通常は回路のデザインファイルと同じ言語で記述されます。デザインファイルは、論理合成・配置配線を行い、回路としてFPGAやASICに実装されるのに対し、テストベンチファイルはシミュレーションでのみ使用されます。テストベンチは通常、実機に実装する前に実行して、設計の欠陥を発見するのに役立て、回路の品質と信頼性を向上させることができます。
VerilogやVHDLで記述するデジタル回路においては、回路を動作させるためにクロック、リセット、クロックイネーブルなどの信号を入力する必要があります。入力信号は、時系列のベクトルデータとして定義します。テストベンチにおいて、クロック、リセット、クロックイネーブルに加えて、デザインファイル用の入力信号のことをスティミュラス(Stimulus)、入力信号と出力信号の期待値をテストベクタ(Test vector)と呼びます。また、動作を検証するためには、デザインファイルの出力を期待値のデータと照合して合否を判定する条件を記述します。条件には、出力信号の期待される値、許容誤差、タイミングチェックなどが含まれます。
![テストベンチのファイル構成 図2. テストベンチのファイル構成](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_223298195.adapt.full.medium.png/1732770301939.png)
図2. テストベンチのファイル構成
// ------------------------------------------------------------- // // File Name: DUT_tb.v // // ------------------------------------------------------------- `timescale 1 ns / 1 ns module DUT_tb; reg clk; reg reset; wire clk_enable; wire Out1_done; // ufix1 wire rdEnb; wire Out1_done_enb; // ufix1 reg [5:0] Out1_addr; // ufix6 wire Out1_active; // ufix1 reg [5:0] Data_Type_Conversion_out1_addr; // ufix6 wire Data_Type_Conversion_out1_active; // ufix1 reg tb_enb_delay; wire Data_Type_Conversion_out1_enb; // ufix1 wire [5:0] Data_Type_Conversion_out1_addr_delay_1; // ufix6 reg signed [31:0] fp_In1; // sfix32 reg signed [23:0] rawData_In1; // sfix24_En22 reg signed [31:0] status_In1; // sfix32 reg signed [23:0] holdData_In1; // sfix24_En22 reg signed [23:0] In1_offset; // sfix24_En22 wire signed [23:0] In1; // sfix24_En22 reg check1_done; // ufix1 wire snkDonen; wire resetn; wire tb_enb; wire ce_out; wire signed [23:0] Out1; // sfix24_En18 wire Out1_enb; // ufix1 wire Out1_lastAddr; // ufix1 wire [5:0] Out1_addr_delay_1; // ufix6 reg signed [31:0] fp_Out1_expected; // sfix32 reg signed [23:0] Out1_expected; // sfix24_En18 reg signed [31:0] status_Out1_expected; // sfix32 wire signed [23:0] Out1_ref; // sfix24_En18 reg Out1_testFailure; // ufix1 wire testFailure; // ufix1 assign Out1_done_enb = Out1_done & rdEnb; assign Out1_active = Out1_addr != 6'b101100; assign Data_Type_Conversion_out1_active = Data_Type_Conversion_out1_addr != 6'b101100; assign Data_Type_Conversion_out1_enb = Data_Type_Conversion_out1_active & (rdEnb & tb_enb_delay); // Count limited, Unsigned Counter // initial value = 0 // step value = 1 // count to value = 44 always @(posedge clk or posedge reset) begin : DataTypeConversion_process if (reset == 1'b1) begin Data_Type_Conversion_out1_addr <= 6'b000000; end else begin if (Data_Type_Conversion_out1_enb) begin if (Data_Type_Conversion_out1_addr >= 6'b101100) begin Data_Type_Conversion_out1_addr <= 6'b000000; end else begin Data_Type_Conversion_out1_addr <= Data_Type_Conversion_out1_addr + 6'b000001; end end end end assign #1 Data_Type_Conversion_out1_addr_delay_1 = Data_Type_Conversion_out1_addr; // Data source for In1 initial begin : In1_fileread fp_In1 = $fopen("In1.dat", "r"); status_In1 = $rewind(fp_In1); end always @(Data_Type_Conversion_out1_addr_delay_1, rdEnb, tb_enb_delay) begin if (tb_enb_delay == 0) begin rawData_In1 <= 24'bx; end else if (rdEnb == 1) begin status_In1 = $fscanf(fp_In1, "%h", rawData_In1); end end // holdData reg for Data_Type_Conversion_out1 always @(posedge clk or posedge reset) begin : stimuli_Data_Type_Conversion_out1 if (reset) begin holdData_In1 <= 24'bx; end else begin holdData_In1 <= rawData_In1; end end always @(rawData_In1 or rdEnb) begin : stimuli_Data_Type_Conversion_out1_1 if (rdEnb == 1'b0) begin In1_offset <= holdData_In1; end else begin In1_offset <= rawData_In1; end end assign #2 In1 = In1_offset; assign snkDonen = ~ check1_done; assign resetn = ~ reset; assign tb_enb = resetn & snkDonen; // Delay inside enable generation: register depth 1 always @(posedge clk or posedge reset) begin : u_enable_delay if (reset) begin tb_enb_delay <= 0; end else begin tb_enb_delay <= tb_enb; end end assign rdEnb = (check1_done == 1'b0 ? tb_enb_delay : 1'b0); assign #2 clk_enable = rdEnb; initial begin : reset_gen reset <= 1'b1; # (20); @ (posedge clk) # (2); reset <= 1'b0; end always begin : clk_gen clk <= 1'b1; # (5); clk <= 1'b0; # (5); if (check1_done == 1'b1) begin clk <= 1'b1; # (5); clk <= 1'b0; # (5); $stop; end end DUT u_DUT (.clk(clk), .reset(reset), .clk_enable(clk_enable), .In1(In1), // sfix24_En22 .ce_out(ce_out), .Out1(Out1) // sfix24_En18 ); assign Out1_enb = ce_out & Out1_active; // Count limited, Unsigned Counter // initial value = 0 // step value = 1 // count to value = 44 always @(posedge clk or posedge reset) begin : c_2_process if (reset == 1'b1) begin Out1_addr <= 6'b000000; end else begin if (Out1_enb) begin if (Out1_addr >= 6'b101100) begin Out1_addr <= 6'b000000; end else begin Out1_addr <= Out1_addr + 6'b000001; end end end end assign Out1_lastAddr = Out1_addr >= 6'b101100; assign Out1_done = Out1_lastAddr & resetn; // Delay to allow last sim cycle to complete always @(posedge clk or posedge reset) begin : checkDone_1 if (reset) begin check1_done <= 0; end else begin if (Out1_done_enb) begin check1_done <= Out1_done; end end end assign #1 Out1_addr_delay_1 = Out1_addr; // Data source for Out1_expected initial begin : Out1_expected_fileread fp_Out1_expected = $fopen("Out1_expected.dat", "r"); status_Out1_expected = $rewind(fp_Out1_expected); end always @(Out1_addr_delay_1, ce_out, tb_enb_delay) begin if (tb_enb_delay == 0) begin Out1_expected <= 24'bx; end else if (ce_out == 1) begin status_Out1_expected = $fscanf(fp_Out1_expected, "%h", Out1_expected); end end assign Out1_ref = Out1_expected; always @(posedge clk or posedge reset) begin : Out1_checker if (reset == 1'b1) begin Out1_testFailure <= 1'b0; end else begin if (ce_out == 1'b1 && Out1 !== Out1_ref) begin Out1_testFailure <= 1'b1; $display("ERROR in Out1 at time %t : Expected '%h' Actual '%h'", $time, Out1_ref, Out1); end end end assign testFailure = Out1_testFailure; always @(posedge clk) begin : completed_msg if (check1_done == 1'b1) begin if (testFailure == 1'b0) begin $display("**************TEST COMPLETED (PASSED)**************"); end else begin $display("**************TEST COMPLETED (FAILED)**************"); end end end endmodule // DUT_tb
デザインファイルはFPGA/ASIC実装が前提で、論理合成を行う必要があるので、論理合成可能な記述を行う必要があります。他方、テストベンチファイルは論理合成を行わずに、シミュレーションのみで使用されるファイルのため、論理合成できない様々な記述、例えばファイルI/Oや文字列表示などの記述を行うことができます。
テストベンチで扱うことができる記述スタイル(ファイルI/Oや標準入出力)
テストベンチはシミュレーションのみで使用されるため、論理合成出来ない記述をすることも可能です。例えばテストベクタは、別ファイルに記述したデータをファイルI/O記述を使って読み込みます。
Verilog HDLではシステムタスクによる標準入出力、ファイルI/O、三角関数などの数学関数を扱う関数が用意されています。$fopen
, $fclose
, $fscanf
, $display
, $monitor
などがあります。ファイルからデータを読み込むには次のように記述します。
fp = $fopen("data_in0.dat", "r"); outputData = $fscanf(fp, "%h", dataFormat);
エラーや合否判定結果は次のように$displayを使って記述し、シミュレータに文字列を出力します。
if (ce_out == 1'b1 && Out1 !== Out1_ref) begin Out1_testFailure <= 1'b1; $display("ERROR at time %t : Expected '%h' Actual '%h'", $time, expextedData, outputData); end
VHDLには同様の機能として、TEXT I/O機能があります。これを利用するためには、パッケージを呼び出す必要があります。
LIBRARY IEEE; USE IEEE.std_logic_textio.ALL; LIBRARY STD; USE STD.textio.ALL;
次にファイルを呼び出して、ファイルから1行ずつデータを取得します。
VARIABLE l: LINE; FILE fp: TEXT open READ_MODE is "data_in0.dat"; READLINE(fp, l); -- 1行読み出す HREAD(l, outputData); -- 読み出した16進数値を変数に代入
エラーや合否判定は次のようにREPORTを使って記述し、シミュレータに文字列を出力します。
IF ce_out = '1' AND Out1_signed /= Out1_ref THEN Out1_testFailure <= '1'; ASSERT FALSE REPORT "Error : Expected " & to_hex(expectedData) & (" Actual " & to_hex(outputData)) SEVERITY ERROR; END IF;
テストベンチの肝はスティミュラスと期待値
テストベンチを作成する際には、まずクロック、リセット、クロックイネーブルなどの回路を制御する信号を定義します。次に、回路を駆動するためのスティミュラスを考え、その処理結果となる期待値を作成して行きます。
スティミュラスを作る際に重要なこととして、次のことを考慮します。
- 実際のユースケースに沿った信号となっていること
- 設計対象の回路をできるだけ網羅的にテストする信号となっていること
- HDLシミュレータの中には網羅性=カバレッジ率を測定できるものもがあります。
- 100%のカバレッジを満たすのは現実的に難しいので、妥協やツールの手助けも必要です。
- 実行時間がかかりすぎないこと
- 網羅的なテストをするために長尺の信号を作成して、シミュレーションに1年かかってしまうようでは、現実的に使用することができないので、実行時間を考慮する必要があります。
期待値との比較を行う際に重要なこととして、次のことを考慮します。
- クロックサイクル、ビット精度を正確に表現した値となっていること
- クロックサイクルで動作する設計対象内のパイプライン処理を考慮したレイテンシが正確に反映されている必要があります。でないと設計対象の出力と期待値のタイミングがずれてしまい、照合結果が合わなくなります。
- 丸め誤差を含めたビット精度を正確に表現していないと、期待値と出力の間に誤差が発生する可能性があります。
- 結果の合格、不合格を判断できる内容となっていること
- 期待値と出力を照合して、合格、不合格を判断し、文字列を出力するなどして合否結果を示します。
コード例
always @(posedge clk) begin : completed_msg if (check1_done == 1'b1) begin if (testFailure == 1'b0) begin $display("**************TEST COMPLETED (PASSED)**************"); end else begin $display("**************TEST COMPLETED (FAILED)**************"); end end end
![結果を文字列で示すHDLシミュレータ実行画面 図3. 結果を文字列で示すHDLシミュレータ実行画面](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_1048550627.adapt.full.medium.png/1732770301994.png)
図3. 結果を文字列で示すHDLシミュレータ実行画面
テストベンチを実行するHDLシミュレータ
テストベンチの実行には、VHDL/Verilogコードを実行するHDLシミュレータを利用します。ASICやFPGA開発用にEDAソフトウェアベンダー各社からHDLシミュレータが販売されています。
- SIEMENS社(旧Mentor社) ModelSim, Questa advanced simulator
- Cadence社 Xcelium(旧Incisive)
- Synopsys社 VCS®
- Aldec社 Rivera-PRO™, Active-HDL™
フリーのHDLシミュレータも入手できます。
- Icarus Verilog (Verilog用)
- Veritak (Verilog用)
- Alliance (VHDL用)
- GHDL (VHDL用)
AMD社はFPGA設計ツールVivado®にHDLシミュレータを同梱しており、追加料金無しで使用できます。
![AMD Vivado SimulatorでHDLシミュレーションを行った結果 図4. AMD Vivado SimulatorでHDLシミュレーションを行った結果](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_903908011.adapt.full.medium.png/1732770302009.png)
図4. AMD Vivado SimulatorでHDLシミュレーションを行った結果
テストベンチの重要性
Wilson research Groupによる調査では、FPGAプロジェクトで検証に使われた時間の中央値は40-50%という結果が出ています。設計を効率化するCベース高位合成やモデルベースデザイン (モデルベース開発、MBD)などの新しい手法だけでなく、検証においても効率化手法に対する関心が高まっています。アサーション・ベース検証、UVM、モデルベースデザインについての詳細は後述します。
![検証に割かれるFPGAプロジェクト時間の割合 図5. 検証に割かれるFPGAプロジェクト時間の割合](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_903908011_copy.adapt.full.medium.png/1732770302026.png)
図5. 検証に割かれるFPGAプロジェクト時間の割合
テストを効率化する新しい検証手法
テストを効率化するために、次のような新しい検証手法が使用されています。
- アサーション・ベース検証 (ABV):設計対象やそのインターフェイスに期待される内部動作を定義しておき、自動的にそれを監視しながらシミュレーションを実行する検証手法です。SVA (SystemVerilog Assertions)やPSL (Property Specification Language)が代表的な言語です。
- UVM (Universal Verification Methodology):再利用性による効率化を目的に、検証分野で推奨されている技術、ルール、慣習、規律等をコードとして具体化したSystemVerilogのクラス・ライブラリーです。
- モデルベースデザイン (モデルベース開発、MBD):設計対象および影響を与える周辺環境も含めたシステムレベルでモデリングしたものを、シミュレーション~自動コード生成・テストベンチ生成を行って設計する手法です。ブロック線図や状態遷移図で描いたモデルから、等価なVHDL/Verilogコードの回路記述およびテストベンチを自動生成します。
テストベンチの作成例
いくつかのフォーマットで作成されたテストベンチの作成例を見てみましょう。
VHDL/Verilogテストベンチ
このテストベンチはテストベンチ本体、スティミュラスと期待値のデータ、合計3種類のファイルで構成しています。これらのファイルと設計対象のファイルをHDLシミュレータで実行して、期待通りの動作をしているかどうかの検証を行います。
// ------------------------------------------------------------- // // File Name: DUT_tb.v // // ------------------------------------------------------------- `timescale 1 ns / 1 ns module DUT_tb; reg clk; reg reset; wire clk_enable; wire Out1_done; // ufix1 wire rdEnb; wire Out1_done_enb; // ufix1 reg [5:0] Out1_addr; // ufix6 wire Out1_active; // ufix1 reg [5:0] Data_Type_Conversion_out1_addr; // ufix6 wire Data_Type_Conversion_out1_active; // ufix1 reg tb_enb_delay; wire Data_Type_Conversion_out1_enb; // ufix1 wire [5:0] Data_Type_Conversion_out1_addr_delay_1; // ufix6 reg signed [31:0] fp_In1; // sfix32 reg signed [23:0] rawData_In1; // sfix24_En22 reg signed [31:0] status_In1; // sfix32 reg signed [23:0] holdData_In1; // sfix24_En22 reg signed [23:0] In1_offset; // sfix24_En22 wire signed [23:0] In1; // sfix24_En22 reg check1_done; // ufix1 wire snkDonen; wire resetn; wire tb_enb; wire ce_out; wire signed [23:0] Out1; // sfix24_En18 wire Out1_enb; // ufix1 wire Out1_lastAddr; // ufix1 wire [5:0] Out1_addr_delay_1; // ufix6 reg signed [31:0] fp_Out1_expected; // sfix32 reg signed [23:0] Out1_expected; // sfix24_En18 reg signed [31:0] status_Out1_expected; // sfix32 wire signed [23:0] Out1_ref; // sfix24_En18 reg Out1_testFailure; // ufix1 wire testFailure; // ufix1 assign Out1_done_enb = Out1_done & rdEnb; assign Out1_active = Out1_addr != 6'b101100; assign Data_Type_Conversion_out1_active = Data_Type_Conversion_out1_addr != 6'b101100; assign Data_Type_Conversion_out1_enb = Data_Type_Conversion_out1_active & (rdEnb & tb_enb_delay); // Count limited, Unsigned Counter // initial value = 0 // step value = 1 // count to value = 44 always @(posedge clk or posedge reset) begin : DataTypeConversion_process if (reset == 1'b1) begin Data_Type_Conversion_out1_addr <= 6'b000000; end else begin if (Data_Type_Conversion_out1_enb) begin if (Data_Type_Conversion_out1_addr >= 6'b101100) begin Data_Type_Conversion_out1_addr <= 6'b000000; end else begin Data_Type_Conversion_out1_addr <= Data_Type_Conversion_out1_addr + 6'b000001; end end end end assign #1 Data_Type_Conversion_out1_addr_delay_1 = Data_Type_Conversion_out1_addr; // Data source for In1 initial begin : In1_fileread fp_In1 = $fopen("In1.dat", "r"); status_In1 = $rewind(fp_In1); end always @(Data_Type_Conversion_out1_addr_delay_1, rdEnb, tb_enb_delay) begin if (tb_enb_delay == 0) begin rawData_In1 <= 24'bx; end else if (rdEnb == 1) begin status_In1 = $fscanf(fp_In1, "%h", rawData_In1); end end // holdData reg for Data_Type_Conversion_out1 always @(posedge clk or posedge reset) begin : stimuli_Data_Type_Conversion_out1 if (reset) begin holdData_In1 <= 24'bx; end else begin holdData_In1 <= rawData_In1; end end always @(rawData_In1 or rdEnb) begin : stimuli_Data_Type_Conversion_out1_1 if (rdEnb == 1'b0) begin In1_offset <= holdData_In1; end else begin In1_offset <= rawData_In1; end end assign #2 In1 = In1_offset; assign snkDonen = ~ check1_done; assign resetn = ~ reset; assign tb_enb = resetn & snkDonen; // Delay inside enable generation: register depth 1 always @(posedge clk or posedge reset) begin : u_enable_delay if (reset) begin tb_enb_delay <= 0; end else begin tb_enb_delay <= tb_enb; end end assign rdEnb = (check1_done == 1'b0 ? tb_enb_delay : 1'b0); assign #2 clk_enable = rdEnb; initial begin : reset_gen reset <= 1'b1; # (20); @ (posedge clk) # (2); reset <= 1'b0; end always begin : clk_gen clk <= 1'b1; # (5); clk <= 1'b0; # (5); if (check1_done == 1'b1) begin clk <= 1'b1; # (5); clk <= 1'b0; # (5); $stop; end end DUT u_DUT (.clk(clk), .reset(reset), .clk_enable(clk_enable), .In1(In1), // sfix24_En22 .ce_out(ce_out), .Out1(Out1) // sfix24_En18 ); assign Out1_enb = ce_out & Out1_active; // Count limited, Unsigned Counter // initial value = 0 // step value = 1 // count to value = 44 always @(posedge clk or posedge reset) begin : c_2_process if (reset == 1'b1) begin Out1_addr <= 6'b000000; end else begin if (Out1_enb) begin if (Out1_addr >= 6'b101100) begin Out1_addr <= 6'b000000; end else begin Out1_addr <= Out1_addr + 6'b000001; end end end end assign Out1_lastAddr = Out1_addr >= 6'b101100; assign Out1_done = Out1_lastAddr & resetn; // Delay to allow last sim cycle to complete always @(posedge clk or posedge reset) begin : checkDone_1 if (reset) begin check1_done <= 0; end else begin if (Out1_done_enb) begin check1_done <= Out1_done; end end end assign #1 Out1_addr_delay_1 = Out1_addr; // Data source for Out1_expected initial begin : Out1_expected_fileread fp_Out1_expected = $fopen("Out1_expected.dat", "r"); status_Out1_expected = $rewind(fp_Out1_expected); end always @(Out1_addr_delay_1, ce_out, tb_enb_delay) begin if (tb_enb_delay == 0) begin Out1_expected <= 24'bx; end else if (ce_out == 1) begin status_Out1_expected = $fscanf(fp_Out1_expected, "%h", Out1_expected); end end assign Out1_ref = Out1_expected; always @(posedge clk or posedge reset) begin : Out1_checker if (reset == 1'b1) begin Out1_testFailure <= 1'b0; end else begin if (ce_out == 1'b1 && Out1 !== Out1_ref) begin Out1_testFailure <= 1'b1; $display("ERROR in Out1 at time %t : Expected '%h' Actual '%h'", $time, Out1_ref, Out1); end end end assign testFailure = Out1_testFailure; always @(posedge clk) begin : completed_msg if (check1_done == 1'b1) begin if (testFailure == 1'b0) begin $display("**************TEST COMPLETED (PASSED)**************"); end else begin $display("**************TEST COMPLETED (FAILED)**************"); end end end endmodule // DUT_tb
-- ------------------------------------------------------------- -- -- File Name: DUT_tb.vhd -- ------------------------------------------------------------- LIBRARY IEEE; USE IEEE.std_logic_textio.ALL; USE IEEE.std_logic_1164.ALL; USE IEEE.numeric_std.ALL; LIBRARY STD; USE STD.textio.ALL; LIBRARY work; USE work.DUT_pkg.ALL; USE work.DUT_tb_pkg.ALL; ENTITY DUT_tb IS END DUT_tb; ARCHITECTURE rtl OF DUT_tb IS -- Component Declarations COMPONENT DUT PORT( clk : IN std_logic; reset : IN std_logic; clk_enable : IN std_logic; In1 : IN std_logic_vector(23 DOWNTO 0); -- sfix24_En22 ce_out : OUT std_logic; Out1 : OUT std_logic_vector(23 DOWNTO 0) -- sfix24_En18 ); END COMPONENT; -- Component Configuration Statements FOR ALL : DUT USE ENTITY work.DUT(rtl); -- Signals SIGNAL clk : std_logic; SIGNAL reset : std_logic; SIGNAL clk_enable : std_logic; SIGNAL Out1_done : std_logic; -- ufix1 SIGNAL rdEnb : std_logic; SIGNAL Out1_done_enb : std_logic; -- ufix1 SIGNAL Out1_addr : unsigned(5 DOWNTO 0); -- ufix6 SIGNAL Out1_active : std_logic; -- ufix1 SIGNAL Data_Type_Conversion_out1_addr : unsigned(5 DOWNTO 0); -- ufix6 SIGNAL Data_Type_Conversion_out1_active : std_logic; -- ufix1 SIGNAL tb_enb_delay : std_logic; SIGNAL Data_Type_Conversion_out1_enb : std_logic; -- ufix1 SIGNAL Data_Type_Conversion_out1_addr_delay_1 : unsigned(5 DOWNTO 0); -- ufix6 SIGNAL rawData_In1 : signed(23 DOWNTO 0); -- sfix24_En22 SIGNAL holdData_In1 : signed(23 DOWNTO 0); -- sfix24_En22 SIGNAL In1_offset : signed(23 DOWNTO 0); -- sfix24_En22 SIGNAL In1 : signed(23 DOWNTO 0); -- sfix24_En22 SIGNAL In1_1 : std_logic_vector(23 DOWNTO 0); -- ufix24 SIGNAL check1_done : std_logic; -- ufix1 SIGNAL snkDonen : std_logic; SIGNAL resetn : std_logic; SIGNAL tb_enb : std_logic; SIGNAL ce_out : std_logic; SIGNAL Out1 : std_logic_vector(23 DOWNTO 0); -- ufix24 SIGNAL Out1_enb : std_logic; -- ufix1 SIGNAL Out1_lastAddr : std_logic; -- ufix1 SIGNAL Out1_signed : signed(23 DOWNTO 0); -- sfix24_En18 SIGNAL Out1_addr_delay_1 : unsigned(5 DOWNTO 0); -- ufix6 SIGNAL Out1_expected : signed(23 DOWNTO 0); -- sfix24_En18 SIGNAL Out1_ref : signed(23 DOWNTO 0); -- sfix24_En18 SIGNAL Out1_testFailure : std_logic; -- ufix1 BEGIN u_DUT : DUT PORT MAP( clk => clk, reset => reset, clk_enable => clk_enable, In1 => In1_1, -- sfix24_En22 ce_out => ce_out, Out1 => Out1 -- sfix24_En18 ); Out1_done_enb <= Out1_done AND rdEnb; Out1_active <= '1' WHEN Out1_addr /= to_unsigned(16#2C#, 6) ELSE '0'; Data_Type_Conversion_out1_active <= '1' WHEN Data_Type_Conversion_out1_addr /= to_unsigned(16#2C#, 6) ELSE '0'; Data_Type_Conversion_out1_enb <= Data_Type_Conversion_out1_active AND (rdEnb AND tb_enb_delay); -- Count limited, Unsigned Counter -- initial value = 0 -- step value = 1 -- count to value = 44 DataTypeConversion_process : PROCESS (clk, reset) BEGIN IF reset = '1' THEN Data_Type_Conversion_out1_addr <= to_unsigned(16#00#, 6); ELSIF clk'EVENT AND clk = '1' THEN IF Data_Type_Conversion_out1_enb = '1' THEN IF Data_Type_Conversion_out1_addr >= to_unsigned(16#2C#, 6) THEN Data_Type_Conversion_out1_addr <= to_unsigned(16#00#, 6); ELSE Data_Type_Conversion_out1_addr <= Data_Type_Conversion_out1_addr + to_unsigned(16#01#, 6); END IF; END IF; END IF; END PROCESS DataTypeConversion_process; Data_Type_Conversion_out1_addr_delay_1 <= Data_Type_Conversion_out1_addr AFTER 1 ns; -- Data source for In1 In1_fileread: PROCESS (Data_Type_Conversion_out1_addr_delay_1, tb_enb_delay, rdEnb) FILE fp: TEXT open READ_MODE is "In1.dat"; VARIABLE l: LINE; VARIABLE read_data: std_logic_vector(23 DOWNTO 0); BEGIN IF tb_enb_delay /= '1' THEN ELSIF rdEnb = '1' AND NOT ENDFILE(fp) THEN READLINE(fp, l); HREAD(l, read_data); END IF; rawData_In1 <= signed(read_data(23 DOWNTO 0)); END PROCESS In1_fileread; -- holdData reg for Data_Type_Conversion_out1 stimuli_Data_Type_Conversion_out1_process: PROCESS (clk, reset) BEGIN IF reset = '1' THEN holdData_In1 <= (OTHERS => 'X'); ELSIF clk'event AND clk = '1' THEN holdData_In1 <= rawData_In1; END IF; END PROCESS stimuli_Data_Type_Conversion_out1_process; stimuli_Data_Type_Conversion_out1_1: PROCESS (rawData_In1, rdEnb) BEGIN IF rdEnb = '0' THEN In1_offset <= holdData_In1; ELSE In1_offset <= rawData_In1; END IF; END PROCESS stimuli_Data_Type_Conversion_out1_1; In1 <= In1_offset AFTER 2 ns; In1_1 <= std_logic_vector(In1); snkDonen <= NOT check1_done; resetn <= NOT reset; tb_enb <= resetn AND snkDonen; -- Delay inside enable generation: register depth 1 u_enable_delay_process: PROCESS (clk, reset) BEGIN IF reset = '1' THEN tb_enb_delay <= '0'; ELSIF clk'event AND clk = '1' THEN tb_enb_delay <= tb_enb; END IF; END PROCESS u_enable_delay_process; rdEnb <= tb_enb_delay WHEN check1_done = '0' ELSE '0'; clk_enable <= rdEnb AFTER 2 ns; reset_gen: PROCESS BEGIN reset <= '1'; WAIT FOR 20 ns; WAIT UNTIL clk'event AND clk = '1'; WAIT FOR 2 ns; reset <= '0'; WAIT; END PROCESS reset_gen; clk_gen: PROCESS BEGIN clk <= '1'; WAIT FOR 5 ns; clk <= '0'; WAIT FOR 5 ns; IF check1_done = '1' THEN clk <= '1'; WAIT FOR 5 ns; clk <= '0'; WAIT FOR 5 ns; WAIT; END IF; END PROCESS clk_gen; Out1_enb <= ce_out AND Out1_active; -- Count limited, Unsigned Counter -- initial value = 0 -- step value = 1 -- count to value = 44 c_3_process : PROCESS (clk, reset) BEGIN IF reset = '1' THEN Out1_addr <= to_unsigned(16#00#, 6); ELSIF clk'EVENT AND clk = '1' THEN IF Out1_enb = '1' THEN IF Out1_addr >= to_unsigned(16#2C#, 6) THEN Out1_addr <= to_unsigned(16#00#, 6); ELSE Out1_addr <= Out1_addr + to_unsigned(16#01#, 6); END IF; END IF; END IF; END PROCESS c_3_process; Out1_lastAddr <= '1' WHEN Out1_addr >= to_unsigned(16#2C#, 6) ELSE '0'; Out1_done <= Out1_lastAddr AND resetn; -- Delay to allow last sim cycle to complete checkDone_1_process: PROCESS (clk, reset) BEGIN IF reset = '1' THEN check1_done <= '0'; ELSIF clk'event AND clk = '1' THEN IF Out1_done_enb = '1' THEN check1_done <= Out1_done; END IF; END IF; END PROCESS checkDone_1_process; Out1_signed <= signed(Out1); Out1_addr_delay_1 <= Out1_addr AFTER 1 ns; -- Data source for Out1_expected Out1_expected_fileread: PROCESS (Out1_addr_delay_1, tb_enb_delay, ce_out) FILE fp: TEXT open READ_MODE is "Out1_expected.dat"; VARIABLE l: LINE; VARIABLE read_data: std_logic_vector(23 DOWNTO 0); BEGIN IF tb_enb_delay /= '1' THEN ELSIF ce_out = '1' AND NOT ENDFILE(fp) THEN READLINE(fp, l); HREAD(l, read_data); END IF; Out1_expected <= signed(read_data(23 DOWNTO 0)); END PROCESS Out1_expected_fileread; Out1_ref <= Out1_expected; Out1_signed_checker: PROCESS (clk, reset) BEGIN IF reset = '1' THEN Out1_testFailure <= '0'; ELSIF clk'event AND clk = '1' THEN IF ce_out = '1' AND Out1_signed /= Out1_ref THEN Out1_testFailure <= '1'; ASSERT FALSE REPORT "Error in Out1_signed: Expected " & to_hex(Out1_ref) & (" Actual " & to_hex(Out1_signed)) SEVERITY ERROR; END IF; END IF; END PROCESS Out1_signed_checker; completed_msg: PROCESS (clk) BEGIN IF clk'event AND clk = '1' THEN IF check1_done = '1' THEN IF Out1_testFailure = '0' THEN ASSERT FALSE REPORT "**************TEST COMPLETED (PASSED)**************" SEVERITY NOTE; ELSE ASSERT FALSE REPORT "**************TEST COMPLETED (FAILED)**************" SEVERITY NOTE; END IF; END IF; END IF; END PROCESS completed_msg; END rtl;
400000 3bfb6f 306e33 1ecad5 0947fe f29982 dd997a ccec7c c2ac63 c02466 c5a72b d28404 e51cee fb1a6e 11b6fc 26185f 35abd1 3e7942 3f6333 384b08 2a153a 168cc4 00295c e9bfc1 d62619 c7d809 c0a610 c17a7e ca3b77 d9cdeb ee392a 04e6a9 1af5e7 2d99d5 3a73f3 3fe17a 3d30d7 32b882 21cc58 0c92a6 f5be67 e03772 cebbd2 c38645 c00555
000000 000000 000005 000077 000408 0014ed 004be0 00cd4b 01ac06 02b7b3 0368ba 032177 01ae22 ff8e83 fda881 fc99b3 fc523d fc5bed fc7483 fccde8 fdbb12 ff39c2 00e1d6 023f68 032708 03adf4 03e59c 03b64d 02feee 01c946 005287 fee3b3 fdaae7 fcbb1c fc2504 fc02e9 fc68ff fd4f4b fe8f54 fff668 01579d 028e99 037903 03f5ad 03ee57
※Simulinkモデルから自動生成されたテストベンチです。
VHDL/Verilogテストベンチ
Simulinkモデルで作成されたHDLテストベンチです。上側に置かれているブロック線図モデル(DUT)と、下側に置かれているHDLコードをシミュレーションするブロック(DUT_vs)に対して、ダイナミックに等価性検証を行う仕組みになっています。HDLコードの実行はHDLシミュレータで行われ、HDLシミュレータとSimulinkが連携(Co-simulation)して動作します。入力信号の種類や特性は、ブロックパラメータで変更できるため、スティミュラスの変更が容易というメリットがあります。
![コシミュレーションモデル外観 図6. コシミュレーションモデル外観](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_903908011_copy_2108772242.adapt.full.medium.png/1732770302179.png)
図6. コシミュレーションモデル外観
System Verilog DPI コンポーネント生成およびUVMテストベンチ
テストベンチをスティミュラス/期待値のデータセットではなく、Simulinkモデルと等価なビヘイビア記述として生成する機能がSystem Verilog DPIです。もともとSystem VerilogにはDPI-C(Direct Programming Interface)と呼ばれるCコードの関数を、簡単な記述で呼び出す非常に便利な機能があります。System Verilog DPI コンポーネント生成機能は、この機能を用いてHDLシミュレーション可能な、MATLABやSimulinkモデルから生成したビヘイビア記述のCコードと、ラッパーとなるSystem Verilogコードを自動生成します。
大規模ASICの開発では、System Verilogで記述されるUVM(Universal Verification Methodology)を使用して、フレームワーク化したテストベンチにおいて、そこで使用されるコンポーネントを再利用しやすいようにライブラリ化しています。MATLAB/Simulinkモデルから、このUVMのフレームワークで使用できるコンポーネントを生成することができます。
System Verilog DPI コンポーネント生成について:HDL Verifier > Verification with UVM and SystemVerilog Components
FPGA実機のテストベンチ
HDLコードでのシミュレーションの利点は、クロックサイクルで手軽に回路記述をテストできる点です。その際、任意のポイントで波形をモニターでき、詳細なレベルまで回路動作を確認することができます。欠点は、回路規模やスティミュラスのデータ量の増大によって、テストベンチ作成やシミュレーション実行の時間が増大してしまうことです。FPGA実機でのテストは、モニターできるポイントやサンプル数には物理的な成約があるものの、そのような問題を解決してくれる側面があります。
ロジック アナライザー実機をFPGAボードに接続
オシロスコープよりも、たくさんのチャンネルを同時に測定できるデジタル信号の解析に特化した測定器です。FPGAのピンにプローブを接続して波形をモニターしてデバッグします。実機で生じている問題をリアルタイムで解析できる利点がある一方、不具合の再現性に難がある点や、プローブを接続して波形表示の設定をするなどの手間がかかる点が欠点です。キーサイト テクノロジー社、テクトロニクス社など測定機器メーカーが販売しています。
インサーキットテスタ
FPGAの内部信号を取得してPC上で表示する機能です。追加でFPGAのI/Oピンを設定したり、高価な測定器を接続したりすることなく、動作中のデバイスの内部信号を、FPGA内部のRAMにロギングし、取得後データをPCにアップロードしてモニターします。HDLコードを論理合成する前に、予め取得ポイントの設定を行っておく必要があります。この機能はFPGAメーカーおよび関係各社が提供しています。
- AMD社 Integrated Logic Analyzer
- Intel社 Signal Tap Logic Analyzer
- Microchip社 SmartDebug
- MathWorks社 FPGA Data Capture
![FPGA Data Capture 図7. FPGA Data Capture](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_903908011_copy_1541944636.adapt.full.medium.png/1732770302200.png)
図7. FPGA Data Capture
FPGA-In-the-Loop(FIL)
モデルベースデザイン (モデルベース開発、MBD)で利用されるモデルとFPGA間の等価性検証手法の一つです。この機能では、USB-JTAG/Ethernet/PCI Expressなどのインターフェイスにより通信を行い、MATLAB/Simulinkで作成したテストベンチを使用してFPGAを動作させて検証を行います。HDLコシミュレーションよりも大幅に高速に動作するため、膨大な量のスティミュラスを使用したり、検証時間を短縮したりする効果があります。
![FPGA-In-the-Loop 図8. FPGA-In-the-Loop](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_903908011_copy_969099449.adapt.full.medium.png/1732770302215.png)
図8. FPGA-In-the-Loop
PC上で動作するMATLABおよびSimulinkはFPGAと比較すると実行速度が遅いので、FPGAはスティミュラスを待ちながら動作することになります。そのため、リアルタイムでのタイミングやペリフェラルを含めた動作検証というよりも、論理の正しさを検証するための機能となっています。
AXI Manager
AXI ManagerもFPGA-In-the-Loopと同様にPCからFPGAにアクセスする機能ですが、リアルタイムで動作するオンボードFPGA内のレジスタや接続されているDDRメモリに対してRead/Writeを行ってデバッグをするために利用します。
本機能のインターフェイスもUSB-JTAG/Ethernet/PCI Expressを選択できます。
![AXI Manager 図9. AXI Manager](https://jp.mathworks.com/discovery/verilog-testbench/_jcr_content/mainParsys/image_903908011_copy_256906919.adapt.full.medium.png/1732770302235.png)
図9. AXI Manager
製品使用例および使い方
テストベンチに関連した機能へのリンク
FPGA関連製品情報
参考: FPGA、ASIC、および SoC 開発向け MATLAB, HDL Coder, HDL Verifier, Vision HDL Toolbox, MATLAB Coder, Simulink Coder