SystemVerilog基本語法


SV在線仿真平台:https://www.edaplayground.com

注:平台需機構郵箱注冊,還支持Perl、python等腳本語言以及UVM驗證。

1.數據類型

  • VerilogHDL中有2種變量類型:wirereg,這兩種變量是4值類型的(即有四種狀態)。

  • SystemVerilog在此基礎上拓展了一種變量類型:logic類型,該變量類型可以取代wire型變量和reg型變量。但需要注意的是,logic型的變量不能夠有多個結構性的驅動,所以在對inout端口變量進行聲明的時候,需要使用wire變量類型。

  • 其余的變量類型如下表所示:

       
  • 定寬數組。Verilog要求在數組的聲明中必須給出明確的上下界,例如:int array[8]。需要注意的是,當越界訪問數組的時候,若數據是四狀態類型,那么將返回X值;若數據是雙狀態類型,那么將直接返回0

  • 數組的初始化。數組在定義的時候即可進行初始化,例如:int array[8] = '{1,2,3,4,,default=7},又例如:int array[8] = '{8{7}}

  • 合並數組。對於某些數據類型,用戶可能希望它能夠作為數組訪問,也希望有時它可以作為一個整體訪問,這就是合並數組的意義所在,例如:bit [3:0][7:0] array,這個合並數組既可以當作4個byte分別訪問,也可以作為一個32bit的數據進行整體訪問。

  • 動態數組。定寬數組在使用之前必須規定好數組的長度,有時這樣的操作帶有局限性,而動態數組在定義的時候不需要指明數組的長度,在使用的時候再對數組長度進行new[]操作即可。例如,定義一個動態數組:int array[],在使用的時候,對array進行new[]操作並指明數組長度即可:array = new[5]

  • 隊列。隊列是SystemVerilog新引進的數據類型,它結合了鏈表和數組的優點,可以隨時增刪數組里的元素,也可以做到快速的數組操作。在對隊列進行聲明的時候,需要使用[$]

  • 關聯數組。類似於Python里的字典。關聯數組的聲明有些復雜,例如:bit [63:0] assoc[bit[63:0]]。另外一個簡單的栗子:

 1 program automatic assoc_test();  2   int assoc[string];  3 
 4  initial begin  5     assoc["super"] = 1;  6     assoc["special"] = 2;  7     assoc["offer"] = 3;  8     
 9     foreach(assoc[i]) begin 10       $display("assoc[%s] = %0d", i, assoc[i]); 11  end 12  end 13 endprogram 14 //result:assoc[offer]=3, assoc[special]=2, assoc[super]=1
  • 數組的方法。數組擁有各類方便用戶的方法,但是這些方法不能夠使用在合並數組上。
      
  • 流操作符。(bit-stream)表示方式為{>>{}} 和 {<<{}}。前者會把數據塊按照從左到右的形式流(stream),后者則是從右到左。流操作符常用於把其后面的數據打包成一個比特流。
實際項目中,經常需要打包(pack)和解包(unpack)數據。其實這些操做可以通過流操作符簡單的實現。
 1 module test;  2   function void pack_array_int(const ref bit[7:0] array[4],     output int a);  3     a = {<<byte{array}};  4  endfunction  5 
 6   bit[7:0] array[4] = '{ 8'h8C, 8'h00, 8'hA4, 8'hFF };
 7   int pack_result;  8   
 9  initial begin 10  pack_array_int(array, pack_result); 11     
12     $display("The result is 0x%h", pack_result); 13  end 14 endmodule

       

  注意,此處的 const ref是為了避免子程序對數組的改變。ref是對數組直接的引用,避免子程序需要拷貝數組,從而釋放仿真器的壓力。

另一個實例是,項目中會涉及的大小端(big endian,little endian)的轉換,且需要將數據解包。

 1 module test;  2   function void unpack(bit[127:0] big_endian_data, output bit[63:0] little_endian_array[2]);  3     bit[127:0] temp;  4     temp = {<<byte{big_endian_data}};  5     for(int i=0; i<2; i++) begin  6       little_endian_array[i] = temp[63:0];  7       temp = temp >> 64;  8  end  9  endfunction 10 
