一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

systemverilog 總結

 小櫻業(yè)余書吧 2013-07-05

Systemverilog
數(shù)據(jù)類型

      合并數(shù)組和非合并數(shù)組

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ù)組:  bit [3:0] [7:0] arrys;   大小在變量名前面放得,且降序

二維數(shù)組:  int arrays[0:7] [0:3] ;  大小在變量名后面放得,可降序可升序

位寬在變量名前面,用于識別合并和非合并數(shù)組,位寬在后面,用于識別數(shù)組中元素個數(shù)。

 

3)非合并數(shù)組

一般仿真器存放數(shù)組元素時使用32bit的字邊界,byte、shortint、int都放在一個字中。

非合并數(shù)組:字的地位存放變量,高位不用。

表示方法:

 Bit   [7:0] bytes; 

       4)合并數(shù)組和非合并數(shù)組的選擇

        (1)當需要以字節(jié)或字為單位對存儲單元操作。

        (2)當需要等待數(shù)組中變化的,則必須使用合并數(shù)組。例如測試平臺需要通過存儲器數(shù)據(jù)的變化來喚醒,需要用到@,@只能用于標量或者合并數(shù)組。

      Bit[3:0] [7:0] barray[3]  ; 表示合并數(shù)組,合并數(shù)組中有3個元素,每個元素時8bit,4個元素可以組成合并數(shù)組

     可以使用barry[0]作敏感信號。

 

      動態(tài)數(shù)組

隨機事物不確定大小。

使用方法:數(shù)組在開始是空的,同時使用new[]來分配空間,在new[n]指定元素的個數(shù)。

Int dyn[];

   Dyn = new[5];     //分配5個元素空間

          Dyn.delete() ;     //釋放空間

      隊列

在隊列中增加或刪除元素比較方便。

      關聯(lián)數(shù)組

當你需要建立一個超大容量的數(shù)組。關聯(lián)數(shù)組,存放稀疏矩陣中的值。

表示方法:

采用在方括號中放置數(shù)據(jù)類型的形式聲明:

Bit[63:0] assoc[bit[63:0]];

 

      常量:

1)Verilog 推薦使用文本宏。

好處:全局作用范圍,且可以用于位段或類型定義

缺點:當需要局部常量時,可能引起沖突。

2)Parameter

   作用范圍僅限于單個module

3)Systemverilog:

   參數(shù)可以在多個模塊里共同使用,可以用typedef 代替單調乏味的宏。

 


過程語句

      可以在for循環(huán)中定義變量,作用范圍僅在循環(huán)內部

for(int i=0;i<10;i++)

array[i] =i;

 

      任務、函數(shù)及void函數(shù)

1) 區(qū)別:

Verilog中task 和function最重要的區(qū)別是:task可以消耗時間而函數(shù)不能。函數(shù)中不能使用#100的延時或@的阻塞語句,也不能調用任務;

Systemverilog中函數(shù)可以調用任務,但只能在fork  joinnone生成的線程中。

2)使用:

   如果有一個不消耗時間的systemverilog任務,應該把它定義成void函數(shù);這樣它可以被任何函數(shù)或任務調用。

  從最大靈活性角度考慮,所有用于調用的子程序都應該被定義成函數(shù)而非任務,以便被任何其它任務或函數(shù)調用。(因為定義成任務,函數(shù)調用任務很有限制)

 

      類靜態(tài)變量

作用:

1)類的靜態(tài)變量,可以被這個類的對象實例所共享。

當你想使用全局變量的時候,應該先想到創(chuàng)建一個類的靜態(tài)變量

靜態(tài)變量在聲明的時候初始化。

2)

類的每一個實例都需要從同一個對象獲取信息。

      靜態(tài)方法

作用:

當靜態(tài)變量很多的時候,操作它們的代碼是一個很大的程序,可以用在類中創(chuàng)建一個靜態(tài)方法讀寫靜態(tài)變量,但是靜態(tài)方法不能讀寫非靜態(tài)變量。

      ref高級的參數(shù)類型

Ref 參數(shù)傳遞為引用而不是復制。Ref比 input 、output、inout更好用。

Function void print_checksum(const ref bit [31:0] a[ ]);

1)       也可以不用ref進行數(shù)組參數(shù)傳遞,這時數(shù)組會被復制到堆棧區(qū),代價很高。

