System Verilog基礎(二)
這一篇筆記主要記錄Procedural,Process,Task and function,Interface和Communication中值得註意的點。
1.Procedural
寫testbench的時候,除了tb與硬件交互的地方使用非阻塞賦值,tb裏面其他地方一律用阻塞賦值,OK
1 logic [3:0] d0,d1; 2 initial begin 3 d0 <= 3; 4 $display("d0 value %0d",d0); //d0=x;logic在未被初始化的時候是x 5 d1 = 4; 6 $display("d1 value %0d d0 value %0d",d1); //d1=4,d0=x;註意這個時候d0依然是x 7 #1 8 $display("d1 value %0d d0 value %0d",d1); //d1=4,d0=3;只有#1往前走了之後,<=才會賦值生效 9 end
Loop循環中的foreach,是專門針對數組輪詢時候用的。對二維數組遍歷,如下代碼:
1 int data[3][4]; 2 initial 3 foreach(data[1]) begin 4 foreach(data[i][j]) begin 5 </**/> 6 end 7 </**/> 8 end 9 end
在兩個for循環中,可以在裏面直接定義index,例如for(int i; i<10; i++) 這樣,如果有兩個for裏面都定義了int i,這兩個index i是相互不影響的。
哪些地方可以加label? Module...endmodule; begin...end; task...endtask; fork...join; interface...endinterface; 加標簽的主要好處是增加代碼的可讀性,例如下面的代碼:
1 module test();2 begin: b0 3 <...> 4 begin: b1 5 <...> 6 end: b1 7 end: b0 8 9 begin: b2 10 <...> 11 end: b2 12 endmodule:test
Final Blocks。這個塊在Verilog中沒有,當遇到$finish的時候,會進入到final塊中。一般用在打印一些信息,註意final塊中是不能加延遲#操作的,不然會報錯。
2.Process
initial塊和always塊都會產生進程Process。在SV中,可以使用fork來動態地產生子進程。fork有三種形式:fork...join fork...join_any fork...join_none 。使用fork...join_none時,不等待子進程執行,直接先執行主進程,就是fork...join_none外面的代碼,但是fork...join_none裏面的代碼在後臺也在執行,註意執行的先後順序。如下代碼:
1 fork: fork 1 2 begin 3 #1; 4 end 5 begin 6 #2; 7 end 8 join 9 $dispaly($time); // time is 2ns 10 fork: fork 2 11 begin 12 #1; 13 end 14 begin 15 #2; 16 end 17 join_any 18 $dispaly($time); // time is 1ns 19 fork: fork 3 20 begin 21 #1; 22 end 23 begin 24 #2; 25 end 26 join_none 27 $dispaly($time); // time is 0ns
使用wait fork來等待所有的進程執行完,如下代碼,只有exec1(), exec2(), exec3(), exec4()都執行完了,task才會結束。
1 task mytask; 2 fork: fork1 3 exec1(); 4 exec2(); 5 join_any 6 fork: fork2 7 exec3(); 8 exec4(); 9 join_none 10 11 wait fork; 12 endtask
使用disable fork,可以停止後臺掛起的進程。在fork...join_any中disable fork使用的較多,用來檢測程序,當fork...join_any中的任何一個監測進程執行OK後,就會使用disable fork來殺死fork中剩余的其他監測進程。
為了對Process更精細控制,SV中內建有Class,在UVM中使用到。(遇到再更新吧~.~)
3. task and function
1 task mytask(a, int b, output logic [15:0]u, v) 2 // a沒有定方向和類型,默認input logic; 3 // b默認是input方向 4 // v的方向和類型會繼承前一個參數,所以v也是output logic [15:0] 5 <...> 6 endtask
task和function也可以加標簽,如下代碼:
1 task mytask(a = 3, int b, output logic [15:0]u, v); 2 // a的默認值是3,如果傳參沒有傳,就會使用默認值 3 <...> 4 endtask: mytask 5 function my_func (a, int b, output logic [15:0]u, v); 6 <...> 7 endfunction: my_func
C語言有數值傳參和指針傳參,SV也有,在定義task的時候使用ref關鍵字去修飾形參,還可以加const,參考C語言的用法。
task中可以有時間消耗,function中不能有時間消耗。task可以調用function,反過來不可以。
4. Interface
使用interface...endinterface來定義,用來解決模塊之間的連接,模塊和硬件件的連接問題。可以像理解module一樣去理解interface,如下代碼:
1 // 定義一個interface類型 2 interface bus_A (input clk); //定義interface時可以像module一樣有端口 3 logic req; 4 logic [7:0] addr,data; 5 logic start,rdy; 6 endinterface 7 // 使用這個interface,在module中例化 8 module tb_top; 9 logic clk = 0; 10 bus_A sb_intf(clk); //例化 11 memMode u_inst_mem1(clk,sb_intf); //把memMode模塊,通過名字相同的線與sb_intf連接起來 12 endmodule
在定義interface的裏面,使用modport來對一些信號接口做子集分類,這樣在外層module中,例化一個模塊,模塊可以只連接interface其中的一個子集。
interface可以像module一樣,用#(AWIDTH=8,BWIDTH=9)來定義參數,在傳遞的過程中來改變值。
interface裏面還可以使用clock塊,在實際項目中遇到了再總結吧(~.~)!
在calss中使用interface,必須使用關鍵字virtual interface。
5. 同步和通信
同步和通信有三種:Semaphone, Event, Mailbox。最後一個是用來傳遞數據的。
5.1 Semaphones
解決進程資源間共享問題,提供四個函數:new, put, get, try_get。其中get相當於一個task,裏面有延遲,會阻塞其他的get拿到鑰匙,try_get相當於一個function,立即執行,不會產生阻塞。下面代碼示例:
1 semaphone sem0 = new(1); //鑰匙數量為1 2 initial begin:process 0 3 #10ns 4 void‘(sem0.try_get()) //不會阻塞,拿不到鑰匙直接執行下面的語句;拿到鑰匙返回值為1 5 sem0.get(); //不會拿到鑰匙,會一直阻塞在這裏,直到下面的進程100ns之後把鑰匙還了 6 sem0.put(); //還鑰匙 7 end:process 1 8 9 initial begin:process 1 10 sem0.get(); 11 #100ns 12 sem0.put(); //還鑰匙,key number 為1 13 end:process 1
5.2 Event
用來觸發事件,使用->;用來等待事件使用@或者wait。那麽@和wait有什麽區別呢?看下面的代碼:
1 event a; //使用關鍵字event來聲明一個事件a 2 initial begin 3 #1; 4 ->a; 5 end 6 initial begin 7 #1; 8 @a; //第一個進程在1ns後觸發了事件a,那麽第二個進程在1ns的時候等待a,有可能等的到,有可能等不到,產生競爭 9 end 10 initial begin 11 #1; 12 wait(a.triggered); //使用wait來等待事件a,這種方式是一定可以等到a的,這是和使用@來等待的區別 13 end
wait像是增強版的@,用來解決同一時刻的競爭冒險問題。
5.3 Mailbox
可以理解為能存儲任意數據類型的隊列。先挖坑,以後遇到了再做筆記(~.~)!
System Verilog基礎(二)