Systemverilog l 1)合并數(shù)組: 存儲方式是連續(xù)的,中間沒有閑置空間。 例如,32bit的寄存器,可以看成是4個8bit的數(shù)據(jù),或者也可以看成是1個32bit的數(shù)據(jù)。 表示方法: 數(shù)組大小和位,必須在變量名前指定,數(shù)組大小必須是【msb:lsb】 Bit[3:0] [7:0] bytes 2)二維數(shù)組和合并數(shù)組識別: 合并數(shù)組: 二維數(shù)組: 位寬在變量名前面,用于識別合并和非合并數(shù)組,位寬在后面,用于識別數(shù)組中元素個數(shù)。 3)非合并數(shù)組 一般仿真器存放數(shù)組元素時使用32bit的字邊界,byte、shortint、int都放在一個字中。 非合并數(shù)組:字的地位存放變量,高位不用。 表示方法: l 隨機事物不確定大小。 使用方法:數(shù)組在開始是空的,同時使用new[]來分配空間,在new[n]指定元素的個數(shù)。 Int dyn[]; l 在隊列中增加或刪除元素比較方便。 l 當你需要建立一個超大容量的數(shù)組。關聯(lián)數(shù)組,存放稀疏矩陣中的值。 表示方法: 采用在方括號中放置數(shù)據(jù)類型的形式聲明: Bit[63:0] assoc[bit[63:0]]; l 1)Verilog 推薦使用文本宏。 好處:全局作用范圍,且可以用于位段或類型定義 缺點:當需要局部常量時,可能引起沖突。 2)Parameter 3)Systemverilog:
l for(int i=0;i<10;i++) array[i] =i; l 1) 區(qū)別: Verilog中task 和function最重要的區(qū)別是:task可以消耗時間而函數(shù)不能。函數(shù)中不能使用#100的延時或@的阻塞語句,也不能調用任務; Systemverilog中函數(shù)可以調用任務,但只能在fork 2)使用: l 作用: 1)類的靜態(tài)變量,可以被這個類的對象實例所共享。 當你想使用全局變量的時候,應該先想到創(chuàng)建一個類的靜態(tài)變量 靜態(tài)變量在聲明的時候初始化。 2) 類的每一個實例都需要從同一個對象獲取信息。 l 作用: 當靜態(tài)變量很多的時候,操作它們的代碼是一個很大的程序,可以用在類中創(chuàng)建一個靜態(tài)方法讀寫靜態(tài)變量,但是靜態(tài)方法不能讀寫非靜態(tài)變量。 l Ref 參數(shù)傳遞為引用而不是復制。Ref比 input 、output、inout更好用。 Function void print_checksum(const ref bit [31:0] a[ ]); 1) 2) 3) l 增加了return語句。Task任務由于發(fā)現(xiàn)了錯誤而需要提前返回,如果不這樣,那么任務中剩下的語句就必須被放到一個else條件語句中。體會下 Task load_array(int len. Ref int array[ ]); If(len<0) //任務中其它代碼 … l Verilog中由于任務中局部變量會使靜態(tài)存儲區(qū),當在多個地方調用同一個任務時,不同線程之間會竄用這些局部變量。 Systemverilog中,module和program塊中,缺省使用靜態(tài)存儲;如果想使用自動存儲,需加入automatic關鍵詞。
l 背景 一個信號可能連接幾個設計層次,如果增加一個信號,必須在多個文件中定義和連接。接口可以解決這些問題。 好處: 如果希望在接口中增加一個信號,不需要改變其他模塊,如TOP模塊。 使用方法: (1)接口中去掉信號的方向類型; (2)DUT 和測試平臺中,信號列表中采用接口名,例化一個名字 注意: 因為去掉了方向類型,接口中不需要考慮方向信號,簡單的接口,可以看做 是一組雙向信號的集合。這些信號使用logic類型[d1] 雙向信號為何可以使用logic呢? 這里的雙向,只是概念上的雙向,不想verilog中databus多驅動的雙向。 雙向信號如何做接口? (1)仲裁器的簡單接口 Interface arb_if( input bit clk); Endinterface DUT 使用接口: Module arb(arb_if arbif); … Always @(posedge arbif.clk or negedge arbif.rst) endmodule (2)DUT 不采用接口,測試平臺中使用接口(推薦) l 背景: 端口的連接方式包含了方向信息,編譯器依次來檢查連續(xù)錯誤;接口使用無信號的連接方式。Modport將接口中信號分組并指定方向。 例子: l 并非接口中每個信號都必須連接。Data總線接口中就解決不了,個人覺得? 因為data是一個雙驅動 l 作用: 一旦定義了時鐘塊,測試平臺就可以采用@arbif.cb等待時鐘,而不需要描述確切的時鐘信號和邊沿,即使改變了時鐘塊中的時鐘或邊沿,也不需要修改測試代碼 應用: 將測試平臺中的信號,都放在clocking 中,并指定方向(以測試平臺為參考的方向)。并且在modprot test(clocking cb, 最完整的接口: Interface arb_if(input bit clk); Logic[1:0] grant,request; Logic rst; Clocking cb @(posedge clk); Endclocking Modport test (clocking cb, Modport dut (input clk, request,rst, 變化:將request 和grant移動到時鐘塊中去了,test中沒有使用了。 l Interface master_if(input bit clk); endinterface program test(master_if mif); initial begin @mif.cb; $display(mif.cb.data); @mif.cb; Mif.cb.data <= 8’h5a; @mif.cb; Mif.cb.data <= ‘z; 注: (1)interface 列表中clk 采用的是input bit clk;為什么要用bit? (2)時鐘塊 clocking cb 中,一般將testbench中需要的信號,方向指定在這里; (3)interface中信號,不一定都用logic,也可采用wire(雙驅動);systemverilog 中如果采用C代碼的風格(參數(shù)列表中方向和類型寫一起),必須采用logic類型 (4)現(xiàn)在的風格,DUT 沒才用clocking cb ,測試平臺和DUT的時鐘如何統(tǒng)一? l DUT和測試平臺之間時序必須密切配合。 l 好的風格: 使用非阻塞賦值可以減少競爭。 systemverilog驗證中initial 中都采用<= 賦值,而等待延遲采用@arbif.cb等待一個周期來實現(xiàn)。 而verilog中采用的風格時,initial 中采用 =阻塞賦值,沿時可以采用#2,等實現(xiàn)。 因此時鐘發(fā)生器,只能放在module 中,而不能放在program中 l 測試平臺可以使用initial 但不能使用always,使用always 模塊不能正常工作。 原因:測試平臺的執(zhí)行過程是進過初始化、驅動和響應等步驟后結束仿真。 如果確實需要一個always塊,可以使用initial forever 來完成。比如:在產生時鐘時。
l 背景: 如果一個變量需要被其他對象所共享,如果沒有OPP,就需要創(chuàng)建全局變量,這樣會污染全局名字空間,導致你想定義局部變量,但變量對每個人都是可見的。 1)作用: 類中static變量,將被這個類的所有實例(對象)所共享,使用范圍僅限于這個類。 例:class transaction; Endclass Trasaction tr1,tr2; Id不是靜態(tài)變量,所以每個trasaction對象都有自己的id;count 是靜態(tài)變量,所有對象只有一個count變量。 如何用? 當你打算創(chuàng)建一個全局變量的時候,首先考慮創(chuàng)建一個類的靜態(tài)變量。 2)static變量的引用 句柄或類名加:: 4) static變量通常在聲明時初始化。不能在構造函數(shù)中初始化,因為每一個新的對象都會調用構造函數(shù)。 l 背景:當類的每一個對象,都需要從同一個對象(另一個類)中獲取信息的時候。如果定義成非靜態(tài)句柄,則每個對象都會有一份copy,造成內存浪費。 l 背景: 當使用更多靜態(tài)變量的時候,操作他們的代碼會很長。 作用: 可以在類中創(chuàng)建一個靜態(tài)方法用于讀寫靜態(tài)變量。 注:systemverilog不允許,靜態(tài)方法讀寫非靜態(tài)變量。 l 背景:解決類太長的問題。類最好控制在一頁內,如果方法很都很長。 l 背景:如果在類很深的底層作用域,卻想引用類一級的對象。在構造函數(shù)中最常見。 作用:this指向類一級變量 l 上限:類不能太大 當類中存在多處相同的代碼,你需要將這段代碼做成當前類的一個成員函數(shù)或父類的成員函數(shù)。 下限:類不能太小 類太小,增加了層次。 方法:如果一個小類只被例化了一次,可以將它合并到父類中去。 l 概念區(qū)分:方法中修改對象 和修改句柄 修改對象——將對象的變量重新賦值。 修改句柄——在任務中new()對象。 1) 當你將對象傳遞給方法 背景:句柄,new()后變成對象,在將其作為參數(shù)傳遞給方法。 實質和作用: 傳遞的是句柄。這個方法可以讀取對象中的值;也以改變對象中的值 2) 修改標量變量的值 背景:在方法的參數(shù)中,前面加ref;(用ref傳遞,ref傳遞的是變量的地址)。 作用: 引申: 方法可以改變對象,即使沒有使用ref 修飾句柄。 例: Task endtask trancation t; initilal beign t = new(); t.addr = 42; transmit(t); end 3) 在任務中修改句柄 背景: 在方法中,參數(shù)為句柄,前面加ref。 作用: 可以在方法中new()對象,并將初始化放在方法中;在主程序中僅僅調用。 注意:正確的事物發(fā)生器,參數(shù)是帶ref的句柄 Function void create(ref transaction tr) Endfunction 方法的參數(shù)是句柄,句柄前有ref 和沒ref的差別: 沒ref,在方法中不能new()該句柄的對象,因為沒ref,句柄是不能傳遞到主程序的; 有ref,可以在方法中new()該句柄的對象。 例子: Function void create( Transcation tr) Endfunction Transcation t; Initial begin $diasplay (t.addr); End l 背景: 應該在循環(huán)中,new()多個對象,而不是先new()對象再循環(huán)發(fā)送事物。 作用: 創(chuàng)建多個對象 正確產生器,創(chuàng)建多個對象: Task generator (int n); Transaction t; Repeat(n) begin 將new()放在循環(huán)內,這樣創(chuàng)建了許多對象。 l 目的:防止對象的方法修改原始對象的值?;蛟谝粋€發(fā)生器中保留約束。 分兩種情況,類中不包含其他類的句柄和包含 方法: 1) Transaction src,dst; Src = new() dst = new src 局限: 如果類中包含一個指向另外一個類的句柄,那么只有最高一級的對象被new復制,下層的對象都不會被復制。 會出現(xiàn)意想不到的錯誤。 當前類中變量和句柄被復制,這樣兩個對象,都有指向另外一個類的對象statistic(會帶來意想不到的錯誤),但是statistic沒有被復制。如果其中一個transaction對象,修改了statistic對象值,會影響到另一個transaction看到static的值。 2) 簡單的復制函數(shù) 如何實現(xiàn): Copy函數(shù)一般放在類內部,函數(shù)名為該類的一個句柄,copy函數(shù)中new()對象。 局限: 類中不包含其他類。 3) 深層的復制函數(shù) ——深層copy 目的:解決類中包含另外一個類,copy帶來的問題。 實現(xiàn): 在copy函數(shù)中,將調用另一個類的copy函數(shù),賦值給該句柄;同時需要為statistic類和層次結構中每一個類增加一個copy()方法;copy函數(shù)的ID域也要保持一致,copy函數(shù),copy本類,所以ID也要++. Id =count++;
l 1)dist權重分布 dist帶有一個值的列表及相應的權重,中間用:= 或 :/分開。值或權重可以是常量或變量。權重的和不必是100. 2)Inside 產生一個值的集合,在值的集合中取隨機值時,機會相等。 3)在集合中使用數(shù)組 l Systemverilog支持兩種關系操作 –>和if—else —>可產生和case效果類似的語句塊,可以用于枚舉類型的表達式。 l l 作用:可以打開或關閉某個約束 可以使用內建的Handle.constraint.constraint_mode()打開或關閉。 l 背景:很多測試只會在代碼的一個地方隨機化對象,但是約束越來越復雜時, Systemverilog可以使用randomized with 來增加額外的約束,這和在類里增加的約束是等效的。 l 有時候需要再調用randomize()之前或之后立即執(zhí)行一些操作。 隨機化前:設置類里的一些非隨機變量(如上下限、權重), 隨機化后:計算數(shù)據(jù)的誤差矯正值。 l 1) 2) 如果一套約束在已產生了幾乎所有想要的激勵向量,但還缺少幾種。 可以使用rand_mode把這些變量設置為非隨機變量。 l Systemverilog可以用foreach對數(shù)組中的每一個元素進行約束。
l Systemverilog引入兩種新的創(chuàng)建線程的方法—fork…join_none和fork…join_any 1) 使用fork…join_none來產生線程 在調度其內部語句時,父線程繼續(xù)執(zhí)行。 2) 使用fork…join_any實現(xiàn)線程同步 在調度塊內語句,當?shù)谝粋€語句執(zhí)行完,父線程才繼續(xù)執(zhí)行。 l Systemverilog中可以動態(tài)創(chuàng)建線程。 用法: fork…join_none放在了任務中,而不是包含兩個線程。 原因: 主程序中有連個線程:發(fā)送和檢測線程。但是不能同時啟動,發(fā)送事物后,才能檢測,否則還未產生數(shù)據(jù),就開始檢測;但是檢測又不能阻塞下一次發(fā)送事物的線程。所以fork…join_none 放在了檢測task 任務(后作用的線程中)中, 例:測試平臺產生隨機事物并發(fā)送到DUT中,DUT把事物返回到測試平臺。測試平臺必須等到事物完成,但同時不希望停止隨機事物的發(fā)送。 Program automatic test(bus_ifc.Tbbus); Task Fork Join_noe Endtask Initial begin #100; End endprogram l l l 1) 使用fork ..join_any 后加disable。 3) 停止多個線程 Disable fork 應該使用fork ..join 把目標代碼包含起來,以限制Disable fork的作用范圍。 l 背景: Verilog中當一個線程在一個事件上發(fā)生阻塞的同時,正好另一個線程觸發(fā)了這個事件,則競爭就出現(xiàn)了。如果觸發(fā)線程先于阻塞線程,則觸發(fā)無效(觸發(fā)是一個零寬度的脈沖)。 解決方法: Systemverilog 引入了triggered()函數(shù),用于檢測某個事件是否已被觸發(fā)過,包括正在觸發(fā)。線程可以等待這個結果,而不用在@操作符上阻塞。 例子: Event e1,e2; Initial begin End Initial begin End 上面的代碼,假設先執(zhí)行第一個塊,再執(zhí)行第二個塊。第一個塊會阻塞在@e2(阻塞先執(zhí)行),直到e2觸發(fā),再運行(觸發(fā)后執(zhí)行);在執(zhí)行第二個塊時,會阻塞在@e1,但是e1已經(jīng)觸發(fā)(觸發(fā)先執(zhí)行,阻塞后執(zhí)行,觸發(fā)是個零寬度的脈沖,會錯過第一個事件而鎖?。?/p> 解決方法:用wait(e1.triggered())來代替阻塞@el,如果先觸發(fā),也可以執(zhí)行。 l 最好的辦法是:采用線程計數(shù)器來等待多個線程。 l Get()可以獲取一個或多個鑰匙,put()可以返回一個或多個鑰匙。Try_get()獲取一個旗語而不被阻塞。 l 背景:如何在兩個線程中傳遞信息?考慮發(fā)生器需要創(chuàng)建很多事物并傳遞給驅動器的情況。 問題:如果使用發(fā)生器的線程去調用驅動器的任務。這樣,發(fā)生器需要知道驅動器的層次化路徑(類的層次化),降低了代碼的可重用性;還迫使發(fā)生器和驅動器同一速率運行,當一個發(fā)生器需控制多個驅動器時會發(fā)生同步問題。 解決辦法:把驅動器和發(fā)生器當成各個處理事物的對象,之間通過信道交換數(shù)據(jù)。信道允許驅動器和發(fā)生器異步操作;引入問題:你可能傾向于僅僅使用一個共享的數(shù)據(jù)或隊列,但這樣,編寫實現(xiàn)線程間的讀寫和阻塞代碼會很困難。解決辦法:可以使用systemverilog中的信箱。把信箱看出一個具有源端和收端的FIFO. 1)信箱的容量可以指定,new(size),size限制信箱中的條目,size為0,或沒指定,則信箱是無限大。 2)Put()放數(shù)據(jù),get()可以移出數(shù)據(jù)。Peek()可以獲取信箱中數(shù)據(jù)的copy而不移出。 3)信箱中可以放句柄,而不是對象。 漏洞:在循環(huán)外只創(chuàng)建一個對象,然后使用循環(huán)對對象隨機化,信箱中是句柄,最終得到的是一個含有多個句柄的信箱,多個句柄都指向同一個對象。 解決辦法:在循環(huán)中,創(chuàng)建多個對象。 l 背景: 很多情況下,由信箱連接的兩個線程應該步調一致,這樣生產方 才不至于跑到消費方前。 好處:最好層的generator需要等待低層的數(shù)據(jù)發(fā)完后才能結束。測試平臺能精確知道所有激勵發(fā)出去的時間。 兩種情況 兩個線程同步,需要額外的握手信號。否則,出現(xiàn)生產方運行到結束,消費方還啟動。 1) 因阻塞,連個線程不需要握手信箱 3) 需要使用握手信號,以使producer不超前于consumer;如果consumer超前于prodecer會阻塞。 解決辦法 1) 消費方:consumer 使用信箱方法peek()獲取信箱里的數(shù)據(jù)的copy而不將其移出,當consumer處理完數(shù)據(jù)后,便使用get()移出數(shù)據(jù)。 特點:信箱容量定義為1,不需要握手信號。 Calss endcalss 如果直接使用get()替代peek(),那么事務會被立刻移出,這樣可能會在consumer完成事務前,producer生成新的數(shù)據(jù)。 - 2)使用信箱和事件實現(xiàn)線程同步 局限:如果遇到producer線程的阻塞和consumer線程的觸發(fā)同時發(fā)生,則可能出現(xiàn)次序上的問題。 3)使用兩個信箱實現(xiàn)線程同步 使用另一個信箱把consumer的完成信息發(fā)回給producer。 目的:在producer線程中,處理完事物后,用一個get()來阻塞。 特點:信箱容量大于1. Maibox Class prodecer For(int i=0; i<4;i++) begin …. Mbx.put(i); Rtn.get(i); End Endclass Class consumer Repeat(3) begin …. Mbx.get(i); Rtn.put(-i); End Endclass 說明:信箱的構造函數(shù)中Mbx =new();Rtn =new(),信箱容量為無窮大。如何實現(xiàn)同步? 雖然信箱容量為無窮大,producer線程發(fā)完一個數(shù)據(jù)后遇到get()會阻塞,不能放入第二個數(shù)據(jù);等到consumer得到第一個數(shù)據(jù)并且處理完后,通過另一個信箱返回一個數(shù)據(jù),producer才繼續(xù)放第二個數(shù)據(jù)。 因為get()得到數(shù)據(jù)后,將信箱中數(shù)據(jù)取出。表象:信箱容量定義為無窮大,但是實際上也是producer放一個數(shù)據(jù),consumer取一個數(shù)據(jù);然后producer再放第二個數(shù)據(jù),依次類推。 這樣確保producer不會超前于consumer線程,而將數(shù)據(jù)都寫入信箱。 4) 其他的同步技術 通過變量或旗語阻塞也可以實現(xiàn)握手。事件是最簡單的結構,其次是通過變量阻塞。旗語相當于第2個信箱,但是沒有交換信息。Systemverilog中的信箱比其他技術要差,原因是無法在producer放入第一個事務時,讓它阻塞。Producer一直比consumer提前一個事務的時間。 l 1) 2) 3) 注意事項: 1) 2)
l 背景: 為總線事務增加一個錯誤功能并帶可變延時的復雜類。方法如下: 1) 2)使用擴展類 作用: 當需要增加事務,而對現(xiàn)有的測試代碼修改越少越好,。例如增加錯誤注入功能。 擴展類和類合成區(qū)別: 擴展類解決,增加新事務,使用類合成中,大量修改代碼的麻煩。 如何使用: 擴展類共享基類的變量和子程序。 1)基本類中的方法,需標記為virtual,這樣擴展類中才可以重新定義。擴展類中函數(shù),和基類中函數(shù)名一樣時,通過supper.函數(shù)名,調用基類中函數(shù)。Systemverilog中不允許supper.supper.new方式經(jīng)行多層調用。 2)如果基類構造函數(shù)new()有參數(shù),那么擴展類,必須有一個構造函數(shù),并在構造函數(shù)的第一行調用基類的構造函數(shù)。 class endclass 3)OPP規(guī)則指出:基類的句柄,也可以指向擴展類的對象。(好好體會) l 1)背景:一個簡單的發(fā)生器,通過信箱將數(shù)據(jù)傳遞給驅動器。 class generator mailbox transaction tr; function new(input mailbox gen2drv) endfunction task run; endtask 存在問題:這個例子在循環(huán)內部創(chuàng)建事務對象,而不是在循環(huán)外部,避免了測試平臺常見的錯誤。New()放在循環(huán)外部,錯誤原因是,mailbox中放入的是句柄,而不能是對象,所有的句柄都指向同一個對象。(1)任務Run創(chuàng)建了一個事物并立即隨機化,意味著事務使用了默認的所有約束。要修改,必須要修改transaction類。(2)無法使用擴展 解決辦法:將tr的創(chuàng)建和初始化分開,使用藍圖模式。 另一個問題:如果簡單的把創(chuàng)建和初始化分開,而放在循環(huán)外部,而避免測試平臺錯誤(P200),如何解決?藍圖模式如何解決 2)藍圖模式概念: 首先構建一個對象藍圖(金屬模),然后修改它的約束,甚至可以用擴展對象替換它,隨機化這個藍圖時,就得到想賦予的隨機值;然后復制這個對象,將copy發(fā)給下游。 藍圖:是一個鉤子,允許你改變發(fā)生器類的行為而無需修改其類代碼。藍圖對象在一個地方構建(new()),在另一個地方(任務run)使用 3)P200與P221相對比分析:重要 藍圖模式,也就比new()在循環(huán)外地generator多了一個copy函數(shù)。問題(1)藍圖模式,new()在循環(huán)外,也只有一個對象,而mailbox中放入的只能是句柄,如何解決常見的平臺錯誤? (2)藍圖模式下,因為只有一個ID號,那么任務run循環(huán)中,下發(fā)了許多數(shù)據(jù),這些只有一個ID號了? l 為了注入錯誤,需要將藍圖對象transaction變成Badtransaction(改變藍圖)。必須在環(huán)境的創(chuàng)建和運行階段之間完成這個操作。注意:所有的badTr引用都在這一個文件中,這樣就不需要改變environment類或generator類。 Env.build(); Begin End Env.run 目的是:將一個對象取代另一個對象。New()后都是對象了,將對象賦值給對象,這是什么寫法?不是復制呀?復制本質是將一個句柄指向一個對象。 解釋:上述是句柄的復制,將擴展類句柄bad賦值給基類句柄blueprint,這樣基類句柄指向擴展類對象,后面的代碼調用的時候,就直接指向擴展類bad了,改變了藍圖。 l Env.new()僅僅new()函數(shù) nev.build()是將各個模塊new(),并傳達一些參數(shù),通過這些參數(shù)將環(huán)境的各個模塊,連接起來。P213 l 背景:基類句柄可以指向擴展類對象,不需要額外的代碼; 擴展類句柄指向基類對象,一般情況下會出錯,但有時候是可以的,前提是基類句柄指向了它的一個擴展類對象。 作用:擴展類句柄指向基類對象時,使用$cast()函數(shù)。在非法的情況下,不會編譯報錯,會返回了一個0. $cast做任務使用時,systemverilog會在運行時,檢查源對象類型和目的對象類型不匹配,會報錯; $cast 做函數(shù)使用時,運行時,仍做類型檢查,在不匹配時,不會報錯,$函數(shù)返回0. 前面所述:基類句柄可以指向任何它的擴展類的對象、 1) 基類句柄指向擴展類對象——出現(xiàn)情況:修改藍圖,不改過多代碼,增加功能 Transaction tr; //基類句柄 BadTr Bad = new(); Tr tr.display; 2) 擴展類句柄指向基類對象——出現(xiàn)情況:基類virtual 方法copy函數(shù),它的繼承類中copy函數(shù) 將基類句柄賦值給擴展類句柄,使擴展類句柄指向基類對象,一般編譯器會出錯,不能運行,所以非常小心;只有基類句柄指向擴展類對象時,再將擴展類句柄指向基類對象時,不出錯。為了檢測基類句柄是否指向了擴展對象,并且不讓編譯器報錯,可以使用$cast()函數(shù)檢測。 當把擴展類句柄指向基類對象時,發(fā)生什么? Tr= new(); Bad = tr; 上述會發(fā)生錯誤,編譯不會被通過。因為有些屬性在基類中不存在;但是擴展類句柄指向基類句柄不總是非法的(見下面代碼,是可以的),當基類句柄指向一個擴展類對象時是允許的。 l 個人理解: Transaction tr; 句柄類型:關鍵字 對象類型:類中成員的類型差異 l 多態(tài):多個程序使用一個共同的名字的現(xiàn)象。 多態(tài)解決問題:計算機建構面臨的一個問題。讓物理內存很小的情況下,讓處理器能夠對很大的地址空間尋址。針對這個問題引入了虛擬內存。 虛擬方法繼承劣勢: 基類使用了虛擬方法,擴展類也必須使用相同的“簽名”,擴展類中虛擬子程序不能增加或刪除參數(shù),這意味著必須提前做好規(guī)劃。 l 1) 但是要copy的是badtr類型的,所以要new一個bad 帶有copy 的事物基類。 Class transaction ; Rand bit[31:0] src,dst,data[8]; Bit[31:0] crc; Virtual function transaction copy (); Endfunction Endclass 帶有copy的擴展類 Calss badtr extends transaction Virtual function badtr copy(); Virtual function transaction copy(); Return Rendfunction 2)優(yōu)化途徑一,創(chuàng)建一個獨立的函數(shù)copy_data,這樣每個類只負責copy其局部變量,即擴展類中的copy函數(shù)用super.copy_data(tr),代替了基類中變量的復制。代碼的重用性提高。P8.22 $cast(bad,tr); 使用的情況: 因為virtual 函數(shù),在繼承中,虛擬函數(shù)必須和基類中名稱和參數(shù)也一致。這樣擴展類中copy_data函數(shù)參數(shù)仍然是transaction類型的tr,這樣出現(xiàn)了參數(shù)是基類句柄,但是copy_data函數(shù)內要作的確實擴展類的成員,就要將基類句柄參數(shù)賦值給擴展類句柄, 要將擴展類badtr類型的數(shù)據(jù)返回,所以必須用$cast(bad,tr)。 2) l 背景:驗證的目標之一是創(chuàng)建多個項目共享的代碼。 目的:systemverilog 有兩種方法創(chuàng)建共享的基類:抽象類和純虛方法 Virtual class (抽象類):可以被擴展但是不能被直接例化。 Pure 1) 2) l 背景:測試平臺目的:創(chuàng)建一個不做任何修改就能在所有測試中使用的驗證環(huán)境。要做到這點的關鍵是測試平臺使用鉤子,(什么是鉤子?)鉤子作用,在不修改原始類的情況下注入新的代碼。采用virtual 方法,也可以在擴展類中覆蓋基類方法,但是需要重復原方法的所有代碼,并且它的修改將傳播到它的所有擴展類中。 作用:回調就是一個鉤子,在不修改原始類的情況下注入新的代碼。 實現(xiàn):回調任務在頂層中創(chuàng)建,在最低級即驅動器中調用。這樣驅動器不需要知道測試的任何信息,它只需要使用一個可以在測試中擴展的通用類。 1) 回調的一個常見用法就是注入干擾,例如引入一個錯誤或者延遲。下面測試平臺使用回調對象,隨機地丟棄數(shù)據(jù)包。 擴展類是如何作用的?在擴展的回調類中注入錯誤,如何在驅動器中作用的? 關鍵是數(shù)據(jù)隊列的作用,驅動器中使用了,回調基類的數(shù)據(jù)隊列 回調基類是抽象類,在擴展的回調類中加入錯誤注入,而drive驅動類中,是回調基類的數(shù)據(jù)隊列,在環(huán)境中將擴展類句柄讓入驅動器類,回調基類的數(shù)據(jù)隊列中。 Driver_cbs_drop dcd = new(); env.drv.cbs.push_back(dcd); // Put into driver end 與前面擴展類作用的差異? 前面代碼,要使擴展類中增加代碼,需要使基類句柄指向擴展類句柄。 l 下面的代碼如何解釋 |
|
來自: 小櫻業(yè)余書吧 > 《技術類》