2)       用帶ref 進行數(shù)組參數(shù)傳遞,僅僅是引用,不需要復制;向子程序傳遞數(shù)組時,應盡量使用ref以獲得最佳性能,如果不希望子程序改變數(shù)組的值,可以使用const ref。

3)       Ref參數(shù),用ref 傳遞變量;可以在任務里修改變量而且,修改結果對調用它的函數(shù)可見,相對于指針的功能。

 

      Return語句

增加了return語句。Task任務由于發(fā)現(xiàn)了錯誤而需要提前返回,如果不這樣,那么任務中剩下的語句就必須被放到一個else條件語句中。體會下

Task load_array(int len. Ref int array[ ]);

If(len<0)  begin

  $display(“Bad len”);

  Returun;

//任務中其它代碼

    endtask

 

      局部數(shù)據(jù)存儲 automatic作用

Verilog中由于任務中局部變量會使靜態(tài)存儲區(qū),當在多個地方調用同一個任務時,不同線程之間會竄用這些局部變量。

Systemverilog中,module和program塊中,缺省使用靜態(tài)存儲;如果想使用自動存儲,需加入automatic關鍵詞。

 


測試平臺

      Interface

背景  

一個信號可能連接幾個設計層次,如果增加一個信號,必須在多個文件中定義和連接。接口可以解決這些問題。

好處:

如果希望在接口中增加一個信號,不需要改變其他模塊,如TOP模塊。

使用方法:

(1)接口中去掉信號的方向類型;

(2)DUT 和測試平臺中,信號列表中采用接口名,例化一個名字

注意:

因為去掉了方向類型,接口中不需要考慮方向信號,簡單的接口,可以看做

是一組雙向信號的集合。這些信號使用logic類型[d1] 

雙向信號為何可以使用logic呢?

這里的雙向,只是概念上的雙向,不想verilog中databus多驅動的雙向。

雙向信號如何做接口?

 

(1)仲裁器的簡單接口

Interface arb_if( input bit clk);

     Logic [1:0] grant,request;

     Logic rst;

Endinterface

DUT 使用接口:

Module arb(arb_if arbif);

Always @(posedge arbif.clk or negedge arbif.rst)

  …

endmodule

 

(2)DUT 不采用接口,測試平臺中使用接口(推薦)

    DUT 中源代碼不需要修改,只需要再top中,將接口連接到端口上。

   Module top;

       Bit clk;

       Always #2 clk =~clk;

      Arb_if arbif(clk);

      Arb_port al(.grant(arbif.grant),

                .request(arbif.grant),

                .rst(arbif.rst),

                .clk(arbif.clk)

                );

      Test t1(arbif);

   Endmodule

 

      Modport

背景:

端口的連接方式包含了方向信息,編譯器依次來檢查連續(xù)錯誤;接口使用無信號的連接方式。Modport將接口中信號分組并指定方向。

例子:

      在總線設計中使用modport

并非接口中每個信號都必須連接。Data總線接口中就解決不了,個人覺得?

因為data是一個雙驅動

 

      時鐘塊

作用:

一旦定義了時鐘塊,測試平臺就可以采用@arbif.cb等待時鐘,而不需要描述確切的時鐘信號和邊沿,即使改變了時鐘塊中的時鐘或邊沿,也不需要修改測試代碼

應用:

