設計師使用HDL已超過十年,用它們取代基于原理圖的設計方法并傳達設計理念。 Verilog和VHDL是電子設計中使用最廣泛的兩種HDL。 Verilog擁有約35,000名活躍的設計師,他們使用Cadence的Verilog軟件套件完成了超過50,000個設計。
即使Verilog成功,許多經驗豐富的Verilog用戶仍然認為其編程語言界面( PLI)作為“軟件任務”。一步一步的方法可以幫助您在編寫PLI函數時“打破僵局”。通過學習PLI設計的基本知識而不會被太多細節困擾,您將獲得可以立即使用的PLI基礎知識。
為什么要使用PLI?
PLI為Verilog提供了應用程序接口(API)。本質上,PLI是一種從Verilog代碼調用C函數的機制。人們通常會調用在Verilog中調用PLI例程的構造,如果它是模擬器的一部分,則調用“系統任務”或“系統函數”,如果用戶編寫它,則調用“用戶定義的任務”或“用戶定義的函數”。由于PLI的基本機制在兩種情況下都保持不變,因此本文使用術語“系統調用”來表示這兩種結構。大多數Verilog模擬器包含的常見系統調用示例是$ display,$ monitor和$ finish。
您使用PLI主要用于執行使用Verilog語法無法執行的任務。例如,IEEE Standard1364-1995 Verilog有一個用于執行文件寫入的預定義構造($ fwrite,這是使用PLI編寫的另一個內置系統調用),但它沒有用于直接從a讀取寄存器值的構造。文件(參考文獻2)。更常見的任務是PLI是實現預期結果的唯一方法,包括編寫功能模型,計算延遲和獲取設計信息。 (例如,沒有Verilog構造在設計層次結構中給出當前模塊的父實例名稱。)
為了說明創建PLI例程的基本步驟,請考慮清單1中的問題。問題比使用PLI解決的現實問題簡單得多,但它顯示了許多用于構建PLI例程的基本步驟。當您在列表中運行Verilog時,它應該在時間100打印寄存器的值為10,在時間300打印3.您可以考慮創建一個PLI例程作為兩個步驟:首先,編寫PLI例程在C;然后,編譯并將此例程鏈接到模擬器的二進制代碼。
編寫PLI例程
PLI例程與模擬器接口的方式因模擬器而異,盡管主要功能保持不變。本文討論兩種最流行的商業模擬器的接口機制,Cadence的Verilog-XL(參考文獻3)和Synopsys的VCS(參考文獻4)。雖然其他商業模擬器支持PLI,但它們的接口機制與這兩者沒有顯著差異。多年來,Verilog PLI已發展為PLI 1.0和Verilog程序接口(VPI)(參見側欄“Verilog PLI的簡史”)。本文僅涉及PLI 1.0。盡管接口部件和版本存在差異,但您可以將PLI例程的創建分解為四個主要步驟。
步驟1:包含頭文件
按照慣例,C程序在文件veriuser.c中實現PLI例程。雖然您可以更改此名稱,但在Verilog-XL環境中生成編譯腳本時,vconfig工具會采用此默認名稱。現在,假設您將PLI例程保留在文件veriuser.c中。
在Verilog-XL環境中,文件veriuser.c必須以以下行開頭:
在VCS環境中,文件必須以:
開頭
這些頭文件包含程序將使用的Verilog PLI的最基本數據結構。
步驟2:聲明函數原型和變量
PLI例程由幾個函數組成。正如您對普通C程序所做的那樣,您應該在函數定義之前放置函數的原型聲明。對于這種情況,函數顯示為:
在上面的函數中,int原型聲明意味著這些函數在它們結束時返回一個整數執行。如果沒有錯誤,則正常返回值為0.但是,如果函數位于單獨的文件中,則應將它們聲明為外部函數:
與任何其他C程序一樣,典型的PLI例程可能需要一些其他管家變量。
步驟3:設置基本數據結構
您必須定義一個數字PLI程序中的數據結構。 Verilog模擬器通過這些變量與C代碼通信。 Open Verilog International(OVI)(www.ovi.org/pubs.html)是一個標準化Verilog的組織,它只推薦一個強制數據結構veriusertfs。但是,這些數據結構的確切數量和語法因模擬器而異。例如,Verilog-XL需要四個這樣的數據結構及其功能,以便任何PLI例程工作; VCS不需要它們,而是使用單獨的輸入文件。
Verilog-XL的主要接口數據結構是一個結構數組或一個名為veriusertfs的表。 Verilog-XL使用此表來確定與此PLI例程對應的系統調用關聯的屬性。模擬器無法識別除veriusertfs之外的任何名稱。 veriusertfs的每個元素都有一個獨特的函數,你需要所有這些函數來實現正確編寫PLI例程的總體目標。 veriusertfs中的行數與用戶定義的系統調用數加上最后一個條目的行數相同,這是強制性的0.在這種情況下,veriusertfs數組應如下所示:
第一個條目usertask表示系統調用沒有返回任何內容。它等同于Pascal中的過程或函數返回C中的void。
在前面的數據結構中,my_ checktf和my_calltf是用于實現系統調用$ print_reg的兩個函數的名稱。這些名稱是任意的,您可以用其他名稱替換它們。函數my_checktf通常稱為checktf例程。它檢查傳遞的參數的有效性。同樣,my_calltf(通常稱為calltf例程)執行系統調用的主要任務。這些名稱在veriusertfs中的位置非常重要。例如,如果要將my_checkt用作任何其他名稱作為檢查函數,則它必須是第三個元素。函數veriusertfs為您在此例程中不會使用的一些其他用戶定義函數提供選項。零替換任何您不使用的函數。因此,條目中的第二,第四和第六個元素是零。表1總結了veriusertfs行中每個條目的目標。如果有其他系統調用,則需要在veriusertfs中為每個系統調用單獨的條目。
Verilog-XL還需要以下變量或函數:
第一個變量veriuser_version_str是一個字符串,表示應用程序的用戶定義 - 版本信息。 bool(布爾)變量是一個整數子類型,允許值為0和1.在大多數情況下,您可以使用Cadence為這些變量或函數提供的默認值。
在VCS中,而不是表,您在單獨的文件中使用等效信息,通常稱為pli.tab。該名稱也是用戶定義的。在使用$ print_reg的當前示例中,此文件的內容為:
步驟4:組成函數
原型聲明到位后,您就可以編寫兩個函數my_checktf()和my_calltf(),它們構成了PLI應用程序的主體。
如前所述,checktf例程檢查傳遞參數的有效性。最好檢查參數總數是否與預期相同,以及是否需要每個參數。例如,在這種情況下,您希望程序只傳遞一個類型為register的參數。您可以在函數my_checktf()中執行此任務(清單2)。
以tf_開頭的函數是庫函數,通常稱為實用程序例程。庫函數在前一個函數中使用,它們的用法是tf_nump(),它確定傳遞的參數數量,以及tf_typep(),它根據Verilog代碼中系統調用中的位置確定參數類型。在這種情況下,系統調用是$ print_reg。
因此,tf_typep(1)給出第一個參數的類型,tf_typep(2)給出第二個參數的類型,依此類推。如果參數不存在,則tf_typep()返回錯誤。 (在這種情況下,tf_typep(2)不存在。)在當前示例中,您希望程序將寄存器值作為參數傳遞。因此,類型應該是tf_readwrite。如果導線是預期參數,則類型應為tf_readonly。為了便于錯誤條件檢查,最好首先檢查參數的數量,然后檢查它們的類型。 tf_error()函數輸出錯誤消息并通知模擬器增加其錯誤計數。這些庫函數和常量是您在步驟1中包含在文件頂部的頭文件的一部分。
calltf函數是PLI例程的核心。它通常包含PLI例程的主體。在這種情況下,它應該讀取寄存器的值,然后打印該值。以下代碼顯示了如何完成此任務:
在上面的代碼中,io_printf()與C中的printf()執行相同的工作,在標準輸出中打印值。此外,該函數在Verilog日志文件中打印相同的信息。函數tf_getp()獲取寄存器的整數值。函數tf_gettime()返回當前模擬時間,不需要輸入參數。 Calltf是PLI例程中最復雜的函數。它經常運行幾百行。
現在你已經創建了兩個主要功能,你需要把它們放在一個地方。清單3顯示了如何在Verilog-XL環境中實現$ print_reg。
接下來的任務是讓Verilog模擬器了解新系統調用的存在。要完成此任務,您必須編譯PLI例程,然后將其鏈接到模擬器的二進制文件。雖然您可以通過運行C編譯器并合并目標文件來手動將PLI代碼與Verilog二進制文件集成,但使用腳本更方便。在Verilog-XL中,一個名為vconfig的程序會生成此腳本。此腳本的默認名稱是cr_vlog。在生成此腳本時,程序vconfig會詢問您喜歡的已編譯Verilog的名稱。它還詢問是否包含來自標準供應商的模型庫,它們是PLI代碼。對于大多數這些問題,如果您在不輸入任何內容的情況下按回車鍵輸入的默認答案就足夠了,除非您有自定義環境。最后,vconfig會詢問您的veriuser.c文件的路徑。一旦生成腳本cr_vlog,您只需要運行腳本來生成可以執行新系統調用的自定義Verilog模擬器。
使用此PLI例程編譯的Verilog示例運行生成輸出Verilog-XL模擬器(清單4)。
修改寄存器的值
使用PLI是讀取設計信息并在設計數據庫中修改此信息。以下示例顯示了如何進行此修改。創建系統調用$ invert,以打印寄存器的值(如上例所示),按位反轉內容,并打印此更新值。存在許多方法來反轉二進制數的值。通過使用簡單的算法進行反演,如下面的列表所示,可以幫助您更深入地了解PLI機制和庫函數:
將寄存器的內容讀取為string;
將字符串中的所有字符串轉換為twos,將所有零轉換為1,將所有二進制轉換為零,將所有Z轉換為X,并使X保持不變;和
將修改后的字符串放回寄存器。
第二步將所有1轉換為零和0。請注意,在這種情況下,checktf函數與前一個函數沒有區別,因為在這兩種情況下輸入參數的數量和類型都是相同的。
創建PLI例程
列表圖5顯示了VCS環境中$ invert例程的實現。該程序使用以下庫函數:
tf_strgetp()以字符串形式返回寄存器的內容。只需liketf_getp(),它將寄存器的內容讀作十進制。在清單5中,tf_strgetp(1,b)以二進制格式讀取第一個參數的內容。 (h或o代替b,分別以十六進制或八進制格式讀取)。例程然后將內容復制到字符串val_string。
tf_strdelputp()將值從字符串值規范寫入參數。該函數有許多參數,包括參數索引號(系統調用中參數的相對位置,最左邊的參數從1開始);參數包含其值的字符串的大小(在這種情況下,它應與寄存器的大小相同);編碼基數;實際的字符串;和另外兩個與延遲相關的參數,你可以通過為它們傳遞零來忽略它們。雖然在當前示例中沒有顯示,但是tf_strdelputp()的更簡單的十進制對應是tf_putp()。
重要的是要記住程序只能更改或覆蓋來自PLI例程的某些對象的值。任何嘗試更改無法放在Verilog中的過程賦值左側的變量(例如連線)都會導致錯誤。通常,您可以修改tf_readwrite類型的變量的內容。 checktf函數my_checktf()檢查此功能。
清單5使用的另一個用戶定義函數是move(),它有三個參數。在第三個參數中,第二個參數替換第一個參數的每個匹配項。在當前情況下,對move()的一系列調用將零更改為1和1更改為零。但是,一旦程序將零轉換為1,您必須將字符串中的轉換元素與原始元素區分開來。為了做出這種區分,程序首先將所有初始的更改為兩個。二是二進制邏輯的無效值,但您可以在此示例的中間步驟中使用它。最后,程序將所有兩個轉換為零。程序通過使用tf_strdelputp()函數將反轉的值放回寄存器來完成其任務。
清單6顯示了一個包含調用$ invert的Verilog程序示例。在VCS環境中,文件列出與PLI例程關聯的功能。雖然這個文件可以有任何名稱,但通常稱之為pli.tab。該文件等同于Verilog-XL中的veriusertfs []結構。當前示例中此文件的內容為:
通過將此程序保存在test.v文件中,可以生成可執行二進制文件pli_invert命令:
執行pli_invert,你得到:
你現在了解PLI例程的基本結構以及將其鏈接到構建Verilog自定義版本的機制。您還可以通過PLI例程讀取,轉換和修改Verilog中設計數據庫的基本組件的值,并讀取例程的模擬時間。這些任務是PLI例程執行的基本任務,也是最常用的任務。考慮到PLI提供的所有信息,這些信息只是冰山一角。您還可以使用PLI訪問寄存器內容以外的設計信息。 (有關Verilog和C建模的更多信息,請參閱參考文獻5和6。)
-
編程
+關注
關注
88文章
3633瀏覽量
93853 -
PCB打樣
+關注
關注
17文章
2968瀏覽量
21760 -
華強PCB
+關注
關注
8文章
1831瀏覽量
27847 -
華強pcb線路板打樣
+關注
關注
5文章
14629瀏覽量
43109
發布評論請先 登錄
相關推薦
評論