上一篇文章介紹了SystemVerilog的各種隨機化方法,本文將在其基礎(chǔ)上引入SystemVerilog的隨機約束方法(constraints)。通過使用隨機約束,我們可以將隨機限制在一定的空間內(nèi),有針對性地提高功能覆蓋率。
SV隨機約束的應用,就像是我們用陳述性(declarative)的語句告訴仿真器我們要的隨機數(shù)要滿足哪些條件,然后仿真器的約束解算器(constraint solver)就會去找到能夠滿足我們所有描述語句的解,再從這些解中隨機選出來一個值作為隨機的結(jié)果。
約束解算器
約束解算器是SV仿真器重要的一部分,它被專門用來求解約束。這里說的求解,就是指的仿真程序通過某種算法,找到能夠滿足我們所有約束條件的隨機值的過程。如果隨機被過約束(over-constraint)了,或者存在隨機值的組合情況不能滿足約束,則約束解算器就會解算失敗。在實際應用中,仿真程序就會報錯,然后打印出來告訴我們是哪段約束沒有解算成功。
理解約束解算器的重要性,可以想想如果沒有約束解算器會怎么樣?舉一個簡單的栗子,現(xiàn)在我們有一個約束條件:變量A的隨機值總是小于變量B的隨機值。如果沒有約束功能,代碼可以這么實現(xiàn):
do begin
A = $urandom;
B = $urandom;
end while (! (A
使用約束語句代碼是這樣的:
class ictalking;
rand bit [7:0] A, B;
constraint c_ab { A < B; }
endclass
可以看得出來,如果沒有約束解算器,我們在描述約束的時候就會變得比較繞,通常會花掉很多時間去重復執(zhí)行相同的一段代碼,有時候甚至會跑了半天都撞不出來一個滿足約束的隨機值,更別說那些復雜的約束了。有了約束解算器,我們就可以在其框架內(nèi)加入各種約束語句,它總能幫我們快速找到那個解。
約束關(guān)系和控制
約束的解算順序: 約束的解算順序可以使用solve-before來控制。約束解算器會優(yōu)先求解before之前的約束,因此使用solve-before會影響隨機數(shù)組合的概率分布情況。
class ictalking;
rand bit [7:0] A, B;
constraint c_a { A > B; }
constraint c_order {solve A before B;}
// 順序約束可以寫在同一個約束塊中, // 也可以分開寫在不同的約束塊中(如本例)endclass
硬約束和軟約束: 當我們在不同的層次對隨機變量附加約束的時候,軟約束可以被后面指定的約束給覆蓋。典型的應用場景是在UVM的sequence_item(或者叫transaction)定義時,我們可以通過軟約束指定默認的隨機約束,這樣方便我們后面在繼承或者例化的時候可以使用更高優(yōu)先級的約束對其覆蓋。
class ictalking;
rand int count;
constraint c_count {
soft count inside {[666:888]}; // 指定軟約束需要使用關(guān)鍵字soft
}
endclass
ictalking ict = new();
ict.randomize() with { count inside {[123:456]}; }
約束的控制開關(guān): 默認情況下,所有的約束一寫上就默認使能,即約束解算器就會按照這些約束開始算。但SV提供約束條件的控制方法constraint_mode(),可以很方便的控制約束是否啟用,以及查詢約束的啟用狀態(tài)。
// 繼續(xù)上面的例子
int con_status;
ictalking ict_obj1 = new();
ictalking ict_obj2 = new();
ict_obj1.c_count.constraint_mode(0); // 不啟用ict_obj1中的約束c_count
ict_obj2.c_count.constraint_mode(1); // 啟用ict_obj2中的約束c_count
ict_obj1.count.rand_mode(0); // 順便提一嘴,隨機變量類似的可以使用rand_mode開關(guān)隨機功能
con_status = ict_obj1.c_count.constraint_mode(); // 獲得ict_obj1中約束c_count的啟用狀態(tài)
五花八門的約束代碼
SV中的約束非常靈活,下面給出一些常用的約束代碼,可以作為參考和總結(jié)。
- 范圍約束: 使用inside指定隨機數(shù)的范圍或者枚舉值
rand int temp_var;
constraint c_var_1 {temp_var inside {[2000:2021]}; } // 限定范圍
constraint c_var_2 {temp_var inside {2008, 2016, 2019}; } // 限定枚舉值
constraint c_var_3 {! (temp_var inside {[1:2007]}); // 反向限定范圍
- 條件約束: SV中有兩種寫條件約束的方式:implication(有些地方會翻譯成蘊藏或者關(guān)聯(lián)等等)和 if-else,用來指定在某些條件下才做進一步的約束,這兩種方法使用上幾乎沒有區(qū)別。
rand bit mode;
rand int count;
constraint c_var_1 { mode == 1 -> count < 2021; } // 使用implication操作符->
constraint c_var_2 { if (mode == 1) {count < 2021;} else {count > 6000;} } // 使用if-else
- 權(quán)重約束: 約束可以指定隨機值的權(quán)重,主要有兩種方式:dist和randcase。dist一般用在constraint約束塊中,但randcase一般用在程序執(zhí)行塊中,比如某個函數(shù)、任務或者initial塊等。
rand bit mode;
rand int count;
constraint c_var_1 {
mode == READ -> count inside {[2008:2016]};
mode == WRITE -> count inside {[2017:2021]};
mode dist {READ := 4, WRITE := 6}; // mode隨機成READ的概率為40%,WRITE為60%
}
initial begin
repeat (100) begin
randcase 2: $display("In 1st branch."); // 在100次循環(huán)中,執(zhí)行分支1的概率是20% 7: $display("In 2nd branch."); // 在100次循環(huán)中,執(zhí)行分支2的概率是70%
1: $display("In 3rd branch."); // 在100次循環(huán)中,執(zhí)行分支3的概率的10%
endcase
end
end
- 唯一約束: 唯一約束使用unique關(guān)鍵字來限定變量之間的值是唯一的,即兩兩之間互不相等。
rand int a, b, c;
rand int array[5];
int q[$] = `{200, 53, 656};
constraint c_unique {
unique {a, b, c}; // 該約束要求a和b和c兩兩之間互不相等
unique {a, b, array}; // 該約束要求a和b和array中的所有值互不相等
unique {array}; // 該約束要求array數(shù)組內(nèi)的5個值互不相等
unique {a, q}; // 該約束要求a隨機出來的值不等于q中的任一值
}
- 循環(huán)約束: 在對隊列或者數(shù)組進行隨機化的時候,可以使用foreach來對其循環(huán)施加約束。
rand int q[$];
constraint c_foreach {
q.size() inside {[3:8]};
foreach (q[i]) {
if (i > 0) q[i] > q[i-1]; // 約束q隊列的下一個值總比上一個值大
}
}
- 縮位約束: 縮位約束會相對復雜一些,但是很好理解。1. 縮位運算:比如對于數(shù)組arr[5],arr.sum就是arr[0] + arr[1] + ... + arr[4]。除sum之外,還有product、and、or、xor,分別表示乘積、與、或、異或運算。2. 這些縮減運算的方法的返回值類型,跟隊列或者數(shù)組的元素類型一樣,因此當類型不匹配的時候,需要做類型轉(zhuǎn)換的操作。3. 縮減運算的方法可以可選的加with條件,用來篩選隊列或者數(shù)組中的元素。另外關(guān)鍵字item表示當前的數(shù)組元素,item.index表示當前數(shù)組元素的索引。
rand bit qbit[$];
rand int qint[$];
constraint c_qbit {
qbit.size() inside {[4:6]};
(qbit.sum with (int'(item))) == 3; // 將當前元素item轉(zhuǎn)為int類型,并約束所有元素有且只有3個為1
}
constraint c_qint {
qint.size() inside {[5:9]};
(qint.sum with ((item.index < 3) ? item : 0)) == 45; // 約束qint隊列的前三個加起來等于45
}
- 靜態(tài)約束: 靜態(tài)約束使用的關(guān)鍵字static跟靜態(tài)變量是一樣的。靜態(tài)約束表示的是所有的對象實例都使用的同一個約束,所以使用constraint_mode()進行開關(guān)控制的時候具有全局性。
class ictalking;
rand int count;
static constraint c_count {count > 34;}
endclass
module testbench;
initial begin ictalking ict_obj1 = new(); ictalkingict_obj2=new(); ict_obj1.c_count.constraint_mode(0); // 關(guān)掉的之后ict_obj2中的c_count也會失效 ...
end
endmodule
參考文獻
[1] IEEE Standard Association. "IEEE Standard for SystemVerilog-Unified Hardware Design, Specification, and Verification Language." (2013).
-
Verilog
+關(guān)注
關(guān)注
28文章
1343瀏覽量
109925 -
代碼
+關(guān)注
關(guān)注
30文章
4722瀏覽量
68234 -
約束
+關(guān)注
關(guān)注
0文章
82瀏覽量
12708
發(fā)布評論請先 登錄
相關(guān)推薦
評論