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