將測試平臺中的信號,都放在clocking 中,并指定方向(以測試平臺為參考的方向)。并且在modprot test(clocking cb,

 

 

 

最完整的接口:

Interface arb_if(input bit clk);

Logic[1:0] grant,request;

Logic rst;

 

Clocking cb @(posedge clk);

    Output request;

    Input grant;

Endclocking

 

Modport test (clocking cb,

           Output rst);

Modport dut (input clk, request,rst,

           Output grant);

   endinterface

 

變化:將request 和grant移動到時鐘塊中去了,test中沒有使用了。

 

      接口中的雙向信號

Interface master_if(input bit clk);  //在類中為了,不使用有符號數(shù),常用bit[]定義變量

   Wire [7:0] data;

  Clocking cb@(posedge clk);

     Inout data;

  Endclocking

 Modport TEST(clocking cb);

endinterface

 

program test(master_if mif);

initial begin

   mif.cb.data <= ‘z;

@mif.cb;

$display(mif.cb.data);     //總線中讀數(shù)據(jù)

@mif.cb;

Mif.cb.data <= 8’h5a;     //驅動總線

@mif.cb;

Mif.cb.data <= ‘z;        //釋放總線

 

注:

(1)interface 列表中clk 采用的是input bit clk;為什么要用bit?

(2)時鐘塊 clocking cb 中,一般將testbench中需要的信號,方向指定在這里;

 而在modprot 指定test信號方向的時候,采用clocking cb。

(3)interface中信號,不一定都用logic,也可采用wire(雙驅動);systemverilog

中如果采用C代碼的風格(參數(shù)列表中方向和類型寫一起),必須采用logic類型

(4)現(xiàn)在的風格,DUT 沒才用clocking cb ,測試平臺和DUT的時鐘如何統(tǒng)一?

      激勵時序

DUT和測試平臺之間時序必須密切配合。

 

      測試平臺和設計間的競爭狀態(tài)

好的風格:

使用非阻塞賦值可以減少競爭。

systemverilog驗證中initial 中都采用<= 賦值,而等待延遲采用@arbif.cb等待一個周期來實現(xiàn)。

而verilog中采用的風格時,initial 中采用 =阻塞賦值,沿時可以采用#2,等實現(xiàn)。

因此時鐘發(fā)生器,只能放在module 中,而不能放在program中

 

 

      Program中不能使用always塊

測試平臺可以使用initial 但不能使用always,使用always 模塊不能正常工作。

原因:測試平臺的執(zhí)行過程是進過初始化、驅動和響應等步驟后結束仿真。

如果確實需要一個always塊,可以使用initial forever 來完成。比如:在產生時鐘時。

 

 



      類中static變量

背景:

如果一個變量需要被其他對象所共享,如果沒有OPP,就需要創(chuàng)建全局變量,這樣會污染全局名字空間,導致你想定義局部變量,但變量對每個人都是可見的。

1)作用:

類中static變量,將被這個類的所有實例(對象)所共享,使用范圍僅限于這個類。

例:class transaction;

        Static int count=0;

          Int id;

Endclass

Trasaction tr1,tr2;

Id不是靜態(tài)變量,所以每個trasaction對象都有自己的id;count 是靜態(tài)變量,所有對象只有一個count變量。

如何用?

當你打算創(chuàng)建一個全局變量的時候,首先考慮創(chuàng)建一個類的靜態(tài)變量。

2)static變量的引用

句柄或類名加::

4)       static 變量的初始化

static變量通常在聲明時初始化。不能在構造函數(shù)中初始化,因為每一個新的對象都會調用構造函數(shù)。

 

      靜態(tài)句柄:

背景:當類的每一個對象,都需要從同一個對象(另一個類)中獲取信息的時候。如果定義成非靜態(tài)句柄,則每個對象都會有一份copy,造成內存浪費。

 

      靜態(tài)方法

背景:

當使用更多靜態(tài)變量的時候,操作他們的代碼會很長。

作用:

可以在類中創(chuàng)建一個靜態(tài)方法用于讀寫靜態(tài)變量。

注:systemverilog不允許,靜態(tài)方法讀寫非靜態(tài)變量。

      類之外的方法

背景:解決類太長的問題。類最好控制在一頁內,如果方法很都很長。

      This

背景:如果在類很深的底層作用域,卻想引用類一級的對象。在構造函數(shù)中最常見。

作用:this指向類一級變量

      如何做類,類做多大?

上限:類不能太大

當類中存在多處相同的代碼,你需要將這段代碼做成當前類的一個成員函數(shù)或父類的成員函數(shù)。

下限:類不能太小

類太小,增加了層次。

方法:如果一個小類只被例化了一次,可以將它合并到父類中去。

 

      動態(tài)對象

概念區(qū)分:方法中修改對象 和修改句柄

修改對象——將對象的變量重新賦值。

修改句柄——在任務中new()對象。

 

1) 當你將對象傳遞給方法

背景:句柄,new()后變成對象,在將其作為參數(shù)傳遞給方法。

實質和作用:

傳遞的是句柄。這個方法可以讀取對象中的值;也以改變對象中的值

 

2) 修改標量變量的值

