Module 4: Introduction to Verilog HDL ยท 4.2 Verilog Language Basics

Compiler Directives

You type imput_a instead of input_a. In most languages the compiler stops. In Verilog, by default, nothing happens: the tool invents a one-bit wire called imput_a, drives it with nothing, and the design produces wrong answers until you notice, an hour later, that the tooltip in the waveform viewer has the name spelt wrong. One directive stops that entire class of bug, and it is the last one in this article.

What a directive is

Lines starting with a backtick are handled by the preprocessor before compilation. They are not hardware and produce nothing in the netlist; they set up the environment, time units, text substitutions, conditional sections, and apply from where they appear to the end of the compilation unit, across module boundaries. The backtick is a different character from the apostrophe in a literal:

`timescale    a directive
4'b1010       a literal

timescale

`timescale <unit> / <precision>

The unit is what #1 means; the precision is the finest step the simulator tracks.

`timescale 1ns / 1ps    // #1 is 1 ns, resolved to 1 ps; the usual choice for RTL
`timescale 1us / 1ns    // board-level work with slow signals
`timescale 1ps / 1fs    // multi-gigabit serial links

It goes before the first module in the file and applies to everything after it:

`timescale 1ns / 1ps
`default_nettype none

module clk_gen (
    output reg clk
);
    initial clk = 1'b0;
    always #5 clk = ~clk;   // 5 ns half period, 100 MHz; testbench only
endmodule

Synthesis ignores timescale along with every # delay, which is one more reason delays live in testbenches.

define and ifdef

`define is a text macro. Wherever `NAME appears afterwards, the text is substituted before compilation:

`define WIDTH 8

wire [`WIDTH-1:0] bus;    // becomes wire [8-1:0] bus

`undef WIDTH              // tidy up so it does not leak into other files

Macros are global and untyped, and a parameter or localparam is nearly always better for a constant. Where macros earn their place is conditional compilation:

`timescale 1ns / 1ps
`default_nettype none

module uart_tx #(
    parameter CLK_FREQ  = 50_000_000,
    parameter BAUD_RATE = 115_200
)(
    input  wire       clk,
    input  wire       rst_n,
    input  wire [7:0] tx_data,
    input  wire       tx_valid,
    output wire       tx_serial,
    output wire       tx_ready
);

    localparam BAUD_DIV = CLK_FREQ / BAUD_RATE;

    // ... the transmitter ...

`ifdef SIMULATION
    // a monitor that only exists in simulation
    always @(posedge clk) begin
        if (tx_valid && tx_ready)
            $display("[UART_TX t=%0t] sending 8'h%02h", $time, tx_data);
    end
`endif

endmodule

SIMULATION is defined on the simulator’s command line, iverilog -DSIMULATION ..., not in the RTL file, so the synthesis run never sees the $display. The full set is `ifdef, `ifndef, `elsif, `else, `endif, and the usual shape is one branch for simulation, one for an FPGA target and a default for the ASIC.

include

`include "defines.vh"

pastes a file in at that point, as #include does in C. The .vh extension marks a header, a file of macros and shared definitions rather than modules. Guard it against double inclusion:

`ifndef PROJECT_DEFINES_VH
`define PROJECT_DEFINES_VH

`define DATA_WIDTH  32
`define ADDR_WIDTH  12

`endif

default_nettype

This is the one that matters. Without it, an undeclared identifier becomes an implicit one-bit wire:

module broken_adder (
    input  wire [7:0] input_a,
    input  wire [7:0] input_b,
    output wire [7:0] result
);
    assign result = imput_a + input_b;   // imput_a is quietly created, 1 bit, undriven
endmodule

It compiles. It simulates. The result is wrong, and nothing tells you why. With the directive:

`timescale 1ns / 1ps
`default_nettype none

module correct_adder (
    input  wire [7:0] input_a,
    input  wire [7:0] input_b,
    output wire [7:0] result
);
    assign result = imput_a + input_b;   // compile error: imput_a not declared
endmodule

`default_nettype wire

The compiler stops at the line and names the identifier. Put `default_nettype none at the top of every file you write from now on. The restore to wire at the bottom is a courtesy to files compiled after yours that were written without it; teams that mandate the directive everywhere can skip the restore.

celldefine

`celldefine and `endcelldefine mark a module as a library cell for tools that treat cells differently from user RTL. You will see them in standard-cell and I/O pad models and never write them.

The file template

// =============================================================================
// Module:      <module_name>
// Description: <one line>
// =============================================================================

`timescale 1ns / 1ps
`default_nettype none

module <module_name> #(
    // parameters
)(
    // ports
);

    // body

endmodule

`default_nettype wire

Scope

A directive applies from its line to the end of the compilation unit, and module boundaries do not stop it (Figure 1). A define inside one module is visible in the next module of the same file, and in the next file if the tool compiles them as one unit. That is why headers need include guards and why restoring default_nettype is polite.

Directive scope
Figure 1: Directives apply from where they appear to the end of the compilation unit

Practice Exercise

Three things are missing or wrong in this file. Find them and rewrite it.

module sensor_read (
    input  wire clk,
    input  wire rst_n,
    input  wire [11:0] raw_adc,
    output reg  [11:0] filtered
);
    always @(posedge clk) begin
        if (!rst_n)
            filtered <= 0;
        else
            filtered <= raw_adc;
    end
endmodule
Solution

No timescale, so # delays in any testbench that includes this file have undefined units; no default_nettype none, so a misspelt signal would become a floating wire; and the reset value is an unsized literal.

`timescale 1ns / 1ps
`default_nettype none

module sensor_read (
    input  wire        clk,
    input  wire        rst_n,      // synchronous, active low
    input  wire [11:0] raw_adc,
    output reg  [11:0] filtered
);

    always @(posedge clk) begin
        if (!rst_n)
            filtered <= 12'd0;
        else
            filtered <= raw_adc;
    end

endmodule

`default_nettype wire

The last article of this section is about style: naming, resets, and how to lay out a module so that the person debugging it in three years, who may be you, can read it.

โ† Number Literals Style and Conventions โ†’