Parameterized class
Verilogではmoduleに対してパラメータを渡すことができますが、SystemVerilogではclassに対してもパラメータを渡すことができます。クラスハンドルの宣言の時に設定し、変数のビット幅など、メソッドの引数では対応できない変数を決めることができます。
値 (Value)を渡す
簡単な例としては、クラスのデータメンバのビット幅を指定するのに使用できます。以下が簡単なサンプルプログラムです。本例では渡す値の型はintですが、logicでもrealでも何でも指定することができます。
ちなみにクラスのパラメータ宣言部のparameterキーワードは省略しても良いようです。以下の例ですと#(parameter int WIDTH = 1)
はparameterを省略して#(int WIDTH = 1)
と記述しても大丈夫なようです。ようですというのは、LRMのBNFではparameterキーワードは必須のように読み取れるのですが、LRMのプログラム例では省略されたものが多用されているからです。
class ParamClass #(parameter int WIDTH = 1);
logic [WIDTH - 1 : 0] sig;
function void displaySigWidth();
$display("sig's width is %d", $size(sig));
endfunction
endclass
program top();
initial begin
ParamClass#(.WIDTH(8)) paramClassByte;
ParamClass#(.WIDTH(32)) paramClassWord;
paramClassByte = new();
paramClassWord = new();
paramClassByte.displaySigWidth(); // Display 8
paramClassWord.displaySigWidth(); // Display 32
end
endprogram
型 (Type)を渡す
parameterには値だけではなく、型そのものを指定することもできます。intやlogic以外にもvirtual interfaceや、クラスなど何でもTypeとすることが可能です。
Typeをパラメータに使う場合の典型的な利用方法はいわゆるコンテナクラスを作成する時になります。任意の型を格納できるクラスで、全ての格納オブジェクトに対して同じインターフェースを提供します。下は型をパラメータとするスタックのサンプルプログラムです。
class ParamStack #(parameter type T = int);
T stk[$];
function void push(T t);
stk.push_front(t);
endfunction
function T pop();
stk.pop_front();
endfunction
endclass
class MyClass;
endclass
program top();
initial begin
ParamStack#(.T(int)) paramStackInt;
ParamStack#(.T(MyClass)) paramStackMyClass;
MyClass myClass;
paramStackInt = new();
paramStackMyClass = new();
myClass = new();
paramStackInt.push(1);
paramStackMyClass.push(myClass);
end
endprogram
他の用途としてはダックタイピングが挙げられます。これは、継承関係が無いクラス間であっても同じメソッドさえ持っていれば、同じように扱う手法です。
以下の例ではMasterのインスタンスを2つ生成しています。1つはパラメータにDuckタイプを渡し、もう一つはDogタイプを渡します。DuckとDogには継承関係はありませんが、Masterクラスからはどちらのオブジェクトに対しても同じようにcall()を呼んで処理できます。
Parameterized typeを使わないで同様のことを行おうとするとそれぞれに専用のMasterクラスを定義しなければなりません。
class Duck;
function string call();
return "quack";
endfunction
endclass
class Dog;
function string call();
return "bow wow";
endfunction
endclass
class Master #(parameter type T = Duck);
function void callMyPet(T myPet);
$display("%s", myPet.call());
endfunction
endclass
program top();
initial begin
Duck duck;
Dog dog;
Master #(.T(Duck)) duckMaster;
Master #(.T(Dog)) dogMaster;
duck = new();
dog = new();
duckMaster = new();
dogMaster = new();
duckMaster.callMyPet(duck); // quack
dogMaster.callMyPet(dog); // bow wow
end
endprogram
使用上の注意点
- 渡すパラメータはコンパイル時に決定されなければならない。つまり静的に解決できるものでなければならない
- Parameterには必ずdefaultの指定が必要 (指定しなくてもエラーを吐かないシミュレータもありますが)
- Parameterized classはパラメータ毎に別クラスとして扱われる。オブジェクトサイズの増加に注意
- Parameterized Classを継承時、派生クラスで同じパラメータを引数としてとりたい場合、派生クラスで再度parameter宣言すること
下の2つについてはもう少し詳しく説明します。
Parameterized classはパラメータ毎に別クラスとして扱われる
同じクラス名でパラメータが異なるものはコンパイル時に全て展開され、それぞれ異なるクラスとして扱われます。
例えば以下のコードでは、baseClass1とbaseClass2は型が非互換なのでコンパイルエラーとなります。
class BaseClass #(parameter int i = 1);
endclass
program top();
initial begin
BaseClass #(.i(1)) baseClass1;
BaseClass #(.i(2)) baseClass2;
baseClass1 = new();
baseClass2 = baseClass1; // Error. Incompatible type
end
endprogram
このため、定義したクラスの数が少なくても異なるパラメータを使用していくとどんどんコンパイル後のオブジェクトサイズが増大して行きますので注意してください。
Parameterized classの継承
Parameterized Classでは、ベースクラスのパラメータを派生クラスでも同様に扱いたい場合は、もう一度パラメータ宣言し、その値を親クラスに渡します。そうでないとデフォルトの値が適用されてしまいます
派生クラスでParameterを再宣言しない場合
class BaseClass #(parameter int I = 1);
virtual function void printParameter();
$display("I = %d", I);
endfunction
endclass
class DerivedClass extends BaseClass;
endclass
program top();
initial begin
DerivedClass derivedClass; // There is no way to set parameter to derived class
derivedClass = new();
derivedClass.printParameter(); // Print default value of base class of 1
end
endprogram
派生クラスでParameterを再宣言した場合
class BaseClass #(parameter int I = 1);
virtual function void printParameter();
$display("I = %d", I);
endfunction
endclass
class DerivedClass #(parameter int J = 1) extends BaseClass #(.I(J));
endclass
program top();
initial begin
DerivedClass #(.J(8)) derivedClass;
derivedClass = new();
derivedClass.printParameter(); // Print 8
end
endprogram
参考文献
"9.25 Parameterized classes". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp. 191-192.