SystemCで4bitカウンタを作ってみた②
前回作った4bitカウンタを少し変えつつ、もう少し理解を深める。
SC_THREADのsensitivityについて
SC_THREADでsensitivityがない件について、SC_THREADはクロック同期しておらず、プロセス側でクロックを待っているのでは?という予想を立てた。とすると、プロセスのwait文をなくすとどうなるのだろう。simクロックごとに実行される?
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 ); } }
実行結果は、
$ ./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) ^Z [1]+ 停止 ./run.x
いつまで待っても何も表示されない。ということで全く時間概念なくひたすらmain_thread()がコールされているっぽい。テストベンチの
sc_start( SC_ZERO_TIME ); cout << sc_time_stamp() << " : " << "out = " << out.read() << endl;
すら表示されないことから、時間概念とは全く無関係のようだ。
というわけでcounter.cppのコンストラクタでSC_THREADのsensitivityリストにclkを追加してみた。
Counter4::Counter4( sc_module_name name ) : sc_module( name ) , clk( "clk" ) , out( "out" ) { // process setting SC_THREAD( main_thread ); sensitive << clk; // 新規追加 }
これだけだと不十分で、やっぱりsimが一向に進まない。sensitivityリストに追加した信号変化を待つwait()をプロセス本体に書かないといけないらしい。
void Counter4::main_thread() { sc_uint<4> cnt = 0; out.write( 0 ); while (true) { // wait (clk.posedge_event() ); wait(); // 新規追加 if(cnt == 0xf){ cnt = 0; } else { cnt++; } out.write( cnt ); } }
としたところ、simが進んだ。
$ ./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 = 2 20 ns : out = 4 30 ns : out = 6 40 ns : out = 8 50 ns : out = 10 60 ns : out = 12 70 ns : out = 14 80 ns : out = 0 90 ns : out = 2 100 ns : out = 4 110 ns : out = 6 120 ns : out = 8 130 ns : out = 10 140 ns : out = 12 150 ns : out = 14 160 ns : out = 0 170 ns : out = 2 180 ns : out = 4 190 ns : out = 6 200 ns : out = 8 210 ns : out = 10
動いたは動いたが、1クロックで2カウントしている。clkの両エッジでカウントしているようだ。
以下のようにsensitivityリストを変更。これでposedgeだけに反応するはず。
Counter4::Counter4( sc_module_name name ) : sc_module( name ) , clk( "clk" ) , out( "out" ) { // process setting SC_THREAD( main_thread ); sensitive << clk.pos(); // clkのposedgeのみ }
これでうまくいった。
$ ./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_CTHREAD
上記ではclk.pos()をsensitivityリストに入れたSC_THREADを使用したが、SC_CTHREADというものも用意されている。引数として
SC_CTHREAD(process, event)
をとり、eventが変化するとprocessが呼び出されるらしい。always@(posedge clk)みたいなのを記述する際にはSC_CTHREADが適しているそう。SC_THREADはテストベンチ記述向けという話も聞く。 文法上の違いはSC_THREADはsensitiveに複数の信号を登録できるけど、SC_CTHREADは単一の信号しか登録できない、くらいなものなのか?
コンストラクタを以下のように変更した。
Counter4::Counter4( sc_module_name name ) : sc_module( name ) , clk( "clk" ) , out( "out" ) { // process setting SC_CTHREAD( main_thread, clk.pos() ); // SC_CTHREADを使用 //sensitive << clk.pos(); // sensitivityリストは削除 }
また、main_threadのほうは、もとのwaitなしに戻してみた。
void Counter4::main_thread() { sc_uint<4> cnt = 0; out.write( 0 ); while (true) { // wait (clk.posedge_event() ); // wait(); // waitをコメントアウト if(cnt == 0xf){ cnt = 0; } else { cnt++; } out.write( cnt ); } }
sim実行結果は、
$ ./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 ^Z [1]+ 停止 ./run.x
予想通り無限ループだが、SC_THREADの時とは挙動が異なる。SC_CTHREADはclk.pos()で呼び出されるため、0時間まではsimが進んでいる。 wait()を元に戻して、
void Counter4::main_thread() { sc_uint<4> cnt = 0; out.write( 0 ); while (true) { // wait (clk.posedge_event() ); wait(); // もとにもどした if(cnt == 0xf){ cnt = 0; } else { cnt++; } out.write( cnt ); } }
再実行したところ、
$ ./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 = 0 20 ns : out = 1 30 ns : out = 2 40 ns : out = 3 50 ns : out = 4 60 ns : out = 5 70 ns : out = 6 80 ns : out = 7 90 ns : out = 8 100 ns : out = 9 110 ns : out = 10 120 ns : out = 11 130 ns : out = 12 140 ns : out = 13 150 ns : out = 14 160 ns : out = 15 170 ns : out = 0 180 ns : out = 1 190 ns : out = 2 200 ns : out = 3 210 ns : out = 4
ちゃんと動いた。
長くなったので、また次回。