Verilog系列:package封装成员
在module、interface、program等结构中可以对类、变量、线网、任务、函数等多种成员进行声明,并且这些成员很多具有相同的声明定义形式,那么有没有一种方法将这些“同名同姓”的成员放在一个共享的地方呢?SystemVerilog借鉴了VHDL中的package结构,通过将共享的内容添加到package结构中实现了这些成员的共享,实现了代码的分割、共享和重用.
package pkg_name; Net_declaration; Data_declaration; Task_declaration; Function_declaration; Checker_declaration; Dpi_import_export; Extern_constraint_declaration; Class_declaration; Class_constructor_declaration; Localparameter_declaration; Parameter_declaration; Coverage_declaration; Overload_declaration; Assertion_iterm_declaration; Package_export_declaraton; Timeunits_declaration; endpackage
注意,这里的parameter不能像module中那样在例化时指定(package是不能例化的),所以其中的parameter和localparam是等同的.又因为package中的成员会被其他结构引用,所以package的编译必须先于使用该package的模块的编译.而一般情况下,package通过以下两种方式实现其中成员在其他模块中的引用.
-
在设计代码中通过"::"作用域符号直接引用package中的元素;
-
在设计代码中通过import使package中的元素可以被访问;
1 在设计代码中通过作用域符号引用package中的元素
`timescale 1 ns /1 ps `define DLY 1 package p1; typedef enum {ADD,SUB,MUL} opts_t; typedef enum {FALSE,TRUE} bool_t; logic [31:0] opt1,opt2; reg imhere; wire im2here; function automatic [31:0] multx(input [31:0] d1,input [31:0] d2); return d1*d2; endfunction endpackage // p1 module disp(input logic bt); always @(bt) begin if (bt == 1'b1) begin $display("==============================="); $display("Opt1 is %h and Opt2 is %h!",p1::opt1,p1::opt2); end if (bt == 1'b0) $display("==============================="); end endmodule module top_tb; p1::opts_t opts; p1::bool_t blt; logic [31:0] result; logic bt; initial begin p1::opt1 = 32'h12; p1::opt2 = 32'h34; #`DLY opts = p1::SUB; blt = p1::TRUE; #`DLY opts = p1::ADD; blt = p1::FALSE; #`DLY opts = p1::MUL; end always @(opts) begin case(opts) p1::ADD : result = p1::opt1 + p1::opt2; p1::SUB : result = p1::opt1 + p1::opt2; p1::MUL : result = p1::multx(p1::opt1,p1::opt2); endcase $display("The option result is %h",result); end always @(blt) begin if (blt == p1::TRUE) bt = 1'b1; if (blt == p1::FALSE) bt = 1'b0; end disp u_dut(bt); endmodule【仿真结果】
# The option result is 0000000f # =============================== # Opt1 is 00000012 and Opt2 is 00000003! # The option result is 00000015 # =============================== # The option result is 00000036
示例中32行和33行通过作用域符好实现了对package中成员的引用,即完成了对opt1和opt2的赋值操作.在模块disp中调用了p1中的opt1和opt2,通过仿真可以观测到,在top_tb中对p1中的opt1和opt2的修改会影响到所有调用p1中opt1和opt2的模块,所以对于一般可综合的结构不建议在package中声明全局变量(线网)或者静态的函数或任务.
上例中package元素比较少还可以使用作用域符号实现对package中元素的引用,那么当package中被引用的成员很多时,如果还是用上例通过作用域符号引用的方式代码将会变得很冗繁,而且还特别容易出错.为此,可以使用下属的import方式对package中元素的引用.常用的import方式有两种,如下:
-
import package_name::var_name;;指定特定元素;
- import package_name::*;使package中所有元素在被import结构中可见(这里需要注意具体哪些元素可见,取决于当前范围引用了package中的哪些元素);
2 通过import导入package中的元素
【示例】
`timescale 1 ns /1 ps `define DLY 1 package p1; typedef enum {ADD,SUB,MUL} opts_t; typedef enum {FALSE,TRUE} bool_t; logic [31:0] opt1,opt2; reg imhere; wire im2here; function automatic [31:0] multx(input [31:0] d1,input [31:0] d2); return d1*d2; endfunction endpackage // p1 module disp(input logic bt); always @(bt) begin if (bt == 1'b1) begin $display("==============================="); $display("Opt1 is %h and Opt2 is %h!",p1::opt1,p1::opt2); end if (bt == 1'b0) $display("==============================="); end endmodule module top_tb; // below explicitly imported import p1::ADD; import p1::SUB; import p1::MUL; import p1::FALSE; import p1::TRUE; import p1::opt1; import p1::opt2; import p1::multx; import p1::opts_t; import p1::bool_t; // end explicitly imported opts_t opts; bool_t blt; logic [31:0] result; logic bt; initial begin p1::opt1 = 32'h12; p1::opt2 = 32'h3; #`DLY opts = SUB; blt = TRUE; #`DLY opts = ADD; blt = FALSE; #`DLY opts = MUL; end always @(opts) begin case(opts) ADD : result = opt1 + opt2; SUB : result = opt1 - opt2; MUL : result = multx(opt1,opt2); endcase $display("The option result is %h",result); end always @(blt) begin if (blt == TRUE) bt = 1'b1; if (blt == FALSE) bt = 1'b0; end disp u_dut(bt); endmodule【仿真结果】
# The option result is 0000000f # =============================== # Opt1 is 00000012 and Opt2 is 00000003! # The option result is 00000015 # =============================== # The option result is 00000036
通过import导入p1中指定元素后,该元素对于当前模块可见,可以不用再使用作用域符号进行引用.但是这里需要注意,在对枚举类型进行引用时,不止要import定义的类型,还要import其中定义的符号,如示例中28-32行,如果不对这些枚举类型中的符号进行import,那么在当前模块不能直接引用这些符号.可见,相较之前的示例,本例中使用import可以简化代码,但是,如果package中需要引用的元素非常多时,这样这个通过import指定元素同样是一件非常繁重的体力劳动.为此SystemVerilog中提供了另一种方式,使对package中元素的引用更加简洁使用.
【示例】
`timescale 1 ns /1 ps `define DLY 1 package p1; typedef enum {ADD,SUB,MUL} opts_t; typedef enum {FALSE,TRUE} bool_t; logic [31:0] opt1,opt2,opt3; reg imhere; wire im2here; function automatic [31:0] multx(input [31:0] d1,input [31:0] d2); return d1*d2; endfunction endpackage // p1 module disp(input logic bt); always @(bt) begin if (bt == 1'b1) begin $display("==============================="); $display("Opt1 is %h and Opt2 is %h!",p1::opt1,p1::opt2); end if (bt == 1'b0) $display("==============================="); end endmodule module top_tb; import p1::*; opts_t opts; bool_t blt; logic [31:0] result; logic bt; initial begin p1::opt1 = 32'h12; p1::opt2 = 32'h3; #`DLY opts = SUB; blt = TRUE; #`DLY opts = ADD; blt = FALSE; #`DLY opts = MUL; end always @(opts) begin case(opts) ADD : result = opt1 + opt2; SUB : result = opt1 - opt2; MUL : result = multx(opt1,opt2); endcase $display("The option result is %h",result); end always @(blt) begin if (blt == TRUE) bt = 1'b1; if (blt == FALSE) bt = 1'b0; end disp u_dut(bt); endmodule【仿真结果】
# The option result is 0000000f # =============================== # Opt1 is 00000012 and Opt2 is 00000003! # The option result is 00000015 # =============================== # The option result is 00000036
可见,使用import::*的方式,可以仅将被使用到package的元素可见范围拓展到当前作用域,比使用import p1::item逐个的指定特定元素的方式更加简洁.但是注意这种方式并不是将package中的所有元素都可以导入到当前范围,而是仅限于被当前作用范围使用到的package中的元素.示例中p1的opt3在module中未被使用,所以在module中并不可见.
通过上面几个例子,在具体使用package的过程中可以总结如下例: