シミュレーション終了時に実行されるfinal文

最近知ったのですが、SystemVerilogでは、シミュレーション終了時に実行されるfinal文というものが追加されています。使い方は簡単で、シミュレーション終了時にfinal文内の記述が1回だけ実行されます。なお手続き的処理(#遅延とか)は記述できません。

moduel tb;
  initial begin
    #100;
    $finish;
  end

  final begin
    $display("%t:owata", $time);
  end
endmodule
#    Time: 100 ps  Iteration: 0  Instance: /tb
#                  100:owata

electric-buffer-listの挙動がおかしい・・・

Macのターミナルでemacsを使うと、どうもelectric-buffer-listの挙動がおかしい。色々ググっても解決法が分からず。何となくM-x versionしてみるとemacs 22.1.1だったので、バージョンを23に上げてみたところ解決しましたorz

ちなみに、Homebrewでemacsをインストールすると、emacs23がインストールされました。

$ brew install emacs
~
$ echo alias emacs='/usr/local/bin/emacs' >> ~/.bashrc
$ source ~/.bashrc
$ emacs -version
GNU Emacs 23.3.1
Copyright (C) 2011 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.

MacにGTKWaveをインストール

SystemCでダンプした波形を表示するために波形ビューアGTKWaveをインストールします。

まずはパッケージ管理ツールHomebrewをインストール。

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"

あとは全部Homebrewがやってくれます。

$ brew install gtkwave

インストール完了後、gtkwaveと打ち込むとXが立ち上がってウィンドウが表示されます。

$ gtkwave

Mac OS XにSystemCをインストール

※SystemCのバージョン2.3からMac OS Xでもmakeできるようになりました。


最近購入したMacBook AirにSystemCをインストールしてみたので、やり方をメモ。

1. Xcodeのインストール

gccを使うためにXcodeをインストールします。DockにあるApp Storeからダウンロードできます。

2. SystemCのインストール

公式のものをmakeしようとするとエラーが出る。代わりにMac OS X用のインストーラを公開しているサイトがあるので、ありがたく使わせていただく。下記から「SystemC Installation Suite for OSX」をダウンロード。

http://www.logicpoet.com/downloads/

あとは適当にクリックしてインストールすると、/Library/SystemC/にインストールされます。

3. 動作確認

まずは、作業ディレクトリを作成。

$ mkdir ./work
$ mkdir ./work/test
$ cd ./work/test

いつものhelloworld.cppを./wokr/testに作成。

#include <systemc.h>

int sc_main(int argc, char* argv[])
{
     cout << "Hello, World" << endl;

     return 0;
}

exampleにあるMakefile.defsをコピー。

$ cp /Library/SystemC/systemc-2.2.0/examples/systemc/Makefile.defs ../

2行目のSystemCディレクトリのパスを変更。

#SYSTEMC = ../../..
SYSTEMC = /Library/SystemC/Current 

こちらのサイトを参考にmakefileを作成します。さっきコピーしたMakefile.defをインクルードします。また、コンパイルオプションに"m32"を付けないと、エラーになるので注意。

CC          = g++
CFLAGS      = -Wall -g -m32
EXTRA_LIBS  = -O2
TARGET_ARCH = macosx

MODULE = run

SRCS = helloworld.cpp
OBJS = $(SRCS:.cpp=.o)

include ../Makefile.defs

makeして実行。

$ make
$ ./run.x

             SystemC 2.2.0 --- Oct 10 2009 07:49:18
        Copyright (c) 1996-2006 by all Contributors
                    ALL RIGHTS RESERVED
Packaged for MacOS by Logic Poet: http://www.logicpoet.com

Hello, World

連想配列を使ってメモリをモデリング

SystemVerilogでメモリをモデリングする場合は、連想配列を使うとすごく便利。
連想配列を使うとなにが嬉しいかというと、下記のように大きなサイズのメモリを簡単に記述できる。

byte mem[int]; // 配列のインデックスに"int"、"string"等と書くと連想配列になる

initial begin
  mem[32'h00000000] = 8'h11; // 0番地に0x11を書き込む
  mem[32'hfffffffe] = 8'h22; // fffffffe番地に0x22を書き込む

  $display("address=%h, data=%h", 32'h00000000, mem[32'h00000000]);
  $display("address=%h, data=%h", 32'hfffffffe, mem[32'hfffffffe]);
end

・実行結果

# address=00000000, data=11
# address=fffffffe, data=22

もし、同じように2^32ビットのメモリをVerilogで使おうとして、

reg [7:0] mem[0:32'hffffffff];

と記述しても、パソコンのメモリを確保できないために、恐らくエラーが出ると思う。


また、仮にメモリ領域を確保できたとしても、初期化で下記のように書こうものならものすごく時間が掛かるはず。

initial begin
  for(ad=0;ad<32'hffffffff;ad=ad+1)
    mem = 8'h0;
end

この点、SystemVerilogの場合、2値型のbyteやbitを使えば、initial文で初期化しなくても初めから0か1が入っている。
(2値型の初期値がシミュレータ依存なのか言語仕様で決まっているのかは分りません)

SystemVerilogでクラス内のタスクを常に走らせておく方法

色々調べまわって、always文のようにクラス内のタスクを並列して常に走らせておく方法がやっと分った。
まず実行したいタスクをforever文で書いて、さらにそのタスクをfork-join_noe文内で実行すればできた。
クラスを使った記述だと並列処理を書きにくいような気がするが、どうもよく分らん。

class test;
    logic clk;

    function new(ref logic clk);
        fork // 並列処理できるようにfork-join_none内でタスクを実行
            run(clk);
        join_none
    endfunction

    task run(ref logic clk);
        forever begin // foreverを使って常時実行するタスクを生成
            @(clk);
            $display("%t: clk=%b", $time,clk);
        end
    endtask
endclass

module tb;
    logic clk;
    test tst;

    initial begin
        clk  = 1'b0;
        forever #10 clk = ~clk;
    end

    initial begin
        tst  = new(clk);
        #50;
        $finish;
    end
endmodule

・実行結果

#                   10: clk=1
#                   20: clk=0
#                   30: clk=1
#                   40: clk=0

classを使ってみる

クラスの基本的な使い方

クラスを使う場合は、クラスを宣言した後に手続き文(initial、always)内で「クラス名=new();」と書くことでインスタンスが生成される。
クラスというのは動的に生成するものなので、モジュールのインスタンス生成と異なり、宣言するだけでは実体が生成されない、ということらしい。

class test;
    function new();
        $display("%t:new", $time);
    endfunction

    task print(input string str);
        $display("%t:%s", $time, str);
    endtask
endclass

module tb_top;
    test tst; // クラス宣言

    initial begin
        tst  = new(); // インスタンス生成
        #100;
        tst.print("print");
        #100;
        $finish;
    end
endmodule

実行結果

#                    0:new
#                  100:print

interfaceでクラス内の信号と接続

interfaceを使ってクラス内信号と接続したい場合は、まず、クラス内で下記の様にvirtualを付けてインターフェイスを宣言する。

class test;
    virtual bus_if bus_c; // vritual interface
    〜
endclass

次に、newメソッドの引数を上と同じvirtualインターフェイスで宣言し、上述のvirtualインターフェイスと接続する。

class test;
    virtual bus_if bus_c; // vritual interface

    function new(virtual bus_if bus_l);
        bus_c      = bus_l;
    endfunction
  〜
endclass

最後に、クラスを呼び出す側のモジュール内で、クラス生成時に実体を持ったインターフェイスと接続する。

module tb_top;
    test tst;     // クラス宣言
    bus_if bus(); // インターフェイス宣言

    initial begin
        tst  = new(bus); // クラスのインスタンス生成
  end
endmodule

thisを使う

上記の例ではnewメソッドの引数名(bus_l)とクラス内のインターフェイス名(bus_c)を変えているが、thisを使うことで同じ名前を使用できる。

class test;
    virtual bus_if bus_l; // vritual interface

    function new(virtual bus_if bus_l);
        this.bus_l      = bus_l;
    endfunction
  〜

まとめ

以上をまとめたサンプルコードを示す。下記のように書くのが定石っぽい。

interface bus_if;
    logic sig;
endinterface

class test;
    virtual bus_if bus; // vritual interface

    function new(virtual bus_if bus);
        this.bus      = bus;
    endfunction

    task asrt();
        bus.sig <= 1'b1;
    endtask
endclass

module tb_top;
    test tst;
    bus_if bus();

    initial begin
        tst  = new(bus);
        bus.sig <= 1'b0;
        #100;        
        $display("%t:%b", $time, bus.sig);
        #100;
        tst.asrt();
        #100;
        $display("%t:%b", $time, bus.sig);
        $finish;
    end
endmodule

実行結果

#                  100:0
#                  300:1