1. 隨機穩定性(random stability)
隨機穩定性是指每個線程(thread)或對象(object)的random number generator(RNG)是私有的,一個線程返回的隨機值序列與其他線程或對象的RNG是無關的。隨機穩定性適用于以下情況:
- 系統隨機方法調用:$urandom()和$urandom_range();
- 對象和進程設置種子方法:srandom();
- 對象隨機化方法:randomize();
在用戶代碼發生小改動時,Testbench帶有這個特性就可以有更穩定的RNG行為。另外,可以通過手動調整線程和對象的seed,這樣就可以更精確控制隨機值的產生。
隨機穩定性包含以下特性:
- Initialization RNG(初始化RNG):每個module例化、interface例化、program例化和package都有一個初始化RNG。每一個初始化RNG都有默認的seed,它的值實現定義。一個初始化RNG用于創建靜態進程和靜態初始化器(initializer)。
- Thread stability(線程穩定性):每一個線程都有一個獨立的RNG,該線程中調用隨機化的所有系統都會使用這個RNG。當一個新的動態線程創建時,它RNG的seed值是從parent線程生成的下一次隨機值,該方式稱為hierarchical seeding。對于一個靜態進程創建時,它RNG的seed值是從聲明該進程的初始化RNG (來自module instance、interface instance、program instance或者package)的下一個隨機值來的。程序和線程的隨機穩定性可以通過保持之前創建線程和RNG的順序一致即可。當給現存testcase增加新的線程時,可以加在代碼塊的末尾,這樣來保持之前創建流程的隨機數穩定性。
- Object stability(對象穩定性):每一個類的例化(object)都有一個獨立的RNG,用于該類內部所有隨機化方法。當一個object創建時,它RNG的seed是來源于創建該object的線程。當一個object是被靜態初始化器創建時,它RNG的seed將會來源于該初始化RNG(來自module instance、interface instance、program instance或者package)的下一個隨機值,因為這時候沒有active線程提供seed的。對象穩定性可以通過保持之前創建object和RNG的順序一致即可。為了保持RNG穩定性,新objects、線程和隨機數的創建應該在現存objects創建之后進行。
- Manual seeding(手動設置種子):所有沒有初始化的RNGs可以手動設置seed。結合hierarchical seeding,該機制可以用于在一個subsystem的root thread定義一個簡單seed,然后這個seed會作用到整個subsystem的運行。
2. 線程穩定性(Thread stability)例子
$urandom系統調用返回的隨機值與線程執行順序無關。如下例子:
integer x, y, z;forkbeginprocess::self.srandom(100);? // set a seed at the start of a threadx = $urandom;endbeginy = $urandom;process::self.srandom(200);? // set a seed during a threadendbeginz = $urandom + $urandom ;? // draw 2 values from the thread RNGendjoin
上述程序片段演示了一下幾個特性:
- Thread locality(線程局部性):x、y和z的返回值與線程的執行順序無關。這是一個很重要的特性,因為它允許開發獨立、可控和可預測的子系統;
- Hierarchical seeding(分層設置種子):在創建線程時,將使用父線程的下一個隨機值作為種子初始化它的隨機狀態。因此,這三個fork引起的線程都是從父線程中得到種子的;
每個線程都有一個唯一的種子值,僅由其父線程決定。線程執行的根(root)決定了它的子節點隨機種子。這就允許設置根線程來自動和保留子節點線程的行為。
3. 對象穩定性(Object stability)例子
每個類中內置的randomize()可以確保對象的穩定性,這使得在一個實例中調用randomize()獨立于其它實例中調用randomize(),并且獨立于調用其它randomize()函數。例如:
class C1;rand integer x;endclassclass C2;rand integer y;endclassinitial beginC1 c1 = new();C2 c2 = new();integer z;void'(c1.randomize());// z = $random;void'(c2.randomize());end
- c1.x和c2.x返回的值是互相獨立的;
- randomize()的調用是獨立于$random系統調用。如果沒有注釋掉上述z=$random,那么分配給c1.x和c2.y的值也不會發生變化;
- 每個實例都有一個可以獨立設置種子的唯一的隨機值源。該隨機種子可以從父線程創建實例時獲取;
- Object可以在任何時候使用srandom()方法進行設置種子;
class C3function new (integer seed);//set a new seed for this instancethis.srandom(seed);endfunctionendclass
一旦對象被創建,就不能保證創建該對象的線程可以在另一個線程訪問該對象之前更改該對象的隨機狀態。因此,對象最好從它們內部new()構造函數而不是從外部進行自我設置種子。
一個object的seed可以在任何線程里面改變。但是一個線程的seed只能在線程內部修改的。
4. 手動進行隨機化設置例子
每個對象都維護自己內部的RNG,該RNG僅由其randomize()方法調用。這就允許對象獨立于其它對象和調用其它系統隨機化函數進行隨機化。當一個對象被創建時,它的RNG將使用創建該對象的線程的RNG中的下一個值作為種子,這個過程稱為分層對象設置種子(hierarchical object seeding)。
有時需要使用srandom()方法手動設置對象RNG的種子,這可以在類方法中完成,也可以在類定義外部完成。一個在類內部設置RNG種子的例子為:
class Packet;rand bit[15:0] header;...function new (int seed);this.srandom(seed);...endfunctionendclass
從外部設置RNG種子的例子為:
Packet p = new(200); // Create p with seed 200.p.srandom(300);? // Re-seed p with seed 300.
在對象的new()函數中調用srandom()確保對象的RNG在任何類成員值隨機化之前使用新設置的種子。在類方法內部設置會比較靠譜點。
5. 唯一性約束(uniqueness constraints)
可以使用unique關鍵字來約束一組變量的隨機值,使得該組變量再隨機之后,任何兩個變量的值都不一樣。能使用該特性的變量類型有整型scalar變量、整型unpacked數組變量的一個leaf element或一片(slice) leaf element或全部leaf element。Leaf element指的是unpacked數組變量拆解到最末尾非unpacked數組類型。例子如下:
rand byte a[5];rand byte b;rand byte excluded;constraint u { unique {b, a[2:3], excluded}; }constraint exclusion { excluded == 5; }
這上述這個例子中,a[2], a[3], b和excluded變量的值在隨機之后將會都不一樣。因為在exclusion 塊中excluded被約束為5,所以a[2], a[3]和b的值將都不會是5。
6. 迭代約束(iterative constraints)
迭代約束允許數組變量使用循環變量、索引表達式或數組縮減法(array reduction methods)去約束。Foreach迭代約束語法如下:
foreach ( ps_or_hierarchical_array_identifier [ loop_variables ] ) constraint_setloop_variables ::= [ index_variable_identifier ] { , [ index_variable_identifier ] }
例子如下:
class C;rand byte A[] ;constraint C1 { foreach ( A [ i ] ) A[i] inside {2,4,8,16}; }constraint C2 { foreach ( A [ j ] ) A[j] > 2 * j; }endclass
C1塊約束了A的每個約束在{2,4,8,16},C2塊約束了A的每個元素值大于它index的兩倍。
循環變量與數組索引之間的映射是由維度的基數所決定的,如下所示:
//???? 1?? 2? 3????????? 3???? 4????? 1?? 2??? -> Dimension numbersint A [2][3][4];??? bit [3:0][2:1] B [5:1][4];foreach( A [ i, j, k ] ) ...foreach( B [ q, r, , s ] ) ...
第一個foreach會使i從0到1迭代,j從0到2迭代,k從0到3迭代。第二個foreach會使q從5到1迭代,r從0到3迭代,s從2到1迭代。
對于動態數組和隊列來說,數組大小也可以被約束的,如果數組大小約束和迭代約束同時發生,那么會先求解數組大小約束,然后求解迭代約束。還有一個就是要防止迭代超出數組大小,用戶必須自己排除這些無效的索引。比如下面例子的k<A.size-1就是防止數組索引溢出。
class C;rand int A[] ;constraint c1 { A.size inside {[1:10]}; }constraint c2 { foreach ( A[ k ] ) (k < A.size - 1) -> A[k + 1] > A[k]; }endclass
數組縮減法可以根據unpacked數組的元素值產生一個整型值,如:
class C;rand bit [7:0] A[] ;constraint c1 { A.size == 5; }constraint c2 { A.sum() with (int'(item)) < 1000; }endclass
c2約束塊將會翻譯成:
( int'(A[0])+int'(A[1])+int'(A[2])+int'(A[3])+int'(A[4]) ) < 1000
7. soft約束
與soft約束相對立的是hard約束,求解器solver必須要滿足hard約束,否則solver求解失敗。Soft約束可以被其它hard約束塊或更高優先級的soft約束塊覆蓋掉,因此,soft約束通常用于給隨機變量指定默認值或分布。
Soft約束之所以可以被其它soft約束覆蓋掉,是因為soft約束之間有不同的優先級,在越后面約束的soft約束優先級越高,因此,驗證環境中子層次比父層次具有更高的優先級。另外,如果只對soft約束求解,那么該次調用求解可以永遠不失敗。如果soft約束求解沒有失敗,那么求解的結果相當于是對hard約束求解一樣。
Soft約束可以被discard掉,也就是不使能,使用disable soft語法可以將比該定義disable的約束塊低優先級的soft約束都失效掉。比如:
class A;rand int x;constraint A1 { soft x == 3; }constraint A2 { disable soft x; } // discard soft constraintsconstraint A3 { soft x inside { 1, 2 }; }endclassinitial beginA a = new();a.randomize();end
A2約束塊要求求解器忽略隨機變量x所有優先級較低的soft約束,從而導致A1約束塊被忽略。因此,求解器只需要滿足最后的A3約束。上述示例將導致隨機變量x取值為1或2。需要注意的是,如果省略了A3約束塊,那么變量x將處于無任何約束的狀態。
8. 隨機化方法
每一個類內部都自帶有randomize()這個virtual方法,它的定義如下:
virtual function int randomize();
randomize()是一個virtual方法,它為類對象中所有active隨機變量提供了隨機值,當然隨機值要符合active約束。如果隨機化成功,那么返回1;反之,返回0。
每一個類也自帶pre_randomize()和post_randomize()方法,注意這兩個方法不是virtual的。pre_randomize()是在randomize()之前調用的,post_randomize()是在randomize()之后調用的。定義如下:
function void pre_randomize();function void pre_randomize();
如果randomize()失敗,那么post_randomize()也不會被調用的。如果randomize()失敗,那么隨機變量要保持原來的值。
9. In-line約束
In-line約束是指用randomize with的方法去隨機類中的變量。當randomize()調用時沒有傳參數,那么它內部所有active 隨機變量都要隨機新值。當randomize()調用時傳參數了,那么這些傳的參數是該object指定隨機變量的全集,其余隨機變量都當作state variables。如下:
class CA;rand byte x, y;byte v, w;constraint c1 { x < v && y > w );endclassCA a = new;a.randomize(); ?????????// random variables: x, y?? state variables: v, wa.randomize( x );?????? // random variables: x????? state variables: y, v, wa.randomize( v, w );? // random variables: v, w? state variables: x, ya.randomize( w, x );? // random variables: w, x? state variables: y, v
據以上可知,randomize()傳參可以改變類內部的任何屬性,設置沒有定義為rand或randc的都可以。不過該機制對于randc是無效的,它不能將非隨機變量變成randc,也不能將randc變成non-randc的。
如果調用randomize()里傳null參數,那么表示該次調用沒有任何隨機變量需要隨機賦新值的,那么該方法相當于是一個checker,它只是返回隨機狀態,這樣就可以用于檢查內部約束塊是否正確。如:
success = a.randomize( null );? // no random variables
10. local范圍解析
在使用randomize() with constraint block的時候,可以引用類屬性或調用方法中(randomize with)的local變量。如果這些變量沒有加限制,那么工具會先搜尋object 類的內部,然后才去搜去調用方法(包含method)的范圍。這樣的話,如果object類和調用方法的范圍內有相同名字的變量,且想要用local的就比較麻煩了,因此SV提供了local::去修改搜尋順序。當一個變量采用local::標識了,那么工具會去local 范圍內找,忽略object類內部的變量。如下例子:
class C;rand integer x;endclassfunction int F(C obj, integer x);F = obj.randomize() with { x < local::x; };endfunction
在obj.randomize() 調用的in-line約束塊中,沒有限定x則會綁定到C類屬性(即正在被隨機化的對象的范圍內),而限定名稱local::x則會綁定到函數F()的參數(局部作用域)。
11. 控制隨機變量和約束塊
rand_mode()可以用于控制是否將隨機變量處于active或inactive狀態。當隨機變量是inactive時,那么它就像是沒有被rand或randc修飾的變量一樣。Inactive變量不會被randomize()隨機賦值的,它們的值會被solver當作state variables。所有的隨機變量的初始態都是active的。rand_mode()方法的定義如下:
task object[.random_variable]::rand_mode( bit on_off );orfunction int object.random_variable::rand_mode();
這里object是指任何能夠獲取到定義該隨機變量的對象句柄的表達式。random_variable是指要對其執行操作的隨機變量的名稱。在作為任務調用時,可以未指定random_variable,則該操作將應用于指定對象內的所有隨機變量。task方式是用于設定variable的mode,參數傳1為active,參數傳0為inactive。function方式時用于返回隨機變量的mode,返回1表示active,返回0表示inactive。
constraint_mode()可以用于控制一個約束塊是否是active或inactive的。如果約束塊是inactive的,那么randomize()在調用時不會考慮該約束塊的了。所有約束塊的初始狀態時active。constraint_mode()方法的定義如下:
task object[.constraint_identifier]::constraint_mode( bit on_off );orfunction int object.constraint_identifier::constraint_mode();
這里object是指任何能夠獲取定義該約束塊所對應的對象句柄的表達式。constraint_identifier是指要被操作的約束塊名字。如果constraint_identifier省略掉,那么object內部的所有constraints都一塊設定。task方式用于設定約束塊的mode,傳1表示active,傳0表示inactive。function方式時用于返回約束塊的mode,返回1表示active,返回0表示inactive。
12. 隨機數系統方法
系統函數$urandom()在每次調用時會返回一個32-bit的unsigned的偽隨機數。定義如下:
function int unsigned $urandom [ (int seed ) ] ;
seed是可選的,seed可以是任何整數表達式。如果seed一致,那么random number generator(RNG)會產生相同的隨機數序列。RNG是確定的,每一次程序執行時,它會從同一個隨機序列周期性的取值。不過我們可以通過指定$urandom的不同seed來使得它變得不確定,如將每一天的時間作為seed。
系統函數$urandom_range()會返回在指定范圍內的無符號整數。定義如下:
function int unsigned $urandom_range(int unsigned maxval, int unsigned minval = 0 );
它將返回maxval到minval之間的數,如果minval省略掉,那么范圍是maxval到0。如果maxval小于minval,那么會自動翻轉,確保第一個參數不小于第二個參數。
srandom()可以用于手動設置object或thread的RNG的種子。srandom()方法使用給定種子的值初始化對象的RNG。定義如下:
function void srandom( int seed );
還有get_randstate()和set_randstate()是用于獲取或設置object的RNG的狀態。