在上一篇文章中,我們討論并得出了一種構(gòu)建可控隨機的方案:
【芯片驗證】異步電路碎碎念(十)在靜態(tài)對象內(nèi)構(gòu)建可控隨機·方案一
這篇文章呢我們討論另外一種方案,為啥有方案了還要嘗試其他的方案呢?我自己對$random這個函數(shù)相當?shù)牟恍湃,這個函數(shù)不受seed控制到低隨機時候從哪找的種子我也不太清楚,萬一跟地域、時間、服務器、工具等特性有關(guān)系,環(huán)境一遷移導致隨機無法復現(xiàn)怎么辦呢。所以我希望有一種原理明明白白的可控隨機方案,當然這里并不是說$random+$urandom不能用哈,實際上我知道的很多地方就是直接用這個的。
原理明明白白的可控隨機方案怎么來呢?既然不信任$random那必然是從$urandom入手看了,還記得$random的問題是啥來著吧?不能感知靜態(tài)模塊的不同例化路徑差異。那么讓他感知一下例化路徑差異不就好了嘛,怎么感知這個事,相信大家對這個字符都很熟悉:%m,對就是在打印里用的那個字符。這個字符的作用是打印當前模塊(或獨立命名域)的例化路徑,打印里的前半部分就是靠這個打印出來的:
$display("%m new_rand_value = 'h%0h", new_rand_value);
----->
testbench.u_rand_test1 new_rand_value = 'hd7也就是說,一個靜態(tài)模塊自身只要例化在了DUT中,那么我們就能夠獲取到其例化路徑,以字符串的方式我們來獲取以下路徑:string path_str;
initial begin
path_str = $psprintf(path_str, "%m");
$display("inst path is %s", path_str);
end對應的打印結(jié)果是:inst path is testbench.u_rand_test0
inst path is testbench.u_rand_test1好的那么也就是說,雖然$urandom不能感知例化路徑,但是模塊自身能夠感知,因此只要把這個路徑引入到$urandom中去就可以了。這時有人可能就要問了,%m是字符串,$urandom的種子是數(shù)字啊,這要怎么引進去呢?年輕了不是,誰說%m就一定是字符串了:string path_str;
int path_int;
initial begin
path_str = $psprintf(path_str, "%m");
path_int = path_str;
$display("inst path is %s, path_int = 'h%0h", path_str, path_int);
end
----->
inst path is testbench.u_rand_test0, path_int = 'h65737430
inst path is testbench.u_rand_test1, path_int = 'h65737431你看根據(jù)例化路徑得到的數(shù)字這不就出來了嘛,不過仔細觀察一下會發(fā)現(xiàn),這路徑差異比較近,得出的數(shù)字差異也太小了,這個數(shù)給到$urandom里去隨機出來的值別在差不多那就不好了。所以直接把這個值給$urandom肯定不行,那么怎么和$urandom結(jié)合使用能夠起到作用呢?下面是我選擇一種方式:string path_str;
int path_int;
initial begin
path_str = $psprintf(path_str, "%m");
path_int = $urandom();
for(int i=path_str.len; i>=0; i=i-1)begin
path_int = path_int ^ path_str.getc(i);
path_int = $urandom(path_int);
end
path_int = $abs(path_int);
$display("inst path is %s, path_int = 'h%0h", path_str, path_int);
end具體解釋一下這些代碼,先把path_int通過$urandom給一個初始隨機值,不同模塊是一樣的沒關(guān)系,這個值結(jié)合后面路徑的微小差異就可以成就“蝴蝶效應”。之后從尾部到頭部路徑字符,因為差異集中在尾部所以要從尾部開始,str.getc(i)獲取字符串對應位置字符的ASCII碼,再跟path_int取異或,累計擴大差異性,而后再加入一個$urandom進一步擴大差異性。循環(huán)之后得到了擴大差異之后的數(shù),此時最后的結(jié)果可能是負數(shù),這不是我們所需要的因此通過$abs函數(shù)全部取正。經(jīng)過這些操作后,咱們來看下進行隨機后的結(jié)果:
inst path is testbench.u_rand_test0, path_int = 'h66b884a6
inst path is testbench.u_rand_test1, path_int = 'h49aee46多測試幾個種子:inst path is testbench.u_rand_test0, path_int = 'h21669974
inst path is testbench.u_rand_test1, path_int = 'h19e00dfa
tc seed = 62302096
inst path is testbench.u_rand_test0, path_int = 'h19bc271c
inst path is testbench.u_rand_test1, path_int = 'h7eef9304
tc seed = 81054216
inst path is testbench.u_rand_test0, path_int = 'h268220ce
inst path is testbench.u_rand_test1, path_int = 'h44c8d731
tc seed = 53991314對其中的一個種子結(jié)果進行復現(xiàn):inst path is testbench.u_rand_test0, path_int = 'h21669974
inst path is testbench.u_rand_test1, path_int = 'h19e00dfa
tc seed = 62302096穩(wěn)定復現(xiàn),那么這種方法顯然是符合我們預期的。其實后來我想了下,如果想擴大差異,是不是多嵌套幾個$urandom就可以了,比如$urandom($urandom($urandom($urandom(path_int)))),于是試了試:string path_str;
int path_int;
initial begin
path_str = $psprintf(path_str, "%m");
path_int = path_str;
path_int = $urandom($urandom($urandom($urandom(path_int))));
$display("inst path is %s, path_int = 'h%0h", path_str, path_int);
end
----->
inst path is testbench.u_rand_test0, path_int = 'h6a6ed13
inst path is testbench.u_rand_test1, path_int = 'h86b6f7a1
tc seed = 0
inst path is testbench.u_rand_test0, path_int = 'h6a6ed13
inst path is testbench.u_rand_test1, path_int = 'h86b6f7a1
tc seed = 38445033emm果然是不行,所以還是用上面提供的方法吧。那么下一個問題就來了,這么用起來呢,總不能每次需要隨機時候都寫這么一大片的代碼吧?答案也很簡單,封裝成一個函數(shù)不就好了嘛:
string path_str;
initial path_str = $psprintf(path_str, "%m");
function integer urandom;
integer seed, i;
begin
seed = $urandom();
for(i=path_str.len; i>=0; i=i-1)begin
seed = seed ^ path_str.getc(i);
seed = $urandom(seed);
end
urandom = $abs(seed);
end
endfunction
initial begin
int rand_test;
rand_test = urandom();
$display("%m, rand_test = %0d", rand_test);
end測試一下結(jié)果:testbench.u_rand_test0.unnamed$$_0, rand_test = 1641770003
testbench.u_rand_test1.unnamed$$_0, rand_test = 621788312
tc name = sanity
tc seed = 59908161
testbench.u_rand_test0.unnamed$$_0, rand_test = 1271321994
testbench.u_rand_test1.unnamed$$_0, rand_test = 588367474
tc name = sanity
tc seed = 73017818
testbench.u_rand_test0.unnamed$$_0, rand_test = 97687901
testbench.u_rand_test1.unnamed$$_0, rand_test = 704389240
tc name = sanity
tc seed = 75798405看起來是沒有什么問題的。那么繼續(xù)往下探索,即使封裝為函數(shù)了,難不成每個要進行隨機的模塊里面都寫一次這個函數(shù)嗎?當然是不需要的,有兩種思路可以處理這個問題,一是將函數(shù)定義為全局可見的方法,而path_str作為作為函數(shù)的輸入,這樣調(diào)用時傳參本模塊的path_str就可以了。第二種思路是進行宏封裝,我選擇的是第二種思路因為我確實不喜歡在RTL里有太多冗余的代碼。在宏定義文件中做宏:
`define module_urandom_define \
string path_str; \
initial path_str = $psprintf(path_str, "%m"); \
\
function integer urandom; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom = $abs(seed); \
end \
endfunction而后在需要進行隨機的模塊里RTL前面的地方找個犄角旮旯寫一下宏,后面就可以:`module_urandom_define
initial begin
int rand_test;
rand_test = urandom();
$display("%m test1, rand_test = %0d", rand_test);
rand_test = urandom();
$display("%m test2, rand_test = %0d", rand_test);
end然后就可以使用下看看了:testbench.u_rand_test0.unnamed$$_0 test1, rand_test = 1970541737
testbench.u_rand_test0.unnamed$$_0 test2, rand_test = 128592474
testbench.u_rand_test1.unnamed$$_0 test1, rand_test = 849448805
testbench.u_rand_test1.unnamed$$_0 test2, rand_test = 1978420876
tc name = sanity
tc seed = 17411917
testbench.u_rand_test0.unnamed$$_0 test1, rand_test = 887806234
testbench.u_rand_test0.unnamed$$_0 test2, rand_test = 216253277
testbench.u_rand_test1.unnamed$$_0 test1, rand_test = 1150158848
testbench.u_rand_test1.unnamed$$_0 test2, rand_test = 1381425996
tc name = sanity
tc seed = 50449658
testbench.u_rand_test0.unnamed$$_0 test1, rand_test = 439486355
testbench.u_rand_test0.unnamed$$_0 test2, rand_test = 1519906787
testbench.u_rand_test1.unnamed$$_0 test1, rand_test = 219267827
testbench.u_rand_test1.unnamed$$_0 test2, rand_test = 133804852
tc name = sanity
tc seed = 64935128事情到這里基本就已經(jīng)解決了,我們以自定義的urandom()函數(shù)平替了系統(tǒng)的$urandom()函數(shù),達到了在靜態(tài)模塊中等價于$urandom()的可控和差異化隨機功能。最后再來拓展實現(xiàn)另外一個功能,我們想隨機一個數(shù)值那是不是經(jīng)常會使用$urandom_range(),可是在靜態(tài)模塊中怎么做到這一點呢?也很簡單,加一個宏不就可以了:function integer urandom_range(); \
input integer min, max; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom_range = min + $abs(seed % (max - min)); \
end \
endfunction測試一下:initial begin
int rand_test;
rand_test = urandom_range(0,100);
$display("%m test1, rand_test = %0d", rand_test);
rand_test = urandom_range(100,1000);
$display("%m test2, rand_test = %0d", rand_test);
end打印結(jié)果為:testbench.u_rand_test0.unnamed$$_0 test1, rand_test = 75
testbench.u_rand_test0.unnamed$$_0 test2, rand_test = 892
testbench.u_rand_test1.unnamed$$_0 test1, rand_test = 71
testbench.u_rand_test1.unnamed$$_0 test2, rand_test = 461
tc name = sanity
tc seed = 16870716
testbench.u_rand_test0.unnamed$$_0 test1, rand_test = 63
testbench.u_rand_test0.unnamed$$_0 test2, rand_test = 944
testbench.u_rand_test1.unnamed$$_0 test1, rand_test = 54
testbench.u_rand_test1.unnamed$$_0 test2, rand_test = 172
tc name = sanity
tc seed = 47491380如果想要小數(shù)、負數(shù)和浮點數(shù)怎么辦呢?就根據(jù)這套方法來拓展下就可以。
系列文章入口——
【芯片設計】SoC 101(一):緒論 | 【芯片設計】FIFO漫談(零)從無處不在的FIFO開始說起 | 【芯片設計】計算機體系結(jié)構(gòu)(一)虛擬內(nèi)存 | 【芯片設計】深入理解AMBA總線(零)緒論
| 【芯片設計】握手協(xié)議的介紹與時序說明 | 【芯片設計】復位那些小事 —— 復位消抖 | 【芯片設計】快速入門數(shù)字芯片設計(一)Introduction | 【芯片驗證】UVM源碼計劃(零)下定決心讀源碼前的自測環(huán)節(jié)
| 【芯片設計】異步電路碎碎念(一) 到底什么是異步電路
| 【芯片設計】從RTL到GDS(一):Introduction
| 其他文章鏈接——
【芯片驗證】sva_assertion: 15道助力飛升的斷言練習 | 【芯片驗證】可能是RTL定向驗證的巔峰之作 | 【芯片驗證】RTL仿真中X態(tài)行為的傳播 —— 從xprop說起 | 【芯片驗證】年輕人的第一個systemVerilog驗證環(huán)境全工程與解析 |
【芯片設計】verilog中有符號數(shù)和無符號數(shù)的本質(zhì)探究
| 【芯片設計】論RTL中always語法的消失術(shù) | 【芯片設計】代碼即注釋,注釋即代碼 | 【芯片設計】700行代碼的risc處理器你確實不能要求太多了 |
入職芯片開發(fā)部門后,每天摸魚之外的時間我們要做些什么呢 | 如何計算系統(tǒng)的outstanding 和 burst length? | 芯片搬磚日!け扑缽娖劝Y的關(guān)鍵詞不對齊事件 | 熟人社會里,一群沒有社會價值的局外人 |
|