systemverilog學習(6)並發進程與內部通信


sv提供了下列處理並發進程的能力:

    fork...join並發結構,

    通過mailbox實現進程間的通信,

    通過semaphore實現進程互斥與仲裁,

    通過event實現進程之間的同步

一:fork...join

 fork...join能夠啟動產生多個並發進程,提供三種並發方式:fork...join,fork...join_any,fork...join_none

1: fork...join

  執行fork語句的進程,並阻塞父進程的執行,直到fork...join中所有進程(子進程)中止

  可以將子進程放在begin...end里,如下圖。process1與process2同時執行;直到20ns並且eventA被觸發,才結束整個fork...join的結構,執行父線程。

  

2:fork...join...any

  父進程會被阻塞直到任意一個子進程結束

  

 3:fork...join...none

  父進程不會被阻塞,父進程會立即與產生的所有進程並發操作

  

 4:wait fork

  等待所有fork進程全部執行完

  

5:disable fork

  用於看門狗等,在fork..join any中,任何一個fork進程執行完,其他進程被disable掉。

  如下例,第一個進程結束,其他進程則不執行,跳出fork,執行下面的語句。

  

二:events

1:事件觸發

  命名事件可以通過“->”操作符觸發。觸發一個事件可以接觸當前所有等待這個事件被阻塞的進程。當被觸發后,命名事件的行為就好像一次射擊,也就是說,觸發狀態本身是不可觀測的,我們僅僅能夠觀測到它的效果。這種方式類似於邊沿觸發一個觸發器,但邊沿的狀態卻不能永遠持續和獲取,也就是說,if(posedge clock)是非法的。

  阻塞事件用“->>”操作符觸發,“->>”操作符的效果1是語句會無阻塞地執行,並且在延時控制過期或者事件控制發生的時候,會產生一個非阻塞的賦值更新事件。

  狀態標志位trigered:檢查事件是否被觸發,若以及被觸發,則返回1。

2:事件等待

  @ event1; wait(event1.triggered);

3:wait_order

  用於看狀態機由一個狀態跳到另一個狀態

  wait_order(a,b,c)

4:事件可相互賦值,而且觸發的狀態也可賦給另一個值。

  

5:事件賦於null

  event e1 = null;

  事件賦值為空,則@E1;wait(E1.triggered);->E1;均無效。

6:事件比較

  可用==/!=進行事件的比較。

三:semaphores(旗語)

   當多個進程進行通信時,就可能出現競爭。多個進程可能需要使用一個稀缺資源,但是由於資源的約束,只有一定數量的進程才可以有訪問權限。現實的例子就是:如果多個進程需要通過一個接口發送數據包,每個進程需要等待自己獲取權限才可以發送(因為每次只能允許一個包在該接口上發送)。

  每當出現資源競爭的時候,獲取一個共享資源就必須通過仲裁。sv通過semaphore提供了仲裁的功能,提供對共享資源的訪問。

  從概念上講,semaphore可以理解成一個存儲桶。當為semaphore分配內存的時候,會創建一個帶有一定數目key的存儲桶。使用semaphore的進程在繼續執行之前必須首先從存儲桶中獲得一個key。同時執行的進程數目與key的數目一致。所有其他進程必須等待直到足夠數目的key被放回到存儲桶中。如圖下所示:

  

1:定義及其使用

  semaphore smtx;

  semaphore是一個內建的類,它提供下列方法:

  1)創建一個具有指定數目key的semaphore:new()

  原型:function new(int keyCount = 0)

  semaphore使用new()方法創建。KeyCount指定了最初被分配到semaphore存儲桶地key的數目。當放入存儲桶的key比取出的數目多的時候,存儲桶中key的數目可以超過KeyCount。KeyCount的缺省值為0。new()函數返回semaphore的句柄,如果沒有產生semaphore,則返回null。

  2) 從存儲桶里取出一個或者多個key:get()

  原型:task get(int KeyCount = 1)

  get()方法被用來從一個semaphore中獲得指定數目的key,KeyCount指定了需要獲得的key的數目,它的缺省值為1.如果指定數目的key存在,那么方法返回並且進程會繼續執行;如果不存在指定數目的key,進程會阻塞對應數量的key出現。

  3) 向存儲桶里放回一個或者多個key:put()

  原型:task put(int KeyCount = 1)

  KeyCount指定了放回到key的數目,它的缺省值為1。當調用semaphore.put任務的時候,指定數目的key被放回到semaphore中。如果一個進程已經被掛起來等待一個key,當返回了足夠的key之后,這個進程可以繼續執行。

  4) 嘗試無阻塞地獲取一個或者多個key:try_get()

  原型:function int try_get(int KeyCount = 1)

  try_get方法用來無阻塞地獲得指定數目的key,keyValue缺省值為1。如果指定數目的key存在,那么key_get方法返回1並且進程會繼續執行;如果指定數目的key不存在,那么try_get方法返回0.

