SystemC : SC_MODULE, SC_CTORなしのモジュール記述

sc_moduleの利用



SC_MODULE, SC_CTORマクロは初心者には便利な反面、設計の制約になることがあります。特にSC_CTORでは、モジュール名のみを引数とするコンストラクタしか利用できません。
ここでは、コンストラクタ引数によるメンバ変数msgの初期化を実現します。


マクロを使わないモジュールの記述は以下の通りです。
・クラス定義でsc_moduleをpublic継承する
・SC_HAS_PROCESS(クラス名)を定義する
・コンストラクタの第一仮引数にsc_module_nameを定義する
・コンストラクタの初期化にて、継承したメンバ変数sc_moduleを第一仮引数で初期化する
・アクセス指定子を記述する(SC_MODULEなしの場合、デフォルトがprivate)


ただし、高位合成ツールがサポートしないかもしれませんのでご注意ください。

#include "systemc.h"
#include <string>

class speaker : public sc_module // sc_moduleの継承
{
  public: // アクセス指定子の記述
    sc_in<bool>  say;

  private:
    const std::string msg;

  public:
    SC_HAS_PROCESS( speaker );  // SC_HAS_PROCESSの定義
    speaker ( 
      // 第一仮引数にsc_module_nameを定義
      const sc_core::sc_module_name &nm = 0,
      const std::string &msg = 0
    )
      // 継承メンバ変数sc_moduleの初期化
      : sc_module( nm )
      , msg( msg )
    {
      SC_THREAD( proc );
      sensitive << say.pos();
    }

    void proc ( void )
    {
      while(1)
      {
        wait();
        std::cout << msg << " at " << sc_time_stamp() << std::endl; 
      }
    }
};

class test : public sc_module
{
  public:
    sc_out<bool> say;

  public:
    SC_HAS_PROCESS( test );
    test( const sc_core::sc_module_name &nm = 0 )
      : sc_module( nm )
    {
      SC_THREAD( proc );
    }

    void proc ( void )
    {
      say.write( false ); // 初期化
      wait(SC_ZERO_TIME); // speakerがsayの受信待ち状態になるのを待つ

      wait(5, SC_NS);
      say.write( true );
      wait(0.1, SC_NS);
      say.write( false );

      wait(2.9, SC_NS);
      say.write( true );
      wait(0.1, SC_NS);
      say.write( false );

      sc_stop();
    }
};

int sc_main ( int argc, char **argv )
{
  sc_signal<bool> say;

  speaker sp0( "sp0", "hello world!" );
  sp0.say( say );

  test test0( "test0" );
  test0.say( say );

  sc_start();
  return 0;
}

SystemC:hello world

SystemCの勉強ログです。
初日はhello worldから。


case 1. メイン関数(sc_main)のみ



もっとも簡単な例です。
main関数ではなく、sc_main関数を使用します。

#include "systemc.h"

int sc_main ( int argc, char **argv )
{
  std::cout << "hello world!" << std::endl;
  return 0;
}





case 2. クラス(C++)の利用



こちらはC++のクラスを利用したhello worldになります。
speakerクラスはメンバ関数say()がコールされることで喋ります。
sc_mainにてspeakerクラスのインスタンス生成と操作を行います。

#include "systemc.h"

class speaker
{
  public:
    speaker ( void )
    {
    }

    void say ( void )
    {
      std::cout << "hello world!" << std::endl;
    }
};

int sc_main ( int argc, char **argv )
{
  speaker sp0;
  sp0.say();
  return 0;
}





case 3. SC_MODULEの利用(1)



ここからSystemC特有の記述を使っていきます。


上記のspeakerクラスをSC_MODULEに変更します。
変更は以下の通りです。
・クラス定義をSC_MODULEマクロ、コンストラクタ定義をSC_CTORマクロに置換
・プロセス関数(proc)の定義とコンストラクタ内でのプロセス登録
speakerは自律的には何もせず、シミュレーション開始から完了までずっと待ちぼうけです。外部からsay()がコールされたら喋ります。