背景:在方法的參數(shù)中,前面加ref;(用ref傳遞,ref傳遞的是變量的地址)。

作用:

 方法可以修改變量的值,并將修改的值,傳遞給主程序。

 

 

引申:

方法可以改變對象,即使沒有使用ref 修飾句柄。

    因為傳遞的是句柄,句柄是地址。不要將句柄和對象混為一談,如果傳遞的是對象,對象是單向的,那方法以外也不能傳遞回來。可以這樣理解吧。

   讀寫對象中的值:

例:

Task  transmit(Transcation t);

     Cbbus.rx_data <= t.data;

     t.stats.startT  =  $time;     //在任務中,改變了對象

endtask

 

trancation t;

initilal beign

t = new();

t.addr = 42;

transmit(t);

end

 

  既然傳遞的是句柄,那數(shù)據(jù)就沒傳過去,如何讀取值?

   答:主程序中new()創(chuàng)建了一個對象,而句柄是指向對象的指針,傳遞的是句柄,transmit中也指向了對象,所以transmit中可以讀寫對象。

  

3) 在任務中修改句柄

背景:

在方法中,參數(shù)為句柄,前面加ref。

作用:

可以在方法中new()對象,并將初始化放在方法中;在主程序中僅僅調用。

注意:正確的事物發(fā)生器,參數(shù)是帶ref的句柄

Function void create(ref transaction tr)

Endfunction

 

 

 

方法的參數(shù)是句柄,句柄前有ref 和沒ref的差別:

沒ref,在方法中不能new()該句柄的對象,因為沒ref,句柄是不能傳遞到主程序的; 有ref,可以在方法中new()該句柄的對象。

  原因:沒ref傳遞的是句柄,不能修改句柄,有ref,傳遞的是句柄的地址,可以修改句柄。

 

例子:

Function void create( Transcation tr)

   tr = new();    不正確

   tr.addr = 42;

Endfunction

 

Transcation t;

Initial begin

  Create(t);

$diasplay (t.addr);

End

      程序中修改對象

背景:

應該在循環(huán)中,new()多個對象,而不是先new()對象再循環(huán)發(fā)送事物。

作用:

創(chuàng)建多個對象

正確產生器,創(chuàng)建多個對象:

Task generator (int n);

Transaction t;

Repeat(n) begin

   t=new();

   t.addr =$random();

   transmit(t);

    endtask

 

將new()放在循環(huán)內,這樣創(chuàng)建了許多對象。

 

      對象的復制

目的:防止對象的方法修改原始對象的值?;蛟谝粋€發(fā)生器中保留約束。

分兩種情況,類中不包含其他類的句柄和包含

方法:

1)  使用new復制一個對象——簡易復制(shallow copy)

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也要++.

 Copy.stats = stats.copy();

Id =count++;

 

 


約束

      約束塊中,只能包含表達式,不能賦值。

1)dist權重分布

dist帶有一個值的列表及相應的權重,中間用:= 或 :/分開。值或權重可以是常量或變量。權重的和不必是100.

   := 表示范圍內,每一個值的權重是相同的;

   :/ 表示范圍內,權重要均勻分布

2)Inside

產生一個值的集合,在值的集合中取隨機值時,機會相等。

3)在集合中使用數(shù)組

        條件約束

Systemverilog支持兩種關系操作 –>和if—else

—>可產生和case效果類似的語句塊,可以用于枚舉類型的表達式。

        雙向約束

        控制多個約束塊

作用:可以打開或關閉某個約束

可以使用內建的Handle.constraint.constraint_mode()打開或關閉。

        內嵌約束

背景:很多測試只會在代碼的一個地方隨機化對象,但是約束越來越復雜時,

Systemverilog可以使用randomized with 來增加額外的約束,這和在類里增加的約束是等效的。

        Pre_randomize 和post_randomize函數(shù)

有時候需要再調用randomize()之前或之后立即執(zhí)行一些操作。

隨機化前:設置類里的一些非隨機變量(如上下限、權重),

隨機化后:計算數(shù)據(jù)的誤差矯正值。

        約束的技巧

1)  約束中使用變量

2)  使用非隨機值

如果一套約束在已產生了幾乎所有想要的激勵向量,但還缺少幾種。

