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;
}

Makefile

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);