sc_mainにてシミュレーションの実行制御とインスタンスの操作を行います。

#include "systemc.h"

SC_MODULE( speaker )
{
  public:
    SC_CTOR( speaker )
    {
      SC_THREAD( proc );
    }

    void proc ( void )
    {
      wait(); // 静的センシティビテイなしのため待ちぼうけ
    }
    
    void say ( void )
    {
      std::cout << "hello world!" << " at " << sc_time_stamp() << std::endl; 
    }
};

int sc_main ( int argc, char **argv )
{
  speaker sp0("sp0");
  sc_start( 3, SC_NS );
  sp0.say();
  sc_start( 5, SC_NS );
  sp0.say();
  return 0;
}





case 4. SC_MODULEの利用(2)



case 3の構成では、speakerにアクセスするたびにシミュレーションを停止させる必要があります。
そこで、speakerにアクセスする機能をSC_MODULE化(test)して、シミュレーション実行中のspeakerへのアクセスを可能にします。
testの役割
・speakerクラスのインスタンス
・時間経過に合わせたsay()の要求とシミュレーションの完了
sc_mainの役割
・testクラスのインスタンス
・シミュレーション開始


#include "systemc.h"

SC_MODULE( speaker )
{
  public:
    SC_CTOR( speaker )
    {
      SC_THREAD( proc );
    }

    void proc ( void )
    {
      wait(); // 静的センシティビテイなしのため待ちぼうけ
    }
    
    void say ( void )
    {
      std::cout << "hello world!" << " at " << sc_time_stamp() << std::endl; 
    }
};

SC_MODULE( test )
{
  private:
    speaker *sp0;

  public:
    SC_CTOR( test )
    {
      sp0 = new speaker("sp0");
      SC_THREAD( proc );
    }

    void proc ( void )
    {
      wait(3, SC_NS);
      sp0->say();

      wait(5, SC_NS);
      sp0->say();

      wait(1, SC_NS);
      sc_stop();
    }
};

int sc_main ( int argc, char **argv )
{
  test test0( "test0" );
  sc_start();
  return 0;
}





case 5. SC_MODULEの利用(3)



case 4ではtestがspeakerの動作を制御していました。
実設計では、speakerが自律的に外部の要求を観測し、動作をすることが求められます。


