Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

Intel版Questaで SystemVerilog + SysmteC

はじめに

下記のVerilatorの薄い本、第三弾、SystemC編の例題を、Intel版Questaで動くようにしました。

vengineer.hatenablog.com

Intel版Questaでは、SystemVerilog + SystemC が動く!

Verilator + SystemC で動くなら、Intel版Questaでも動くじゃんということでやってみました。

テストベンチ側がSystemCなので、SystemVerilog側のコードをSystemC側から呼び出せるような仕組みが必要です。

top の修正

Verilatorでは、DUT(RTL記述のSystemVerilog)のトップ階層名は、top にしてあります。トップテストベンチ側は SystemC (sc_main.cpp) です。sc_main.cpp の中で DUTをインスタンスすると、Vtop になります。Verilatorでは、--cc オプションを付けると、top というモジュールから Vtop という Verilated Module という C++モデルを生成します。また、--sc オプションを付けると、VtopというSystemCモデルを生成しました。

Questaでは、DUTのトップ階層名と同じ名前のモジュールを SystemC 側におけるので、Verilator の top.v を下記のように変更します。VERILATORの時は、top のままですが、それ以外の時は、Vtop にします。

`ifdef VERILATOR
module top
`else
module Vtop
`endif
(
    input  logic        clk,
    input  logic        reset,

    /* verilator lint_off UNUSED */
    input  logic [15:0] addr,
    /* verilator lint_on UNUSED */
    input  logic        cs,
    input  logic        rw,
    input  logic [31:0] data_in,
    output logic        ready,
    output logic [31:0] data_out
    );

SystemCラッパーの生成

Questaの場合は、SystemVerilogのコードに対して、scgenmod コマンドを使って、SystemCのラッパーを生成します。

最初にSystemVerilogのコードを vlog コマンドでコンパイルし、そのコンパイルした top から SystemCラッパーを scgenmod コマンドで生成します。scgenmod コマンドの引数、-bool -sc_uint は ポートの信号の型として、スカラー信号は bool を、ベクター信号は sc_unit を使うように指示しています。

vlog -sv top.v
scgenmod -bool -sc_uint Vtop > Vtop.h

top.v (Vtop) から scgenmod コマンドが生成した Vtop.h は、以下のようになっています。logic が bool、logic [X] が sc_uint に変換されています。

#ifndef _SCGENMOD_Vtop_
#define _SCGENMOD_Vtop_

#include "systemc.h"

class Vtop : public sc_foreign_module
{
public:
    sc_in<bool> clk;
    sc_in<bool> reset;
    sc_in<sc_uint<16> > addr;
    sc_in<bool> cs;
    sc_in<bool> rw;
    sc_in<sc_uint<32> > data_in;
    sc_out<bool> ready;
    sc_out<sc_uint<32> > data_out;


    Vtop(sc_module_name nm, const char* hdl_name,
       int num_generics, const char** generic_list)
     : sc_foreign_module(nm),
       clk("clk"),
       reset("reset"),
       addr("addr"),
       cs("cs"),
       rw("rw"),
       data_in("data_in"),
       ready("ready"),
       data_out("data_out")
    {
        elaborate_foreign_module(hdl_name, num_generics, generic_list);
    }
    ~Vtop()
    {}

};

#endif

コンストラクタの中では、elaborate_foreign_module(hdl_name, num_generics, generic_list); を呼び出しています。elaborate_foreign_module 関数には、HDLのインスタンス名、parameterの数とそれに対する値を渡しています。Vtopでは、parameterは無いので num_generics は 0 で generic_list は NULL になります。

SystemC コードの修正

sc_main.cpp は、Questaに対応するために、かなり修正する必要があります。Questa では、sc_main は使えません。その代わりに、下記のようにSystemC のクラス (ここでは、sc_top) として記述する必要があります。

信号の型は、スカラーは bool、ベクターは sc_uint です。bfm クラスの信号の型も同様に変更する必要があります。変更したものを bfm.h にストアします。

sc_main.cpp の Vtop と bfm の接続を sc_top クラスのコンストラクタ内で行います。

#include <memory>
#include <systemc.h>
#include "Vtop.h"
#include "bfm.h"

SC_MODULE(sc_top) {

    sc_clock clk;

    sc_signal<bool>     reset;
    sc_signal<bool>     cs;
    sc_signal<bool>     rw;
    sc_signal<bool>     ready;
#ifdef VERILATOR
    sc_signal<uint32_t> addr;
    sc_signal<uint32_t> data_in;
    sc_signal<uint32_t> data_out;
#else
    sc_signal<sc_uint<16>> addr;
    sc_signal<sc_uint<32>> data_in;
    sc_signal<sc_uint<32>> data_out;
#endif

    Vtop *u_top;
    bfm *u_bfm;

    SC_CTOR(sc_top) :
        clk("clk", 10, SC_NS, 0.5, 5, SC_NS, true),
        reset("reset"), cs("cs"), rw("rw"), addr("addr"), ready("ready"), data_in("data_in"), data_out("data_out") {

#ifdef VERILATOR
        u_top = new Vtop{"top"};
#else
        u_top = new Vtop{"top", "Vtop", 0, NULL};
#endif

        u_top->clk(clk);
        u_top->reset(reset);
        u_top->cs(cs);
        u_top->rw(rw);
        u_top->addr(addr);
        u_top->data_in(data_in);
        u_top->ready(ready);
        u_top->data_out(data_out);

        u_bfm = new bfm("bfm");

        u_bfm->clk(clk);
        u_bfm->reset(reset);
        u_bfm->cs(cs);
        u_bfm->rw(rw);
        u_bfm->addr(addr);
        u_bfm->data_in(data_out);
        u_bfm->ready(ready);
        u_bfm->data_out(data_in);
    }

    ~sc_top() {
#ifdef VERILATOR
        u_top->final();
#endif
        delete u_top;
        delete u_bfm;
    }
};

Vtop クラスのインスタンスを生成するときに、下記のように、HDLのインスタンス名、その後に、0とNULLを指定しています。

        u_top = new Vtop{"top", "Vtop", 0, NULL};

sc_top.cpp は、下記のようになります。SC_MODULE_EXPORT(sc_top); を設定するところがポイントです。

#include "sc_top.h"
#include <iostream>


SC_MODULE_EXPORT(sc_top);

sc_main は下記のようになります。

#include <memory>
#include <systemc.h>

#include "sc_top.h"

int sc_main(int argc, char* argv[]) {

    if (false && argc && argv) {}

    Verilated::debug(0);
    Verilated::randReset(2);
    Verilated::commandArgs(argc, argv);

    ios::sync_with_stdio();

    const std::unique_ptr<sc_top> u_sc_top{new sc_top("sc_top")};

    sc_start();

    cout << "done, time = " << sc_time_stamp() << endl;
    return 0;
}

#ifdef VL_USER_STOP
void vl_stop(const char *filename, int linenum, const char *hier) VL_MT_UNSAFE
{
    sc_stop();
    cout << "call vl_stop" << endl;
}
#endif

SystemC コードのコンパイル & リンク

SystemC コードは、下記のように sccom コマンドでコンパイル & リンクします。最初の sccom コマンドでは、-g オプション(デバッグオプション)にて sc_top.cpp をコンパイルし、オブジェクトファイルを生成し、2番目の sccom コマンドでは、-link オプションにてリンクします。

sccom -g sc_top.cpp
sccom -link

vsim にて、シミュレーション実行

vsim コマンドにて、トップ階層である sc_top にてシミュレーションを実行します。-c オプションは、GUI無し(コマンドライン)にて実行します。-do オプションにて、vsim コマンド内のコマンドとして、"run -all"、シミュレーションをすべて実行します。

vsim -c sc_top -do "run -all"

おわりに

Intel Questaでも、SystemVerilog + SystemC のシミュレーションができることを確認しました。

便利ですね。