アンパックド連接 (Unpacked Concatenation)


VerilogHDLの連接では、ビット単位の連接しか出来ませんでしたが、SVでは、アンパックド次元の連接も可能になりました。



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
module unpacked_concatenation;

int A3[1:3];

	initial begin
		A3 = {1, 2, 3};
		$display("A3=%p",A3);
	end


endmodule

A3は、一次元アンパックド配列です。その要素を{ }で一気に代入することができます。

1
2
3
4
5
***** Veritak SV32 Engine Version 431 Build Dec  9 2012*****

A3='{1,2,3}

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


しかし、{}自体は、通常のVerilogHDL連接と変わりありません。一体なにが違うのでしょうか?

下の例で、Auはアンパックド次元配列、Abは、ビットベクトルです。同じ{連接}を代入して結果の違いを見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
module unpacked_concatenation;
     int Au [];
     bit [32*3:1] Ab;
	
	initial begin
		Au={32'h1, 32'h2, 32'h3};
		Ab={32'h1, 32'h2, 32'h3};

		$display("Au=%p",Au);
		$display("%p %h",Ab,Ab);
	end


endmodule


アンパックド配列の方は、要素毎に代入されますが、ビットベクターの方は、32*3=96ビットのベクターとして認識されます。コンパイラは、左辺の配列次元をみて、ビットベクターとしての連接か、配列としての連接かを判断しています。 

1
2
3
4
5
6
***** Veritak SV32 Engine Version 431 Build Dec  9 2012*****

Au='{1,2,3}
18446744082299486211 000000010000000200000003

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


しかし、そうすると、アサインメントパターンとの違いが何か?という疑問が湧いてきます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
module unpacked_concatenation;

int A3[1:3], A3a[1:3];

	initial begin
		A3 = {1, 2, 3};
		A3a ='{1, 2, 3};
		$display("A3=%p",A3);
		$display("A3a=%p",A3a);
	end


endmodule


実際、上の結果は、同じになります。

1
2
3
4
5
6
***** Veritak SV32 Engine Version 431 Build Dec  9 2012*****

A3='{1,2,3}
A3a='{1,2,3}

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


しかし、次の例は、アンパックド連接にしかできません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
module unpacked_concatenation;

int A3[1:3];
int A6[6];
	initial begin
		A3 = {1, 2, 3};
		$display("A3=%p",A3);
		
		A6={A3,4,5,6};//アサインメントパターン、 '{A3,4,5,6} は、コンパイルエラー
		$display("A6=%p",A6);



	end


endmodule


アサインメントパターンでは、連接内の項は、LHSの配列の要素である必要があります。一方、アンパックド連接では、要素または、要素の配列であればよいことになっている点が異なります。以下は、アサインメントパターンとアンパックド連接に関する違いの例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
module unpacked_concatenation;

int A3[1:3];
int A6[];

typedef int AType [3];

	initial begin
	
//		A3 = {6{1}};//コンパイルエラー
		A3= '{3{1}};
		$display("A3=%p",A3);
//		A3={default:0};//コンパイルエラー
		A3='{default:0};
		$display("A3=%p",A3);

		A3={1,2,3};
	
		A6 = {A3, {16'h4,16'h5}};//アンパックド連接ネストは不可
		$display("%p   A6[3]=%h",A6,A6[3]);
		A6 = {A3, AType'{4,5,6}};
		$display("%p",A6);
		



	end


endmodule

アンパックド連接では、リピート連接(10行目)、default パターン(13行目)は、コンパイルエラーになります。これらは、アサインメントパターンでのみ使用可能です。

また、19行目、アンパックド連接のネストも使用できません。ネストした場合は、ビットベクターやストリング連接と解釈されます。そのようにしたくない場合は、21行目のようにアサインメントパターンとしてまとめます。

1
2
3
4
5
6
7
8
***** Veritak SV32 Engine Version 431 Build Dec  9 2012*****

A3='{1,1,1}
A3='{0,0,0}
'{1,2,3,262149}   A6[3]=00040005
'{1,2,3,4,5,6}

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


このように、アンパックド連接は、アサインメントパターンや、従来のビットベクター連接/ストリング連接と紛らわしく、上述のような制限もあるので使いにくい気もします。

次はキューに備わっている関数(push_back/ push_fornt..) 等を使わずにアンパックド連接で同様の操作が可能という例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
module queue_operation;

	int j=1;
	int q[$],q2[$];

initial begin
	$display("キューの初期状態ではデータがありません。q=%p",q);

	$display("キューにデータを10個追加します");
	repeat(10) 	q.push_back( q.size());
	$display("q=%p",q);

	$display("\nゼロベースの4番目に40を挿入します。");
	q.insert(4,40);		
	$display("q=%p",q);
	$display("\n4番目のデータを削除します。");
	q.delete(4);
	$display("q=%p",q);

	$display("\n上と同じ操作をアンパックド配列の連接で行ってみます。");

	$display("キューにデータを代入します");
	q2={0,1,2,3,4,5,6,7,8,9};
	$display("q2=%p",q2);
	q2={q2[0:3],40,q2[4:$]};
	$display("q2=%p",q2);
	q2={q2[0:3],q2[5:$]};
	$display("q2=%p",q2);

	$display("\nキューの最初にデータを追加します。");
	q.push_front(100);
	$display("q=%p",q);

	$display("キューの最後にデータを追加します");
	q.push_back(200);
	$display("q=%p",q);

	$display("\n上と同じ操作をアンパックド配列の連接で行ってみます。");
	q2={100,q2};
	$display("q2=%p",q2);
	q2={q2,200};
	$display("q2=%p",q2);

	$display("\n最初のデータを取り出します");
	j=q.pop_front();
	$display("j=%d, q=%p",j,q);

	$display("最後のデータを取り出します");
	j=q.pop_back();
	$display("j=%d, q=%p",j,q);

	$display("\n上と同じ操作がビットセレクト・パートセレクトを使ってもできます。");
	j=q2[0];
	q2=q2[1:$];
	$display("j=%d, q2=%p",j,q2);
	j=q2[$];
	q2=q2[0:$-1];
	$display("j=%d, q2=%p",j,q2);
	
	$display("\nキューを空にします。");
	q.delete();
	$display("q=%p",q);

	$display("\n上と同じ操作が連接を使ってもできます。");
	q2={};
	$display("q2=%p",q2);

end




endmodule


しかしながら、キューの性質上、先頭や最後尾の操作が、効率がよいように設計されているのが普通なので、push_back/push_front/pop_back/pop_frontの使用をお勧めします。( 配列がサイズがこのように小さいうちはよいのですが、巨大になると速度の違いが顕著になります。)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
***** Veritak SV32 Engine Version 431 Build Dec  9 2012*****

キューの初期状態ではデータがありません。q='{}
キューにデータを10個追加します
q='{0,1,2,3,4,5,6,7,8,9}

ゼロベースの4番目に40を挿入します。
q='{0,1,2,3,40,4,5,6,7,8,9}

4番目のデータを削除します。
q='{0,1,2,3,4,5,6,7,8,9}

上と同じ操作をアンパックド配列の連接で行ってみます。
キューにデータを代入します
q2='{0,1,2,3,4,5,6,7,8,9}
q2='{0,1,2,3,40,4,5,6,7,8,9}
q2='{0,1,2,3,4,5,6,7,8,9}

キューの最初にデータを追加します。
q='{100,0,1,2,3,4,5,6,7,8,9}
キューの最後にデータを追加します
q='{100,0,1,2,3,4,5,6,7,8,9,200}

上と同じ操作をアンパックド配列の連接で行ってみます。
q2='{100,0,1,2,3,4,5,6,7,8,9}
q2='{100,0,1,2,3,4,5,6,7,8,9,200}

最初のデータを取り出します
j=        100, q='{0,1,2,3,4,5,6,7,8,9,200}
最後のデータを取り出します
j=        200, q='{0,1,2,3,4,5,6,7,8,9}

上と同じ操作がビットセレクト・パートセレクトを使ってもできます。
j=        100, q2='{0,1,2,3,4,5,6,7,8,9,200}
j=        200, q2='{0,1,2,3,4,5,6,7,8,9}

キューを空にします。
q='{}

上と同じ操作が連接を使ってもできます。
q2='{}

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


下は、上の4行目の、キューの宣言をダイナミックアレーに置き換えただけのものですが、同様に動作します。(上と全く同じ結果になります。)
ダイナミックアレーの場合は、push_back/push_front.. 関数はありませんので、存在意義はあると思います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
module queue_operation;

	int j=1;
	int q[$],q2[];

initial begin
	$display("キューの初期状態ではデータがありません。q=%p",q);

	$display("キューにデータを10個追加します");
	repeat(10) 	q.push_back( q.size());
	$display("q=%p",q);

	$display("\nゼロベースの4番目に40を挿入します。");
	q.insert(4,40);		
	$display("q=%p",q);
	$display("\n4番目のデータを削除します。");
	q.delete(4);
	$display("q=%p",q);

	$display("\n上と同じ操作をアンパックド配列の連接で行ってみます。");

	$display("キューにデータを代入します");
	q2={0,1,2,3,4,5,6,7,8,9};
	$display("q2=%p",q2);
	q2={q2[0:3],40,q2[4:$]};
	$display("q2=%p",q2);
	q2={q2[0:3],q2[5:$]};
	$display("q2=%p",q2);

	$display("\nキューの最初にデータを追加します。");
	q.push_front(100);
	$display("q=%p",q);

	$display("キューの最後にデータを追加します");
	q.push_back(200);
	$display("q=%p",q);

	$display("\n上と同じ操作をアンパックド配列の連接で行ってみます。");
	q2={100,q2};
	$display("q2=%p",q2);
	q2={q2,200};
	$display("q2=%p",q2);

	$display("\n最初のデータを取り出します");
	j=q.pop_front();
	$display("j=%d, q=%p",j,q);

	$display("最後のデータを取り出します");
	j=q.pop_back();
	$display("j=%d, q=%p",j,q);

	$display("\n上と同じ操作がビットセレクト・パートセレクトを使ってもできます。");
	j=q2[0];
	q2=q2[1:$];
	$display("j=%d, q2=%p",j,q2);
	j=q2[$];
	q2=q2[0:$-1];
	$display("j=%d, q2=%p",j,q2);
	
	$display("\nキューを空にします。");
	q.delete();
	$display("q=%p",q);

	$display("\n上と同じ操作が連接を使ってもできます。");
	q2={};
	$display("q2=%p",q2);

end




endmodule


次の例は、ストリング連接・ビットベクターとアンパックド連接の違いです。コンパイラは、左辺の次元を見て判断しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
module string_concatenation2;

	string S, hello;
	string SA[2];
	byte B;
	byte BA[2];

	initial begin
		hello = "hello";
		S = {hello, " world"}; // string concatenation: "hello world"
		$display("%p",S);
		SA = {hello, " world"}; // array concatenation
		$display("%p",SA);

		// SA[0]="hello", SA[1]=" world"
		B = {4'h6, 4'hf}; // vector concatenation: B=8'h6f
		$display("%b",B);
		BA = {4'h6, 4'hf}; // array concatenation: BA[0]=8'h06, BA[1]=8'h0f
		$display("%p",BA);
	end

endmodule
1
2
3
4
5
6
7
8
***** Veritak SV32 Engine Version 431 Build Dec  9 2012*****

"hello world"
'{"hello","hello"}
01101111
'{6,15}

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


次の例で、14行目、ネストした{} は、アンパックド連接とはみなされません。各々を配列の要素として分けたい場合には、17行目のようにアサインメントパターンを使います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
module string_concatenation;

string S1, S2,S12;
typedef string T_SQ[$];
T_SQ SQ,SR,SS;

	initial begin


		S1 = "S1";
		S2 = "S2";

		SQ = '{"element 0", "element 1"}; // assignment pattern, two strings
		SR = {S1, SQ, {"element 3 is ", S2} };

		$display("SR=%p",SR);	
		SS = {S1, SQ, T_SQ'{"element 3 is ", S2} };
		$display("SS=%p",SS);
	end

endmodule
1
2
3
4
5
6
***** Veritak SV32 Engine Version 431 Build Dec  9 2012*****

SR='{"S1","element 0","element 1","element 3 is S2"}
SS='{"S1","element 0","element 1","element 3 is ","S2"}

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