はじめに
下記の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,
input logic [15:0] addr,
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 のシミュレーションができることを確認しました。
便利ですね。