SV -- Randomization 隨機化
@(SV)
0. 基礎
下面幾種類型可以隨機化:
- 單個變量或整形數
- 數組
- 數組長度
- 對象句柄
語法:
rand bit [3:0] addr;
生成0-15的隨機數randc bit [3:0] addr;
生成0-15的隨機數,完全遍歷完16個數之后才會開始開始下一輪,每一輪隨機的數不重樣object.randomize();
對象內變量隨機化命令
rand_mode:
<object_hanlde>.<variable_name>.rand_mode(enable);
打開或關閉某個變量的隨機化,或者
<object_hanlde>.rand_mode(enable);
打開或關閉某個類內的所有變量的隨機化。
class packet;
rand byte addr;
rand byte data;
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
//disable rand_mode of addr variable of pkt
pkt.addr.rand_mode(0);
//calling randomize method
pkt.randomize();
$display("\taddr = %0d \t data = %0d",pkt.addr,pkt.data);
$display("\taddr.rand_mode() = %0d \t data.rand_mode() = %0d",pkt.addr.rand_mode(),pkt.data.rand_mode());
end
endmodule
輸出:
addr = 0 data = 110
addr.rand_mode() = 0 data.rand_mode() = 1
1. 隨機化方法
- randomize()
- pre_randomize()
- 可以為類設置隨機化的先決條件,例如rand_mode()
- post_randomize()
- 用來在隨機化后進行檢查
例如下面的例子,如果在wr_rd為1的時候,address需要保持不變,定義一個pre_randomize方法來控制變量是否隨機化。
//class
class packet;
rand bit [7:0] addr;
randc bit wr_rd;
bit tmp_wr_rd;
//pre randomization function - disabling randomization of addr,
//if the prevoius operation is write.
function void pre_randomize();
if(tmp_wr_rd==1) addr.rand_mode(0);
else addr.rand_mode(1);
endfunction
//post randomization function - store the wr_rd value to tmp_wr_rd
//and display randomized values of addr and wr_rd
function void post_randomize();
tmp_wr_rd = wr_rd;
$display("POST_RANDOMIZATION:: Addr = %0h,wr_rd = %0h",addr,wr_rd);
endfunction
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
repeat(4) pkt.randomize();
end
endmodule
輸出:
POST_RANDOMIZATION:: Addr = 6e,wr_rd = 1
POST_RANDOMIZATION:: Addr = 6e,wr_rd = 0
POST_RANDOMIZATION:: Addr = 88,wr_rd = 1
POST_RANDOMIZATION:: Addr = 88,wr_rd = 0
2. Constraint
為隨機變量設置約束:
第一種,在內部設置:
class packet;
rand bit [3:0] addr;
constraint addr_range { addr > 5; }
endclass
第二種,在外部設置:
class packet;
rand bit [3:0] addr;
//constraint block declaration
constraint addr_range;
endclass
//constraint implementation outside class body
constraint packet::addr_range { addr > 5; }
同樣,constraint也會被子類繼承。
3. inside
讓隨機值約束在inside指定的范圍內:
constraint addr_range { addr inside {1,3,[5:10],12,[13:15]}; }
constraint addr_range { addr !(inside {[5:10]}); }
也可以將上下界指定為隨機值(上下界都可以取到):
class packet;
rand bit [3:0] addr;
rand bit [3:0] start_addr;
rand bit [3:0] end_addr;
constraint addr_1_range { addr inside {[start_addr:end_addr]}; }
endclass
4. dist
可以使得隨機化的數據
constraint addr_1_range { addr_1 dist { 2 := 5, [10:12] := 8 }; }
上面的權重是這樣的:
addr == 2 , weight 5
addr == 10, weight 8
addr == 11, weight 8
addr == 12, weight 8
constraint addr_2_range { addr_2 dist { 2 :/ 5, [10:12] :/ 8 }; }
上面的權重是這樣的:
addr == 2 , weight 5
addr == 10, weight 8/3
addr == 11, weight 8/3
addr == 12, weight 8/3
5. 條件約束
class packet;
rand bit [3:0] addr;
string addr_range;
constraint address_range { if(addr_range == "small")
addr < 8;
else
addr > 8;
}
endclass
6. foreach 約束
class packet;
rand byte addr [];
rand byte data [];
constraint avalues { foreach( addr[i] ) addr[i] inside {4,8,12,16}; }
constraint dvalues { foreach( data[j] ) data[j] > 4 * j; }
constraint asize { addr.size < 4; }
constraint dsize { data.size == addr.size; }
endclass
7. disable constraint
跟randomize_mode一樣,constraint也可以設置mode控制是否添加約束:
pkt.addr_range.constraint_mode(0);
0表示關閉約束
7. static constraint
靜態約束跟普通約束不同的是,靜態約束的使能和關閉對所有實例都生效:
class packet;
rand bit [7:0] addr;
static constraint addr_range { addr == 5; }
endclass
如果下面實例化了兩個packet:a1和a2,如果對a1的addr變量設置constraint_mode(0),則a2的addr的約束也會關閉。
8. inline constraint
inline的約束允許使用with關鍵字在類外對變量增加新的約束:
class packet;
rand bit [3:0] addr;
constraint addr_range {addr inside {[6:12]};};
endclass
module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
pkt.randomize() with { addr == 8;};
$display("\taddr = %0d",pkt.addr);
end
end
endmodule
上面pkt.randomize() with { addr == 8;};
將給變量添加新的約束,使得輸出都為8,但是通過with添加的新約束必須包含在類中定義的范圍內,否則會報錯。
9. soft constraint
軟約束是為了解決inline方式的弊端。比如有這樣的場景,我們已經測試了很多正常約束的case,可能會使用with的方式來生成約束的子集,但我們永遠無法通過with來生成約束的補集,而這可能在測試異常情況時需要用到。一個方法是修改constraint,但無疑會有很大麻煩。
這時只需要在constraint中為變量增加soft關鍵字即可:
class packet;
rand bit [3:0] addr;
constraint addr_range { soft addr > 6; }
endclass
module soft_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
pkt.randomize() with { addr < 6;};
$display("\taddr = %0d",pkt.addr);
end
end
endmodule
10. unique
使用unique關鍵詞加在一組隨機變量或者隨機數組前,可以使得這些變量在約束范圍內各不相同:
rand bit [31:0] array[10];
constraint array_c {unique {array}; foreach(array[i]) array[i] < 10;}
11. 約束的雙向性質
上面的例子一般都只涉及單個變量的約束,而實際上sv中約束的變量是並行地設置的,因此約束中如果涉及多個約束變量的約束,則這一約束是雙向的。
例如:
constraint c_name { if(a == 0) b == 1;
else b == 0; }
上面的例子b的值依賴a,而實際上a的值也會依賴b,如果將b通過inine的方式約束為1,則a也被約束為0。
class packet;
rand bit a;
rand bit b;
constraint a_value { a == 1; }
constraint b_value { if(a == 0) b == 1;
else b == 0; }
endclass
module bidirectional_const;
initial begin
packet pkt;
pkt = new();
pkt.randomize() with { b == 1; };
$display("Value of a = %0d \tb = %0d",pkt.a,pkt.b);
end
endmodule
上面的例子試圖用inline的方式約束b,但b的約束會反作用到a,使得a約束為0,但a已經約束為只能為1,所以程序報錯。
12. Solve before
solve before可以強制約束中先進行哪個變量的約束。
在上面提到的約束雙向性中,多個變量之間有依賴關系時約束是雙向的,這可能會導致一些問題,比如下面的例子:
class packet;
rand bit a;
rand bit [3:0] b;
constraint a_b { (a == 1) -> b == 0; }
endclass
module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
$display("\tValue of a = %0d, b = %0d",pkt.a,pkt.b);
end
end
endmodule
b可以取0-15中的值,而只要b不為0,a都會被約束為0,所以這個例子產生的結果是這樣的:
Value of a = 0, b = 6
Value of a = 0, b = 3
Value of a = 1, b = 0
Value of a = 0, b = 15
Value of a = 0, b = 7
Value of a = 0, b = 2
Value of a = 0, b = 15
Value of a = 0, b = 4
Value of a = 0, b = 7
Value of a = 0, b = 11
a=1的情況非常少。如果我們希望a的產生不依賴b,可以使用solve before:
class packet;
rand bit a;
rand bit [3:0] b;
constraint sab { solve a before b;}
constraint a_b { (a == 1) -> b == 0;}
endclass
module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
$display("\tValue of a = %0d, b = %0d",pkt.a,pkt.b);
end
end
endmodule
輸出:
Value of a = 0, b = 9
Value of a = 0, b = 14
Value of a = 0, b = 3
Value of a = 0, b = 13
Value of a = 1, b = 0
Value of a = 1, b = 0
Value of a = 1, b = 0
Value of a = 0, b = 5
Value of a = 0, b = 3
Value of a = 0, b = 4
13. 隨機系統函數
隨機化的系統函數主要有兩個:
- urandom() 生成無符號隨機數,括號內可以填入不同隨機種子
- urandom_range(max,min) 生成范圍內的隨機數,上下界可以不按順序寫,默認下屆為0
- random() 生成有符號隨機數。
module system_funcations;
bit [31:0] addr1;
bit [31:0] addr2;
bit [64:0] addr3;
bit [31:0] data;
initial begin
addr1 = $urandom();
addr2 = $urandom(89);
addr3 = {$urandom(),$urandom()};
data = $urandom * 6;
$display("addr1=%0d, addr2=%0d, addr3=%0d, data=%0d",addr1,addr2,addr3,data);
addr1 = $urandom_range(30,20);
addr2 = $urandom_range(20); //takes max value as '0'
addr3 = $urandom_range(20,30); //considers max value as '30' and min value as '20'
$display("addr1=%0d, addr2=%0d, addr3=%0d",addr1,addr2,addr3);
end
endmodule
輸出:
addr1=303379748, addr2=2153631232, addr3=423959822444962108, data=546103870
addr1=27, addr2=6, addr3=25