電子產(chǎn)業(yè)一站式賦能平臺(tái)

PCB聯(lián)盟網(wǎng)

搜索
查看: 44|回復(fù): 0
收起左側(cè)

【芯片驗(yàn)證】異步電路碎碎念(十一)在靜態(tài)對象內(nèi)構(gòu)建可控隨機(jī)·方案二

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
匿名  發(fā)表于 2024-11-11 12:01:00 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
在上一篇文章中,我們討論并得出了一種構(gòu)建可控隨機(jī)的方案:
【芯片驗(yàn)證】異步電路碎碎念(十)在靜態(tài)對象內(nèi)構(gòu)建可控隨機(jī)·方案一
這篇文章呢我們討論另外一種方案,為啥有方案了還要嘗試其他的方案呢?我自己對$random這個(gè)函數(shù)相當(dāng)?shù)牟恍湃,這個(gè)函數(shù)不受seed控制到低隨機(jī)時(shí)候從哪找的種子我也不太清楚,萬一跟地域、時(shí)間、服務(wù)器、工具等特性有關(guān)系,環(huán)境一遷移導(dǎo)致隨機(jī)無法復(fù)現(xiàn)怎么辦呢。所以我希望有一種原理明明白白的可控隨機(jī)方案,當(dāng)然這里并不是說$random+$urandom不能用哈,實(shí)際上我知道的很多地方就是直接用這個(gè)的。
原理明明白白的可控隨機(jī)方案怎么來呢?既然不信任$random那必然是從$urandom入手看了,還記得$random的問題是啥來著吧?不能感知靜態(tài)模塊的不同例化路徑差異。那么讓他感知一下例化路徑差異不就好了嘛,怎么感知這個(gè)事,相信大家對這個(gè)字符都很熟悉:%m,對就是在打印里用的那個(gè)字符。這個(gè)字符的作用是打印當(dāng)前模塊(或獨(dú)立命名域)的例化路徑,打印里的前半部分就是靠這個(gè)打印出來的:
$display("%m new_rand_value = 'h%0h", new_rand_value);
----->
testbench.u_rand_test1 new_rand_value = 'hd7也就是說,一個(gè)靜態(tài)模塊自身只要例化在了DUT中,那么我們就能夠獲取到其例化路徑,以字符串的方式我們來獲取以下路徑:string   path_str;
initial  begin
  path_str = $psprintf(path_str, "%m");
  $display("inst path is %s", path_str);
end對應(yīng)的打印結(jié)果是:inst path is testbench.u_rand_test0
inst path is testbench.u_rand_test1好的那么也就是說,雖然$urandom不能感知例化路徑,但是模塊自身能夠感知,因此只要把這個(gè)路徑引入到$urandom中去就可以了。這時(shí)有人可能就要問了,%m是字符串,$urandom的種子是數(shù)字啊,這要怎么引進(jìn)去呢?年輕了不是,誰說%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ù)字這不就出來了嘛,不過仔細(xì)觀察一下會(huì)發(fā)現(xiàn),這路徑差異比較近,得出的數(shù)字差異也太小了,這個(gè)數(shù)給到$urandom里去隨機(jī)出來的值別在差不多那就不好了。所以直接把這個(gè)值給$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給一個(gè)初始隨機(jī)值,不同模塊是一樣的沒關(guān)系,這個(gè)值結(jié)合后面路徑的微小差異就可以成就“蝴蝶效應(yīng)”。之后從尾部到頭部路徑字符,因?yàn)椴町惣性谖膊克砸獜奈膊块_始,str.getc(i)獲取字符串對應(yīng)位置字符的ASCII碼,再跟path_int取異或,累計(jì)擴(kuò)大差異性,而后再加入一個(gè)$urandom進(jìn)一步擴(kuò)大差異性。循環(huán)之后得到了擴(kuò)大差異之后的數(shù),此時(shí)最后的結(jié)果可能是負(fù)數(shù),這不是我們所需要的因此通過$abs函數(shù)全部取正。經(jīng)過這些操作后,咱們來看下進(jìn)行隨機(jī)后的結(jié)果:
inst path is testbench.u_rand_test0, path_int = 'h66b884a6
inst path is testbench.u_rand_test1, path_int = 'h49aee46多測試幾個(gè)種子: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對其中的一個(gè)種子結(jié)果進(jìn)行復(fù)現(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)定復(fù)現(xiàn),那么這種方法顯然是符合我們預(yù)期的。其實(shí)后來我想了下,如果想擴(kuò)大差異,是不是多嵌套幾個(gè)$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果然是不行,所以還是用上面提供的方法吧。那么下一個(gè)問題就來了,這么用起來呢,總不能每次需要隨機(jī)時(shí)候都寫這么一大片的代碼吧?答案也很簡單,封裝成一個(gè)函數(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ù)了,難不成每個(gè)要進(jìn)行隨機(jī)的模塊里面都寫一次這個(gè)函數(shù)嗎?當(dāng)然是不需要的,有兩種思路可以處理這個(gè)問題,一是將函數(shù)定義為全局可見的方法,而path_str作為作為函數(shù)的輸入,這樣調(diào)用時(shí)傳參本模塊的path_str就可以了。第二種思路是進(jìn)行宏封裝,我選擇的是第二種思路因?yàn)槲掖_實(shí)不喜歡在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而后在需要進(jìn)行隨機(jī)的模塊里RTL前面的地方找個(gè)犄角旮旯寫一下宏,后面就可以:`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ù),達(dá)到了在靜態(tài)模塊中等價(jià)于$urandom()的可控和差異化隨機(jī)功能。最后再來拓展實(shí)現(xiàn)另外一個(gè)功能,我們想隨機(jī)一個(gè)數(shù)值那是不是經(jīng)常會(huì)使用$urandom_range(),可是在靜態(tài)模塊中怎么做到這一點(diǎn)呢?也很簡單,加一個(gè)宏不就可以了: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ù)、負(fù)數(shù)和浮點(diǎn)數(shù)怎么辦呢?就根據(jù)這套方法來拓展下就可以。

