連想配列 (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 |
***** 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自体持っていなかったので、当時の言語設計では、導入・実装が難しかったと思います。
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