++ --  オペレータ

C言語と同様の++ -- 演算子が使用可能です。

module for_loop_test;
 
	 initial begin
			for (int i=0;i< 3;i++) $display("i=%d",i);
	  end
 
  
endmodule
generate 構文でも使用可能です。
 
module top;
	 parameterint N=3;
 
	for (genvar g=0; g< N; g++) for_loop_test dut();
 
endmodule

module for_loop_test;
 
	 initial begin
   			$display("%m");
			  for (int i=0;i< 3;i++) $display("i=%d ",i);
	  end
 
endmodule



 









動作の詳細


基本的には、C言語のそれと同じです。C言語でもこの演算子の使い方は、注意を要するのですが、SystemVerilogでは、さらに注意を要します。まずは、C言語の動作から見ていきます。

C言語の入門書には、よく、

++a は、「a のインクリメントをしてから、その値を使う」
a++ は、「a の値を使ったあとで、a をインクリメントする」

と書かれています。

C言語の規格を読むと、実は、これは、ちょっと違います。


実は、「評価(式の値を決定する)」のと「作用を行う(インクリメントする)」というのは、独立したものです。


a++ は、「その式が完了するまでに(というのが、つまり、「副作用完了点までに」という意味) a をインクリメントする」「式の値としては、インクリメントする前の a の値を採用する」
++a は、「その式が完了するまでに at をインクリメントする」「式の値としては、インクリメントした後の at の値を採用する」

です。ですから、実際の a の値がいつインクリメントするかは全く関係なく、 a++ という式の値は、インクリメントする前の a の値を採用するということになります。で、作用であるインクリメントがいつ行われるかというのは、副作用完了までに行えばよいことになっています。副作用完了とは、おおよそ、文(statement)末や、項(expression)の区切り,になります。


ですから、

 func(a++) + func(a++);

は、a=4だった場合、 func(4) + func(5) か、func(5)+ func(4)、あるいは、func(4)+func(4)かは、実装依存です。この場合の副作用完了点は、;です。

賢いコンパイラは、警告を出してくれますが、現状、VeritakSV含め、警告しないコンパイラの方が多いと思います。


前置インクリメント 後置インクリメント

i++; ++i; のように、 単項=statement である場合は、動作の違いはありません。 

j=++i ;の場合、インクリメントした値が評価値として代入されます。インクリメント動作は、式が完了する;前に行われます。

j=i++;の場合、インクリメントする前の値が評価値として代入されます。インクリメント動作は、式が完了する;前に行われます。

$display("\nundefined result: i=%d i=%d",i++,i++);

は、結果は、実装依存になりますが、C言語仕様上は、未定義動作なので、同一Versionの最適化有りなしで値が変わったとしても、文句を言えません。(SV LRMには、この辺に関する詳しい記述は、ありません。SystemVerilog includes the C increment and decrement assignment operators ++i, --i, i++, and i--. )

module incremental_test;
	int i,j;
 
	 initial begin
  		i++;//単項Statementの場合
		 ++i;//ポスト・プリの 違いはありません
 
  		j=++i;
  		$display("pre-inc result: j=%d  i=%d",j,i);
  		j=i++;
  		$display("post-inc result: j=%d  i=%d",j,i);
 
  		$display("\nundefined result: i=%d  i=%d",i++,i++);//未定義
  		$display("undefined result: i=%d  i=%d",i,i);//未定義結果
 
  		i=2;
  		$display("\npre increment");
		 while (++i <4) begin
   			$display("i=%d",i);
		 end
 
 
  		i=2;
  		$display("post increment");
		 while (i ++<4) begin
   			$display("i=%d",i);
		  end
 end
endmodule
VeritakSVでの結果です。
***** Veritak SV32 Engine Version 431 Build Nov 26 2012*****

pre-inc result: j= 3 i= 3
post-inc result: j= 3 i= 4

undefined result: i= 4 i= 5
undefined result: i= 6 i= 6

pre increment
i= 3
post increment
i= 3
i= 4

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



SystemVerilog上での注意点

++ の動作としては、ブロッキングアサイン=になります。

ここで、問題です。次の4つの記述スタイルで、推奨されるスタイルは、何でしょうか?

module incremental_test;
	int i,j;
	bit [31:0] c1,c2,c3,c4;
	bit clock=0;
 
 
 always @(posedge clock) begin
 		c1<=c1+1;
 end
 
 always @(posedge clock) begin
  		c2<=++c2;
		end
 
 always @(posedge clock) begin
  		c3<=c3++;
		end
 
 always @(posedge clock) begin
  		c4++;
		end

 initial begin
		 repeat(10) begin
				 #10;
  				 clock=~clock;	
		 end
 		 $display("c1=%2d c2=%2d c3=%2d c4=%2d",c1,c2,c3,c4);
 		 $finish;
	  end
endmodule

c3 が0になったのを除いて、すべて同じ値になりましたが、推奨される記述スタイルは、結局、従来通りのc1 だけでしょう。

c4は、ブロッキングアサインなので、ノンブロッキング動作<=ではありません。レースを生む可能性があります。
c2<=++c2 は、よくよく検討すると問題なさそうですが、そんなことで悩むよりは、HW記述では使わないと決めておいたほうがよさそうです。

次の例は、やってしまいそうな記述です。注意しましょう。


always_ff @(posedge clock) begin 
		if (!reset) counter<=0;
		else counter++;
	end



速度の差異


前置演算と後置演算で、シミュレーション速度の差異は、あるのでしょうか? VeritakSV実装の話になりますが、++i; --i; 等の単項演算では、最適化が容易で違いはありません。そうでない場合は、後置演算の方が遅くなります。後置演算では、値のコピー作業が必要になるため、その分遅くなります。