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
 Simulation Log
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.

 Simulation Log
	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.

 Simulation Log
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
 Simulation Log
ncsim> run
READ1 = 1 READ2 = 0 
ncsim: *W,RNQUIE: Simulation is complete.