Table of Contents
Introduction to Using Constants in External Files with System Verilog
Much like in software development, digital designs often use the same constants spread among multiple files. ECEn 330 showed that when programming in C, the solution is generally to define it in a single header file (.h) that can be included and used in multiple places. ECEn 330 also touched on explaining that the compiler has to be told about (include) those header files in order to link the source files together. In ECEn 330, this is simplified through a CMakeLists.txt
file that handles creating a Makefile
file with C compiler commands which properly includes external files to the executable. With cmake and make, the student just has to run cmake ..
and make
in a build directory and all the binaries will be compiled as expected.
The ECEn 330 (software development in general) solution above is described in a bit more detail than usual to help understand how the same goal of using header files to “distribute” constants can be accomplished in System Verilog. There are a lot of similarities but there are some important differences (one of the biggest being that cmake and make aren’t tools used for System Verilog).
Creating External Files with Constants
As mentioned in the introduction, a header file (.h) is created to hold constants to be shared between other C files. In System Verilog, a System Verilog header file (.sv, .svh but use .sv) is used in the same manner but for System Verilog files. An example of a System Verilog header file is provided below of a named riscv_alu_constants.sv
with two ALU operation constants.
// ALU Constants for available ALU operations
localparam[3:0] ALUOP_AND = 4'b0000;
localparam[3:0] ALUOP_OR = 4'b0001;
When comparing the above System Verilog header to a header file from C, one might notice that there is no header guard like shown below (which is strongly encouraged in C):
#ifndef MYFILE
#define MYFILE
#define FILE_CONSTANT 5
#endif
This will be further explored and explained a bit later but for this class, DO NOT use a header guard on System Verilog header files.
Using External Files with Constants within the Module
Again looking at C, to use constants defined in a header file with another source file you use the #include filename
in the source file needing the constants from the header file where filename is the header file name with the constants such as #include myFile.h
. In System Verilog you use:
`include filename
using the ALU constants as an example:
`include "riscv_alu_constants.sv"
One really big difference between C and System Verilog with includes is where you put the include in the code. Anytime a module uses a constant from an external file, you should place the include inside the module at the top near the ports. Here is the format using the alu as an example:
module alu();
`include "riscv_alu_constants.sv"
// Code for alu
.
.
.
endmodule
Once again, this will be further explored and explained a bit later but for this class, place the include inside the module definition.
Telling the Compiler about the External Files
Lastly, whenever simulation or synthesis is ran in Vivado then the System Verilog source code has to be translated to a format that resembles logic gates. Like C, System Verilog uses a compiler which instead of generating machine code to be ran on a processor (like a C compiler), will generate the design using logic gates. When adding sources to Vivado through the GUI, the Vivado compiler is told about files that contain modules. The problem is that the header file previously defined does not contain any modules so we can’t add something like riscv_alu_constants.sv
like other sources. Below are two options through the TCL console or through the Vivado GUI.
Method 1 - TCL Command
You can add an include directory by executing the following command (replace
set_property include_dirs <path> [current_fileset]
Method 2 - GUI
You can add a directory by completing the following GUI steps.
Select Project Manager->Settings to open project settings dialog box
Select the “…” icon next to the ‘Verilog Options’ under the ‘Language Options’ on the right side pane of the project settings dialog box
Press ‘+’ on the “Verilog Include Files Search Paths” section of the Verilog Options dialog box
Select the directory for the include path.
Click ‘OK’ then click ‘Apply’
(Optional Reading) Header Guard and Include Placement in System Verilog
It’s already been stated above that for this class, you should not have any header guards. Header guards do exist in System Verilog (they are represented as `ifdef
, `define
and `endif)
and work like C header guards. The big difference is in placement of the include statement. If the include statement is placed inside the module, the constants (localparams) are defined inside and only inside the module. This means header guards are not required when placing the include inside each module because module definitions only exist within the module. If the include is placed outside of the module, the localparams are defined globally (which is less common) and will most likely require header guards. However, when localparams are defined globally, it can lead to possiblely overwritting another module’s localparam that share the same name which can result in difficult bugs. If header guards are added to the header file and the include is placed inside the module, what will happen is file 1 will contain the localparams from the header but file 2 will reach the `ifdef
that will result false (since it’s been defined in file 1) and thus file 2 will not contain the localparams from the header. For these reasons, it’s best to NOT include any header guards and to place the include inside the module.