SystemVerilog Package Globals instead of `include

Intro

I had used other programming languages before working with SystemVerilog.  So when I started looking at SystemVerilog code for the first time, I was perplexed why there were all of these `includes all over - even in purely object oriented verification logic.  I had so many questions like:

  • Why inject a file (`include) into your source code?
  • Isn't that what packages and import were for?
  • Isn't it scary to import a file into your source code and not have any control what you are bringing in?
  • You have no scope visibility anymore after that `include to know where a variable came from or what variables are now available to you.
  • Are you making a package now, but then just `include everything into it just to mess with me because I'm asking?

Apparently, SystemVerilog users have toughened up and don't consider these questions anymore.

Part of the reason for this misunderstanding is that the "package" construct does not exist in regular Verilog.  It is a SystemVerilog only construct.  And, SystemVerilog is relatively new.  I wouldn't be surprised if you were writing something that had to be synthesized into gates you may have some pain with using packages and imports too, which might scare away people from even considering doing things this way.

This method requires a SystemVerilog compiler.

Constants with Packages Rather than `include

There is a problem and proposed solution I would like to share.  Using packages instead of `include to store constants.  I wrote the example both ways: package and `include; so you can see something that I see typically done with `include done using package.  There are two files:

  • globals2.svh (our include file)
  • example.sv (the main module)

globals2.svh:

const int I_PORTS_NUM2 = 1;
const real R_CONSTX2 = 1.66;

typedef enum {
E_MODE_X2 = 0,
E_MODE_Y2 = 1,
E_MODE_Z2 = 2
} e_modes2;

This header file (.svh) sets up some constants and is the typical type of thing you would see in an `include file.

example.sv:

package globals;

const int I_PORTS_NUM = 0;
const real R_CONSTX = 1.33;

typedef enum {
E_MODE_X = 0,
E_MODE_Y = 1,
E_MODE_Z = 2
} e_modes;

endpackage // globals

module x;

`include "globals2.svh"

initial begin
$display("I_PORTS_NUM : %h", globals::I_PORTS_NUM);
$display("R_CONSTX : %f", globals::R_CONSTX);

$display("E_MODE_X : %h", globals::E_MODE_X);
$display("E_MODE_Y : %h", globals::E_MODE_Y);
$display("E_MODE_Z : %h", globals::E_MODE_Z);


$display("--");
$display("I_PORTS_NUM2: %h", I_PORTS_NUM2);
$display("R_CONSTX2 : %f", R_CONSTX2);

$display("E_MODE_X2 : %h", E_MODE_X2);
$display("E_MODE_Y2 : %h", E_MODE_Y2);
$display("E_MODE_Z2 : %h", E_MODE_Z2);
end

endmodule // x

We create a package called "globals" which has constants defined in it - the same ones with a slightly different name than in globals2.svh.  Then, we define a module "x" and have $display statements to show how to use those variables the package way and the `include way.

You have a choice to use the scope of the "globals::" package, which I think is very nice for things like constants - you get to know where the constant came from.  Or, you could choose to import everything "import globals::*" and have those variables in your name space.  Or lastly, you could choose to only import certain constants "import globals::I_PORTS_NUM".  The choice is up to you.  But, you do have a choice and some of those possibilities might lead to easier code to reuse or read.

Run it with Mentor Questa

If you were to run this program in Mentor Questa your output would look like the below.  The variables ending in "2" were the ones from the `include.


# I_PORTS_NUM : 00000000
# R_CONSTX : 1.330000
# E_MODE_X : 00000000
# E_MODE_Y : 00000001
# E_MODE_Z : 00000002
# --
# I_PORTS_NUM2: 00000001
# R_CONSTX2 : 1.660000
# E_MODE_X2 : 00000000
# E_MODE_Y2 : 00000001
# E_MODE_Z2 : 00000002

Questa Command Line to Run:

> rm -rf work; vlib work; vlog example.sv; \
vsim -c x -do "run 1ns; exit"

For More