11   bit[127:0] big_endian_data = 128'h1122334455667788_aabbccddeeff1122;
12   bit[63:0] little_endian_array[2]; 13   
14  initial begin 15  unpack(big_endian_data, little_endian_array); 16     
17     foreach(little_endian_array[i]) begin 18     $display("The result is 0x%h",little_endian_array[i]); 19  end 20     
21  end 22 endmodule

  該實例實現了將一個16byte的大端數據(big endian)轉換為小端數據后,unpack成為2個64bit的數組:output bit[63:0] result[2]。

注:使用bit而不是byte是因為byte是有符號數,可能會引起產生負數。所以實際驗證中一般使用bit。

  • 枚舉類型。枚舉類型用enum和一個中括號{}定義,常用於狀態機中的狀態定義,常用的枚舉類型方法如下所示。
      

 

2.過程語句和子程序

  • 任務和函數之間有着很大的差別,最大的差別在於:任務內部可以消耗仿真時間,而函數是表達式,不能夠消耗仿真時間,例如:不能有wait或者posedge clk這樣的語句存在。並且對於函數來說,必須要有返回值,而且返回值必須要立即使用,不帶返回值的函數需要用void進行聲明。
  • 參數的方向:inputoutputinoutrefref類型是參考類型,直接傳遞參數的指針,所以在對ref型的數據進行修改時,是直接對原始數據進行修改,類似於C語言的“實參”。
  • VerilogHDL中的對象是靜態分配的,不像其它的編程語言那樣將對象放在堆棧里面,所以在多個地方同時調用同一個子程序(函數或者任務),可能會使用到同一個局部變量。解決這一問題的方法在於聲明任務或者函數的時候,添加automatic自動存儲關鍵詞,這樣會迫使仿真器使用堆棧進行仿真。
  • 仿真時間值。SystemVerilog提供多種指明仿真時間的方法:timescale 1ns/1pstimeunittimeprecision$timeformat(時間標度,小數點后的時間精度,后綴字母(單位),顯示數值的最小寬度)。使用timescale可以對全局的文件進行統一的仿真時間設置,而后面的方法可以單獨對某個模塊進行設置。

3.連接設計和測試平台

  • SystemVerilog使用接口作為驗證平台的通信管道,接口包含了連接、同步、多個模塊之間的通信,是連接測試平台與DUT的關鍵橋梁。一個接口的示例如下所示:

interface my_if(input logic clk, input logic rst_n);
    logic valid, ready;
endinterface
  • 在接口interface中,可以使用modport關鍵詞定義一組信號並規定其方向,這類似於將interface中的某些信號用線捆好。在使用modport信號組的時候,需要指明信號組的名稱。
interface my_if(input logic clk, input logic rst_n);
    logic valid, ready;
    modport DRIVER(input clk, rst_n, ready, output ready);
    modport MONITOR(input clk, rst_n, valid, ready);
endinterface

//using interface.MONITOR
module my_dut(my_if.MONITOR vif);
    ...
endmodule
  • 接口inerface可以使用時鍾塊clock blocking對接口內部的信號進行同步采樣和驅動,這樣可以保證測試平台在正確時間點的信號交互。另外,在時鍾塊里可以用default語句指定時鍾偏移,但是在默認情況下輸入信號僅在設計執行之前被采樣。
interface my_if(input logic clk);
    logic valid, rst_b, ready;
    clocking drv_cb@(posedge clk);
        input ready;
        output valid;
    endclocking
endinterface
  • SystemVerilog是以事件調度來進行仿真的語言,它將程序運行時發生的事情按一定的順序進行調度並執行,在一段特定時間內會發生所有的事件調度,這段時間叫做time slot(時間片),某時刻仿真進入一個time slot,執行完其中的所有事件后,便進入下一個time slot。仿真時間(而非現實時間)是按照time slot向前推進的,如果程序中某一段代碼反復調度回該time slot(無延時的死循環),仿真會因為無法進入下一個time slot而卡死,此時雖然現實時間在流逝,但仿真時間不再向前推進了。

  • 對於時間片而言,主要有以下幾個執行區域,其中AcitiveInactiveNBA區域屬於Active Region,主要執行的是module(即RTL)中的代碼;而ReacitiveRe-inactiveRe-NBA區域屬於Reactive Region,主要執行的是program(即testbench)中的代碼:

       
  • SystemVerilog中提供了斷言,用戶可以使用斷言對時序進行判斷。斷言分為兩類:立即斷言和並發斷言。

4.面向對象編程基礎

  • 面向對象編程的三大特點:封裝、繼承、多態。

  • 在SystemVerilog中要分清楚句柄和對象的區別。

