連想配列 (Associative Array)

通常の配列では、0番目、1番目、2番目…といった風に数字のインデックスで値を取り出しましたが、連想配列では、例えば、インデックスに文字列を使うことができます。 

module assoc13;

	int map[string];

	initial begin
		map["山田"]=90;
		map["菅原"]=100;
		map["佐々木"]=75;

		$display("山田の点数は%3d点",map["山田"]);		
		$display("菅原の点数は%3d点",map["菅原"]);		
		$display("佐々木の点数は%3d点",map["佐々木"]);		

		map["菅原"]=80;

		$display("\n%p",map);
	end
endmodule


連想配列では、下のように、インデックスに相当するキー(key)と値を組にして記憶しています。

キー(string) 値(int)
山田 90
菅原 80
佐々木 75

6-8行目のように、キーが無ければ、キーと値をペアとする記憶領域が割り当てられます。14行目のように、キーが存在した場合には、値の書き換えとなります。
***** Veritak SV32 Engine Version 431 Build Dec 13 2012*****

山田の点数は 90点
菅原の点数は100点
佐々木の点数は 75点

'{"山田":90,"菅原":80,"佐々木":75}

---------- シミュレーションを終了します。time=0ns

勿論、キーを数字にすることも可能です。

module assoc13;

	string map[int];
	string str;
	
	initial begin
		map[90]="山田";
		map[100]="菅原";
		map[75]="佐々木";
	
	
	
		$display("90点を取ったのは %s",map[90]);
		$display("100点を取ったのは %s",map[100]);
		$display("75点を取ったのは %s",map[75]);

	

		$display("\nmap=%p",map);


	end
endmodule



***** Veritak SV32 Engine Version 431 Build Dec 13 2012*****

90点を取ったのは 山田
100点を取ったのは 菅原
75点を取ったのは 佐々木

map='{75:"佐々木",90:"山田",100:"菅原"}

---------- シミュレーションを終了します。time=0ns


連想配列のメンバーファンクション

要素数を出力するのは、size() または num()です。12行目

書き込まずに、キーが存在するかどうかを問い合わせるのが、exists です。15/18行目

特定のキーと値の組を消去するのが、delete() です。引数なしで全消去になります。

module assoc13c;

	string map[int];
	string str;
	
	initial begin
		map[90]="山田";
		map[100]="菅原";
		map[75]="佐々木";
	
		//num()/size()
		$display("mapに格納されている要素数は、%2d %2d ",map.size(),map.num());

		//exists
		if (map.exists(90)) begin
			$display("90をキーとする値は、%s",map[90]);
		end
		if (!map.exists(110)) begin
			$display("110をキーとする値は存在しません。");
		end

		$display("\n要素を消去(index指定)します。");
		
		//delete(i)
		$display("消去前 %p",map);		
		map.delete(90);
		$display("消去後=%p",map);
		
		//delete_all
		$display("\n全消去します。");
		map.delete();
		$display("消去後=%p",map);
	

		


	end
endmodule

***** Veritak SV32 Engine Version 431 Build Dec 13 2012*****

mapに格納されている要素数は、 3  3 
90をキーとする値は、山田
110をキーとする値は存在しません。

要素を消去(index指定)します。
消去前 '{75:"佐々木",90:"山田",100:"菅原"}
消去後='{75:"佐々木",100:"菅原"}

全消去します。
消去後='{}

---------- シミュレーションを終了します。time=0ns


マルチマップを書いてみる

SVの連想配列は、C++STLで言えばmapに相当する機能で重複のないキーになります。SVでは、C++STLでいうmultimap (キーの重複が許されるmap)は、用意されていません。そこで、擬似的なmultimapを書いてみました。

下の5行目Imapがmapのinversed multimap versionです。 string Imap[int] は、int map[string]の逆の関係になりますが、その際複数の値を持つことがあるので、それをqueueに格納する2次元配列としました。これは、 intを引数とし、string q[$] を値とする連想配列になります。つまり、

typedef string Qtype [$];
Qtype Imap[int] ;

としても同じことです。

