$urandom/$urandom_range


1. $randomと$urandomの違い

$urandomは、SVで追加されたランダム関数です。符号なしのランダム関数になりますが、次表の通り、その中身は、随分違います。

$random $urandom/$urandom_range
符号  signed unsigned
戻り値  integer 32bit (4値 ただしzxが返ることはない) int 32bit (2値)
引数(seed option) 次回seed値が、書き戻される仕様のために
引数は、integeral variable(記憶のある変数タイプ)でなければならない
seed値について特に型の制限はない(次回seed値は書き戻されない。)
ランダムシーケンス LRMで規定されている。Vendor間で互換あり 実装依存。Vendor間の互換はない
適用 グローバル スレッドローカル
階層Seed グローバルのためにそういう概念はない スレッド生成時に親スレッドからSeed値をもらう
つまり子スレッドのランダムシーケンスは、親スレッドに依存する
使い方

bit [23:0] rand;
rand = $random % 60;//-59から59まで

符号付のために注意が必要。符号なしが欲しい場合、例えば{}もしくは $unsigned($random)で、符号なしにする。

bit [23:0] rand;
rand = {$random} % 60;//0から59まで
 rand=$urandom_range(59);//0から59まで
 rand=$urandom_range(59,0);//0から59まで
 rand=$urandom_range(0,59);//0から59まで

1.1 $urandomは、実装依存

 $randomは、Verilog2001のLRMにCソースが掲載されており、これに基づいて実装されているために、各Vendor間で互換があります。$urandomは、実装依存です。各Vendor間の互換はありません。

1.2 $urandomシーケンスは、$randomとは無関係
 $randomがどのように動こうと、$urandomのランダムシーケンスは、影響を受けません。反対もまたしかりです。

1.3 $randomの問題 
 テストベンチの開発において、しばしば悩ましいのは、$randomは、グローバルに効いてしまうことです。 テストベンチでは、他のスレッドの影響を受けない独立したランダムシーケンスが欲しい場合が多いのですが、$randomは、どこにあっても影響してしまいます。 テストベンチの開発過程においては、コードの追加削除は日常茶飯事ですので、その度に$randomシーケンスが変化すると余計なデバッグの手間が増えてしまいます。テストベンチの開発効率に影響してしまいます。

1.4 seed でランダムシーケンスを制御できる

 まず、seed指定のない状態で見てみます。
module urandom_test3;
	 initial begin
		 repeat (10) $display("initial1: %8x",$urandom());
   		$display("");
		end
  
  initial begin
		 repeat (10) $display("initial2:%8x",$urandom());
	 end
 
endmodule

 実行結果です。
***** Veritak SV6464 Engine Version 0.440 Build Dec.13.2011 *****

initial1: c6d422b3
initial1: 6cfac2aa
initial1: 9ce97ec5
initial1: ab657674
initial1: 54c90b67
initial1: 8b07d88e
initial1: fe3ecd19
initial1: 1b2fdb78
initial1: f74aec5b
initial1: c61f05b2

initial2:c6d766b0
initial2:16f72ab3
initial2:e2b1aaaa
initial2:8964c6c5
initial2:7d139e74
initial2:9a5e9367
initial2:0a67408e
initial2:0fc09519
initial2:b7ca8378
initial2:2e1af45b

---------- シミュレーションを終了します。time=0ns
initial1とinitial2 のランダムシーケンスが異なることが分かります。これに同じseed、例えば0を追加してみます。
module urandom_test4;
	 initial begin
    		$urandom(0);
		  repeat (10) $display("initial1: %8x",$urandom());
    		$display("");
		end
 
  initial begin
    		$urandom(0);
		  repeat (10) $display("initial2:%8x",$urandom());
	end
 
endmodule

結果です。同じSeedを指定すると同じシーケンスになります。異なるSeedを指定すると異なるシーケンスになります。

***** Veritak SV6464 Engine Version 0.440 Build Dec.13.2011 *****

initial1: 1e278e7a
initial1: d2f65b55
initial1: 098520c4
initial1: a2974c77
initial1: 2e15555e
initial1: 20ad96a9
initial1: 7e1dbec8
initial1: a8d2826b
initial1: 77948382
initial1: 96dd9c3d

initial2:1e278e7a
initial2:d2f65b55
initial2:098520c4
initial2:a2974c77
initial2:2e15555e
initial2:20ad96a9
initial2:7e1dbec8
initial2:a8d2826b
initial2:77948382
initial2:96dd9c3d

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



1.5 $urandomは、スレッドローカル
 上の反省versionが、$urandomです。スレッドとは、他のスレッドに一旦行っても、自分のコンテキストを復元して戻ってこれるプログラム単位のことです。
 verilog HDLでは、次が、スレッドで実装されます。

 従って、これらのステートメント上にある、$urandomは、独立したランダムシーケンスを持ちます。(2^32ビット周期のシーケンスとしてみると一つですが、異なる
Seedを与えることによって、短い期間でみると異なるランダムシーケンスに見えます。)