class transaction; logic data[100]; function new(); foreach(data[i]) begin data[i] = 2*i; end endfunction endclass transaction tr;//聲明句柄
tr = new();//為對象開辟地址空間
  • OOP中的靜態變量和全局變量。每個類的內部都有自己的成員變量,當這些成員變量沒有被static關鍵詞進行聲明的時候,這些變量是局部的,而被static關鍵詞修飾的成員變量則是共享的。除此之外,靜態函數只能讀寫靜態變量,不能夠讀寫非靜態變量,例如下面的例子中靜態函數只能讀寫count的值,而不能對id的值進行訪問。
class transaction; static int count=0; int id; function new(); id = count++; endfunction endclass transaction tr1, tr2; tr1=new();//id=0,count=1
tr2=new();//id=1,count=2
  • this指代類一級的變量。當使用一個變量名的時候,SystemVerilog首先會在當前作用域進行尋找,假若找不到則會在上一級作用域進行尋找,這樣循環往復直到找到為止。而若在某一局部作用域中想使用class一級的成員變量,可以使用this進行指代。

  • 對象的復制。對象有2種不同的復制方式:簡易復制和深層次復制。簡易復制方式就像是對原對象的簡單粗暴賦值,不會考慮對象內部的類;深層次復制可以解決這個問題,但用戶需要自己維護復制函數。

 1 class transaction;  2     static int count=0;  3     int id;  4     int addr, data;  5     statistics stats;//另一個類,內有成員變量int starttime
 6     
 7     function new();  8         stats = new();  9         id = count++; 10  endfunction 11 endclass 12 
13 //shallow copy
14 transaction src, dst; 15 initial begin 16     src = new(); 17     src.stats.starttime = 30; 18     dst = new src; 19     dst.stats.starttime = 90;//同時src.stats.starttime也變成了90
20 end 21 
22 //deep copy
23 class transaction; 24     static int count=0; 25     int id; 26     int addr, data; 27     statistics stats;//另一個類,內有成員變量int starttime
28     
29     function new(); 30         stats = new(); 31         id = count++; 32  endfunction 33     
34  function transaction copy(); 35         copy = new(); 36         copy.addr = addr; 37         copy.data = data; 38         copy.stats = stats.copy();//類statistics內部也需要維護一個復制函數
39         id = count++; 40  endfunction 41 endclass
  • 類的公有和私有。在SystemVerilog中,所有的成員都是公有的,若在聲明時加localprotected關鍵詞則可以將其變為私有成員或保護成員。網頁、游戲等代碼需要的是長時間的穩定性,為此需要將所用到的類都聲明為私有類型,但是測試平台不同,有時我們不僅需要注入正確的激勵,還需要注入錯誤的激勵,這時候公有的類可以使我們的操作更加方便。

5.隨機化

  • 帶有約束的隨機是SV的靈魂,我們不可能指望用一個接着一個的定向激勵去覆蓋所有的DUT功能點,也不可能完全讓激勵放任自由地隨機化,最好的設想就是利用帶有約束的隨機產生某一個方向上的隨機。下面的代碼展示了一個簡單的帶有隨機的類:

 1 class packet;
 2     rand bit [7:0] data;
 3     randc int count;
 4     constraint count_cons {count>10;count<15;}//約束是聲明性語句,需要用到{}
 5 endclass
 6 
 7 //創建一個對象並對其進行隨機化
 8 packet pkt;
 9 initial begin
10     pkt = new();
11     assert(pkt.randomize())
12     else $fatal(0, "packet::randomize failed!");
13 end
  • 隨機的權重。使用dist關鍵詞可以改變值的隨機概率,即讓某些值更加頻繁地出現,或者更加不容易被隨機出來。通常情況下,dist關鍵詞的后面會跟着一個值及其相應的權重,中間用:=或者:/分開。