module assoc13a;
//Implementation of quasi-multimap in STL

		int map[string];
		string Imap[int][$];
		string q[$];
	
	int i;
	initial begin
		map["山田"]=90;
		map["菅原"]=100;
		map["佐々木"]=75;
		map["菜々子"]=100;
		map["美月"]=100;
	
		$display("山田の点数は%2d点",map["山田"]);
		$display("菅原の点数は%2d点",map["菅原"]);
		$display("佐々木の点数は%2d点",map["佐々木"]);
		$display("菜々子の点数は%2d点",map["菜々子"]);
		$display("美月の点数は%2d点",map["美月"]);

		Imap[90].push_back("山田");
		Imap[100].push_back("菅原");
		Imap[75].push_back("佐々木");
		Imap[100].push_back("菜々子");
		Imap[100].push_back("美月");
		
		$display("\n%p",Imap);

		//昇順
		$display("\n昇順に出力します。");
		if ( Imap.first( i ) )begin
			do
				begin
					q=Imap[i];
					$display( "%3d点を取ったのは%3d人 : %p ", i,q.size(),q );
				end
			while ( Imap.next( i ) );
		end

		//降順
		$display("\n降順に出力します。");
		if ( Imap.last( i ) )begin
			do
				begin
					q=Imap[i];
					$display( "%3d点を取ったのは%3d人 : %p ", i,q.size(),q );
				end
			while ( Imap.prev( i ) );
		end

	end
endmodule
***** Veritak SV32 Engine Version 431 Build Dec 13 2012*****

山田の点数は90点
菅原の点数は100点
佐々木の点数は75点
菜々子の点数は100点
美月の点数は100点

'{75:'{"佐々木"},90:'{"山田"},100:'{"菅原","菜々子","美月"}}

昇順に出力します。
 75点を取ったのは  1人 : '{"佐々木"} 
 90点を取ったのは  1人 : '{"山田"} 
100点を取ったのは  3人 : '{"菅原","菜々子","美月"} 

降順に出力します。
100点を取ったのは  3人 : '{"菅原","菜々子","美月"} 
 90点を取ったのは  1人 : '{"山田"} 
 75点を取ったのは  1人 : '{"佐々木"} 

---------- シミュレーションを終了します。time=0ns

連想配列の用途

上で概観したように、SVでは高級言語ぽい機能が搭載されました。

連想配列の内部アルゴリズムは、複雑でアクセス速度は、固定配列よりかなり遅く、ダイナミック配列あるいはキューよりも、少し遅くなります。

(VeritakSVでは、赤黒木で実装していますが、他のシミュレータも似たようなものでしょう。)  このアルゴリズムで特徴的なのは、新たなキーが生成されたときだけ、メモリを使うことにあります。スパース行列や、大規模なメモリのモデリングで威力を発揮するのではないかと思います。

連想配列の初期化


初期化には、アサインメントパターンを使います。

module assoc21;


string words [int] = '{default: "hello"};
// an associative array of 4-state integers indexed by strings, default is ?1
integer tab [string] = '{"Tom":44, "Johnson":22, "Mira":23, default:-1 };

initial begin
        $display("words=%p",words);
        $display("words[0]=%s",words[0]);
        $display("words[1234_5678]=%s",words[1234_5678]);

        $display("\ntab=%p",tab);

        $display("Tom=%1d Johnson=%1d Mira=%1d anybody=%1d",tab["Tom"],tab["Johnson"],tab["Mira"],tab["anybody"]);
end


endmodule

4行目でdefault: を指定しています。Read時、指定されたインデックスが無いときに、defaultの内容が返されます。

この例の場合、words[0]、word[1234_5678]というエントリーは存在しないので、default値 "hello"が出力されています。
tab["anybody"]も同様です。

***** Veritak SV32 Engine Version 431 Build Dec 18 2012*****

words='{}
words[0]=hello
words[1234_5678]=hello

tab='{"Tom":44,"Mira":23,"Johnson":22}
Tom=44 Johnson=22 Mira=23 anybody=-1

---------- シミュレーションを終了します。time=0ns


default値が定義されていない場合、エントリーなしインデックスが指定された場合は、Warningが出力され下記の暗黙default値が返ります。

タイプ 暗黙Read値
4-state integral type ’X
2-state integral type ’0
enumeration base type default initial value
string ""
class null
event null

module assoc21a;


string words [int] ;//= '{default: "hello"};
// an associative array of 4-state integers indexed by strings, default is ?1
integer tab [string] = '{"Tom":44, "Johnson":22, "Mira":23 };

initial begin
	$display("words=%p",words);
	$display("words[0]=%s",words[0]);
	$display("words[1234_5678]=%s",words[1234_5678]);

	$display("\ntab=%p",tab);

	$display("Tom=%1d Johnson=%1d Mira=%1d Anybody=%1d",tab["Tom"],tab["Johnson"],tab["Mira"],tab["anybody"]);
end


endmodule
***** Veritak SV32 Engine Version 431 Build Dec 18 2012*****

words='{}
C:\Users\Public\sv_test\associate\assoc21a.sv(10)::Warning: 連想配列のインデックスが存在しません。default値を返します。
words[0]=    
words[1234_5678]=    

tab='{"Tom":44,"Mira":23,"Johnson":22}
C:\Users\Public\sv_test\associate\assoc21a.sv(15)::Warning: 連想配列のインデックスが存在しません。default値を返します。
Tom=44 Johnson=22 Mira=23 Anybody=x

