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

Style and Conventions

RTL lives a long time. A module written today gets debugged by someone else in three years, moved to a new process in five and dropped into a new chip in ten. Style rules in hardware are about readability, as in software, but most of them are also about correctness: each one exists because of a specific bug that kept happening. This article is the set used in the course, with the reason for each.

Naming

`timescale 1ns / 1ps
`default_nettype none

module naming_demo (
    input  wire        clk,          // the clock is clk; with several, clk_a, clk_b
    input  wire        rst_n,        // _n means active low
    input  wire        cs_n,         // chip select, active low
    input  wire        wr_en,        // active high, no suffix
    input  wire [7:0]  data_in,      // _in and _out where the direction is not obvious
    output wire [7:0]  data_out,
    output wire        valid_out,
    output wire        error_flag
);

    wire [7:0]  data_byte;           // lowercase with underscores
    reg  [3:0]  state_reg;           // _reg marks a flip-flop output
    reg  [3:0]  next_state;          // next_ marks the combinational next value
    wire        pipeline_valid;

    localparam  IDLE   = 2'd0;       // UPPER_CASE constants
    localparam  ACTIVE = 2'd1;

endmodule

The _n suffix on every active-low signal is not optional. Seeing rst_n tells the reader the reset condition is !rst_n, and the commonest reset bug of all is writing if (rst_n) for if (!rst_n). The _reg suffix tells the reader which signals are flip-flop outputs, which matters when reading timing paths. The next_ prefix separates the combinational next state from the registered current state, and mixing those up is the classic state machine error.

Instances take a u_ prefix and a name that says what they do:

adder   #(.WIDTH(16)) u_alu_adder (/* ports */);
fifo    #(.DEPTH(64)) u_tx_fifo   (/* ports */);
uart_rx               u_uart_rx   (/* ports */);

pipeline_stage u_stage0 (/* ports */);
pipeline_stage u_stage1 (/* ports */);

Searching a netlist for u_ then finds instances and nothing else.

Reset

The ASIC convention is an asynchronous, active-low reset. Asynchronous because at power-up the clock may not be running yet and the flip-flops must still reach a known state. Active-low because library flip-flops have active-low reset pins, so an active-high reset in the RTL costs an inverter on every reset path, and because reset comes from a pin, where a pull-up and an open-drain driver naturally give an active-low signal.

`timescale 1ns / 1ps
`default_nettype none

module reset_demo (
    input  wire       clk,
    input  wire       rst_n,     // asynchronous, active low
    input  wire [7:0] d,
    output reg  [7:0] q
);

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            q <= 8'h00;
        else
            q <= d;
    end

endmodule

Three things to see: negedge rst_n in the sensitivity list makes it asynchronous; the condition matches the polarity; the reset value is sized.

Many FPGA flows prefer a synchronous reset, which is the same block without negedge rst_n in the list. It costs nothing in an FPGA flip-flop and it avoids the reset-release timing problem that section 3.2 covered. The simple examples earlier in this module use it. Either is fine. What is not fine is mixing them, or mixing polarities, in one design: two blocks driven from the same reset pin with one inverted can leave one block in reset while the other is running, and everything between them is undefined. Pick one convention and use it everywhere.

One register per always block

When two registers have different reset values or different enables, give each its own block.

