アウトプットブログ

勉強したことをまとめていきます。

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;
}

・test_vhd.vhd

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>