sv包的意義(package)

轉(zhuǎn)自(https://blog.eetop.cn/blog-1561828-2316833.html
SV語言提供了一種在多個module、interface和program之中共享parameter、data、type、task、function、class等等的方法,即利用package(包)的方式來實現(xiàn)。如果用上面裝修一個大房子(MCDF testbench)來看的話,我們喜歡將不同模塊的類定義歸整到不同的package中。這么做的好處在于將同一簇相關(guān)的類組織在了單一的名字空間(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個與模塊驗證相關(guān)的類即stimulator、monitor、checker和env。而在這兩個package中同名的類,實際上內(nèi)容是不相同的,實現(xiàn)的也是不同的功能。如果我們將這些重名的類歸屬到不同的package中編譯,有沒有問題呢?會不會發(fā)生重名的編譯沖突?讀者不需要為此擔(dān)心,package允許你做的,就是將單一的全局名字空間分隔開來,這樣如果要使用不同package中的同名類,他們需要強(qiáng)調(diào)要使用哪一個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更多的意義在于將軟件(類、類型、方法等)封裝在不同的命名空間中,以此來與全局的命名空間進(jìn)行隔離。package需要額外定義,容納各種數(shù)據(jù)、方法和類。
library是編譯的產(chǎn)物,在沒有介紹軟件之前,硬件(module、interface、program)都會編譯到庫中,如果不指定編譯庫的話,會被編譯進(jìn)入默認(rèn)的庫中。從容納的類型來看,庫既可以容納硬件類型,也可以容納軟件類型,例如類和方法,也包括package。
從上面的聯(lián)系來看,不同package之間可能存在著同名的類,不同的library之間也可能存在著同名的module(硬件)或者package(軟件)。如果像上面的例子,遇到了不同package中同名的類,那么用戶可以通過“::”來顯式調(diào)用具體哪個package中的類;如果遇到的是不同library中,有同名的module或者package等,那么應(yīng)該怎么解決呢?常見的方式是通過HDL語言的config文件(VHDL、Verilog、SV均有該特性)來指定具體模塊從哪個library中選取。默認(rèn)情況下,采取的是“就近原則”,即調(diào)用該重名模塊的上層模塊位于哪個library,則仿真器會優(yōu)先從該library中尋找該模塊。這個原則,對于package也是適用的,即使用該重名package的module或者interface會優(yōu)先尋找它們所在的library。如果用戶想讓其優(yōu)先搜尋非默認(rèn)的庫,那么需要在config文件中指定尋找的庫名和順序。
在編譯package時,需要注意的是,不應(yīng)該有同名的package存在。那么,當(dāng)類和方法并沒有被封裝在某個package時,它們會被編譯到哪兒去了呢?實際上,仿真器仍然會將它們編譯到默認(rèn)的package中。但是,只有與該文件放置在一起的module才能識別和引用到它,至于該文件之外的module要想辦法引用到這些類和方法,則沒有什么好的辦法了。所以,如果像讓你的類和方法被更多的人使用,更方便的共享,一個基本方式就是首先將它們組織到一個package中。
此外,對于編譯的module、interface和package這些硬件和軟件,會進(jìn)入哪一個library呢?如果沒有額外的指定,它們都會被編譯到默認(rèn)library(work)中。因此,在默認(rèn)庫中,各個module是互相認(rèn)識的,當(dāng)然module也認(rèn)識同一個library中的package。如果要使用其他library中的module或者package,那么一個config文件是一項好的選擇。

2 接下來是關(guān)于定義package的一些好的建議:

在創(chuàng)建package的時候,已經(jīng)在指定包名稱的時候隱含地指定了包的默認(rèn)路徑,即包文件所在的路徑。如果有其它要被包含在包內(nèi)的文件在默認(rèn)路徑之外,需要在編譯包的時候加上額外制定的搜尋路徑選項“+incdir+PATH”。
如果遵循package的命名習(xí)慣,不但我們要求定義的package名稱獨一無二,其內(nèi)部定義的類也應(yīng)該盡可能地獨一無二。例如,上面的例子中,regs_pkg和arb_pkg中有同名的類,這些類如果攜帶類名的前綴,那么后面的處理會變得更容易一些。從下面這個例子可以發(fā)現(xiàn),如果不同package中定義的類名也不相同時,在頂層的引用也可以通過“import pkg_name::*”的形式,來表示在module mcdf_tb中引用的類如果在當(dāng)前域(mcdf內(nèi)部)中沒有定義的話,會搜尋regs_pkg和arb_pkg中定義的所有類型,又由于它們各自包含的類名不相同,因此也無需擔(dān)心下面的搜尋會遇到同名類發(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

本文最后的部分是關(guān)于使用package的一些注意事項:
用戶可以在module、interface或者program中來引用package。
如果是”import pkg_name::*“,則代表的是該package中定義的類型可能會在module等內(nèi)部有效可見。只有當(dāng)module等無法在內(nèi)部索引到正確地類型時,才會轉(zhuǎn)而去package中去搜尋,如果索引到了那么該package中的這個類型則變得在module中可見。

package a_pkg;
  class mon;
  endclass
endpackage
module module1;
class mon;
endclass
import a_pkg::*;
mon mon1 = new(); // 已經(jīng)有內(nèi)部mon定義,因此不會搜尋a_pkg
endmodule

如果用戶使用了“import pkg_name::type_name”,則表示直接讓package_name::type_name類型在module等內(nèi)部變?yōu)榭梢?,那么此時需要注意的是,module內(nèi)部不應(yīng)該再有其它同名的類型定義,避免發(fā)生同名類型定義的沖突。

package a_pkg;
  class mon;
  endclass
endpackage
module module1;
class mon;
endclass
import a_pkg::mon;
mon mon1 = new(); // 同時有內(nèi)部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操作使得類型可見的域只是調(diào)用該import時當(dāng)前的域。例如下面的例子中,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ā)現(xiàn),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)函數(shù)和任務(wù),例如stop()、randomize()等等凡是帶有“$”符號的方法,另外一種調(diào)用的方式是std::method,例如std::randomize()。這隱含地是所有的系統(tǒng)方法都是預(yù)定義在一個稱之為std包中的。用戶只能使用這些包內(nèi)的方法和類型,無法二次對std包做出修改和添加。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容