speakerの仕様を以下の通りとします。
・speakerはtestがドライブする1bit信号sayを入力とする
・speakerはsayの立ち上がりエッジを検出したら喋る
変更点は以下の通りです。
・各モジュールにポートを宣言 : (1bitの信号say, sc_in/sc_out
・シグナルでポート間を接続  : (sc_signal
・プロセス関数(proc())の処理修正

#include "systemc.h"

SC_MODULE( speaker )
{
  public:
    sc_in<bool>  say;

  public:
    SC_CTOR( speaker )
    {
      SC_THREAD( proc );
      sensitive << say.pos();
    }

    void proc ( void )
    {
      while(1)
      {
        wait(); // sayの立上り待ち
        std::cout << "hello world!" << " at " << sc_time_stamp() << std::endl; 
      }
    }
};

SC_MODULE( test )
{
  public:
    sc_out<bool> say;

  public:
    SC_CTOR( test )
    {
      SC_THREAD( proc );
    }

    void proc ( void )
    {
      say.write( false ); // 初期化
      wait(SC_ZERO_TIME); // speakerがsayの受信待ち状態になるのを待つ

      wait(5, SC_NS);
      say.write( true );
      wait(0.1, SC_NS);
      say.write( false );

      wait(2.9, SC_NS);
      say.write( true );
      wait(0.1, SC_NS);
      say.write( false );

      sc_stop();
    }
};

int sc_main ( int argc, char **argv )
{
  sc_signal<bool> say;

  speaker sp0( "sp0" );
  sp0.say( say );

  test test0( "test0" );
  test0.say( say );

  sc_start();
  return 0;
}

C++:ハマったエラー

1. ポインタの値をostreamで表示したいとき

// ptr.cpp
#include <iostream>

int main(void) {
  char a;
  char* p;
  p = &a;
  std::cout << " p = " << std::hex << (unsinged int) p << std::endl;
  std::cout << "&a = " << std::hex << (unsinged int) p << std::endl;
  return 0;
}

上記の記述をg++で、実行すると以下のエラーがでる。

ptr.cpp:11: error: cast from ‘char*’ to ‘unsigned int’ loses precision

原因:64bit環境のアドレスを32bitにキャストしている
対策:64bitでキャストする

// ptr.cpp
#include <iostream>
#include <stdint.h> // C++11より新しい場合は#include <cstdint>

int main(void) {
  char a;
  char* p;
  p = &a;
  std::cout << " p = " << std::hex << (uint64_t) p << std::endl;
  std::cout << "&a = " << std::hex << (uint64_t) p << std::endl;
  return 0;
}

C++ : 乱数生成

#include <time.h>
#include <iostream>

double randf() {
   return static_cast<double>(rand()) * (1.0 / (RAND_MAX+1));
}

size_t randi(size_t min, size_t max) {
  return min + static_cast<size_t>(rand()) % (max- min);
}

int main(void) {
  srand(time(NULL));
  for (int i = 0; i < 100; ++i) {
    std::cout << randf() << " ";
  }
  std::cout << std::endl;

  for (int i = 0; i < 100; ++i) {
    std::cout << randi(10, 20) << " ";
  }
  std::cout << std::endl;
  return 0;
}

フィルタ系のプログラムの実装

すごーくひさしぶりに日記書いてみる。

cat, grepなど元のデータを加工して,データを出力するフィルタ系のプログラムを書く場合メモ。

1.入力が標準入力の場合とファイル入力の場合の切り替え

これらのプログラムで入力データを標準入力から得る場合と,ファイルから読み込む場合との両方を実装する場合を考えてみた。

filter < hoge.txt  # 標準入力からの読み込み  
filter hoge.txt    # ファイルからの読み込み

このように読み込み元が異なるが,処理自体は同じなのでコードを共通化したい。


C言語の場合(こちらのサイトから引用)

#include <stdio.h>
#include <stdlib.h>

....

char *filename;              /* NULL ならファイル指定なしとします */
FILE *fp;

....

if ( filename == NULL ) {    /* ファイル指定なし */
    fp = stdin;
} else {                     /* ファイル指定あり */
    fp = fopen( filename, "r" );
    if ( fp == NULL ) {
        fprintf( stderr, "'%s'が読み込めません.\n", filename );
        exit(1);             /* 異常終了 */
    }
}


C++の場合その1:入力ストリームを切り替え得るwrapper関数を作成する

#include <iostream>
#include <fstream>
#include <string>

// 読み込むべきストリームを切り替え得るwrapper
//   bool isStd     : 入力ストリームが、std::cin かファイルか指定する
//   ifstream& ifs : 入力ストリームへの参照
std::istream& pickInput( bool isStd, std::ifstream& ifs) {
  if (isStd) {
    return std::cin;
  } else {
    return ifs;
  }
}


int main (void) {
  std::ifstream ifs("hoge.txt"); 
  bool isStd;

  std::string line;

  isStd = true; // 標準入力から受け取る場合

  while (std::getline( pickInput(isStd, ifs), line)) {
    if (isStd) {
      std::cout << "cin : " << line << std::endl;
    } else {
      std::cout << "ifs : " << line << std::endl;
    }
  }

  return 0;
}


C++の場合その2:istream型のポインタ変数を利用

ifstream型変数もstd::cinもistreamクラスを継承していることに着目し,istream型のポインタ変数をもちいて,ifstream型変数,std::cinを切り替える。

#include <iostream>
#include <fstream>
#include <string>


int main (void) {
  std::ifstream ifs("hoge.txt"); 
  std::istream* ptrIs; 
  bool isStd;

  std::string line;

  isStd = true;
  if (isStd) {
    ptrIs = &std::cin;
  } else {
    ptrIs = &ifs;
  } 

  while (std::getline( *ptrIs, line)) {
    if (isStd) {
      std::cout << "cin : " << line << std::endl;
    } else {
      std::cout << "ifs : " << line << std::endl;
    }
  }

  return 0;
}

ここで,ポインタ変数でなく参照変数を利用したくなるが,コンパイルエラーになる。*1

#include <iostream>
#include <fstream>
#include <string>


int main (void) {
  std::ifstream ifs("hoge.txt"); 
  std::istream& refIs = std::cin; 

  bool isStd;

  std::string line;

  isStd = true;
  if (!isStd) {
    refIs = ifs;   // compile error
  }

  while (std::getline( refIs1, line)) {
    if (isStd) {
      std::cout << "cin : " << line << std::endl;
    } else {
      std::cout << "ifs : " << line << std::endl;
    }
  }

  return 0;
}


rubyの場合

ARGF.each_line do |line|
  filename = ARGF.filename
  lineno = ARGF.file.lineno
  puts "#{filename}:#{lineno}:#{line}"
end

*1:参照変数の初期化は可能だが,代入は不可能になる。原因はよくわからんけど,これと同じような理由?→そもそも参照変数は初期化のみ可ですね。

vimperator2.0β2pre

バージョンを1.2から2.0β2preに変えてみました。

僕の環境はfirefox3.0.7で大して他のアドオンも入れてない状態なのですが、現在(3/6)で手に入る本家HPのTry 2.0 beta 2では f でヒントモードになっても、リンク先の番号やらハイライトがうまく表示されないという謎状況でした。
仕方ないので
http://download.vimperator.org/vimperator/nightly/
から、バージョンを変えてちょいちょいと試してみたんですが、2.0β2preとしては一番古いvimperator_2.0b2pre_2009-02-10.xpiでは動作したので、とりあえずはそれで動作させてます。


他の人のとかを参考にしつつ .vimperatorrc の変更点です。

  • visualbell の消去
    " set novisualbell (ver1.2)
    highlight Bell display:none*1
  • ブックマークツールバーの表示
    " set guioptions=b (ver1.2)
    set guioptions=B
  • ヒントのスタイル指定
    " set hintstyle= 〜 (ver1.2)
    highlight Hint 〜


以下は晒しです。


" ブラウザのタイトル
set titlestring=Firefox
" メニューバーとツールバーを表示
set guioptions=mB
" 検索語のハイライト
set hlsearch
" 履歴を1000件
set history=1000
" テキストエリアへのオートフォーカスしない
set focuscontent
" ビープを鳴らさない
set visualbell
highlight Bell display:none
"補完設定
set wildoptions=auto
set complete=sl

"ヒントのスタイル指定
highlight Hint z-index:5000; font-family:monospace; font-size:15px; color:white; background-color:blue; border-color:ButtonShadow; border-width:0px; border-style:solid; padding:0px 1px 0px 1px; position:absolute;

"j/kの移動量を5倍に
map j 5
map k 5
"親ディレクトリに移動
map U gu
" 戻る・進む
map H
map L

" 選択文字列のgoogle検索を に割り当て
map YP

" h/l or ←/→ でタブ移動
map h gT
map l gt
map gT
map gt

" a/s で現在のタブの位置変更
map a :tabmove! -1
map s :tabmove! +1

" 補完リストの選択
imap
cmap
imap
cmap
imap
cmap

" Stop loading
map ,c :stop

map :source ~/.vimperatorrc

" コピペ関係
noremap
inoremap
cnoremap
noremap
inoremap
cnoremap
inoremap
cnoremap
inoremap
cnoremap
inoremap
cnoremap

echo "### vimperatorrc loaded. ###"

*1:デフォルトでvisual bellが鳴らない??