列挙型(Enumerations)
概要
列挙型を使うことによって、変数の持つ値に意味のある名前をつけることができるので、デバッグ時に、列挙名を使用して参照・表示できるようになります。
下のように、%pで、値を名前で表示できます。
例
program enum_test1; enum { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state; initial begin state=FINISHED; $display("state=%p ",state); state=RUNNING; $display("state=%p ",state); state=WAITING; $display("state=%p",state); end endprogram
これを走らせると、
***** Veritak SV32 Engine Version 447 Build Jun 5 2013***** state=FINISHED state=RUNNING state=WAITING ---------- シミュレーションを終了します。time=0ns
値も表示させてみると、FINISHEDは、0, RUNNINGは、1という具合に+1づつ、値が割り振られていることが分かります。これらは、論理合成も同じ解釈をします。
program enum_test; enum { FINISHED, RUNNING, WAITING, SUSPENDED, KILLED } state; initial begin state=FINISHED; $display("state=%p %d ",state,state); state=RUNNING; $display("state=%p %d ",state,state); state=WAITING; $display("state=%p %d",state,state); end endprogram
***** Veritak SV32 Engine Version 447 Build Jun 5 2013***** state=FINISHED 0 state=RUNNING 1 state=WAITING 2 ---------- シミュレーションを終了します。time=0ns
値と名前を明示的に指定することもできます。
program enum_test; enum { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=6, KILLED=5 } state; initial begin $display("state=%p %x ",state,state); state=FINISHED; $display("state=%p %x ",state,state); state=RUNNING; $display("state=%p %x ",state,state); state=WAITING; $display("state=%p %x",state,state); state=SUSPENDED; $display("state=%p %x",state,state); end endprogram
***** Veritak SV32 Engine Version 447 Build Jun 5 2013***** state=0 00000000 state=FINISHED 00000001 state=RUNNING 00000002 state=WAITING 00000004 state=SUSPENDED 00000006 ---------- シミュレーションを終了します。time=0ns **** Test Done. Total 0.00[msec] ****
最初の、電源投入時の値が、0になっていますね。これは、指定がないと int 型として扱われるためです。int型は、32ビット2値型ですから、ハードウェアのモデリングで”不定”が表現できません。そこで、型を指定してみます。
program e5; enum logic [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=6, KILLED=5 } state; initial begin $display("state=%p %b ",state,state); state=FINISHED; $display("state=%p %b ",state,state); state=RUNNING; $display("state=%p %b ",state,state); state=WAITING; $display("state=%p %b",state,state); state=SUSPENDED; $display("state=%p %b",state,state); end endprogram
***** Veritak SV32 Engine Version 447 Build Jun 5 2013***** state=x xxxx state=FINISHED 0001 state=RUNNING 0010 state=WAITING 0100 state=SUSPENDED 0110 ---------- シミュレーションを終了します。time=0ns
ちゃんと4値の指定ビット幅になりました。
enumの参照
enumの値を複数の変数で参照するには、下のようにtypedefでenum型を宣言します。下の例で、STATEは、変数ではなく型であることに注意してください。
program e6; typedef enum logic [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=6, KILLED=5 } STATE; STATE s1,s2; initial begin $display("state=%p %b ",s1,s1); s1=FINISHED; s2=s1; $display("state=%p %b ",s2,s2); s1=RUNNING; s2=s1; $display("state=%p %b ",s2,s2); s1=WAITING; s2=s1; $display("state=%p %b",s1,s1); s1=SUSPENDED; s2=s1; $display("state=%p %b",s1,s1); end endprogram
***** Veritak SV32 Engine Version 447 Build Jun 9 2013***** state=x xxxx state=FINISHED 0001 state=RUNNING 0010 state=WAITING 0100 state=SUSPENDED 0110 ---------- シミュレーションを終了します。time=0ns
クラス中のenum参照
スコープ解決演算子::でスコープを指定します。enum中の値もスコープ解決演算子が必要となりますが、クラスのインスタンス化なしにアクセス可能となります。(これに対して、クラス中の変数は、クラスハンドルを介して(ドット.で)しかアクセスできません。)
program e7; class C1; typedef enum logic [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=6, KILLED=5 } STATE; endclass initial begin C1::STATE s1,s2; $display("state=%p %b ",s1,s1); s1=C1::FINISHED; s2=s1; $display("state=%p %b ",s2,s2); s1=C1::RUNNING; s2=s1; $display("state=%p %b ",s2,s2); s1=C1::WAITING; s2=s1; $display("state=%p %b",s1,s1); s1=C1::SUSPENDED; s2=s1; $display("state=%p %b",s1,s1); end endprogram
派生クラス中のenum参照
親クラスに定義したenumは、派生クラスから見て、可視です。
program e8; class C1; typedef enum logic [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=6, KILLED=5 } STATE; endclass class C2 extends C1; endclass class C3 extends C2; endclass initial begin C3::STATE s1,s2; $display("state=%p %b ",s1,s1); s1=C3::FINISHED; s2=s1; $display("state=%p %b ",s1,s2); s1=C3::RUNNING; s2=s1; $display("state=%p %b ",s2,s2); s1=C3::WAITING; s2=s1; $display("state=%p %b",s1,s1); s1=C3::SUSPENDED; s2=s1; $display("state=%p %b",s1,s1); end endprogram
***** Veritak SV32 Engine Version 449 Build Jun 15 2013***** state=x xxxx state=FINISHED 0001 state=RUNNING 0010 state=WAITING 0100 state=SUSPENDED 0110 ---------- シミュレーションを終了します。time=0ns
パッケージや、$unit中のenumの参照
同じenum名であっても、異なる名前空間では、異なるenumです。次の例では、
で、同じ名前のenumを宣言していますが、これらは、それぞれ異なる名前空間にあるので、異なるenumとしてコンパイルされます。
package std1; class C1 ; typedef enum logic[3:0] { FINISHED=3, RUNNING=4, WAITING, SUSPENDED, KILLED } STATE; endclass endpackage package std2; class C1 ; typedef enum logic[3:0] { FINISHED=1, RUNNING=2, WAITING=3, SUSPENDED, KILLED } STATE; endclass endpackage //$unit class C1 ; typedef enum logic[3:0] { FINISHED=2, RUNNING=1, WAITING=4, SUSPENDED=5, KILLED=3 } STATE; endclass module p2; class C1 ; typedef enum logic[3:0] { FINISHED=15, RUNNING=14, WAITING=13, SUSPENDED=12, KILLED=11 } STATE; endclass std1::C1:: STATE a; initial begin a=std1::C1::WAITING; $display("a=%p %d",a,a); end std2::C1:: STATE b; initial begin b=std2::C1::WAITING; $display("b=%p %d",b,b); end $unit::C1:: STATE c; initial begin c=$unit::C1::WAITING; $display("c=%p %d",c,c); end C1:: STATE d; initial begin d=C1::WAITING; $display("d=%p %d",d,d); end typedef enum logic[3:0] { FINISHED=10, RUNNING=11, WAITING=12, SUSPENDED=13, KILLED=14 } STATE; initial begin typedef enum logic[3:0] { FINISHED=10, RUNNING=11, WAITING=12, SUSPENDED=13, KILLED=14 } STATE; STATE e; e=WAITING; $display("e=%p %d",e,e); end endmodule
***** Veritak SV32 Engine Version 449 Build Jun 13 2013***** a=WAITING 5 b=WAITING 3 c=WAITING 4 d=WAITING 13 e=WAITING 12 ---------- シミュレーションを終了します。time=0ns
パラメタライズドクラス中のenumに対するアクセス
次のように、パラメータ付きで呼び出します。
package s1; class C1 #(parameter P=10); typedef enum bit [P:0] { FINISHED=P, RUNNING, WAITING, SUSPENDED, KILLED} State; State S1; endclass endpackage program top; s1::C1 #(5) ::State state; initial begin state=s1::C1 #(5)::KILLED; $display("%p %d %d",state,state, $bits(s1::C1 #(5)::State)); end endprogram
***** Veritak SV32 Engine Version 447 Build Jun 10 2013***** KILLED 9 6 ---------- シミュレーションを終了します。time=0ns
enumは、強く型づけされている
異なるenumに対する代入操作は、エラーとなります。次の代入は、全てコンパイル時のエラーになります。
module e13; typedef enum bit [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=8 } STATE; STATE S1; initial begin enum bit [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=8 } s1=KILLED;//これはOK S1=1;//enumでない代入 S1++;//インクリメント・デクリメント演算は不可 ++S1;//インクリメント・デクリメント演算は不可 S1=S1+0;//代入時はenumでない S1=s1;//異なるenum S1=KILLED;//異なるenum S1+=1;//代入時はenumでない end endmodule
enumへの代入は、上のように強く型付けされていましたが、反対にenumから出るときは、いつもの(ルースな)verilogHDLルールになります。次のような演算も特に警告を発しません。
module e13a; typedef enum bit [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=8 } STATE; STATE S1; int i; initial begin enum bit [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=8 } s1=KILLED; i=S1+1; i=S1+s1;//異なるenum間の演算も特に警告なし end endmodule
enumの符号
enumの実体は、通常のbit または、logicのvectorです。それにpropertyをつけただけですので、通常の演算が可能です。符号付き属性にするには、次のようにsignedを附加します。
module e11; typedef enum bit signed [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=8 } STATE; STATE S1,S2; int i; initial begin S1=KILLED; S2=SUSPENDED; i=S1*10; $display("i=%d",i); i=KILLED*SUSPENDED; $display("i=%d",i); begin enum bit [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=9 } STATE1=KILLED; i=STATE1*10; $display("i=%d",i); end begin enum bit signed [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=15 } STATE1=KILLED; i=STATE1*10; $display("i=%d",i); end end endmodule
***** Veritak SV32 Engine Version 449 Build Jun 15 2013***** i= -80 i= -40 i= 90 i= -10 ---------- シミュレーションを終了します。time=0ns
enumのmethod
next()や、prev()で注意するのは、現在の値が定義されていない場合は、default値が返るということです。enumの型が指定されていない場合のdefault型は、intですから、そのdefault値は、0になります。loigc
[3:0] の場合は、4'bxxxxが返ります。enumは、上でみたように強く型付けされていて不用意な代入を防いでくれますが、決してメンバー以外の値が入ってこないという訳ではありません。(電源投入時やキャスティング(後述)により。)
記述にあたっては、そのメンバー以外の値が入いってくる可能性を意識してCodingする必要があります。
プロトタイプ | 内容 | 備考 |
function enum first() | 最初のメンバーを返す | |
function enum last() | 最後のメンバーを返す | |
int num() | 定義されたメンバー数を返す。定義されていないメンバーは含まない | |
function enum next( int unsigned N = 1 ) | 次のメンバーを返す。現在の値が定義されていない場合は、default値が返る。Nを定義するとN個先の定義メンバーになる。 | |
function enum prev( int unsigned N = 1 ) | 前のメンバーを返す。現在の値が定義されていない場合は、default値が返る。Nを定義するとN個前の定義メンバーになる。 | |
function string name() | 名前を返す |
module e10; typedef enum logic [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=6 } STATE; initial begin STATE s3,s3o; $display("s3のステート数は%1dです。",s3.num()); $display(" 最初のステートは%p",s3.first()); $display(" 最後のステートは%p",s3.last()); $display(" パワーオン時のステートは%p %bです。",s3,s3);//enum logicのDefaultが呼ばれる。 s3=KILLED; $display("\nnext()で一巡させます。"); $display("%s %d",s3.name(),s3); s3o=s3; while (s3o != s3.next() ) begin s3=s3.next(); $display("%s %d",s3.name(),s3); end $display("\nprev()で一巡させます。"); $display("%s %d",s3.name(),s3); s3o=s3; while (s3o != s3.prev() ) begin s3=s3.prev(); $display("%s %d",s3.name(),s3); end $display("\nnext(3)で一巡させます。"); $display("%s %d",s3.name(),s3); s3o=s3; while (s3o != s3.next(3) ) begin s3=s3.next(3); $display("%s %d",s3.name(),s3); end $display("\nprev(3)で一巡させます。"); $display("%s %d",s3.name(),s3); s3o=s3; while (s3o != s3.prev(3) ) begin s3=s3.prev(3); $display("%s %d",s3.name(),s3); end end endmodule
***** Veritak SV32 Engine Version 449 Build Jun 14 2013***** s3のステート数は5です。 最初のステートはFINISHED 最後のステートはKILLED パワーオン時のステートはx xxxxです。 next()で一巡させます。 KILLED 6 FINISHED 1 RUNNING 2 WAITING 4 SUSPENDED 5 prev()で一巡させます。 SUSPENDED 5 WAITING 4 RUNNING 2 FINISHED 1 KILLED 6 next(3)で一巡させます。 KILLED 6 WAITING 4 FINISHED 1 SUSPENDED 5 RUNNING 2 prev(3)で一巡させます。 RUNNING 2 SUSPENDED 5 FINISHED 1 WAITING 4 KILLED 6 ---------- シミュレーションを終了します。time=0ns
enumのキャスト
コンパイル時に判断するスタティックキャストと、実行時に判断するダイナミックキャストがあります。スタティックキャストでは、メンバー以外の値の代入が出来てしまいますが、ダイナミックキャストの場合は、メンバー以外の代入では、0が返りエラーとなります。(エラーにはなりますが、実行は継続されます。)
形式 | 内容 | 論理合成 | |
スタティックキャスト | 変換先の型 '( 変換元 ) | コンパイル時に型の認識を変える。実行時、メンバー以外の代入チェックは行われない | 可能 |
ダイナミックキャスト | $cast(変換先の変数、変換元の変数) | 実行時に、メンバー以外の代入があった場合: -代入は行われない。戻り値として0が返る。エラーメッセージが出力されるが、実行は継続。 実行時に、メンバーの代入が合った場合: -代入は、行われ、戻りとして、0以外が返る |
不可 |
program e12; typedef enum bit [3:0] { FINISHED=1, RUNNING=2, WAITING=4, SUSPENDED=5, KILLED=8 } STATE; STATE S1; initial begin S1=KILLED; $display("S1=%p",S1); S1=STATE'(9);//常に代入される $display("S1=%p",S1); S1=FINISHED; if (!$cast(S1,9)) begin//enum定義になければ、代入されない。 $display("Cast Failed S1=%p",S1); end else $display("S1=%p",S1); end endprogram
***** Veritak SV32 Engine Version 449 Build Jun 15 2013***** S1=KILLED S1=9 C:\Users\Public\sv_test\enum\e12.sv(14):::ダイナミックキャストエラー。enum定義されたメンバーの代入ではありません。 Cast Failed S1=FINISHED ---------- シミュレーションを終了します。time=0ns