轉自(https://blog.eetop.cn/blog-1561828-2316833.html)
SV語言提供了一種在多個module浓镜、interface和program之中共享parameter盔腔、data、type顶瞳、task猎荠、function、class等等的方法裹驰,即利用package(包)的方式來實現派撕。如果用上面裝修一個大房子(MCDF testbench)來看的話婉弹,我們喜歡將不同模塊的類定義歸整到不同的package中。這么做的好處在于將同一簇相關的類組織在了單一的名字空間(namespace)下终吼,使得分屬于不同模塊驗證環(huán)境的類首先來自于不同的package镀赌,這樣可以通過package來區(qū)別類的歸屬問題。
1 我們來看看這樣一個實際的問題吧际跪,register和arbiter的verifier給自己的package定義是這樣的商佛。
package regs_pkg;
`include "stimulator.sv"
`include "monitor.sv"
`include "chker.sv"
`include "env.sv"
endpackage
package arb_pkg;
`include "stimulator.sv"
`include "monitor.sv"
`include "chker.sv"
`include "env.sv"
endpackage
兩位verifier在各自的package regs_pkg和arb_pkg中都定義了4個與模塊驗證相關的類即stimulator、monitor姆打、checker和env良姆。而在這兩個package中同名的類,實際上內容是不相同的幔戏,實現的也是不同的功能玛追。如果我們將這些重名的類歸屬到不同的package中編譯,有沒有問題呢闲延?會不會發(fā)生重名的編譯沖突痊剖?讀者不需要為此擔心,package允許你做的垒玲,就是將單一的全局名字空間分隔開來陆馁,這樣如果要使用不同package中的同名類,他們需要強調要使用哪一個package中的合愈。譬如下面這個例子:
module mcdf_tb;
regs_pkg::monitor mon1 = new();
arb_pkg::monitor mon2 = new();
endmodule
盡管在regs_pkg和arb_pkg中都存在著一個名字為monitor的類叮贩,我們可以在引用類名的時候通過域名索引“::”操作符的方式來顯式指出所引用的類monitor具體來自于哪一個package,這樣便能很好地通過不同名的package來管理同名的類想暗。從這個簡單的例子來看妇汗,package這個容器可以對類名做一個隔離的作用。那么说莫,有的讀者可能會在pakcage(包)和library(庫)之間有混淆杨箭,我們來看看它們的聯(lián)系和區(qū)別:
package更多的意義在于將軟件(類、類型储狭、方法等)封裝在不同的命名空間中互婿,以此來與全局的命名空間進行隔離。package需要額外定義辽狈,容納各種數據慈参、方法和類。
library是編譯的產物刮萌,在沒有介紹軟件之前驮配,硬件(module、interface、program)都會編譯到庫中壮锻,如果不指定編譯庫的話琐旁,會被編譯進入默認的庫中。從容納的類型來看猜绣,庫既可以容納硬件類型灰殴,也可以容納軟件類型,例如類和方法掰邢,也包括package牺陶。
從上面的聯(lián)系來看,不同package之間可能存在著同名的類辣之,不同的library之間也可能存在著同名的module(硬件)或者package(軟件)掰伸。如果像上面的例子,遇到了不同package中同名的類召烂,那么用戶可以通過“::”來顯式調用具體哪個package中的類碱工;如果遇到的是不同library中娃承,有同名的module或者package等奏夫,那么應該怎么解決呢?常見的方式是通過HDL語言的config文件(VHDL历筝、Verilog酗昼、SV均有該特性)來指定具體模塊從哪個library中選取。默認情況下梳猪,采取的是“就近原則”麻削,即調用該重名模塊的上層模塊位于哪個library,則仿真器會優(yōu)先從該library中尋找該模塊春弥。這個原則呛哟,對于package也是適用的,即使用該重名package的module或者interface會優(yōu)先尋找它們所在的library匿沛。如果用戶想讓其優(yōu)先搜尋非默認的庫扫责,那么需要在config文件中指定尋找的庫名和順序。
在編譯package時逃呼,需要注意的是鳖孤,不應該有同名的package存在。那么抡笼,當類和方法并沒有被封裝在某個package時苏揣,它們會被編譯到哪兒去了呢?實際上推姻,仿真器仍然會將它們編譯到默認的package中平匈。但是,只有與該文件放置在一起的module才能識別和引用到它,至于該文件之外的module要想辦法引用到這些類和方法增炭,則沒有什么好的辦法了街望。所以,如果像讓你的類和方法被更多的人使用弟跑,更方便的共享灾前,一個基本方式就是首先將它們組織到一個package中。
此外孟辑,對于編譯的module哎甲、interface和package這些硬件和軟件,會進入哪一個library呢饲嗽?如果沒有額外的指定炭玫,它們都會被編譯到默認library(work)中味抖。因此躏哩,在默認庫中抹锄,各個module是互相認識的践险,當然module也認識同一個library中的package旬薯。如果要使用其他library中的module或者package拿穴,那么一個config文件是一項好的選擇严卖。
2 接下來是關于定義package的一些好的建議:
在創(chuàng)建package的時候宴抚,已經在指定包名稱的時候隱含地指定了包的默認路徑袄膏,即包文件所在的路徑践图。如果有其它要被包含在包內的文件在默認路徑之外,需要在編譯包的時候加上額外制定的搜尋路徑選項“+incdir+PATH”沉馆。
如果遵循package的命名習慣码党,不但我們要求定義的package名稱獨一無二,其內部定義的類也應該盡可能地獨一無二斥黑。例如揖盘,上面的例子中,regs_pkg和arb_pkg中有同名的類锌奴,這些類如果攜帶類名的前綴兽狭,那么后面的處理會變得更容易一些。從下面這個例子可以發(fā)現缨叫,如果不同package中定義的類名也不相同時椭符,在頂層的引用也可以通過“import pkg_name::*”的形式,來表示在module mcdf_tb中引用的類如果在當前域(mcdf內部)中沒有定義的話耻姥,會搜尋regs_pkg和arb_pkg中定義的所有類型销钝,又由于它們各自包含的類名不相同,因此也無需擔心下面的搜尋會遇到同名類發(fā)生沖突的問題琐簇。
package regs_pkg;
`include "regs_stm.sv"
`include "regs_mon.sv"
`include "regs_chk.sv"
`include "regs_env.sv"
endpackage
package arb_pkg;
`include "arb_stm.sv"
`include "arb_mon.sv"
`include "arb_chk.sv"
`include "arb_env.sv"
endpackage
module mcdf_tb;
import regs_pkg::*;
import arb_pkg::*;
regs_mon mon1= new();
arb_mon mon2 = new();
endmodule
本文最后的部分是關于使用package的一些注意事項:
用戶可以在module蒸健、interface或者program中來引用package座享。
如果是”import pkg_name::*“,則代表的是該package中定義的類型可能會在module等內部有效可見似忧。只有當module等無法在內部索引到正確地類型時渣叛,才會轉而去package中去搜尋,如果索引到了那么該package中的這個類型則變得在module中可見盯捌。
package a_pkg;
class mon;
endclass
endpackage
module module1;
class mon;
endclass
import a_pkg::*;
mon mon1 = new(); // 已經有內部mon定義淳衙,因此不會搜尋a_pkg
endmodule
如果用戶使用了“import pkg_name::type_name”,則表示直接讓package_name::type_name類型在module等內部變?yōu)榭梢娊戎敲创藭r需要注意的是箫攀,module內部不應該再有其它同名的類型定義,避免發(fā)生同名類型定義的沖突幼衰。
package a_pkg;
class mon;
endclass
endpackage
module module1;
class mon;
endclass
import a_pkg::mon;
mon mon1 = new(); // 同時有內部mon定義和引入a_pkg::mon靴跛,發(fā)生同名類型沖突
endmodule
如果在package a_pkg中import了package b_pkg::type_b,那么在module1中import a_pkg::*時渡嚣,無法引用到type_b梢睛。因為a_pkg只是使得b_pkg::type_b在a_pkg域中可見并加以使用,并未定義在a_pkg中识椰。所以绝葡,用戶需要牢記一點的是,import操作使得類型可見的域只是調用該import時當前的域裤唠。例如下面的例子中挤牛,a_pkg中可見b_pkg::b_mon,但是module1則無法可見a_pkg::b_mon种蘸。
package b_pkg;
class b_mon;
endclass
endpackage
package a_pkg;
import b_pkg::b_mon;
class a_mon;
endclass
endpackage
module module1;
import a_pkg::*;
a_mon mon1 = new(); // a_mon可見
b_mon mon2 = new(); // b_mon不可見
endmodule
要解決上面的問題,用戶可以使用export來讓b_mon在a_pkg中得到二次定義竞膳。從下面這個例子中可以發(fā)現航瞭,a_pkg中需要額外使用export來讓b_pkg::b_mon在a_pkg得到定義。因此坦辟,在module1中import a_pkg::*刊侯,可以搜尋到a_pkg中的a_mon和b_mon兩種類型。
package b_pkg;
class b_mon;
endclass
endpackage
package a_pkg;
import b_pkg::b_mon;
export b_pkg::b_mon;
class a_mon;
endclass
endpackage
module module1;
import a_pkg::*;
a_mon mon1 = new();
b_mon mon2 = new();
endmodule
用戶使用到的系統(tǒng)函數和任務锉走,例如randomize()等等凡是帶有“$”符號的方法,另外一種調用的方式是std::method挪蹭,例如std::randomize()亭饵。這隱含地是所有的系統(tǒng)方法都是預定義在一個稱之為std包中的。用戶只能使用這些包內的方法和類型梁厉,無法二次對std包做出修改和添加辜羊。