SystemCで4bitカウンタを作ってみた①
SystemCに同梱されているexampleを勉強するつもりだったけど、結構複雑で言語のお作法がわからないときつい感じだったので、簡単なカウンタ回路をフルスクラッチで作ってみた。これで基本的な構成を理解したい。環境構築にあたって、以下のサイトを大いに参考にさせてもらった…というかほぼ一緒。 w.atwiki.jp
準備したファイルの概要
ファイル名 | 概要 |
---|---|
main.cpp | テストベンチトップ。4bitカウンタのインスタンス、テストベンチの記述 |
counter.h | 4bitカウンタのモジュール定義。どんなポート・変数・プロセスを持っているかを記載 |
counter.cpp | counter.hで宣言したプロセスの中身を記載。今回はコンストラクタとカウンタ部分の2つ |
counter.h
- Counter4というモジュール
- 入力はclk、出力はout。outは符号なし4bitで、カウンタ値を出力させる
- コンストラクタは2種類の書き方ができる
#ifndef __COUNTER_H__ #define __COUNTER_H__ #include <systemc.h> SC_MODULE( Counter4 ) { // ports sc_in_clk clk; sc_out < sc_uint<4> > out; // signals // variables // process functions void main_thread(); // functions // constructor #if 0 SC_HAS_PROCESS( Counter4 ); Counter4( sc_module_name ); #else SC_CTOR( Counter4 ); #endif }; #endif // __COUNTER_H__
counter.cpp
- コンストラクタの中身で、プロセスを定義
- SC_THREADでmain_thread()関数を呼び出す
- Verilogのalwaysのイメージ
- sim中で1度だけ呼び出される。そのため無限ループでずっと回しておくのが一般的
- wait()を入れられる
- always文ということはsensitivityリストが必要なはずだが、本例では記載がない。これは後で確認する
- main_thread()の実体
- clk信号の立ち上がりイベントを待つ
- 上記のsensitivityの話のからみで、ここでクロック同期を実現している?
- あとはただカウントアップして、カウンタ値が15になったら0にクリアする
- clk信号の立ち上がりイベントを待つ
#include "counter.h" // constructor Counter4::Counter4( sc_module_name name ) : sc_module( name ) , clk( "clk" ) , out( "out" ) { // process setting SC_THREAD( main_thread ); } void Counter4::main_thread() { sc_uint<4> cnt = 0; out.write( 0 ); while (true) { wait (clk.posedge_event() ); if(cnt == 0xf){ cnt = 0; } else { cnt++; } out.write( cnt ); } }
main.cpp
- dutのインスタンス
- ベンチの信号との結合
- VCDダンプ記述
- sim記述
#include <systemc.h> #include "counter.h" int sc_main( int argc, char* argv[] ) { Counter4 *counter4_i; // signals sc_clock clk( "clk", 10.0, SC_NS ); sc_signal< sc_uint <4> > out; // module instance & binding counter4_i = new Counter4( "Counter4" ); sc_assert( counter4_i ); counter4_i->clk( clk ); counter4_i->out( out ); // vcd dump sc_trace_file *trace_f; trace_f = sc_create_vcd_trace_file( "systemc" ); // 出力ファイル名を指定する sc_assert( trace_f ); trace_f->set_time_unit( 1.0, SC_NS ); sc_trace( trace_f, clk, "clk" ); sc_trace( trace_f, out, "out" ); // initalize sc_start( SC_ZERO_TIME ); cout << sc_time_stamp() << " : " << "out = " << out.read() << endl; // simulation sc_start( 10.0, SC_NS ); cout << sc_time_stamp() << " : " << "out = " << out.read() << endl; sc_start( 10.0, SC_NS ); cout << sc_time_stamp() << " : " << "out = " << out.read() << endl; # 200nsまでモニタ。ここでは省略。 // delete counter4_i; sc_close_vcd_trace_file( trace_f ); return 0; }
実行結果
実行結果は以下。10nsごとにout値をモニタしている。ちゃんと15→0にリセットされているように見える。 とりあえず超簡単なモジュールとテストベンチを作って、理解は進んだ。不明な点について、デザインを変えつつもう少し調べてみる。
$ ./run.x SystemC 2.3.3-Accellera --- Mar 8 2020 14:00:56 Copyright (c) 1996-2018 by all Contributors, ALL RIGHTS RESERVED Info: (I703) tracing timescale unit set: 1 ns (systemc.vcd) 0 s : out = 0 10 ns : out = 1 20 ns : out = 2 30 ns : out = 3 40 ns : out = 4 50 ns : out = 5 60 ns : out = 6 70 ns : out = 7 80 ns : out = 8 90 ns : out = 9 100 ns : out = 10 110 ns : out = 11 120 ns : out = 12 130 ns : out = 13 140 ns : out = 14 150 ns : out = 15 160 ns : out = 0 170 ns : out = 1 180 ns : out = 2 190 ns : out = 3 200 ns : out = 4 210 ns : out = 5
宿題事項
- コンストラクタの記述に関して
- 型について調べる。sc_clockとかsc_in_clkとか。
- SC_THREADにsensitivityがない理由は?
- sc_assertを調べる