Module 4: Introduction to Verilog HDL · 4.2 Verilog Language Basics

Number Literals

In Verilog the way you write a number decides how many bits it has, and the number of bits decides whether a concatenation is the width you think, whether a comparison means what you think, and whether the synthesised circuit is the right size. An unsized literal in the wrong place is one of the commonest sources of quiet RTL bugs, so this article is about writing every constant precisely.

The format

<size>'<base><value>

The size is the number of bits in decimal; the apostrophe is required; the base is b, o, d or h; and the value uses the digits of that base, with underscores allowed for readability and x and z allowed as digits.

4'b1010          // 4 bits, binary, decimal 10
8'hFF            // 8 bits, hex, 255
8'hff            // the same; case does not matter
16'hDEAD
8'd255           // 8 bits, decimal
8'o377           // 8 bits, octal, 255
1'b0             // a single-bit 0
1'b1             // a single-bit 1
32'h0000_0001    // underscores every four digits
12'hABC          // the size need not be a power of two

Why the size matters

Every signal in the netlist has a width. Assign a constant of a different width and the tool silently truncates or zero-extends, with at most a warning:

wire [3:0] nibble;

assign nibble = 255;    // 255 is a 32-bit literal; the top 28 bits are thrown away, nibble = 4'hF
assign nibble = 4'hF;   // what was meant

Concatenation is where it really goes wrong, because an unsized literal is 32 bits wide:

wire [3:0] nib_a = 4'hA;
wire [7:0] byte_val;

assign byte_val = {nib_a, 0};      // 0 is 32 bits: a 36-bit concatenation truncated to 8, giving 8'h00
assign byte_val = {nib_a, 4'h0};   // 8'hA0, as intended

In a comparison such as if (state == 3) the narrow operand is zero-extended and the result is right, which is why people get away with unsized literals for years and then get bitten in a concatenation or an arithmetic expression. Size everything.

The four values

Verilog signals take four values, not two, and this models real hardware.

Value Meaning
0 driven low
1 driven high
x unknown: not yet initialised, or two drivers fighting
z high impedance: nothing driving

Every reg starts as x at time zero, because a real flip-flop powers up in an unknown state. A wire with two drivers at opposite values is x, because it cannot be both. A case with no default produces x on its outputs for the patterns it does not cover.

`timescale 1ns / 1ps
`default_nettype none

module x_z_demo;

    reg  [3:0] uninit_reg;     // 4'bxxxx at time zero
    wire [3:0] high_z_wire;    // undriven: 4'bzzzz

    initial begin
        $display("uninit_reg at t=0: %b", uninit_reg);   // xxxx
        uninit_reg = 4'b1010;
        $display("after assignment: %b", uninit_reg);     // 1010

        uninit_reg = 4'bxxxx;   // all unknown
        uninit_reg = 4'b10xz;   // mixed
        uninit_reg = 4'hx;      // shorthand for all unknown
        $finish;
    end

endmodule

X spreads. An AND gate with an x input and a 1 on the other gives x, and so on downstream, so an x on an output in the waveform viewer is information: trace it back and you find the register nobody reset. That is what it is for.

Z is a wire nobody is driving. On an internal signal it means something you expected to drive it did not. Deliberately, it is how a tri-state driver lets go of a shared bus:

`timescale 1ns / 1ps
`default_nettype none

module tristate_driver #(
    parameter WIDTH = 8
)(
    input  wire [WIDTH-1:0] data_in,
    input  wire             oe_n,     // output enable, active low
    output wire [WIDTH-1:0] bus
);

    assign bus = oe_n ? {WIDTH{1'bz}} : data_in;

endmodule

Inside an FPGA or ASIC, buses are muxes now, not tri-states. Z belongs at the pins, where an I²C data line or a memory bus really is shared.

Negative numbers

Literals are unsigned. A minus sign is the negation operator applied to one:

-8'd5    // 8'b11111011, which is 251 unsigned or -5 in two's complement

The bits are the same either way; only the interpretation differs, and that is set by the signed qualifier on the signals involved, which the arithmetic article covers.

Choosing a base

Hex for bus values and addresses, because it shows the byte structure:

parameter RESET_VECTOR = 32'h0000_0100;
parameter STATUS_MASK  = 8'h3F;

Binary for masks and one-hot encodings, because it shows the bit positions:

parameter INTERRUPT_MASK = 8'b0000_1111;
localparam S_IDLE = 4'b0001;
localparam S_RUN  = 4'b0010;
localparam S_DONE = 4'b0100;

Decimal for counts:

parameter BAUD_DIVISOR   = 16'd868;    // 50 MHz / 57600
parameter TIMEOUT_CYCLES = 10'd1000;

Underscores every four digits in anything wide, 32'hDEAD_BEEF, 16'b1010_0101_1100_0011. And in comparisons, size the literal to the signal: count == 8'd255, state == 3'd7, flag == 1'b1.

x in synthesis

To a synthesiser an x on the right-hand side means “don’t care”, and it will use the freedom to shrink the logic:

case (sel)
    2'b00: out = a;
    2'b01: out = b;
    2'b10: out = c;
    2'b11: out = 2'bxx;   // never happens, so choose whatever is cheapest
endcase

That is useful and dangerous in equal measure. If the case you called impossible does happen, the circuit does something you did not choose. Use it only when you can prove the pattern cannot occur.

Practice Exercise

Find the literal problems and rewrite:

module bad_literals (
    input  wire [7:0] data,
    output wire [7:0] masked,
    output wire [3:0] upper
);
    assign masked = data & 15;
    assign upper  = data >> 4;
endmodule
Solution
`timescale 1ns / 1ps
`default_nettype none

module good_literals (
    input  wire [7:0] data,
    output wire [7:0] masked,
    output wire [3:0] upper
);
    assign masked = data & 8'b0000_1111;   // sized mask; binary shows which bits survive
    assign upper  = data[7:4];             // a part-select says what is meant
endmodule

The 15 was a 32-bit literal used as an 8-bit mask; it happened to work but only because of zero extension. The shift produced an 8-bit result silently truncated to 4 bits; numerically right, and hiding the intent, which a part-select states directly.

Next, the lines that start with a backtick: compiler directives, including the one that turns a typo in a signal name from an afternoon of debugging into a compile error.

← Parameters Compiler Directives →