系列文章入口——
【芯片設(shè)計(jì)】SoC 101(一):緒論
【芯片設(shè)計(jì)】FIFO漫談(零)從無處不在的FIFO開始說起
【芯片設(shè)計(jì)】計(jì)算機(jī)體系結(jié)構(gòu)(一)虛擬內(nèi)存
【芯片設(shè)計(jì)】深入理解AMBA總線(零)緒論
【芯片設(shè)計(jì)】握手協(xié)議的介紹與時(shí)序說明
【芯片設(shè)計(jì)】復(fù)位那些小事 —— 復(fù)位消抖
【芯片設(shè)計(jì)】快速入門數(shù)字芯片設(shè)計(jì)(一)Introduction
【芯片驗(yàn)證】UVM源碼計(jì)劃(零)下定決心讀源碼前的自測環(huán)節(jié)
【芯片設(shè)計(jì)】異步電路碎碎念(一) 到底什么是異步電路
【芯片設(shè)計(jì)】從RTL到GDS(一):Introduction
其他文章鏈接——
【芯片驗(yàn)證】sva_assertion: 15道助力飛升的斷言練習(xí)
【芯片驗(yàn)證】可能是RTL定向驗(yàn)證的巔峰之作
【芯片驗(yàn)證】RTL仿真中X態(tài)行為的傳播 —— 從xprop說起
【芯片驗(yàn)證】年輕人的第一個(gè)systemVerilog驗(yàn)證環(huán)境全工程與解析
【芯片設(shè)計(jì)】verilog中有符號(hào)數(shù)和無符號(hào)數(shù)的本質(zhì)探究
【芯片設(shè)計(jì)】論RTL中always語法的消失術(shù)
【芯片設(shè)計(jì)】代碼即注釋,注釋即代碼
【芯片設(shè)計(jì)】700行代碼的risc處理器你確實(shí)不能要求太多了
入職芯片開發(fā)部門后,每天摸魚之外的時(shí)間我們要做些什么呢
如何計(jì)算系統(tǒng)的outstanding 和 burst length?
芯片搬磚日常·逼死強(qiáng)迫癥的關(guān)鍵詞不對齊事件
熟人社會(huì)里,一群沒有社會(huì)價(jià)值的局外人

本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒有賬號(hào)?立即注冊

x
回復(fù)

使用道具

發(fā)表回復(fù)

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則


聯(lián)系客服 關(guān)注微信 下載APP 返回頂部 返回列表