// Poor: two unrelated registers in one block
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        counter_reg <= 8'd0;
        status_reg  <= 8'hFF;
    end else begin
        if (count_en)
            counter_reg <= counter_reg + 8'd1;
        status_reg <= {error_flag, 7'b0};
    end
end
`timescale 1ns / 1ps
`default_nettype none

module separate_always (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       count_en,
    input  wire       error_flag,
    output reg  [7:0] counter_reg,
    output reg  [7:0] status_reg
);

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            counter_reg <= 8'd0;
        else if (count_en)
            counter_reg <= counter_reg + 8'd1;
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            status_reg <= 8'hFF;
        else
            status_reg <= {error_flag, 7'b000_0000};
    end

endmodule

The netlist is the same. The difference is that a reader can see each register’s reset and enable without untangling them from the other’s.

Layout

Four spaces, no tabs. Align the direction, type, range and name columns in port lists and declarations, because the eye scans columns faster than it parses ragged lines. Wrap at 100 characters. Separate the sections of a module with blank lines and a short comment:

module well_structured (/* ports */);

    // ---- Parameters ----
    localparam IDLE = 2'd0;
    localparam BUSY = 2'd1;

    // ---- Signals ----
    reg [1:0] state_reg;
    reg [1:0] next_state;

    // ---- State register ----
    always @(posedge clk or negedge rst_n) begin
        /* ... */
    end

    // ---- Next-state logic ----
    always @(*) begin
        /* ... */
    end

endmodule

Comments

Comment why, not what. assign result = a + b; // add a and b says nothing. // wraps on overflow; the caller checks carry says something. Use TODO, FIXME, HACK and NOTE with their usual meanings, and a block comment at the top of the file saying what the module is for.

Do not repeat yourself

If the same logic appears three times, it is a function or a module:

// Poor: the same Gray conversion three times
assign grey_a = {bin_a[3], bin_a[3]^bin_a[2], bin_a[2]^bin_a[1], bin_a[1]^bin_a[0]};
assign grey_b = {bin_b[3], bin_b[3]^bin_b[2], bin_b[2]^bin_b[1], bin_b[1]^bin_b[0]};
assign grey_c = {bin_c[3], bin_c[3]^bin_c[2], bin_c[2]^bin_c[1], bin_c[1]^bin_c[0]};

Three copies are three places to fix a bug. Every large company has an RTL style guide of fifty pages or more, and some are public; the lowRISC one is worth reading once.

A complete example

// =============================================================================
// Module:      debounce
// Description: Button debouncer: the output changes only after FILTER_BITS
//              consecutive identical samples
// =============================================================================

`timescale 1ns / 1ps
`default_nettype none

module debounce #(
    parameter FILTER_BITS = 16
)(
    input  wire clk,
    input  wire rst_n,       // asynchronous, active low
    input  wire btn_raw,
    output reg  btn_clean
);

    // ---- Signals ----
    reg  [FILTER_BITS-1:0] shift_reg;
    wire                   all_ones;
    wire                   all_zeros;

    // ---- Stable-state detection ----
    assign all_ones  = &shift_reg;
    assign all_zeros = ~(|shift_reg);

    // ---- Sample shift register ----
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            shift_reg <= {FILTER_BITS{1'b0}};
        else
            shift_reg <= {shift_reg[FILTER_BITS-2:0], btn_raw};
    end

    // ---- Output: changes only when the filter is saturated ----
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            btn_clean <= 1'b0;
        else if (all_ones)
            btn_clean <= 1'b1;
        else if (all_zeros)
            btn_clean <= 1'b0;
        // otherwise hold: a bounce shorter than FILTER_BITS cycles is ignored
    end

endmodule

`default_nettype wire

Practice Exercise

Find the style problems and rewrite:

module bad_style(input clk, input reset, input [7:0] D, output reg [7:0] Q);
always @(posedge clk) begin
if(reset) Q <= 0;
else Q <= D;
end
endmodule
Solution

No timescale or default_nettype; no wire on the inputs; nothing aligned; an active-high reset with no convention stated and no _n; an unsized reset value; upper-case signal names; no indentation.

// =============================================================================
// Module:      dff_byte
// Description: 8-bit register with asynchronous active-low reset
// =============================================================================

`timescale 1ns / 1ps
`default_nettype none

module dff_byte (
    input  wire       clk,
    input  wire       rst_n,
    input  wire [7:0] d,
    output reg  [7:0] q
);

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            q <= 8'h00;
        else
            q <= d;
    end

endmodule

`default_nettype wire

That completes the grammar. The next section is the operators that go inside the expressions: arithmetic first, and what each one costs in gates.

โ† Compiler Directives Arithmetic Operators โ†’