---------- シミュレーションを終了します。time=0ns

ストリング-リテラルに注意

byteをindexとする連想配列で、何がKeyになるのか、という問題です。内部的には、packed2値に変換しているので、基本的には、packed タイプである必要があります。packed タイプでも、ストリングリテラルの場合とstring型の場合とで、予想される結果が違うので注意が必要です。

program top;
   string foo[byte];
   byte bar;
   bit [8*20:0]  chars;
   string str;   
   initial begin 
        
      $display("%0d", "a string" == foo["not a byte"]);
      foo["e"] = "a string";
      $display("%0d", "a string" == foo["not a byte"]);
      bar = "not a byte";
      $display("bar: %0s", bar);

        chars="not a byte";// a byte "e" is entried in as index of the associative array.
        $display("%x",chars);
        if (foo[chars] ==foo["e"]) begin
                $display("Yes, it is true.");
        end
        str="not a byte";

        if (foo[str[0]] != foo["e"]) begin
                $display("You believe or not %d", foo[str[0]] ==foo["n"]);
                $display("Because str[0]=%c",str[0]);
                

   end 
endprogram 

***** Veritak SV32 Engine Version 432 Build Dec 28 2012*****

C:\Users\Public\sv_test\associate\string.sv(8)::Warning: 連想配列のインデックスが存在しません。default値を返します。
0
1
bar: e
0000000000000000000006e6f7420612062797465
Yes, it is true.
You believe or not 1
Because str[0]=n

---------- シミュレーションを終了します。time=0ns

"ストリング リテラルは verilog HDL 由来で 固定長配列であるreg型に代入する場合、右詰パックが行われます。regに代入された動的文字列が比較することを考えると、固定長のreg配列としての比較になってしまうのでその方が自然な実装ではあります。 一方string型は、C由来で内部的には、char*です。

-"VerilogHDLの言語設計において最初からstring型を持てば.... " その通りかもしれませんが、ダイナミック配列の概念を、VerilogHDL自体持っていなかったので、当時の言語設計では、導入・実装が難しかったと思います。





2次元初期化と代入

2次元の初期化と代入の例です。2次元の場合も、無効なエントリーの扱いは、上と同様です。 固定配列・ダイナミックアレー・キュー間の代入互換性とは異なります。連想配列の場合には、同じ型の連想配列しか代入できません。

module assoc12;

	integer map[int];
	integer map2[int][$];
	integer map2B[int][$];

	typedef integer map2type [int][$];
	map2type M1;


	integer q[$];
	initial begin
		map[0]=0;
		$display("map[1]=%h",map[1]);		

	
		map2='{1:'{2{1,2,3}},default: '{0,1,2}};
		$display("%p",map2);

		map2='{default: '{0,1,2}};
		q=map2[1];
	
		$display("%p",map2);
		$display("%p",q);
	
		map2='{default:'{-1,-2,-3}};
		q=map2[1];
		$display("%p",map2);
		$display("%p",q);

		map2='{1:'{1,2,3}};
		$display("%p",map2);
		$display("%x",map2[0][0]);
	
		map2='{1:'{2{1,2,3}},default:'{-1,-2,-3}};
	

		$display("%x",map2[0][1]);

		map2='{1:'{2{1,2,3}}};
		$display("%x",map2[0][1]);

		map2B=map2;
		$display("map2B=%p",map2B);
		$display("map2B[0][1]=%x",map2B[0][1]);
		
		map2B[10000].push_back(-1);
		M1=map2B;
		$display("M1=%p",M1);
		
	end


endmodule

***** Veritak SV32 Engine Version 431 Build Dec 18 2012*****

C:\Users\Public\sv_test\associate\assoc12.sv(14)::Warning: 連想配列のインデックスが存在しません。default値を返します。
map[1]=xxxxxxxx
'{1:'{1,2,3,1,2,3}}
'{}
'{0,1,2}
'{}
'{-1,-2,-3}
'{1:'{1,2,3}}
C:\Users\Public\sv_test\associate\assoc12.sv(33)::Warning: 連想配列のインデックスが存在しません。default値を返します。
xxxxxxxx
fffffffe
C:\Users\Public\sv_test\associate\assoc12.sv(41)::Warning: 連想配列のインデックスが存在しません。default値を返します。
xxxxxxxx
map2B='{1:'{1,2,3,1,2,3}}
C:\Users\Public\sv_test\associate\assoc12.sv(45)::Warning: 連想配列のインデックスが存在しません。default値を返します。
map2B[0][1]=xxxxxxxx
M1='{1:'{1,2,3,1,2,3},10000:'{-1}}

---------- シミュレーションを終了します。time=0ns