SystemC によるテストベンチ の変更点

更新


[[公開メモ]]

#contents

* Verilator を使って SystemC でテストベンチを書く [#lb00ad13]

SystemC はごく最近勉強し始めたので、分からないことだらけです。

忘れそうなことをここにメモろうと思います。

まだこれからです。

* Verilator を使ったテストの流れ [#u5cb90ff]

Verilator は Verilog のモジュール(ただし合成可能なものに限る)を
SystemC 互換の C++ コードに変換するためのツールです。

同じ verilog モジュールでも、テストベンチのように論理合成ができないものは
変換できないため、Verilator を使った検証では、テストベンチは SystemC 
で書かれるのが一般的なようです。

SystemC は、C++ を使って論理回路を表現するためのライブラリです。
フリーウェアとして開発されています。

SystemC で書かれたコードを通常の C++ コンパイラでコンパイルすることで、
実行可能なファイルを作成することができ、それ実行することで論理回路の
動作をシミュレートできます。

市販の検証ツールと違って検証可能な回路規模に制限などはありませんので、
メモリサイズの許す限り、大規模な回路の検証も高速に行うことができるそうです。

また、テストベンチのコード中では C++ の機能をすべて使うことができますので、
ソフトウェアとの整合性を検証したり、検証結果をレポートする方法を工夫したり、
非常に自由度が高いという利点もあります。

Verilator を使ったテストは、

+ verilog モジュールを Verilator で C++ コードに変換する
+ テストベンチコードおよび SystemC ライブラリと一緒にコンパイルする
+ コンパイル結果を実行し、検証結果を得る

という流れで行います。

* SystemC でのテストベンチ [#a7e84d2f]

テストベンチを書くには、最低限以下の操作が必要になります。

- テスト対象モジュールをインスタンス化する
- 入出力線を宣言する
- 入出力線をモジュールに結合する
- 入力線に値を設定する
- 時刻を進める
- 出力を読み取る

[[電気回路/HDL/SystemC の導入]] で紹介した、
次のテストベンチコードを例に使って、
それぞれのやり方を書いておきます。

 LANG:cpp
 #include "Vbin2bcd.h"
 int sc_main(int argc, char **argv) {
 
     Verilated::commandArgs(argc, argv);
 
     // モジュールのインスタンス化 
     Vbin2bcd top("top");
 
     // 信号ネットの定義
     sc_clock            clk ("clk", 10, SC_NS);
     sc_signal<bool>     rst, start, busy;
     sc_signal<uint32_t> bin;
     sc_signal<uint32_t> bcd;
 
     // モジュールに結線
     top.clk(clk);
     top.rst(rst);
     top.start(start);
     top.busy(busy);
     top.bin(bin);
     top.bcd(bcd);
 
     // 検証開始
     rst   = 1;
     start = 0;
 
     sc_start(100, SC_NS);
     rst = 0;
     sc_start(100, SC_NS);
 
     for(int i=0; i<1024; i++) {
         
         bin = i;
 
         start = 1;
         sc_start(10, SC_NS);
         start = 0;
         
         sc_start(10, SC_NS);
         while(busy)
             sc_start(10, SC_NS);
 
         std::ostringstream os_bin;
         std::ostringstream os_dec;
         
         os_bin << dec << i;     // 元の数値を10進数で表したもの
         os_dec << hex << bcd;   // 変換結果を16進数で表したもの
         sc_assert(os_bin.str()==os_dec.str()); // 等しいことを検証
         
         sc_start(40, SC_NS);
     }
     
     cout << "done";
 
     exit(0);
 }

** テスト対象モジュールをインスタンス化する [#oeed7be7]

SystemC において、モジュールは C++ のクラスに対応しており、
そのクラスのインスタンスを作成することが、
モジュールをインスタンス化することに相当します。

(その後調べたところ、実際にはクラスではなく構造体でしたが、
大勢は変わらないので、そのままにしておきます)

Verilator を使った場合、verilog のモジュール名の先頭に大文字の V 
がついた名前のクラスができます。上記の例では、verilog 
で bin2bcd という名前のモジュールが、SystemC では Vbin2bcd 
という名前になっています。

クラス定義が Vbin2bcd.h というヘッダファイルに宣言されるので、
テストベンチファイルの先頭部分でこれを #include しておきます。

 LANG:cpp
 #include "Vbin2bcd.h"

インスタンス化する際には、
コンストラクタにインスタンス名を文字列で与えるようになっています。

C++ でクラスのインスタンスを作るには、
+ 自動変数として作成する
+ new で作成してポインタ変数で保持する

の2つの方法がありますが、SystemC でもどちらを使っても構いません。

*** 自動変数として作成する [#oc788251]

上でやっているように、

 LANG:cpp
 Vbin2bcd top("top");

とすれば、そのスコープ内のみで有効な自動変数としてインスタンスが作成されます。
このインスタンスはスコープを抜けると自動的に破棄されます。

コンストラクタに与えている "top" がインスタンス名になります。

この場合、インスタンスのメンバーへのアクセスは top.foo 
のように行います。

*** new で作成してポインタ変数に代入する [#gecfb87a]

ヒープ領域にインスタンスを確保するには new を使います。
new した結果はポインタが返るので、ポインタが他の変数に値を保持します。

 LANG:cpp
 Vbin2bcd *top = new Vbin2bcd("top");

ポインタ変数がスコープを抜けても、
new で作成したインスタンスは自動的には削除されないので、
必要なくなった時点で delete を使って明示的に破棄する必要があります。

 LANG:cpp
 delete top;

コンストラクタに与えている "top" がインスタンス名になります。

この場合、インスタンスのメンバーへのアクセスは top->foo 
のように行います。

*** どちらがいい? [#s0a0d73f]

上の例では自動変数としたほうが楽チンと思ってそうしてますが、
SystemC の世界でどちらが一般的かはまだこれから勉強するところです。

もしかするとモジュールインスタンスのサイズが大きくなる場合に備えて、
テストベンチではヒープに確保するほうが良いのかも知れません。
あまり大きな変数をスタックに取ると、スタックあふれが起きる可能性がありますので・・・

変数をヒープに取りつつ、delete のし忘れをなくすには、
std::auto_ptr なんかをうまく使うことになるんですかね。

** 入出力ネットを定義する [#tcf82ff1]

信号線ネット、すなわち verilog でいうところの wire に相当するのは、
sc_signal というクラスのインスタンスです。

sc_signal はテンプレートクラスとして定義されていて、
後ろに < > で括ってビット数を指定します。

原理的には sc_signal も new で作成してポインタ変数で扱ってもよいはずですが、
面倒なので自動変数として定義されることが多いのだと思います。

 LANG:cpp
 sc_signal<bool>        sig1;    //  1 bit
 sc_signal<sc_uint<8> > sig2;    //  8 bit unsigned
 sc_signal<sc_int<8> >  sig3;    //  8 bit signed
 sc_signal<uint32_t>    sig4;    // 32 bit unsigned
 sc_signal<int32_t>     sig5;    // 32 bit signed
 sc_signal<uint64_t>    sig6;    // 64 bit unsigned
 sc_signal<int64_t>     sig7;    // 64 bit signed

sc_signal<sc_uint<8> > を sc_signal<sc_uint<8>> 
と書いてしまうと文法エラーになることに注意してください。
~> と > との間には必ずスペースを入れるようにします。
これは、C++ では >> が独立の演算子として定義されているためです。

また、verilog で定義した複数ビットの信号線は、
verilator では必ずしも同じ幅の信号線として表現されず、
32ビットあるいは64ビットに切り上げられてしまうようです。

bin2bcd で宣言した 10 ビットの入力線 bin に sc_signal<uint32_t> 
として宣言した信号線を結合しているのはこのためです。

これはシミュレーション時の速度を上げるための配慮だそうですが、
オーバーフロー時の動作などに違いが出る可能性もあるため、
注意が必要です。

追記:

bool や sc_uint、uint32_t などは、0, 1 のみの2進数しか表せないのに対して、

VHDL や Verilog のビットは X や Z など、0, 1 以外の状態も取り得ます。

そこで、Verilog のビットのように 0,1,X,Z を表現できる4値ビットを表すために、

 LANG:cpp
 sc_signal<sc_logic>  sig_1bit;    // 1 bit
 sc_signal<sc_lv<4> > sig_4bits;   // 4 bits

のように、sc_logic や sc_lv のような型も定義されているそうです。

Verilator ではこのような多値ビットを使わない形で、
なおかつ上で見たとおりビット数すらワード単位にまとめた形で、
信号線が定義されます。これはシミュレーションの速度を上げるためのようです。

追記2:

verilog では
 LANG:verilog
 wire [7:4] sig1;

のように、wire 定義において 0 から始まらないビット指定が可能ですが、
SystemC ではそのような機能はないようで、
最下位ビットのインデックスは常に 0 になってしまいます。

*** クロック線について [#de7989ac]

クロック線を簡単に定義するため、sc_clock というクラスが用意されています。

このインスタンスは、sc_signal<bool> と同じ1ビットの信号線ですが、
コンストラクタで指定された周期で自動的にクロックを刻みます。

 LANG:cpp
 sc_clock clk("clk", 10, SC_NS);

コンストラクタへの引数は、
- "clk" : クロックソースのインスタンス名
- 10 : クロック周期
- SC_NS : クロック周期の単位 (SC_NS は ns, SC_US は us を意味します)

となっています。

このほかにもオプションの引数を渡すことでデューティー比などを変更できるそうです。

** 入出力線をモジュールに結合する [#x79bda8f]

モジュールへの結合は、
モジュールのメンバとして定義されている入出力ピンに信号線ネットを渡すことで行われます。

 LANG:cpp
     // モジュールに結線
     top.clk(clk);
     top.rst(rst);
     top.start(start);
     top.busy(busy);
     top.bin(bin);
     top.bcd(bcd);

top.clk などの .clk の部分がモジュールピンの名前、カッコ内が信号ネットのインスタンスです。

top をポインタ変数で保持している場合には、top.clk(clk) の代わりに top->clk(clk) となります。

** 入力線に値を設定する [#ia5f9455]

単純に sc_sig のインスタンスに数値を代入します。

 LANG:cpp
     rst   = 1;
     start = 0;

代入する値を計算するためには C++ で利用可能な任意の関数や演算子を使えますので、
verilog などでテストベンチを書く際に苦労するちょっとした計算などを手短に書くことができます。

** 時刻を進める [#z33d32f3]

他にも色々あるのですが、もっとも手軽な方法として、
verilog の #123 のように一定時間だけ時刻を進める方法として、
sc_start に数値を指定するという方法があります。

 LANG:cpp
    sc_start(10.0, SC_NS);

SC_NS は与える数値に付ける単位で、SC_NS ならば ns を表します。

したがって、この例では 10ns 時刻を進めるという意味になります。

数値部分は必ずしも整数でなくても、任意の浮動小数点数を指定できるようです。

** 出力を確認する [#n8fe542f]

sc_sig は暗黙的に整数値に変換されるようなので、
任意の整数と比較したり、整数値を要求する関数の引数として与えることが可能です。

*** 注意点 [#ze1acf45]

ちょっとだけ注意が必要なのは、
assign で定数が割り当てられた wire であっても、
その値は初めて sc_start が呼ばれるまで決定されないことです。

 LANG:verilog
 module test ( output wire [15:0] output_value );
     assign output_value = 1234;
 endmodule // test

に対して、テストベンチを

 LANG:cpp
 #include "Vtest.h"
 
 int sc_main(int argc, char **argv)
 {
   Verilated::commandArgs(argc, argv);
 
   Vtest test("top");
 
   sc_signal<uint32_t> output_value;
 
   test.output_value(output_value);
 
   cout << "sc_start 前 : " << output_value << endl;
   sc_start(0, SC_NS);
   cout << "sc_start 後 : " << output_value << endl;
 
   exit(0);
 }

とすると、結果は

 LANG:console
              SystemC 2.2.0 --- Oct 29 2010 15:47:29
         Copyright (c) 1996-2006 by all Contributors
                     ALL RIGHTS RESERVED
 sc_start 前 : 0
 sc_start 後 : 1234

となります。

** 追記 : sc_initialize() を呼べばよい [#d80e68c7]

上記の件、シミュレーションの開始前に sc_initialize() 呼べば初期化されるようでした。

 LANG:cpp
   cout << "sc_initialize 前 : " << output_value << endl;
   sc_initialize(); 
   cout << "sc_initialize 後 : " << output_value << endl;
   sc_start(0, SC_NS);
   cout << "sc_start 後 : " << output_value << endl;

とすれば、結果は

 LANG:console
              SystemC 2.2.0 --- Oct 29 2010 15:47:29
         Copyright (c) 1996-2006 by all Contributors
                     ALL RIGHTS RESERVED
 sc_initialize 前 : 0
 sc_initialize 後 : 1234
 sc_start 後 : 1234

となります。

このあたりは SystemC の dont_initialize() 関数とも関係してそうなので、
たぶん後でまた勉強することになりそうです。

** 基本は以上で OK ? [#ha8134e1]

と、ここまで分かっていれば、上記のような簡単なテストベンチを書くことができるようになります。

が、より複雑なテストをするには SystemC のモジュールを書いたり、
イベントを使ったりしたくなるので、以下ではそのあたりを勉強しようと思います。

* 情報源 [#z674d084]

- SystemC Tutorial for VHDL Engineers~
http://www.ht-lab.com/howto/vh2sc_tut/vh2sc_tut.html

ここは非常によくまとまっていて、短時間で勉強できそうです。

以下、これをなぞる形で勉強してみます。

* always @(posedge clk) を SystemC で記述する [#s65278c5]

シンプルなフリップフロップ回路を verilog で書いてみます。

dff.v
 LANG:verilog
 module dff (
     input  wire clk,
     input  wire din,
     output reg  dout
 );
     always @(posedge clk)
         dout <= din;
 endmodule

これと同じ回路を SystemC で書くと、次のようになります。

dff.h
 LANG:cpp
 #ifndef __DFF_H
 #define __DFF_H
 
 #include "systemc.h"
 
 SC_MODULE(dff) {
     sc_in <bool> clk;
     sc_in <bool> din;
     sc_out<bool> dout;
  
     // sensitive << clk.pos()
     void assign();
  
     SC_CTOR(dff) {
         SC_METHOD(assign);
           sensitive << clk.pos();
     }
 };
 
 #endif // __DFF_H

dff.cpp
 LANG:cpp
 #include "dff.h"
 
 // sensitive << clk.pos()
 void dff::assign() {
    dout.write( din.read() );
 }

*** ヘッダファイルの慣用句 [#rc1a8c95]

~#ifndef / #define / #endif は dff.h を多重インクルードしても
問題が起きないようにする慣用句です。

*** モジュールの宣言 [#c5346989]

モジュールを表すクラスを宣言するには、SC_MODULE マクロを用います。

正確にはクラスではなく構造体 (struct) として宣言されるので、
デフォルトのアクセス指定子は public です。したがって、
わざわざ private を指定しない限り、宣言されたすべてのメンバーは
外からアクセス可能になります。

*** 入出力ピンの定義 [#na92bab5]

入出力ピンの定義には、上で見た sc_signal の代わりに sc_in / 
sc_out / sc_inout を使います。

*** コンストラクタ [#t09023ef]

モジュールのコンストラクタは SC_CTOR マクロを使って定義します。
コンストラクタでは下位モジュールなど、モジュールクラスのメンバーを初期化するとともに
(ここでは初期化の必要なメンバーはありません)、
verilog における always 等を実現するためにイベントリストの構築を行います。

always を記述するには、
+ 中身をメンバ関数として定義し (上では assign() 関数)、
+ モジュールのコンストラクタにおいて
+ センシティビティリストと共に SC_METHOD 
マクロを使ってイベントリストに登録します

*** sensitive [#dfee7217]

SC_METHOD の次にある sensitive がセンシティビティリストの定義です。

動作としては、sensitive に追加したイベントが起きると、
SC_METHOD で指定したメソッドが呼び出される、という仕掛けです。

上記のように .pos() や .neg() を付けたエッジ検出の他に、
 LANG:cpp
 sensitive << clk;

のように書けば、両エッジを検出することもできます。
また、
 LANG:cpp
 sensitive << sig1 << sig2.pos();

のように、複数個の信号を並べることができます。
あるいは、
 LANG:cpp
 sensitive << sig1;
 sensitive << sig2.pos(); 

のように2つの分に分けて書いても同じ意味になります。

sensitive << clk としておいて、assign() の中で~
if (clk.posedge()) dout = din; と書いても上記と同じ動作になるようですが、
メソッドが呼び出されてから判別するよりも、
始めから sensitive << clk.pos() 
とした方がシミュレーションは速くなると思います。

sensitive 指定は直前の SC_METHOD に対して有効なので、
1つのコンストラクタの中で複数の SC_METHOD とセンシティビティリストを
登録することができます。
 LANG:cpp
 SC_METHOD(method1);
   sensitive << sig1 << sig2.pos();
 SC_METHOD(method2);
   sensitive << sig3;
   sensitive << sig4.pos();
 SC_METHOD(method3);
   sensitive << sig5;

*** assign() の定義 [#n0f02393]

dff.cpp では、dff.h で宣言した assign() 関数を定義します。
(C/C++ では「宣言」と「定義」は専門用語として使い分けられていて、
実体を伴わずインターフェースだけを記述する「宣言」と、
実体を記述する「定義」という使い方になります。)

宣言(実体なし):
 LANG:cpp
 void assign();

定義(実体あり):
 LANG:cpp
 void dff::assign()
 { ... }

メンバー関数の定義では、その関数がどのクラスのメンバーであるかを指定するため、
関数名の前に :: で区切ってクラス名を記述します。

*** コメントの重要性 [#h221cca9]

気になるのは、上記のように .h と .cpp ファイルを分けてしまうと、
センシティビティリストとメソッド内容とが視覚的に離れてしまう点です。

あまり気は乗らないのですが、上記のように dff::assign() 
の定義付近にコメントとしてセンシティビティリストを
書いておくと、後から読みやすいかもしれません。
( // sensitive << clk.pos() のコメント)

#気が乗らない理由は、こういうコメントと、実際のコンストラクタ中の~
#記述とが食い違うと、非常に見つけにくいバグになってしまうためです。

*** assign() の定義をインラインで書いてしまう [#d957ba63]

一方、C++ ではメンバ関数の宣言中に定義も書いてしまうこともできて、

dff.h
 LANG:cpp
 #ifndef __DFF_H
 #define __DFF_H
 
 #include "systemc.h"
 
 SC_MODULE(dff) {
     sc_in <bool> clk;
     sc_in <bool> din;
     sc_out<bool> dout;
  
     // sensitive << clk.pos()
     void assign() {
        dout = din;
     }
  
     SC_CTOR(dff) {
         SC_METHOD(assign);
           sensitive << clk.pos();
     }
 
 };
 
 #endif // __DFF_H

これならセンシティビティリストと中身は少なくとも1つのファイルの中で見渡せます。
この場合、dff.cpp の中身は #include "dff.h" のみでOKです。

C++ の文法的には、これだと assign() 
関数をインライン関数として宣言することになります。
実際には SC_METHOD で assign の関数ポインタを使うことになるので、
動作が食い違うのですが、g++ では問題なくコンパイルできるようでした。

ただ、この形だと、assign() 内を編集すると、
dff.h をインクルードするすべての .cpp 
ファイルをコンパイルしなおさなければなりません。
これは大きな負担となるため、
規模の大きなプロジェクトでは、やはりメソッド定義を .cpp 
に切り出しておくほうがよいのだと思います。

*** 値の代入 [#t10d5c32]

 LANG:cpp
 dout.write( din.read() );

の部分は、

 LANG:cpp
 dout = din;

と書いても同じ動作になるのですが、read / write を使う書き方が推奨されているそうです。

というのも、verilog と異なり C++ では通常 dout = din の形は変数内容の書き換えを表すもので、
信号線から信号線への値の受け渡しを想起しないためです。

ちょっと面倒なので、

 LANG:cpp
 dout.write( din );

でもいいように思うのですが、.read() の必要な理由は後で出てくるのかもしれません。

*** assign で書くには [#c6a1950c]

no_ff.v
 LANG:verilog
 module no_ff (
     input  wire din,
     output reg  dout
 );
     assign dout = din;
 endmodule

ならば、

no_ff.h
 LANG:cpp
 #ifndef __NO_FF_H
 #define __NO_FF_H
 #include "systemc.h"
 
 SC_MODULE(no_ff) {
   sc_in <bool> din;
   sc_out<bool> dout;
 
   sc_signal<bool> din_copy;
 
   SC_CTOR(dff)
   {
     din(din_copy);
     dout(din_copy);
   }
 };
 #endif // __NO_FF_H

となるのでしょうか?

ちょっと面倒ですね。

 LANG:cpp
 dout(din);  // エラーになる

ではうまくいきませんでした。

*** コンパイル方法 [#y31ec5d8]

上記 SystemC コード dff.c をコンパイルしてオブジェクトファイル dff.o を得るには、
 LANG:console
 g++ -c -I$SYSTEMC/include dff.cpp

とします。

*** 注意点 [#ub0b51fc]

最後に、C++ の初心者の方が1回ははまるミスとして、
SC_MODULE(dff) { }; のブロックの後ろにある ; を忘れてしまうというものがあります。

通常 C++ では } の後ろに ; が必要になることはそれほど多くなくて、
例外と言えるのが、この型定義の後の ; になります。

これは、SC_MODULE(dff) { } a_variable_of_dff_type; の形で、
dff 型の変数を定義できるという C++ の文法のせいなのですが、
この間違いは犯しやすいというだけでなく、
コンパイルエラーが出たときにその理由を探しにくいという点でも
犯罪的です。

どうなるかというと、dff.cpp で include された後のコードにおいて、
SC_MODULE(dff) { } void dff::assign() { } の形が現れるため、
エラーは dff.h ではなく dff.cpp の void dff::assign() 
の行に現れることになります。

 LANG:console
 $ g++ -c -I$SYSTEMC/include dff.cpp
 dff.cpp:4: error: new types may not be defined in a return type
 dff.cpp:4: note: (perhaps a semicolon is missing after the definition of ?dff?)
 dff.cpp:4: error: two or more data types in declaration of ?assign?

あら、最近の g++ は親切ですね。ちゃんと dff の定義が悪いことを指摘してくれています。

インクルードファイルが複数あるときは、
次のインクルードファイルの先頭でエラーになったりもしますので、
その際は、直前のインクルードファイルの末尾を見直すという
ひらめきが重要になります。

* Windows 上での C++ 開発環境 [#g87be4d1]

SystemC 上で大規模開発を本気でするなら、
ぜひともまともな IDE を入れることをお勧めします。

cygwin 上の Verilator と合わせて使うことを考えると、
たぶん、Eclipse IDE for C/C++ Developers が便利だと思います。

http://www.eclipse.org/downloads/

それほど使い倒した訳じゃないので無責任なお勧めですが、
ちょっと試した限りでは PATH を設定するだけで Eclipse IDE から
cygwin 上の g++ や gdb を使ったコンパイル&デバッグが可能でした。

gui を使ってステップ実行や変数表示が行えるのは得難いメリットです。

で、そのあたりの設定より苦労したのがフォントの設定の仕方だったり:~
メニューから [Window]-[Preference]-[General]-[Appearance]-[Colors and Fonts] でできました。

* 気になっている点 [#n11e3ce6]

** verilog モジュールにパラメータを指定する [#h5c583fc]

パラメータを含む verilog モジュールを verilator + SystemC で検証するのは難しそう?

http://www.veripool.org/boards/2/topics/show/276-Verilator-Efficient-Usage-of-Verilog-Parameters

この記事の 2010-04-14 の時点では、
- parameter を verilator から設定する方法は無い
- 今後も実装する気はない

との回答でした。

これは結構困ったことで、parameter を含む verilog モジュールをテストするには、
ラッパーとなる verilog モジュールをテストしたいパラメータの数だけ作り、
それらを verilate して SystemC とリンクしなければならないということになるのだと思います。

1つ後で思いついた対応策として、
verilator の +define+ オプションでプリプロセッサー定数を定義することはできるので、
~`ifdef や `SOME_PARAMETER でうまくパラメータを変更する手だてを考える余地はありそうですね。

** SystemC 雑感 [#i8d9dbe3]

書店で SystemC に関する本があまり見つからない理由とか、SystemC をちょっとかじってみて分かった気がしています。

SystemC は所詮、C++ でロジックをシミュレーションするための(マクロ)ライブラリでしか無くて、ロジック記述用の言語としては VHDL や Verilog とは比べるべくも無い出来映えです。

VHDL や Verilog で簡単に書けることが SystemC では書くのも読むのもとても面倒だったり。

C++ を分かってれば「C++ で無理矢理書こうとすれば、そりゃそうなるわな」と納得できなくはないけれど、そういうあきらめがなく「ロジックを書くときに C++ 使えたらどんなに便利だろ」とか期待したりすると、がっかり感が半端無い。

ある意味 SystemVerilog + DPI-C と比べても、ちょっとどうかと思えるところが多いような。

SystemC の使いどころは、あくまでソフトウェアとロジックとの同時開発だと思いました。C++ でばりばりソフトを書いておいて、それを部分的にロジックに直して行くわけですね。

だから、そこそこ C++ が得意でないと SystemC だけ勉強したとしても、あんまり役に立たないし、回路設計の分野の人には参入障壁高そうだし、かなり大がかりなプロジェクトじゃないと採用するメリットも無さそうだし・・・

そんなわけで初心者向けの本とかは需要少ないのかな、と。

ものすごく高級なコンパイラを使って、高度な記述を一気にロジックに落とせたりするようになればまた違うのかもしれませんが、Verilator のようなフリーのソフトを使ってテストベンチだけ SystemC で書いてる限り、個人的な利用ではなかなか使いどころを見出すのが難しそう。

実際の所、どうなんでしょうね。

* 関連記事 [#a01f20f5]

#ls2(電気回路/HDL/Verilator);
#ls2(電気回路/HDL/System);

* コメント [#gf4b24de]

#article_kcaptcha

**C++は敷居が高いw [#i3b644de]
>[アプロ] (2011-06-07 (火) 10:54:06)~
~
SystemVerilog からちょっとずつやるなら、アレですが、~
いきなりC++ だと、歯が立ちませんねぇ orz~

//
- うーん、System Verilog と比べても SystemC はずば抜けて理解しづらいと思います。主な違いはスケジューリングなのです。Verilog や VHDL は言語仕様に並列実行が組み込まれているため、並列動作する回路のエミュレートを同時に1つの命令しか実行できない CPU で実装するための技術的な詳細を考えずにコードを書くことができます。 -- [武内(管理人)] &new{2011-06-07 (火) 13:14:37};
- うーん、System Verilog と比べても SystemC はずば抜けて理解しづらいと思います。主な違いはスケジューリングなのです。Verilog や VHDL は言語仕様に並列実行が組み込まれているため、並列動作する回路のエミュレートを同時に1つの命令しか実行できない CPU で行うための技術的な詳細を考えずにコードを書くことができます。 -- [武内(管理人)] &new{2011-06-07 (火) 13:14:37};
- 一方 C++ は抽象度の低い言語なので、並列処理をしたければ自分でスケジューラを書き、またネイティブスレッドを起動・停止・同期するコードを書かなければなりません。 -- [武内(管理人)] &new{2011-06-07 (火) 13:15:18};
- SystemC はテンプレートやマクロを駆使したかなりえげつないライブラリの中に、疑似スレッドやネイティブスレッドを駆使したスケジューリングを隠蔽しているのですが、言語仕様のあちこちにその片鱗が覗いてしまっています。 -- [武内(管理人)] &new{2011-06-07 (火) 13:15:43};
- ですので SystemC を C++ の視点で見て「うわ、これすごいことやってんな。中はどうなってるんだろう?」と思える人ならゴールは近いですが、Verilog や VHDL と同列の1つの言語として SystemC を1から学ぼうとすると、思った通りの動作をしないときに対処できない状況が頻発するんじゃないかと心配になります。 -- [武内(管理人)] &new{2011-06-07 (火) 13:16:30};
- SC_MODULE と SC_THREAD と SC_CTHREAD がどうして3つも必要なのか、とか、SystemC の仕様を見ていてもたぶん理解できない。それらの中身を C++ でどうやったら書けるかを考えると、「まあ、当然そうなるよな」とあきらめに似た理解ができると思うのです。 -- [武内(管理人)] &new{2011-06-07 (火) 13:20:16};
- そのほかにも、マクロとテンプレートの文法や、C++ のメモリモデルなんかも知っていないとコンパイルエラーに対する対処ですら大変じゃないかと・・・そのあたりは本来回路を記述するだけなら知る必要のないところなのですが、それを C++ の上でエミュレートしようとすると、こうなっちゃうということなんですよね。 -- [武内(管理人)] &new{2011-06-07 (火) 13:21:04};

#comment_kcaptcha

Counter: 19597 (from 2010/06/03), today: 6, yesterday: 0