1. 包的定义
- SV提供了一种在多个module、interface和program中共享parameter、data、type、task、function、class等的方法,即利用package(包)的方法来实现。完整的验证环境会讲不同模块的类定义规整到不同的package中;
- 讲一簇相关的类组织在了单一的命名空间(namespace)下,使得分属于不同模块验证环境的类来自于不同的package,这样可以通过package来解决类的归属问题;
2. 包的定义
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
- 两个package中同名的类,内容是不相同的,实现的功能也是不同的;
- 不同名的package中同名的类不会发生编译冲突:package是将命名空间分隔开了,当使用同名的类是,注明要使用哪一个package中的类;
module test_tb;
regs_pkg::monitor mon1 = new();
arb_pkg::monitor mon2 = new();
endmodule
3. 包与库的区分
-
上述代码例子,regs_pkg和arb_pkg中的存在着同名变量,可以通过域名索引"::"操作符 的方式显式地指出所引用地monitor类具体来自于哪一个package,这样可以通过不同命的package来管理同名的类,也就是package这个容易可以对类名做一个隔离的作用;
-
package更多的意义在于将软件(类、类型、方法等)封装在不同的命名空间中,以此来与全局的命名空间进行隔离;
-
library是编译的产物,在没有介绍软件之前,硬件(module、interface、program)都会编译到库中。从容纳的类型来看,库既可以容纳硬件类型,也可以容纳软件类型;
-
若在顶层需要例化两个同名的模块,需要说明两个模块分别是从哪来的,config配置文件说明
-
Questa中,编译简单的module(.sv中只有module)后,模块的模型就会编译到默认库中,同样的还有interface、program
-
当在Questa工程中再添加同名(不同路径)的module tb,再编译她,后编译的文件也会编译进默认work库中,就会顶掉之前编译的
【同名module不能编译到同一个库中,若在顶层需要例化两个同名的module,就需要说明两个模块分别是从哪来的,用config配置文件说明】 没这样用过,以后遇到补充 -
若Questa工程下有两个源文件package_import.sv有module tb,class_inheritance.sv也有module tb,里面都有tb模块,默认编译会把module添加到默认work库中,也可以在编译时指定编译库,相应跑仿真也就需要改动
编译
vlog -sv package_import.sv
vlog -sv class_inheritance.sv -work.newlib仿真
vsim -novopt -classdebug work.tb
vsim -novopt -classdebug newlib.tb
4. 包的命名规则
- 定义的package名称独一无二,其内部定义的类也应该尽可能地独一无二,比如添加前缀,方便处理;
- 如果不同package中定义的class名也不相同,在顶层的引用可以通过【import pkg_name :: 】的形式,来表示在module mcdf_tb中引用的类,如果在当前域(mcdf内部)中没有定义的话,会搜寻regs_pkg和arb_pkg的定义的类,又由于它们各自包含的类名不相同,因此无需担心下面的搜寻会遇到同名类发生冲突的问题
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 test_tb;
import regs_pkg::*; // *代表全部类型
import arb_pkg::*;
regs_mon mon1 = new();
arb_mon mon2 = new();
endmodule
5. 包的使用
- 在包中可以定义类、静态方法和静态变量
- 如果将类封装在某一个包中,那么它就不应该在其他地方编译,这么做的好处在于之后对类的引用更加方便
- 一个完整模块的验证环境组件类,应该是由一个对应的模块包来封装
- 使用’include关键字完成类在包中的封装,要注意编译的前后顺序来放置各个‘include的类文件
- 编一个包的背后实际是将各个类文件用纯文本替换,按照顺序完成包和各个类的有序编译
- 使用“import”可以将包中所以类或者某一个类导入
补充:包的编译和代码示例
- 在library中编译module的时候,默认会查找所有的library
- 在modle中声明类(类在package中),编译时只能查找到library中有package,不会进一步往下找(package内部),相当于package - endpackage关键字把里面的内容包进了一个盒子,除非主动告诉要到某一个盒子中找类型
package pkg_a;
class packet_a;
endclass
typedef struct {
int data;
int command;
} struct_a;
int va = 1;
endpackage
package pkg_b;
class packet_b;
endclass
typedef struct {
int data;
int command;
} struct_b;
int vb = 2;
endpackage
module mod_a;
endmodule
module mod_b;
endmodule
module tb;
class packet_tb;
endclass
typedef struct {
int data;
int command;
} struct_tb;
mod_a ma();
mod_b mb();
// import pkg_a::packet_a;
// import pkg_b::packet_b;
// import pkg_a::va;
// import pkg_b::vb;
import pkg_a::*;
import pkg_b::*;
initial begin
//-- packet_a pta = new(); // err
//-- packet_b ptb = new(); // err
packet_a pa = new();
packet_b pb = new();
packet_tb ptb = new();
$display("pkg_a::va = %0d, pkg_b::vb = %0d", va, vb);
end
endmodule
class packet_a;
int tb_a;
endclass
class packet_b;
int tb_b;
endclass
mod_a ma();
mod_b mb();
import pkg_a::packet_a; // err: 'packet_a' already declared in this scope 模块内有重名类
import pkg_b::packet_b;
// import pkg_a::va;
// import pkg_b::vb;
// import pkg_a::*; // 如果在当前域中没找到的话,会搜寻包内部,重名不会报错
// import pkg_b::*;
initial begin
//-- packet_a pta = new(); // err
//-- packet_b ptb = new(); // err
packet_a pa = new();
packet_b pb = new();
packet_tb ptb = new();
$display("pkg_a::va = %0d, pkg_b::vb = %0d", va, vb);
end
- 用import::package_name::*导入更安全,这样先在当前域中找,没找到在进包中找,如果当前域和包中有重名的也不会报错
package pkg_a;
class packet_a;
int pkg_a;
endclass
typedef struct {
int data;
int command;
} struct_a;
int va = 1;
int shared = 10;
endpackage
package pkg_b;
class packet_b;
int pkg_b;
endclass
typedef struct {
int data;
int command;
} struct_b;
int vb = 2;
int shared = 20;
endpackage
module mod_a;
endmodule
module mod_b;
endmodule
module tb;
class packet_tb;
endclass
typedef struct {
int data;
int command;
} struct_tb;
class packet_a;
int tb_a;
endclass
class packet_b;
int tb_b;
endclass
mod_a ma();
mod_b mb();
// import pkg_a::packet_a; // err: 'packet_a' already declared in this scope 模块内有重名类
// import pkg_b::packet_b;
// import pkg_a::va;
// import pkg_b::vb;
import pkg_a::*; // 如果在当前域中没找到的话,会搜寻包内部,重名不会报错
import pkg_b::*;
initial begin
//-- packet_a pta = new(); // err
//-- packet_b ptb = new(); // err
packet_a pa = new();
packet_b pb = new();
packet_tb ptb = new();
$display("pkg_a::va = %0d, pkg_b::vb = %0d", va, vb);
$display("shared = %0d", shared); // err: Identifier 'shared' is not directly visible 不直接可见
end
endmodule
- 在两个包中添加同名变量shared,需要显式指明哪个包里的
package pkg_a;
...
int shared = 10;
endpackage
package pkg_b;
...
int shared = 20;
endpackage
... ...
module tb;
...
import pkg_a::*; // 如果在当前域中没找到的话,会搜寻包内部,重名不会报错
import pkg_b::*;
initial begin
packet_a pa = new();
packet_b pb = new();
packet_tb ptb = new();
$display("pkg_a::va = %0d, pkg_b::vb = %0d", va, vb);
// $display("shared = %0d", shared); // err: Identifier 'shared' is not directly visible 不直接可见
$display("shared = %0d", pkg_a::shared);
end
endmodule
- package名一定不能重,建议内部的变量、类也最好不要重
3.16 补充
- 全局变量和局部变量是从变量的作用域的角度划分;
- 静态变量和动态变量是从变量的内存分配的角度划分;
- package中声明的变量是静态的、全局的;