Verilog RTLのデザインをSCV(SystemC Verification Library)で検証する環境のサンプルです。
サンプルの動作環境
このサンプルは次の環境で動作を確認しました。
- OS: CentOS7.7
- Icarus Verilog: 11.0
- SystemC: 2.3.3
- SCV: 2.0.1
サンプルの概要
サンプルの基本的な構成は、Icarus VerilogとSystemCの連携(VPI)と同じです。Verilogでインスタンス化したmod_aという簡単なRTLに対して、SVCでランダムデータの入力を行い、SystemCでRTLの出力とリファレンスデータの一致確認を行っています。
DUTのmod_a.vは、8ビットの2入力を内部で加算して出力する単純な回路です。
module mod_a ( input clk, input rst_x, input i_valid, input [7:0] i_in_a, input [7:0] i_in_b, output reg o_valid, output reg [7:0] o_out ); always @(posedge clk or negedge rst_x) begin if (~rst_x) begin o_valid <= 1'b0; o_out <= 8'h0; end else begin o_valid <= i_valid; o_out <= i_in_a + i_in_b; end end endmodule
ランダムデータ用の構造体
RTLのDUTは、i_valid, i_in_a, i_in_bという3つの入力があります。この入力データをランダムに生成するために、data_tという構造体を定義します。
struct data_t { sc_uint<1> valid; sc_uint<8> a; sc_uint<8> b; };
data_tに対応するscv_extensions<data_t> classを作成します。
#ifndef DATA_EXT_H #define DATA_EXT_H #include "scv.h" #include "data.h" template<> class scv_extensions<data_t> : public scv_extensions_base<data_t> { public: scv_extensions<sc_uint<1> > valid; scv_extensions<sc_uint<8> > a; scv_extensions<sc_uint<8> > b; SCV_EXTENSIONS_CTOR(data_t) { SCV_FIELD(valid); SCV_FIELD(a); SCV_FIELD(b); } }; #endif //DATA_EXT_H
コンパイルスクリプト
SystemC関連のソースファイルは、g++で事前にコンパイルしておき、iverilog-vpiでVPI用のCファイルとリンクさせます。-lscvオプションが必要です。
# compile verilog iverilog -c ../bin/cmd.txt \ -v \ -y ${RTL_DIR} \ -o ${TOP_MODULE} \ ${sim_file} g++ -c -fPIC \ -I${SYSC_DIR} \ -I${STUB_DIR} \ -I${SYSTEMC_DIR}/include \ ${SYSC_DIR}/mod_a.cpp g++ -c -fPIC \ -I${SYSC_DIR} \ -I${STUB_DIR} \ -I${SYSTEMC_DIR}/include \ ${SYSC_DIR}/mod_a_tb.cpp iverilog-vpi \ -I${SYSC_DIR} \ -I${STUB_DIR} \ ${STUB_DIR}/vpi_stub.c \ mod_a.o mod_a_tb.o -lscv -lsystemc -L${SYSTEMC_DIR}/lib-linux64 # simulation export LD_LIBRARY_PATH=${SYSTEMC_DIR}/lib-linux64 vvp -M. -mvpi_stub ${TOP_MODULE} -v
ランダムデータの生成
mod_a::stimulus()内でランダムデータを生成しています。制約条件のあるランダムデータを生成する場合は、 scv_constraint_baseを派生させた制約条件のクラスを作成する必要があります。
SCV_CONSTRAINT
SCV_CONSTRAINTを使うと、「ランダムかつi_in_aとi_in_bを同じ値にする」といった制約条件を指定することができます。
class my_constraint1: public scv_constraint_base { public: scv_smart_ptr < data_t > p ; SCV_CONSTRAINT_CTOR(my_constraint1) { SCV_CONSTRAINT ( p->a() == p->b() ); } };
keep_only
keep_onlyを使うと、ランダムデータの生成範囲を指定できます。
class my_constraint2: public scv_constraint_base { public: scv_smart_ptr < data_t > p ; SCV_CONSTRAINT_CTOR(my_constraint2) { p->a.keep_only(1,7); // aは1~7の範囲 p->b.keep_only(8,15); // bは8~15の範囲 } };
mod_a::stimulus()
ランダムデータを生成するには、scv_smart_ptr()もしくは制約条件を指定したclassをインスタンス化します。ランダムデータは、next()メソッドをコールすると生成されます。次の例は、scv_smart_ptr, my_constraint1, my_constraint2, my_constraint3という順で、それぞれ5回のランダムデータ生成を行っています。 RTLへのデータの書き込みは、ランダムデータ生成後に行っています。
void mod_a::stimulus() { scv_smart_ptr<data_t> data_p ("data"); my_constraint1 c1("const1"); my_constraint2 c2("const2"); my_constraint3 c3("const3"); scv_random::set_global_seed(100); while (true) { while (rst_x.read() == 0) wait(); cout<<"#"<<sc_time_stamp()<<" rst_x is deasserted " << endl; // no constraint for (int i = 0; i < 5; i++) { data_p->next(); scv_out << "Random value for " << std::hex << data_p->get_name() << ":" << endl; data_p->print(scv_out, 0, 2); // RTLにランダムデータをセット i_valid.write(data_p->valid.read()); i_in_a.write(data_p->a.read()); i_in_b.write(data_p->b.read()); wait(); } // constraint1 for (int i = 0; i < 5; i++) { c1.p->next(); scv_out << "Random value for " << std::hex << c1.p->get_name() << ":" << endl; c1.p->print(scv_out, 0, 2); i_valid.write(c1.p->valid.read()); i_in_a.write(c1.p->a.read()); i_in_b.write(c1.p->b.read()); wait(); } // constraint2 for (int i = 0; i < 5; i++) { c2.p->next(); scv_out << "Random value for " << std::hex << c2.p->get_name() << ":" << endl; c2.p->print(scv_out, 0, 2); i_valid.write(c2.p->valid.read()); i_in_a.write(c2.p->a.read()); i_in_b.write(c2.p->b.read()); wait(); } // constraint3 for (int i = 0; i < 5; i++) { c3.p->next(); scv_out << "Random value for " << std::hex << c3.p->get_name() << ":" << endl; c3.p->print(scv_out, 0, 2); i_valid.write(c3.p->valid.read()); i_in_a.write(c3.p->a.read()); i_in_b.write(c3.p->b.read()); wait(); } i_valid.write(0); wait(10); sim_done = true; std::cout << "=======================================" << std::endl; std::cout << " The simulation is successfully done." << std::endl; std::cout << "=======================================" << std::endl; wait(); } }
実行結果
シミュレーションを実行すると、次のようなログが表示されます。SCVで生成されたランダムデータでDUTが動作しているのが分かります。
Icarus Verilog started VCD info: dumpfile dump.vcd opened for output. #0 s SystemC started #100 ns rst_x is deasserted Random value for data: { valid:0 a:76 b:60 } Random value for data: { valid:1 a:3f b:d1 } (中略) Mon Input: #270 ns i_valid = 1 i_in_a = 0aa i_in_b = 0f7 Mon Output: #270 ns o_valid = 1 o_out = 0c5 Random value for : { valid:1 a:77 b:f8 } Mon Input: #280 ns i_valid = 1 i_in_a = 024 i_in_b = 065 Mon Output: #280 ns o_valid = 1 o_out = 0a1 Random value for : { valid:1 a:fd b:d2 } Mon Input: #290 ns i_valid = 1 i_in_a = 077 i_in_b = 0f8 Mon Output: #290 ns o_valid = 1 o_out = 089 Mon Input: #300 ns i_valid = 1 i_in_a = 0fd i_in_b = 0d2 Mon Output: #300 ns o_valid = 1 o_out = 06f Mon Input: #310 ns i_valid = 0 i_in_a = 0fd i_in_b = 0d2 Mon Output: #310 ns o_valid = 1 o_out = 0cf ======================================= The simulation is successfully done. ======================================= #410 ns SystemC stopped Info: /OSCI/SystemC: Simulation stopped by user.