rand int src, dst;
constraint src_dst_cons{
    src dist{0:=40,[1:3]:=60};
  //src=0,weight=40/220
  //src=1,weight=60/220
  //src=2,weight=60/220
  //src=3,weight=60/220
    
    dst dist{0:/40,[1:3]:/60};
    //dst=0,weight=40/100
   //dst=1,weight=20/100
    //dst=2,weight=20/100
    //dst=3,weight=20/100
}
  • 隨機中的集合和inside操作符。可以使用inside操作符產生一個集合,使得隨機變量在這個集合中隨機取值。另外可以使用$運算符代指最小值或最大值。
 1 rand int a, low, high;
 2 const int array[5]='{1,3,5,7,9};
 3 rand bit [6:0] b;
 4 rand bit [5:0] c;
 5 rand int d;
 6 constraint a_cons {a inside [low:high];}//low<=a<=high
 7 //constraint a_cons {!(a inside [low:high]);}//a<low||a>high
 8 constraint b_cons {b inside {[$,4],[20:$]};}//(0<=b<=4)||(20<=b<=127)
 9 constraint c_cons {c inside {[$,2],[20,$]};}//(0<=c<=2)||(20<=c<=63)
10 constraint d_cons {d inside array;}//d==1||d==3||d==5||d==7
  • 條件約束。使用條件約束,可以使得約束條件在某些情況下才起作用,條件約束可以使用->符號,也可以利用關鍵詞if()...else()
 1 class transaction;
 2     bit workmode;
 3     int crc;
 4     constraint cons_crc {if(workmode) crc==0; else crc=$random();}
 5 endclass
 6 
 7 class transaction;
 8     bit workmode;
 9     int crc;
10     constraint cons_crc {workmode -> crc==0;}
11 endclass
  • 解的概率。SystemVerilog並不保證隨機約束器能夠給出精確的解,但是用戶可以控制概率的分布。另外需要注意的是,SystemVerilog中隨機的約束是雙向的。
 1 //x有2種解:0和1,y有4種解:0、1、2、3,每一種隨機結果的概率是一樣的
 2 class imp;
 3     rand bit x;
 4     rand bit [1:0] y;
 5 endclass
 6 
 7 //共有5種解:x=0,y=0;x=1,y=0;x=1,y=1;x=1,y=2;x=1,y=3,但是每種解的概率不一定相同
 8 class imp;
 9     rand bit x;
10     rand bit [1:0] y;
11     constraint cons_xy {(~x)->(y==0);}//當x=0的時候,y只能為0
12 endclass
13 
14 //共有3種解:x=1,y=1;x=1,y=2;x=1,y=3,且每種解的概率一樣,均為1/3
15 class imp;
16     rand bit x;
17     rand bit [1:0] y;
18     constraint cons_xy {y>0;(~x)->(y==0);}//當x=0的時候,y只能為0
19 endclass
20 
21 //共有5種解:x=0,y=0(1/2);x=1,y=0(1/8);x=1,y=1(1/8);x=1,y=2(1/8);x=1,y=3(1/8),但是每種解的概率不一定相同
22 class imp;
23     rand bit x;
24     rand bit [1:0] y;
25     constraint cons_xy {solve x before y;(~x)->(y==0);}//當x=0的時候,y只能為0
26 endclass
27 
28 //共有5種解:x=0,y=0(1/8);x=1,y=0(1/8);x=1,y=1(1/4);x=1,y=2(1/4);x=1,y=3(1/4),但是每種解的概率不一定相同
29 class imp;
30     rand bit x;
31     rand bit [1:0] y;
32     constraint cons_xy {solve y before x;(~x)->(y==0);}//當x=0的時候,y只能為0
33 endclass
  • 對於class內的約束模塊constraint,可以通過constraint_mode()開啟或者關閉約束模塊;而對於class內的成員變量,可以通過rand_mode()開啟或者關閉隨機。

  • SystemVerilog允許使用randomize() with來增加額外的約束,with后面的{}里,SystemVerilog使用的是類的作用域。

  • SystemVerilog提供了許多常用的系統函數,具體的函數列在下表:

      

6.線程的使用

  • SystemVerilog提供了創建多線程的方法,即fork...join方法。

//啟動完之后,所有線程結束才繼續執行后面的程序
fork
    ...
join

//線程啟動之后,只要有一個線程結束就繼續執行后面的程序
fork
    ...
join_any

//線程啟動之后,繼續執行后面的程序
fork
    ...
join_none
  • 線程中的自動變量。當使用循環來創建線程的時候,如果在進入下一個循環之前沒有保存當前的變量,那么該變量會在下一個線程當中被覆蓋,這是一個很難發現的漏洞。
 1 //最終結果會顯示3個3
 2 program no_auto;
 3     initial begin
 4         for(int i=0; i<3; i++) begin
 5             fork
 6                 $write(i);
 7             join_none
 8         end
 9         #0 $display("\n");