次のソースでは、fork-joinで二つの平行プロセスを記述しています。2番目のbegin-end プロセスでは、意図して、$random_range(1000) としています。
module urandom_test5;
 			
				initial 
     fork
       begin
        repeat(3) begin
						   #($urandom_range(10));
         						$display("			I1UT1 %x %d",$urandom(),$time);
        end
       end
      begin
        repeat(3) begin
			      			   #($urandom_range(1000));
         						$display("			I1UT1 %x %d",$urandom(),$time);
	       						 end
      end
     join
endmodule

***** Veritak SV6464 Engine Version 0.440 Build Dec.13.2011 *****

             I1UT1 12700ff6 1
             I1UT1 66bc0e20 5
             I1UT1 28010d9a 12
I1UT2 dd93a09a 924
I1UT2 a32ec3e4 1305
I1UT2 32d1117e 1926

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

この状態だと、下側のプロセスの影響の有無を確認できないので、$urandom_range(10);としてみます。
module urandom_test6;
 			
				initial 
     fork
       begin
        repeat(3) begin
			      			   #($urandom_range(10));
         						$display("			I1UT1 %x %d",$urandom(),$time);
        end
       end
      begin
        repeat(3) begin
			      #($urandom_range(10));
         						$display("			I1UT1 %x %d",$urandom(),$time);
	       						 end
      end
     join
endmodule


上側のプロセスは、下側のプロセス$urandomの影響を受けずに、前の結果と同じであることが分かります。

 ***** Veritak SV6464 Engine Version 0.440 Build Dec.13.2011 *****

I1UT2 dd93a09a                    0
                        I1UT1 12700ff6                    1
                        I1UT1 66bc0e20                    5
I1UT2 a32ec3e4                    7
                        I1UT1 28010d9a                   12
I1UT2 32d1117e                   12

スレッド自身が、$urandomオブジェクト領域を持っており、他のスレッドの影響を受けずに独立したシーケンスを保てます。

1.6 階層Seed

子どもスレッド生成時、親スレッドのseedを元に子どものseedが決定されます。(どのように決定されるかは実装依存)。この時点で親スレッドのランダムシーケンスは、一つ進むことになります。いずれにしても、子どもの$urandom値は、seedを指定しない限り、親スレッドに依存することになります。言い換えると、親のルートのseedを変えてやれば、その子どもスレッド全体に影響は波及することになります。これによりコントロール可能なランダムシーケンスが実現できます。

次のソースでは、

と起動されますが、t2までは、子スレッドです。f1/f2は、t2のurandomオブジェクトを操作します。しかしながら、このランダムシーケンスは、最初の$urandom(0)で
制御されていることに注意してください。このinitial プロセスをコピペしても同じランダムシーケンス結果になります。また、例えば、$urandom(1)とすれば、違うランダムシーケンスとなります。

module urandom_test8;
 
 
	
		initial begin 
   			$display("%x ",$urandom(0));
			
				fork
     begin
		    //			$urandom(0);
					 repeat(3) begin
						 #($urandom_range(10));
       						t1;				 
       						$display("			I1UT1 %x %d",$urandom(),$time);
						end
     end
    begin
		   //			$urandom(0);
					repeat(3) begin 
						#($urandom_range(1000));
      						t1;
      						$display("I1UT2 %x %d",$urandom(),$time);
			  end
    end
  join
 end
 
	
 
 
	function f1;
   			$display("f1:%x",$urandom());
   			f2();
	endfunction
 
	function f2;
   			$display("f2:%x",$urandom());
	endfunction
 
	task t1;
  			$display("t1:%x",$urandom());
  			t2;
	endtask
 
	task t2;
  			$display("t2:%x",$urandom());
  			f1();
	endtask
 
endmodule
***** Veritak SV6464 Engine Version 0.440 Build Dec.13.2011 *****

00269ec3 
t1:d8f9f0c3
t2:fc95bc34
f1:04ff3a27
f2:6b104c4e
                        I1UT1 d8f9f0c3                    5
t1:72f67ec5
t2:dd0bee22
f1:5ea9dc5d
f2:97c8bdac
                        I1UT1 72f67ec5                    5
t1:cca120fb
t2:10fe4a3c
f1:3797b00f
f2:b4508a96
                        I1UT1 cca120fb                   14
t1:a71b0a9d
t2:ee53438a
f1:1dbb7c25
f2:e85cfe54
I1UT2 a71b0a9d                  484
t1:8bb2c0c3
t2:df966c34
f1:9abd2a27
f2:ed967c4e
I1UT2 8bb2c0c3                 1338
t1:08b46ec5
t2:2fde7e22
f1:04722c5d
f2:cbafcdac
I1UT2 08b46ec5                 1938

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

1.7 $urandom_range

$urandomと同じで、スレッドローカルです。

内部宣言は、

function int unsigned $urandom_range( int unsigned maxval,int unsigned minval = 0 );

で、
val = $urandom_range(7,0); $urandom_range(7); $urandom(0,7);は、どれも同じ意味で0から7までを返します。

1.8 $urandomの速度
 $randomは、浮動小数を使った確率分布に基づいた関数になっています。(LRM) 一方$urandomは、実装依存なので一概には言えないのですが、$randomに比べれば、数倍以上速いと思います。