可以使用rand_mode把這些變量設置為非隨機變量。

        數(shù)組約束

Systemverilog可以用foreach對數(shù)組中的每一個元素進行約束。

 


線程及線程間的通信

        測試平臺使用許多并發(fā)執(zhí)行的線程。測試平臺隸屬于程序塊。

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í)行。

 

        動態(tài)線程

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  check_trans(Transaction tr);

Fork

     Begin

      Wait(bus.cb.addr == tr.addr);

     End

Join_noe

Endtask

 

Initial begin

    Repreat(10)  begin

        Tr= new();

        Assert.(tr.randomize());

        //把事物發(fā)送到DUT中

        Transmit(tr);

       //等待DUT的回復

       Check_trans(tr);

    End

#100;

End

endprogram

 

        并發(fā)線程中務必使用自動變量來保持數(shù)值。

        #0 延遲,使得當前線程必須等到fork…join_none語句中產生的線程執(zhí)行完后,才得以運行。

 

 

 

        停止線程

1)       停止單個線程

使用fork ..join_any 后加disable。

3) 停止多個線程

Disable fork  能停止從當前線程中衍生出來得所有子線程。

應該使用fork ..join 把目標代碼包含起來,以限制Disable fork的作用范圍。

 

        事件

背景:

Verilog中當一個線程在一個事件上發(fā)生阻塞的同時,正好另一個線程觸發(fā)了這個事件,則競爭就出現(xiàn)了。如果觸發(fā)線程先于阻塞線程,則觸發(fā)無效(觸發(fā)是一個零寬度的脈沖)。

解決方法:

Systemverilog 引入了triggered()函數(shù),用于檢測某個事件是否已被觸發(fā)過,包括正在觸發(fā)。線程可以等待這個結果,而不用在@操作符上阻塞。

例子:

Event e1,e2;

Initial begin

     ->e1;

     @e2;

End

 

Initial begin

     ->e2;

     @e1;

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í)行。

 

 

        等待多個事件

最好的辦法是:采用線程計數(shù)器來等待多個線程。

 

        旗語

Get()可以獲取一個或多個鑰匙,put()可以返回一個或多個鑰匙。Try_get()獲取一個旗語而不被阻塞。

 

        信箱

背景:如何在兩個線程中傳遞信息?考慮發(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)建多個對象。

 

        異步線程間使用信箱

背景:

很多情況下,由信箱連接的兩個線程應該步調一致,這樣生產方 才不至于跑到消費方前。 好處:最好層的generator需要等待低層的數(shù)據(jù)發(fā)完后才能結束。測試平臺能精確知道所有激勵發(fā)出去的時間。

兩種情況

兩個線程同步,需要額外的握手信號。否則,出現(xiàn)生產方運行到結束,消費方還啟動。

1)       信箱容量為1,兩個線程同步

因阻塞,連個線程不需要握手信箱

3)  容量不為1,線程間同步

需要使用握手信號,以使producer不超前于consumer;如果consumer超前于prodecer會阻塞。

解決辦法

1)  使用定容信箱和peek實現(xiàn)線程同步:(比較好)

消費方:consumer 使用信箱方法peek()獲取信箱里的數(shù)據(jù)的copy而不將其移出,當consumer處理完數(shù)據(jù)后,便使用get()移出數(shù)據(jù)。

特點:信箱容量定義為1,不需要握手信號。

 

Calss  consumer

      Repeat(n)begin

         Mbx.peek(i);

         $display(“consumer:after get( )”,i);

         Mbx.get(i);

      End

endcalss

 

如果直接使用get()替代peek(),那么事務會被立刻移出,這樣可能會在consumer完成事務前,producer生成新的數(shù)據(jù)。

-

2)使用信箱和事件實現(xiàn)線程同步

     使用邊沿敏感的阻塞語句@handshake 代替電平觸發(fā)wait(handshake.triggered())。

    因為:線程中任務run()使用循環(huán),事件阻塞只能使用@handshake。

局限:如果遇到producer線程的阻塞和consumer線程的觸發(fā)同時發(fā)生,則可能出現(xiàn)次序上的問題。

3)使用兩個信箱實現(xiàn)線程同步

使用另一個信箱把consumer的完成信息發(fā)回給producer。

目的:在producer線程中,處理完事物后,用一個get()來阻塞。