10     end
11 endprogram
12 
13 //最終結果:1 2 3
14 program no_auto;
15     initial begin
16         for(int i=0; i<3; i++) begin
17             fork
18                 automatic int j = i;
19                 $write(j);
20             join_none
21         end
22         #0 $display("\n");
23     end
24 endprogram
25 
26 //最終結果:1 2 3
27 program automatic no_auto;
28     initial begin
29         for(int i=0; i<3; i++) begin
30             fork
31                 int j = i;
32                 $write(j);
33             join_none
34         end
35         #0 $display("\n");
36     end
37 endprogram
  • SystemVerilog中可以利用事件event進行線程間的通信,->符號表示觸發一個事件,@符號表示等待一個事件的觸發,該符號是一個邊沿敏感型符號,是阻塞的;另外,可以通過wait(event.triggered())這樣的電平敏感型表達式替換@這樣的邊沿敏感型符號,但這個表達式是非阻塞的,在使用的時候需要保證仿真時間的向前推進。
//仿真卡死
forever begin
    wait(handshake.triggered());//non-blocking
    $display("received next event!");
    process_next_item_function()//a function defined by user
end
  • SystemVerilog提供旗語實現多個線程對同一個資源的訪問控制。例如,DUT中有多個線程請求同一個資源,這就可以使用旗語來完成控制權的仲裁。旗語通過semaphore關鍵詞來聲明,旗語有3個基本的操作:newgetput

  • SystemVerilog提供信箱供線程之間進行信息交換,可以將信箱看作是一個連接了收端和發端的FIFO,當信箱為空的時候,讀取信息(mailbox.get())會被阻塞;當信箱為滿的時候,放入信息(mailbox.put())這個動作會發生阻塞。信箱用mailbox關鍵詞進行聲明,mailbox是一個類,所以在使用之前需要new()操作,在創建對象的時候可以傳入參數,這個參數是可容納數據的上限,如果不傳或者傳入數據為0,則視為信箱的容量無限大。

 1 program automatic synchronized;
 2     class producer;
 3         mailbox mbx;
 4         
 5         function new(mailbox mbx);
 6             this.mbx = mbx;
 7         endfunction
 8         
 9         task run();
10             for(int i=1; i<4; i++) begin
11                 $display("producer:before put information(%d)", i);
12                 mbx.put(i);
13             end
14         endtask
15     endclass
16     
17     class consumer;
18         mailbox mbx;
19         
20         function new(mailbox mbx);
21             this.mbx = mbx;
22         endfunction
23         
24         task run();
25             repeat(3) begin
26                 int i;
27                 mbx.get(i)
28                 $display("consumer:after get information(d%)", i);
29             end
30         endtask
31     endclass
32     
33     producer p;
34     comsumer c;
35     initial begin
36         maibox mbx;
37         mbx = new();
38         p = new(mbx);
39         c = new(mbx);
40         fork
41             p.run();
42             c.run();
43         join
44     end
45 endprogram
46 
47 //result
48 producer:before put information(1)
49 producer:before put information(2)
50 consumer:after get information(1)
51 consumer:after get information(2)
52 producer:before put information(3)
53 consumer:after get information(3)
View Code
  • 假若在用信箱mailbox的同時使用事件event的話,就可以實現線程間的完全同步了。
 1 program automatic synchronized;
 2     class producer;
 3         mailbox mbx;
 4         event handshake;
 5         
 6         function new(mailbox mbx, event handshake);
 7             this.mbx = mbx;
 8             this.handshake = handshake;
 9         endfunction
10         
11         task run();
12             for(int i=1; i<4; i++) begin
13                 $display("producer:before put information(%d)", i);
14                 mbx.put(i);
15                 @handshake;//等待事件handshake的觸發
16             end
17         endtask
18     endclass
19     
20     class consumer;
21         mailbox mbx;
22         event handshake;
23         
24         function new(mailbox mbx, event handshake);
25             this.mbx = mbx;
26             this.handshake = handshake;
27         endfunction
28         
29         task run();
30             repeat(3) begin
31                 int i;
32                 mbx.get(i)
33                 $display("consumer:after get information(d%)", i);
34                 ->handshake;//觸發事件handshake
35             end
36         endtask
37     endclass
38     
39     producer p;
40     comsumer c;
41     initial begin
42         maibox mbx;
43         mbx = new();
44         event handshake;
45         p = new(mbx, handshake);
46         c = new(mbx, handshake);
47         fork
48             p.run();
49             c.run();
50         join
51     end
52 endprogram
53 
54 //producer和consumer兩個線程之間實現了完全同步
55 producer:before put information(1)
56 consumer:after get information(1)
57 producer:before put information(2)
58 consumer:after get information(2)
59 producer:before put information(3)
60 consumer:after get information(3)
View Code

