Including and Using an External File with Constants

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 with the path of the directory that includes additional include files):

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.


Last Modified: 2024-07-01 00:08:02 +0000