列挙型(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