7.面向對象編程的高級技巧

  • OOP中類的變量稱為屬性(property),類中的任務或者函數稱為方法(method),子程序的原型(prototype)是指明了參數列表和返回類型的第一行。

  • 類在使用extends關鍵詞進行繼承的時候,假若構造函數eg.function new(int value)中有參數傳遞,那么擴展類中的構造函數的第一行也必須調用基類的構造函數super.new(value)

  • 可以將一個派生類的句柄賦值給一個基類的句柄,並且不需要任何特殊的代碼;但是相反的,假若將一個擴展類的句柄賦值給一個基類句柄時,最好使用$cast()進行類型匹配,類型不匹配時會返回0值。

 1 //將子類的句柄賦值給父類
 2 program automatic test;
 3   class father;
 4     function void call;
 5       $display("***I am father!!!***");
 6     endfunction
 7   endclass
 8 
 9   class son extends father;
10     function new();
11       super.new();
12     endfunction
13   
14     function void call;
15       $display("***I am son!!!***");
16     endfunction
17   endclass
18 
19   father fa;
20   son so;
21   initial begin
22     so = new();
23     so.call;//輸出是:I am son!!!。假若在function的前面加上virtual關鍵字,那么SV會根據對象的類型進行方法的調用
24     fa = so;
25     fa.call;//輸出是:I am father!!!。假若在function的前面加上virtual關鍵字,那么輸出為:I am son!!!
26   end
27 endprogram
View Code
 1 //將父類的句柄賦值給子類
 2 program automatic test;
 3   class father;
 4     function void call;
 5       $display("***I am father!!!***");
 6     endfunction
 7   endclass
 8 
 9   class son extends father;
10     function new();
11       super.new();
12     endfunction
13   
14     function void call;
15       $display("***I am son!!!***");
16     endfunction
17   endclass
18 
19   father fa;
20   son so;
21   initial begin
22     fa = new();
23     fa.call;//輸出應為:I am father!!!
24     so = fa;//此行為禁止,編譯不通過
25     so.call;
26   end
27 endprogram
View Code
  • 虛方法。虛方法需要在最前面添加virtual關鍵詞進行聲明,類在調用虛方法的時候,會根據對象的類型進行調用而不是句柄的類型。假若沒有對方法進行virtual虛方法的聲明,則SystemVerilog會根據句柄的類型而非對象的類型調用方法。

  • 抽象類和純虛方法。SystemVerilog提供了2種構造方法來創建一個可以共享的基類:第一種是抽象類,即可以被擴展但是不能被直接實例化的類,使用virtual關鍵詞進行修飾;第二種是純虛方法,這是一種沒有實體的方法原型,用關鍵詞pure修飾,並且純虛方法只能夠在抽象類中定義。

 1 virtual class base_transaction;
 2     static int count = 1;
 3     int id;
 4     function new();
 5         id = count++;
 6     endfunction
 7     pure virtual function bit compare(input base_transaction to);
 8     pure virtual function base_transaction copy(input base_transaction to=null);
 9     pure virtual function void display(input string prefix="");        
10 endclass
View Code
  • 回調。回調功能其實是在類的方法中預設了一些虛方法,這些虛方法的內部是沒有代碼實現的,所以在使用回調函數的時候需要對虛方法進行重寫。SystemVerilog中的自建方法randomize()即是一種回調方法,其前后還包括了pre_randomize()post_randomize()
 1 //代碼來自數字IC小站:SystemVerilog中的callback(回調)
 2 class abc_transactor;
 3   virtual task pre_send(); endtask
 4   virtual task post_send(); endtask
 5 
 6   task xyz();
 7       // Some code here
 8       this.pre_send();
 9       // Some more code here
10       this.post_send();
11       // And some more code here
12   endtask : xyz
13 endclass : abc_transactor
14 
15 class my_abc_transactor extend abc_transactor;
16   virtual task pre_send();
17           ...     // This function is implemented here
18   endtask
19 
20   virtual task post_send();
21           ...     // This function is implemented here
22   endtask
23     ...
24 endclass : my_abc_transactor
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM