UVMの入門書
アマゾンでUVMの入門書を見つけました。kindle版だと千円なので試しに買ってみましたが、結構分かりやすいです。
Amazon.co.jp: Getting Started with UVM: A Beginner's Guide
英語ですが、100ページ程度とボリュームが少ないので、入門書として良いのではないでしょうか。
static/automatic/virutal taskの違い
module内でtaskを定義し、そのtaskを並列に2個同時に走らせてみます。
module task_test; task normal_task(string str); #100; $display("%s", str); endtask initial begin $display("***normal task***"); fork begin normal_task("1st task"); end begin #50; normal_task("2nd task"); end join end endmodule
この場合、2個のtaskは同じ変数を参照するため、下記のようにtask内の変数は上書きされてしまいます。
# ***normal task*** # 2nd task # 2nd task
次にタスクにautomaticを付けて同じことをします。
module task_test; task automatic normal_task(string str); #100; $display("%s", str); endtask initial begin $display("***automatic task***"); fork begin normal_task("1st task"); end begin #50; normal_task("2nd task"); end join end endmodule
すると今度は上書きされません。automaticを付けると、taskは実行されるごとに動的に変数を生成します。
# ***automatic task*** # 1st task # 2nd task
SystemVerilogでは、他にもstatic taskとvirutal taskがあります。これらを含む全ての種類のtaskについて、module内で宣言した場合とclass内で宣言した場合でどのように変数が振る舞うか調べてみました。
| taskの種類 | module内で定義 | class内で定義 |
|---|---|---|
| 何も付けないtask | 変数はstatic | 変数はautomatic |
| static task | 変数はstatic | 変数はstatic |
| automatic task | 変数はautomatic | 変数はautomatic |
| virtual task | -(module内では定義不可) | 変数はautomatic |
何も付けないtaskのみ、module内で定義した場合とclass内で定義した場合の変数の扱いが異なるようです。
確認に使ったソースコード
class Task; task class_normal_task(string str); #100; $display("%s", str); endtask task static class_static_task(string str); #100; $display("%s", str); endtask task automatic class_automatic_task(string str); #100; $display("%s", str); endtask virtual task class_virtual_task(string str); #100; $display("%s", str); endtask endclass module task_test; task normal_task(string str); #100; $display("%s", str); endtask task static static_task(string str); #100; $display("%s", str); endtask task automatic automatic_task(string str); #100; $display("%s", str); endtask initial begin Task t = new(); $display("***normal task***"); fork begin normal_task("1st task"); end begin #50; normal_task("2nd task"); end join $display("\n***static task***"); fork begin static_task("1st task"); end begin #50; static_task("2nd task"); end join $display("\n***automatic task***"); fork begin automatic_task("1st task"); end begin #50; automatic_task("2nd task"); end join $display("\n***normal task(class)***"); fork begin t.class_normal_task("1st task"); end begin #50; t.class_normal_task("2nd task"); end join $display("\n***static task(class)***"); fork begin t.class_static_task("1st task"); end begin #50; t.class_static_task("2nd task"); end join $display("\n***automatic task(class)***"); fork begin t.class_automatic_task("1st task"); end begin #50; t.class_automatic_task("2nd task"); end join $display("\n***virtual task(class)***"); fork begin t.class_virtual_task("1st task"); end begin #50; t.class_virtual_task("2nd task"); end join end endmodule
実行結果
# ***normal task*** # 2nd task # 2nd task # # ***static task*** # 2nd task # 2nd task # # ***automatic task*** # 1st task # 2nd task # # ***normal task(class)*** # 1st task # 2nd task # # ***static task(class)*** # 2nd task # 2nd task # # ***automatic task(class)*** # 1st task # 2nd task # # ***virtual task(class)*** # 1st task # 2nd task
MacにSystemC 2.3をインストール
SystemCは、バージョン2.3からMac OS Xに対応したようです。公式からダウンロードしたファイルでmakeできました。インストール手順を以下に示します。
SystemCのインストール手順
下記からsystemc-2.3.0.tgzをダウンロードします。
http://www.accellera.org/downloads/standards/systemc
インストール先のディレクトリを作成します。
$ mkdir /Library/SystemC $ mkdir /Library/SystemC/systemc-2.3.0
makeします。
$ tar zxf systemc-2.3.0.tgz $ cd systemc-2.3.0/ $ mkdir objdir $ cd objdir/ $ ../configure --prefix=/Library/SystemC/systemc-2.3.0 $ make $ make install
動作確認のためにMakefile.defsをコピーしておきます。
$ mkdir ~/work $ cp ../examples/sysc/Makefile.defs ~/work/
動作確認
続いて動作確認を行います。まず、Makefile.defsの2行目をSystemCのインストールディレクトリへ変更します。
・Makefile.defs
#SYSTEMC = ../../.. SYSTEMC = /Library/SystemC/systemc-2.3.0
作業ディレクトリを作成し、cppファイルとMakefileを作成します。
$ mkdir ~/work/test $ cd ~/work/test
・helloworld.cpp
#include <systemc.h> int sc_main(int argc, char* argv[]) { cout << "Hello, World" << endl; return 0; }
CC = g++ CFLAGS = -Wall -g EXTRA_LIBS = -O2 TARGET_ARCH = macosx64 MODULE = run SRCS = helloworld.cpp OBJS = $(SRCS:.cpp=.o) include ../Makefile.defs
最後にmakeして、実行します。
$ make
$ ./run.x
SystemC 2.3.0-ASI --- May 3 2013 16:21:58
Copyright (c) 1996-2012 by all Contributors,
ALL RIGHTS RESERVED
Hello, World
SystemVerilogのLRM無償化
以前は有償だったSystemVerilogのLRM(Language Reference Manual)がフリーでダウンロードできるようになっていました。下記のIEEEのサイトから入手できます。
IEEE Standard for System Verilog--Unified Hardware Design, Specification, and Verification Language
http://standards.ieee.org/getieee/1800/download/1800-2012.pdf
ModelSimの実行スクリプト
ModelSimの実行スクリプトのサンプルです。
プロジェクト名:sample
シミュレーショントップのモジュール名:sample_tb
ModelSimの作業ディレクトリ名(ModelSimが生成したファイルを格納):work
とします。
・ディレクトリ構成
sample/ |- sample_tb.sv |- sample_rtl.v |- file_list |- run.sh |- work/
・実行スクリプト
#!/bin/sh
PRJ_NAME=sample
TOP_NAME=sample_tb
WORK_DIR=work
if [ ! -d $WORK_DIR ]; then
vlib $WORK_DIR
vmap $PRJ_NAME "./$WORK_DIR"
fi
vlog -f file_list \
-work $PRJ_NAME
vsim $TOP_NAME \
-lib $PRJ_NAME \
-do "run -all;quit"・file_list
file_listに読み込むファイルの一覧を書きます。
./sample_tb.sv ./sample_rtl.v
実行スクリプト内のPRJ_NAMEで指定した名前が、ModelSImをGUIで起動した場合の"Library"一覧に表示されます。なお、GUIで起動する場合は、カレントディレクトリで"modelsim"とコマンドを打って起動する必要があります。カレントディレクトリ以外(例えばスタートメニューのショートカット)から起動した場合、ライブラリのパスが記述されているカレントディレクトリのmodelsim.iniが読み込まれないため、GUIの"Library"に表示されません。
FIFO(キュー)のビヘイビアモデル
SystemVerilogを使ったビヘイビアレベルのFIFOの実装方法です。詳細は、SystemVerilog 3.1a LRMの4.14を参照してください。
FIFOの宣言
int fifo0[$]; // 深さを指定しないFIFO int fifo1[$:255]; // 深さ256のFIFO
データの格納方法
FIFOの一番後ろにデータを一つ追加する場合は、下記のように書きます。
fifo = {fifo, val}; // 一番後ろに"val"を格納
もしくは、メソッドを使って下記のようにも書けます。
fifo.push_back(val); // 一番後ろに"val"を格納
データの取り出し方法
FIFOの先頭から一つ取り出す場合は、下記のように書きます
val = fifo[0]; // 先頭のデータを"val"に代入 fifo = fifo[1:$]; // 先頭を消す
もしくは、メソッドを使って下記のようにも書けます。
val = fifo.pop_front( ); // 先頭のデータを取り出して"val"に代入
サンプルコード
module queue; int fifo[$]; // FIFOの宣言 initial begin // データを追加 fifo.push_back(1111); // 1回目のデータ追加 fifo.push_back(2222); // 2回目のデータ追加 fifo.push_back(3333); // 3回目のデータ追加 // データを取り出す $display("%d", fifo.pop_front()); $display("%d", fifo.pop_front()); $display("%d", fifo.pop_front()); end endmodule
実行結果
# 1111 # 2222 # 3333
動的配列の使い方
Verilogでは配列の要素数は宣言時に指定する必要があったので何かと不便でしたが、SystemVerilogでは、要素数を自由に変更できる動的配列が使えます。
動的配列は下記のように『型 配列名[]』で宣言します。
int dynamic_array[];
動的配列はクラスのため、newメソッドで初期化します。また、newメソッドを呼び出すたびに要素数を変更できます。ただし、この場合、配列の中身はクリアされます。
dynamic_array = new[要素数];
配列の中身を保持したまま要素数を変更したい場合は、下記のようにします。
dynamic_array = new[要素数](dynamic_array);
例
int dynamic_array[]; initial begin // new[n]でn個の要素を持つ配列を生成 dynamic_array = new[5]; // newメソッドを再度呼び出すと、配列は初期化される dynamic_array[0] = 'd1234; $display("%d", dynamic_array[0]); dynamic_array = new[10]; // newメソッドを再度呼び出す $display("%d¥n", dynamic_array[0]); // 初期化される // 中身を保持したまま要素数を変更したい場合は、new[要素数](配列名)とする dynamic_array[0] = 'd1234; $display("%d", dynamic_array[0]); dynamic_array = new[10](dynamic_array); $display("%d", dynamic_array[0]); // 初期化されない end
実行結果
# run -all # 1234 # 0 # # 1234 # 1234 # quit
なお、中身を保持したまま要素数を1つだけ増やしたい場合は、sizeメソッドを使って下記のように書けます。
dynamic_array = new[dynamic_array.size+1](dynamic_array);