DPI-CでVHDLモジュールの処理結果をコンペア(別の方法を考える)
前回の方法でもタイトルの内容は可能なのですが、もっと便利で確実に(と思う)方法でコンペアを実施する方法がありました。ざっくりまとめると、
①テストデータ生成と比較値生成を同時に行い、SVモジュールに入力。その際、C関数からSystemVerilogのタスクを呼び出してクロックを進められるようにする。これによりCのfor文を使用可能となる。
②テストデータはVHDLモジュールへ、比較値はキューへ入力。キューはSystemVerilogの標準機能として使用可能。
③VHDLモジュールのレイテンシ分だけ遅れて比較値をキューから読み出し。コンペアして結果を表示。
①のCからSystemVerilogのタスク呼び出し(export)、②のキュー配列使用が新しい要素です。特に①はC関数からクロックを進められるようになったことで柔軟なテストが行えるようになったように思います。クロックを進めるという発想は以下サイトにて紹介されており、基本的な考え方のようです。
DPI-CとBFM、その2 ( 技術職 ) - @Vengineerの戯言 - Yahoo!ブログ
以下ソースです。今回は入力データをインクリメントデータ、比較値を入力の2倍としています。
・test_sv.sv
`timescale 1ns/1ns module test_sv(); // SystemVerilog -> C import "DPI-C" context task testGenerator(); // C -> SystemVerilog export "DPI-C" task test_en; export "DPI-C" task test_i; export "DPI-C" task test_c; export "DPI-C" task wait_clk; // クロック bit clk_i = 1'b0; always begin #5ns clk_i = ~clk_i; end // リセット bit srst_i = 1'b1; initial begin #100ns; @(posedge clk_i) #1ps srst_i = 1'b0; end // VHDLモジュール入出力用 byte en; byte d_i; byte d_o; byte d_c; // テスト入力時1にする task test_en (input bit d); en = d; endtask; // Cからのテストデータ入力 task test_i (input byte d); d_i = d; endtask; // Cからの比較データ入力 task test_c (input byte d); d_c = d; endtask; // Cにて1クロック待つ用タスク task wait_clk (input int n); repeat(n) @(posedge clk_i); endtask; // VHDLモジュール(d_o = d_i + d_i) test_vhd u_test_vhd ( .srst_i(srst_i), //: in std_logic; .clk_i(clk_i), //: in std_logic; .d_i(d_i), //: in std_logic_vector (7 downto 0); .d_o(d_o) //: out std_logic_vector (7 downto 0) ); // シミュレーション開始直後にCテストベンチ呼び出し initial begin @(negedge srst_i) #1ps; testGenerator(); end // 比較値はキュー配列に入力してディレイ byte d_c_que[$]; always @(posedge clk_i) begin if (en == 1) begin d_c_que.push_back(d_c); end end // 比較タイミング生成のためのイネーブル // VHDLモジュールへ入力してから結果出力までのクロック数を指定 bit d_c_en; assign d_c_en = (d_c_que.size() >= 1) ? 1: 0; // キューを読み出して結果と比較 byte cmp; always @(posedge clk_i) begin if (d_c_en == 1) begin cmp = d_c_que.pop_front(); if (d_o != cmp) begin $display("Error: d_o=%d, cmp=%d", d_o, cmp); end else begin $display("OK"); end end end endmodule
・test_c.c
#include <stdio.h> #include "dpiheader.h" // テストパターンと比較用データを生成 int testGenerator() { char i; // 入力,出力 // テスト開始 test_en(1); // テスト対象モジュールは入力に対して2倍の出力 for (i = 0; i < 256; i++) { test_i(i); // 入力をSV側へ入力 test_c(i + i); // 比較値をSV側へ入力 wait_clk(1); // 1クロック待つ } // テスト終了 test_en(0); return 0; }
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity test_vhd is port ( srst_i : in std_logic; clk_i : in std_logic; d_i : in std_logic_vector (7 downto 0); d_o : out std_logic_vector (7 downto 0) ); end test_vhd; architecture rtl of test_vhd is begin -- 加算 process (clk_i) begin if (rising_edge(clk_i)) then if (srst_i = '1') then d_o <= (others => '0'); else d_o <= d_i + d_i; end if; end if; end process; end rtl;
・test_tcl.tcl
vlib work vlog -sv -novopt -dpiheader dpiheader.h test_sv.sv vcom *.vhd vsim test_sv -dpiexportobj cexports -c gcc -c -g -IC:/altera/15.1/modelsim_ase/include test_c.c -o test_c.obj gcc -shared -o cimports.dll test_c.obj cexports.obj -LC:/altera/15.1/modelsim_ase/win32aloem -lmtipli vsim -c -sv_lib cimports test_sv
今回はMSYSをインストールして、ModelSimを完全にCUIから制御しています。比較結果は以下の通りです。
VSIM 1> run 1us # OK # OK # OK # OK # OK # OK # OK # OK VSIM 2>
これだとちゃんと動いているかさっぱり分からないのでCでの比較値生成を以下の通り変更して試してみました。正常動作していそうです。
for (i = 0; i < 4; i++) { test_i(i); // 入力をSV側へ入力 test_c(i + i); // 比較値をSV側へ入力 wait_clk(1); // 1クロック待つ } for (; i < 8; i++) { test_i(i); // 入力をSV側へ入力 test_c(i + i + i); // 比較値をSV側へ入力 wait_clk(1); // 1クロック待つ }
VSIM 1> run 1us # OK # OK # OK # OK # Error: d_o= 8, cmp= 12 # Error: d_o= 10, cmp= 15 # Error: d_o= 12, cmp= 18 # Error: d_o= 14, cmp= 21 VSIM 2>