Packages provide a mechanism for storing and sharing data, methods, property, parameters that can be re-used in multiple other modules, interfaces or programs. They have explicitly named scopes that exist at the same level as the top-level module. So, all parameters and enumerations can be referenced via this scope. Putting such definitions and declarations inside a package also avoids cluttering the global namescope. Packages can then be imported into the current scope where items in that package can be used.
Note that items within packages cannot have hierarchical references to identifiers except those created within the package or made visible by the import of another package.
Example
All packages have to be enclosed within the package
and endpackage
keywords.
package my_pkg;
typedef enum bit [1:0] { RED, YELLOW, GREEN, RSVD } e_signal;
typedef struct { bit [3:0] signal_id;
bit active;
bit [1:0] timeout;
} e_sig_param;
function common ();
$display ("Called from somewhere");
endfunction
task run ( ... );
...
endtask
endpackage
The package shown above can be imported into other modules and class scopes so that items defined in it can be re-used. This is done using the keyword import
followed by the scope resolution operator ::
that then specifies what to import. In the example below, we import everything defined in the package as indicated by the star *
that follows ::
operator, to be able to use any of the items.
// Import the package defined above to use e_signal
import my_pkg::*;
class myClass;
e_signal my_sig;
endclass
module tb;
myClass cls;
initial begin
cls = new ();
cls.my_sig = GREEN;
$display ("my_sig = %s", cls.my_sig.name());
common ();
end
endmodule
ncsim> run my_sig = GREEN Called from somewhere ncsim: *W,RNQUIE: Simulation is complete.
Note that the package had to be imported for the compiler to recognize where GREEN is defined. Had the package not been imported, then you'll get compiler errors like shown below.
e_signal my_sig; | ncvlog: *E,NOIPRT (testbench.sv,15|8): Unrecognized declaration 'e_signal' could be an unsupported keyword, a spelling mistake or missing instance port list '()' [SystemVerilog]. cls.my_sig = GREEN; | ncvlog: *E,UNDIDN (testbench.sv,23|19): 'GREEN': undeclared identifier [12.5(IEEE)].
Instead of importing all the definitions inside a package, you can also import them individually if you know exactly what is used in your piece of code. But, it is seen as an overhead especially when more members are accessed from the package.
import my_pkg::GREEN;
import my_pkg::e_signal;
import my_pkg::common;
Omission of either one of the three import
statements will result in a compiler error simply because it does not know where they are defined unless its imported.
Namespace Collision
Consider the example below where the same definitions exist, one at the top level and the other via an imported package.
package my_pkg;
typedef enum bit { READ, WRITE } e_rd_wr;
endpackage
import my_pkg::*;
typedef enum bit { WRITE, READ } e_wr_rd;
module tb;
initial begin
e_wr_rd opc1 = READ;
e_rd_wr opc2 = READ;
$display ("READ1 = %0d READ2 = %0d ", opc1, opc2);
end
endmodule
Note that even though my_pkg was imported, e_rd_wr
variable opc2 got the value of READ as 1, which implies that the enum value inside the package is not considered.
ncsim> run READ1 = 1 READ2 = 1 ncsim: *W,RNQUIE: Simulation is complete.
In order for the simulator to apply value from the package, the package name should be explicitly mentioned using the ::
operator as shown below.
module tb;
initial begin
e_wr_rd opc1 = READ;
e_rd_wr opc2 = my_pkg::READ;
$display ("READ1 = %0d READ2 = %0d ", opc1, opc2);
end
endmodule
ncsim> run READ1 = 1 READ2 = 0 ncsim: *W,RNQUIE: Simulation is complete.