特點:信箱容量大于1.

Maibox  mbx,rtn;

 

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提前一個事務的時間。

 

        Wait(handshake.triggered())和@handshake 使用范圍

1)  Wait(handshake.triggered()),用于等待一個事件;

2)  循環(huán)中等待事件,只能用@handshake

3)  兩個線程的同步,一般任務run()使用循環(huán),所以只能使用@handshake。

注意事項:

1)  在循環(huán)中,等待事件不能用Wait(handshake.triggered()),因為如果事件觸發(fā)一次,wait()語句一直為真,進入不斷的循環(huán)。下一次循環(huán)中,不會阻塞。

2)  @handshake 如果觸發(fā)事件,先于等待事件。會等不到事件,因為(事件觸發(fā),是一個零寬度的脈沖)

 


OPP的高級編程技巧

        繼承

背景:

為總線事務增加一個錯誤功能并帶可變延時的復雜類。方法如下:

1)  使用合成,即在類中例化另一個類型的類。有時候很難將功能分成獨立的部分。如果使用合成,則需要為正確和錯誤事務分別創(chuàng)建不同的類,正確類的測試平臺需要重寫以處理錯誤類的對象。

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 basel

     Function new(input  int var);

          this.var = var;

     endfunction

     endclass

class  extended   extends basel

    function new(input int var);

      super.new(var);

    endfunction

endclass

3)OPP規(guī)則指出:基類的句柄,也可以指向擴展類的對象。(好好體會)

 

 

 

        藍圖模式

1)背景:一個簡單的發(fā)生器,通過信箱將數(shù)據(jù)傳遞給驅動器。

class generator

mailbox   gen2drv;

transaction tr;

 

function new(input mailbox gen2drv)

    this.gen2drv = gen2drv;

endfunction

 

task run;

   forever begin

       tr = new();

       assert(tr.randmize);

       gen2drv.put(tr);   //mail.put(x)

  end

endtask

    endclass        

存在問題:這個例子在循環(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中放入的只能是句柄,如何解決常見的平臺錯誤?

    因為copy,是對象的復制,而不是句柄的復制。這樣藍圖模式只有一個句柄,但是隨機化后,copy,相當于再循環(huán)中創(chuàng)建了許多對象。而測試平臺常見錯誤的本質是,只創(chuàng)建了一個對象。這樣就避免了問題。

(2)藍圖模式下,因為只有一個ID號,那么任務run循環(huán)中,下發(fā)了許多數(shù)據(jù),這些只有一個ID號了?

    因為copy是對象的復制,所以在copy中ID號也會增加。下發(fā)的每個數(shù)據(jù),都有各自的ID號。

 

 

 

 

        使用擴展的transaction

為了注入錯誤,需要將藍圖對象transaction變成Badtransaction(改變藍圖)。必須在環(huán)境的創(chuàng)建和運行階段之間完成這個操作。注意:所有的badTr引用都在這一個文件中,這樣就不需要改變environment類或generator類。

 

Env.build();

Begin

   Badtr bad = new();

   Env.gen.blueprint = bad;

End 

Env.run

目的是:將一個對象取代另一個對象。New()后都是對象了,將對象賦值給對象,這是什么寫法?不是復制呀?復制本質是將一個句柄指向一個對象。

解釋:上述是句柄的復制,將擴展類句柄bad賦值給基類句柄blueprint,這樣基類句柄指向擴展類對象,后面的代碼調用的時候,就直接指向擴展類bad了,改變了藍圖。

 

 

        Env.new()和nev.build()區(qū)別

Env.new()僅僅new()函數(shù)

nev.build()是將各個模塊new(),并傳達一些參數(shù),通過這些參數(shù)將環(huán)境的各個模塊,連接起來。P213

 

        $cast 作類型向下轉換

背景:基類句柄可以指向擴展類對象,不需要額外的代碼; 擴展類句柄指向基類對象,一般情況下會出錯,但有時候是可以的,前提是基類句柄指向了它的一個擴展類對象。

作用:擴展類句柄指向基類對象時,使用$cast()函數(shù)。在非法的情況下,不會編譯報錯,會返回了一個0.

 

 

$cast做任務使用時,systemverilog會在運行時,檢查源對象類型和目的對象類型不匹配,會報錯;

$cast 做函數(shù)使用時,運行時,仍做類型檢查,在不匹配時,不會報錯,$函數(shù)返回0.

前面所述:基類句柄可以指向任何它的擴展類的對象、

1) 基類句柄指向擴展類對象——出現(xiàn)情況:修改藍圖,不改過多代碼,增加功能

Transaction tr; //基類句柄

BadTr  bad;  //擴展類句柄

Bad = new();

Tr  = bad;  // 基類句柄指向擴展類對象

tr.display;    //掉用的是擴展類的方法

2) 擴展類句柄指向基類對象——出現(xiàn)情況:基類virtual 方法copy函數(shù),它的繼承類中copy函數(shù)

將基類句柄賦值給擴展類句柄,使擴展類句柄指向基類對象,一般編譯器會出錯,不能運行,所以非常小心;只有基類句柄指向擴展類對象時,再將擴展類句柄指向基類對象時,不出錯。為了檢測基類句柄是否指向了擴展對象,并且不讓編譯器報錯,可以使用$cast()函數(shù)檢測。

 

當把擴展類句柄指向基類對象時,發(fā)生什么?

Tr= new();

Bad = tr;   //擴展類句柄指向基類句柄

上述會發(fā)生錯誤,編譯不會被通過。因為有些屬性在基類中不存在;但是擴展類句柄指向基類句柄不總是非法的(見下面代碼,是可以的),當基類句柄指向一個擴展類對象時是允許的。

       Transcation tr;

       BadTr bad,bad2;         

 

       Bad= new();

        Tr = bad;          //基類句柄指向擴展類對象

       $cast(bad2,tr);      //擴展類句柄指向基類對象

   if(!$cast(bad2,tr);

     $display(“cannot assign tr to bad2”);

   $display(bad2.bad_crc);

 

        句柄類型和對象類型差異(書中翻譯的不準,type of handdle 和 object)

個人理解:

Transaction tr;  句柄tr類型是transaction

句柄類型:關鍵字

對象類型:類中成員的類型差異

 

        虛方法和多態(tài)

多態(tài):多個程序使用一個共同的名字的現(xiàn)象。

多態(tài)解決問題:計算機建構面臨的一個問題。讓物理內存很小的情況下,讓處理器能夠對很大的地址空間尋址。針對這個問題引入了虛擬內存。

虛擬方法繼承劣勢:

基類使用了虛擬方法,擴展類也必須使用相同的“簽名”,擴展類中虛擬子程序不能增加或刪除參數(shù),這意味著必須提前做好規(guī)劃。

 

 

 

 

 

 

        對象復制

1)  因為是virtual 函數(shù),擴展類中copy方法也必須是transaction型的,

但是要copy的是badtr類型的,所以要new一個bad

帶有copy 的事物基類。

Class transaction ;

Rand bit[31:0] src,dst,data[8];

Bit[31:0] crc;

 

Virtual function transaction copy ();

    Copy   = new();

    Copy.src = s rc;

    Copy.dst = dst;

    Copy.data = data;

    Copy.crc  = crc;

Endfunction

Endclass

 

帶有copy的擴展類

Calss badtr extends transaction

    Rand bit bad_crc;

 

Virtual function badtr copy();  //錯誤

Virtual function transaction copy();

   Badtr  bad;

   Bad = new();

   Bad.src = src;

   bad.dst = dst;

   bad.data = data;

   bad.crc  = crc;

   Bad.bad_crc = bad_crc;

Return  bad;

Rendfunction

    endclass

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)       優(yōu)化途徑二,最好的。前面的copy子程序都會創(chuàng)建一個新對象,改進的一種方法就是指定復制對象的存放地址。

     Virtual function  transaction copy(transaction to =null);

         if(to == null)

            copy = new();

         else

            copy = to;

         copy_data(copy);

      endfunction

 

        抽象類和純虛方法

背景:驗證的目標之一是創(chuàng)建多個項目共享的代碼。

目的:systemverilog 有兩種方法創(chuàng)建共享的基類:抽象類和純虛方法

Virtual class (抽象類):可以被擴展但是不能被直接例化。

Pure  virtual function(純虛方法):沒有實體的方法原型,相當于一個聲明。

1)       由抽象類擴展而來的類,只有在所以的虛擬方法都有實體的時候才能被例化,

2)       純虛方法只能在抽象類中定義。

    3)抽象類中,純虛方法是沒實體的,非純虛方法最好也不寫實體。

        回調

背景:測試平臺目的:創(chuàng)建一個不做任何修改就能在所有測試中使用的驗證環(huán)境。要做到這點的關鍵是測試平臺使用鉤子,(什么是鉤子?)鉤子作用,在不修改原始類的情況下注入新的代碼。采用virtual 方法,也可以在擴展類中覆蓋基類方法,但是需要重復原方法的所有代碼,并且它的修改將傳播到它的所有擴展類中。

作用:回調就是一個鉤子,在不修改原始類的情況下注入新的代碼。

實現(xiàn):回調任務在頂層中創(chuàng)建,在最低級即驅動器中調用。這樣驅動器不需要知道測試的任何信息,它只需要使用一個可以在測試中擴展的通用類。

 

1)  使用回調注入干擾

回調的一個常見用法就是注入干擾,例如引入一個錯誤或者延遲。下面測試平臺使用回調對象,隨機地丟棄數(shù)據(jù)包。

 

 

擴展類是如何作用的?在擴展的回調類中注入錯誤,如何在驅動器中作用的?

關鍵是數(shù)據(jù)隊列的作用,驅動器中使用了,回調基類的數(shù)據(jù)隊列

 

回調基類是抽象類,在擴展的回調類中加入錯誤注入,而drive驅動類中,是回調基類的數(shù)據(jù)隊列,在環(huán)境中將擴展類句柄讓入驅動器類,回調基類的數(shù)據(jù)隊列中。

      begin // Create error injection callback

Driver_cbs_drop dcd = new();

env.drv.cbs.push_back(dcd); // Put into driver

end

 

與前面擴展類作用的差異?

前面代碼,要使擴展類中增加代碼,需要使基類句柄指向擴展類句柄。 

 

        驅動器類:

下面的代碼如何解釋

 

 

   2)回調也可以想scoreboard 發(fā)送數(shù)據(jù)或收集功能覆蓋率。

     優(yōu)點:你可能想過將scoreboard和功能覆蓋數(shù)據(jù)組置于一個事物處理器中,通過郵箱連接到測試平臺中,這是一種笨拙的方法,原因如下:測試平臺組件幾乎都是被動和異步的,組件只有在測試平臺給他數(shù)據(jù)的時候才被喚醒,而且不會主動地向下游事物處理器傳遞信息。麻煩:1)這樣一個需要同時監(jiān)視多個郵箱的事物處理器復雜了;2)你可能在多個地方采集數(shù)據(jù),但是事物處理器設計用來處理單個數(shù)據(jù)源回調

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美尤物在线视频91| 人体偷拍一区二区三区| 在线日韩中文字幕一区| 欧美三级精品在线观看| 亚洲黄香蕉视频免费看| 不卡视频免费一区二区三区| 国产不卡一区二区四区| 国产传媒精品视频一区| 国产精品久久熟女吞精| 亚洲精品成人福利在线| 可以在线看的欧美黄片| 国产91人妻精品一区二区三区 | 欧美日韩国产二三四区| 免费黄片视频美女一区| 人妻熟女中文字幕在线| 午夜资源在线观看免费高清| 少妇人妻中出中文字幕| 久久精品亚洲欧美日韩| 日本少妇三级三级三级| 麻豆看片麻豆免费视频| 久久精品国产亚洲av久按摩| 91超频在线视频中文字幕| 隔壁的日本人妻中文字幕版 | 有坂深雪中文字幕亚洲中文| 午夜国产福利在线播放| 日韩国产精品激情一区| 黄色av尤物白丝在线播放网址| 国产精品不卡高清在线观看| 国产福利一区二区三区四区| 国产精品亚洲一区二区| 欧美小黄片在线一级观看| 欧美一区二区口爆吞精| 中文字幕中文字幕在线十八区| 国产熟女高清一区二区| 日本丰满大奶熟女一区二区| 国产在线一区中文字幕| 麻豆视频传媒入口在线看| 99视频精品免费视频播放| 国内自拍偷拍福利视频| 日本午夜乱色视频在线观看| 91久久精品在这里色伊人|