チラシの裏は意外と白くない

最近忘れっぽくなったので調べたことをチラシの裏に書きます

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

ちゃんと動いた。

長くなったので、また次回。