SystemVerilog|classの使い方

Systemverilog
田中太郎
田中太郎

class(クラス)の基本的な使い方を紹介します

はじめに

SystemVerilogではclass(クラス)を使用してオブジェクト指向的にコーデイングできます

論理合成対象のRTLではあまり使用しませんが、検証環境ではよく使用します(ex. UVM)

今回は、クラスの基本的な使用方法について紹介します

サンプルコード

以下サンプルコードです。

module test;
    class base_test; // クラス名
        logic [3:0] data; // クラスの要素(変数)
        function new(); // クラスの要素(初期化関数)
            data = 1;
        endfunction
        function void print; // クラスの要素(関数)
            $display("====================");
            $display("data : %d d", data);
            $display("====================");
        endfunction
    endclass

    initial begin
        base_test comp; // base_testクラス型のcompを作成
        comp = new(); // new()で初期化する
        comp.print; // <クラス>.<関数名>でクラスの要素にアクセスできる
        $finish();
    end
endmodule

実行すると、以下のように出力されます

====================
data :  1 d
====================

では、中身について解説していきます

サンプルコードの解説

クラスは

class <クラス名>; // クラス名
endclass

で宣言できます

クラスの要素を追加したいときはclass-endclass内で変数を宣言します

class base_test; // クラス名
 logic [3:0] data; // クラスの要素(変数)
endclass

クラスに関数を登録する場合も同様に、class-endclass内でfunction/taskを宣言します

class base_test; // クラス名
    logic [3:0] data; // クラスの要素(変数)
    function void print; // クラスの要素(関数)
        $display("====================");
        $display("data : %d d", data);
        $display("====================");
    endfunction
endclass

クラスの要素、dataの中身を標準出力する関数です

次に、クラスの初期化関数を宣言します。関数名はnew()で固定です

class base_test; // クラス名
    logic [3:0] data; // クラスの要素(変数)
    function new(); // クラスの要素(初期化関数)
        data = 1;
    endfunction
    function void print; // クラスの要素(関数)
        $display("====================");
        $display("data : %d d", data);
        $display("====================");
    endfunction
endclass

初期化されると、dataに1が入ります

これでクラスを作成できました

次に作成したクラスを使ってみます

まず、作成したクラス、base_testクラス型の変数を宣言します

initial begin
    base_test comp; // base_testクラス型のcompを作成
end

次に、作成したクラスを初期化関数(new)を使用して初期化します

初期化は、

<クラス型> = new();

で行われます。

initial begin
    base_test comp; // base_testクラス型のcompを作成
    comp = new(); // new()で初期化する
end

初期化すると、base_testクラス型の要素のdataに1が入ります
(base_testのnew()の中身をもう一度確認してみてください)

初期化が完了したので、base_testクラス型の中で宣言したprintを使用します

initial begin
    base_test comp; // base_testクラス型のcompを作成
    comp = new(); // new()で初期化する
    comp.print; // <クラス>.<関数名>でクラスの要素にアクセスできる
end

以上がクラスの基本的な使用方法でした

クラスの外にfunction/taskの中身を書く(extern)

クラスの要素が多いときに関数の中身をクラス内に書いていると、可読性が落ちていきます

そこで、関数の宣言のみを行い、中身をクラス外に書くことができます

そのとき使用するのがexternです

以下、サンプルコードです

module tb;
  class base_test;
      logic [3:0] data;
      function new();
          data = 1;
      endfunction
      extern function void print();
  endclass
  function void base_test::print(); // base_testのprintの中身を書く
      $display("====================");
      $display("data : %d d", data);
      $display("====================");
  endfunction

  initial begin
    base_test comp;
    comp = new();
    comp.print;
    $finish();
  end
endmodule

class-endclass内でexternでprintを宣言しました

そしてclass-endclass外で

<クラス名>::<関数名>

でexternで宣言した関数の中身を書くことができます

クラスを継承する(extends)

クラスなので、もちろん継承できます

クラスを継承するときはextendsを使って以下のように書きます

class <新しいクラス名> extends <継承されるクラス>

さきほど作成したbase_testクラスを継承してnew_testクラスを作成してみます

module tb;
  class base_test;
      logic [3:0] data;
      function new();
          data = 1;
      endfunction
      function void print();
          $display("====================");
          $display("data : %d d", data);
          $display("====================");
      endfunction
  endclass

  class new_test extends base_test; // base_testを継承してnew_testを作成
    function new();
      super.print(); // 初期化時にbase_testのprint関数を実行
    endfunction
    function void countup(); // dataに1を加算するcountup関数を要素に追加
      data = data + 1;
    endfunction
  endclass

  initial begin
    new_test comp;
    comp = new();
    comp.print;
    comp.countup();
    comp.print;
    $finish();
  end
endmodule

実行結果

====================
data :  1 d
====================
====================
data :  1 d
====================
====================
data :  2 d
====================

継承元と継承したクラスで同じ要素を作成している場合

super.<要素>で継承元

this.<要素>で継承したクラス

の要素を呼び出せます

クラスをパラメータ化する

moduleのようにクラスもパラメタライズできます

class <クラス名> #(parameter <パラメータ名> = <値>);
    // クラスの中身
endclass

新しいクラスのオブジェクトを宣言するときは以下のように使用します

<クラス名> #(.<パラメータ名>(<値>)) <オブジェクト名>;

以下、サンプルコードです

module tb;
  class base_test #(parameter DATA = 3);
      function void print();
          $display("%d", DATA);
      endfunction
  endclass

  initial begin
    base_test a;
    base_test #(1) b;

    a = new();
    b = new();

    a.print();
    b.print();
    $finish();
  end
endmodule

実行結果

3
1

まとめ

SystemVerilogのclass(クラス)の基本的な使い方についてまとめました

検証は設計の倍の時間がかかると言われており、検証資産を再利用することが重要です

classを使ってオブジェクト指向的に検証環境を作成することで再利用性を高めることが可能です

コメント

タイトルとURLをコピーしました