四:mailbox  

  它可以看成是一個先進先出 ( first in first out,FIFO) 的存儲數組。 通常有一個或者多個進程把數據送入一個 mailbox, 有一個或者多個進程從 maibox 讀出數據。 客戶進程可以被掛起, 直mailbox有可用的數據, 也就是說, mailbox可以實現生產進程和客戶進程的同步。

  

  mailbox是一種通信機制,它使得數據可以在進程間傳遞和通信,數據被一個進程發送到另一個mailbox中,而另外一個進程可以從中可以獲得。

  從概念上講,mailbox的行為相當於一個真實的郵箱。當一封信被分發並且放到郵箱的時候,另一個人可以重新取出這封信(存儲在其中的任何數據)。然而,如果當這個人檢查郵箱的時候,這封信還沒有被分發,那么這個人必須做出選擇:要么等待這封信被分發,要么在下一次檢查郵箱時取出這封信。與此類似,sv的mailbox以一個可控的方式來傳輸和接收數據。在創建mailbox的時候,它可以是有界的隊列,也可以是無邊界的隊列。。當一個有界mailbox存儲的數據達到其邊界數目時,mailbox會變滿,試圖向已經滿了的mailbox放置數據的進程會被阻塞,直到mailbox隊列有足夠的空間;無邊界mailbox永遠也不會阻塞一個發送操作進程。

1:定義及其方法

  mailbox mbxRcv;

  mailbox是一個內建的類,它提供了下列方法:

  1)創建一個mailbox:new()

  缺省下,參數為0,表示mailbox是無邊界的,此時put()操作永遠不會被阻塞;如果參數不為0,那么它表示mailbox隊列的長度(或者理解為FIFO的深度)。長度必須是正的;負的邊界是非法的,並會導致不確定的行為。

  2) 將一個數據放置到mailbox中:put()

  嚴格按照FIFO的順序將一個數據存儲到mailbox中,如果mailbox是一個有界的隊列而且隊列已滿,那么進程會被阻塞直到隊列中有足夠的空間。

  3) 嘗試將一個數據無阻塞地放置到mailbox中:try_put()

  僅對有界mailbox有意義,如果mailbox沒有滿,那么指定的消息被放置mailbox當中,並且函數返回1。如果mailbox滿了,那么函數返回0.

  4) 從一個mailbox中獲取一個數據:get() 或者 peek()

  get():

  從mailbox中取出一個數據並從隊列中將其刪除。如果mailbox是空的,那么當前的進程阻塞直到一個數據被放置到mailbox中。如果在數據變量和mailbox中的數據存在類型不匹配,那么會產生一個運行時錯誤。

  peek():

  從mailbox里復制一個數據,但不會將其從隊列中刪除。如果mailbox是空的,那么當前的進程會阻塞直到一個消息被放置到mailbox中。如果在數據變量和mailbox中的數據存在類型不匹配,那么會產生一個運行時錯誤。

  5) 嘗試無阻塞從一個mailbox中獲取一個數據:try_get()或者try_peek()

  try_get:

  如果mailbox是空的,那么返回0。這里可以看出與get()的差別,try只從mailbox里獲取一次,而get()如果沒有獲取到數據,會等待直到一個數據數據被放到mailbox。

  try_peek:

  從mailbox里復制一個數據,但不會將其從隊列中刪除。如果mailbox是空的,那么返回0。

  6) 查詢mailbox中數據的個數:num()

   只有等到get()或者put()在mailbox上執行后才有效

2:阻塞與非阻塞操作

  

  

 3:參數化mailbox

  缺省的mailbox是無類型的,也就是說,單個mailbox可以發送和接收任何類型的數據。這是一個非常強大的機制,然而致命的是,它會因為存儲數據與用來獲取數據變量間的類型不匹配而導致運行錯誤,一個mailbox經常被用來傳輸一個特定類型的數據,在這種情況下,如果能夠在編譯時發現類型不匹配,那將是十分有用的。

  參數化mailbox與參數化類,模塊和接口使用相同的機制。其定義如下:

  mailbox #(type = dynamic_type)

  其中dynamic_type代表一個特殊的類型,它能夠執行運行時的類型檢查(缺省情況)。

  一個特定類型的參數化的mailbox通過指定類型來聲明。

 4:舉例

A: 同步mailbox

**延遲同步

  

  輸出

  

**握手同步

  

  輸出

  

**mailbox深度設為1:這種方法易導致race,並不適用於同步。

 B:異步mailbox

  由於mailbox產生(即send)一般不浪費時間,而處理(即receive)一般占用時間,所以不加處理的mailbox是異步的

  

  輸出:

  

 

 

  

 

 

 

  

  

 


免責聲明!

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



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