はじめに
下記のVerilatorの薄い本、第三弾、SystemC編の例題を、Intel版Questaで動くようにしました。
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
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 のシミュレーションができることを確認しました。
便利ですね。