Skip to content

Verilog · Chapter 5.2.2 · Data & Variables

integer in Verilog — 32-bit Signed Loop Variable

The integer type is Verilog's convenience variable for procedural code that doesn't need a specific width. It declares a 32-bit signed, four-state variable with fixed width and signedness, and it is used overwhelmingly for one purpose: loop iteration. Almost every for-loop counter in a testbench initial block, every generate-loop iterator, and every scratch counter that just needs to count reaches for integer. It does have a synthesizable form that maps to a 32-bit register, but using it for real RTL is almost always a mistake, because sign-extension surprises and unintended 32-bit width catch engineers who pick integer instead of a sized reg. This lesson explains why integer exists, how it differs from a plain 32-bit reg, and the narrow set of cases where it is the right tool.

Foundation18 min readVerilogintegerLoopSignedTestbench

Chapter 5 · Section 5.2.2 · Data & Variables

1. The Engineering Problem

Two engineers each write a 16-cycle delay counter that asserts done when the count expires:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Engineer A — uses integer
module delay_counter_a (
    input  wire clk, rst_n, start,
    output reg  done
);
    integer cnt;        // 32-bit signed
 
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)         begin cnt <= 0; done <= 1'b0; end
        else if (start)     begin cnt <= 16; done <= 1'b0; end
        else if (cnt > 0)   begin cnt <= cnt - 1; done <= (cnt == 1); end
    end
endmodule
 
// Engineer B — uses reg [4:0]
module delay_counter_b (
    input  wire clk, rst_n, start,
    output reg  done
);
    reg [4:0] cnt;      // 5-bit unsigned
 
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)         begin cnt <= 5'h00; done <= 1'b0; end
        else if (start)     begin cnt <= 5'd16; done <= 1'b0; end
        else if (cnt > 0)   begin cnt <= cnt - 5'd1; done <= (cnt == 5'd1); end
    end
endmodule

Both modules behave correctly in simulation. Both synthesise. The difference is in the generated netlist:

  • Engineer A's cnt is a 32-bit signed register. Synthesis produces 32 flip-flops, a 32-bit subtractor for cnt - 1, and 32-bit comparators for cnt > 0 and cnt == 1. The upper 27 bits are always 0 — wasted area.
  • Engineer B's cnt is a 5-bit unsigned register. Synthesis produces 5 flip-flops, a 5-bit subtractor, and 5-bit comparators. No wasted bits.

For a single counter, the difference is small (a few dozen flops). For a design with hundreds of counters, the cumulative cost is significant — area, power, and timing. The integer keyword's convenience (no width to think about) comes at the cost of always-32-bit hardware that's almost always wider than the actual range needs.

This is the central message of the integer type: the convenience is real, but the cost is real too. Use integer where the convenience pays off — testbench loops, generate-loop iterators, configuration scratch variables — and use reg [N-1:0] everywhere the actual width matters.

2. Anatomy of an integer

The full integer declaration syntax:

integer-syntax.v — every legal form
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
// integer name [= initial_value] [, name2, ...];
 
integer i;                              // scalar integer
integer cnt = 0;                        // with initial value (testbench-only)
integer x, y, z;                        // multiple in one statement
integer arr [0:7];                      // array of 8 integers (rare)

Four properties baked into the keyword:

  • Width is fixed at 32 bits. No [range] modifier; the width cannot be changed.
  • Signedness is fixed at signed. Two's complement, range -2,147,483,648 to +2,147,483,647. No unsigned modifier exists.
  • Value system is 4-state. Supports 0, 1, Z, X per bit — same as reg. The initial value at t = 0 is X on every bit (unless an explicit initial value is provided).
  • Procedural-assignment target. Like reg, an integer is assigned by = or <= inside always or initial blocks. Continuous assign is not legal.

The first three properties are what distinguish integer from reg [31:0] syntactically:

Propertyintegerreg [31:0]reg signed [31:0]
Width32 bits (fixed)32 bits32 bits
Signednesssignedunsignedsigned
Default initial valueXXX
4-stateyesyesyes
Procedural targetyesyesyes
Synthesisableyes (with caveats)yesyes

The only functional distinction between integer and reg signed [31:0] is one is shorter to type. The synthesis tool treats them identically — both produce 32 flip-flops with signed arithmetic semantics. The choice between the two is stylistic for the synthesis case; for testbench use, integer is more conventional.

3. Mental Model

The mental-model has three consequences:

  • integer and reg signed [31:0] are interchangeable. Picking between them is a style choice for synthesisable code; for loops and scratch variables, integer is more idiomatic.
  • integer is signed. Mixing integer with unsigned reg in arithmetic produces sign-extension behaviour that catches every beginner. Use $unsigned() to mask the sign extension when needed (see §6).
  • integer is always 32 bits. A counter that needs to count to 256 still consumes 32 flop bits if declared as integer. The synthesis tool may optimise away the unused upper bits if it can prove they're never used (constant-propagation), but that optimisation is not guaranteed.

4. The Two Use Cases

integer shows up in two contexts in production Verilog. Outside of these, prefer reg [N-1:0].

4.1 Loop iteration in testbenches

The single most common use of integer. A testbench initial block iterates over a range of values to apply stimulus, check expected outputs, or sweep a configuration parameter. The loop variable is integer:

tb/stimulus_sweep.v — integer for testbench iteration
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns/1ps
`default_nettype none
 
module tb_alu;
    reg [7:0]  a, b;
    reg        valid;
    wire [7:0] sum;
    wire       overflow;
 
    alu dut (.a(a), .b(b), .valid(valid), .sum(sum), .overflow(overflow));
 
    integer i, j;    // loop iterators — testbench-only
 
    initial begin
        valid = 1'b0;
        a = 8'h00;
        b = 8'h00;
 
        // Sweep every combination of a and b for the first 16 values
        for (i = 0; i < 16; i = i + 1) begin
            for (j = 0; j < 16; j = j + 1) begin
                a = i[7:0];
                b = j[7:0];
                valid = 1'b1;
                #10;
                if (sum !== (a + b) || overflow !== ((a + b) > 8'hFF))
                    $display("MISMATCH at a=%h, b=%h: got sum=%h, exp=%h",
                             a, b, sum, a + b);
            end
        end
 
        $finish;
    end
endmodule

The pattern is universal. Every testbench has at least one integer loop counter; many have several. The convenience of "no width to declare" matches the throwaway nature of loop variables.

4.2 Generate-loop iteration

The synthesisable counterpart. A generate block uses an iteration variable to replicate hardware structure — e.g., generating 32 identical instances of a sub-module:

rtl/array_instantiation.v — generate-loop iteration
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module wide_adder #(parameter W = 32) (
    input  wire [W-1:0]  a,
    input  wire [W-1:0]  b,
    output wire [W-1:0]  sum,
    output wire          cout
);
    wire [W:0] carry;
    assign carry[0] = 1'b0;
 
    genvar i;                              // genvar — strictly compile-time
    generate
        for (i = 0; i < W; i = i + 1) begin : g_fa
            full_adder u_fa (
                .a    (a[i]),
                .b    (b[i]),
                .cin  (carry[i]),
                .sum  (sum[i]),
                .cout (carry[i+1])
            );
        end
    endgenerate
 
    assign cout = carry[W];
endmodule

The generate-loop iterator is conventionally genvar i; — NOT integer i;. The genvar keyword (covered in Chapter 14.7) is a strictly compile-time iteration variable; it cannot be used at simulation time, only by the elaborator to expand the generate loop into N copies of the instantiated block. Using integer for a generate loop is legal in some flows but flagged by lint as "use genvar for generate iteration"; the strict-discipline answer is genvar.

This page lists generate iteration as a use case for iteration variables in general — but for synthesisable RTL, the variable is genvar, not integer. The two share the convenience of "no width to declare" and serve similar roles.

4.3 What integer is NOT for

Three things integer is not the right type for in synthesisable RTL:

  • Counters and addresses. Use reg [N-1:0] with the exact width. reg [4:0] cnt; for a 0-to-31 counter; reg [11:0] addr; for a 4 KB address; etc.
  • Datapath registers. Use reg [N-1:0] matching the actual data width. reg [31:0] data_q; for a 32-bit data path is fine; using integer data_q; is fine too, but the signed semantics will catch you out the first time you compare against an unsigned value.
  • FSM state registers. Use reg [STATE_W-1:0] with the width that matches the encoded state. One-hot states might be 8-bit; binary-encoded states 3-bit. Using integer wastes flops and risks signed-comparison bugs.

5. The Sign-Extension Trap

The most common integer bug. When an integer (signed 32-bit) is assigned to a narrower reg (unsigned, smaller width), the sign extension produces unexpected results.

rtl/sign_extension_bug.v — the canonical integer bug
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module sign_extension_bug (
    input  wire        clk,
    input  wire        rst_n,
    output reg  [7:0]  data_q
);
    integer cnt;        // 32-bit signed
 
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt    <= -1;          // signed -1 = 32'hFFFF_FFFF in two's complement
            data_q <= 8'h00;
        end
        else begin
            cnt <= cnt + 1;        // increment
 
            // BUG: data_q = cnt produces silent truncation, but cnt's sign bit
            //      makes the truncation different from what the author expects.
            //      cnt = -1 → 32'hFFFF_FFFF → data_q = 8'hFF (correct)
            //      cnt = 0  → 32'h0000_0000 → data_q = 8'h00 (correct)
            //      cnt = 1  → 32'h0000_0001 → data_q = 8'h01 (correct so far)
            //      cnt = 256 → 32'h0000_0100 → data_q = 8'h00 (truncation — bit 8 lost)
            data_q <= cnt[7:0];
        end
    end
endmodule

This isn't really a sign-extension bug — it's a width-truncation bug — but the integer declaration makes the truncation surprising because most engineers carry "the variable's width" as a mental model, and integer invisibly forces that width to 32.

The fix in this case is to use reg [7:0] cnt; — explicit 8-bit width matches the use case, and the truncation problem disappears because the variable was 8-bit all along.

The real sign-extension trap appears when integer values are compared:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer i;
reg [3:0] addr;
 
// ...
 
if (i == addr) ...    // BUG: i is signed 32-bit, addr is unsigned 4-bit
                      // Verilog promotes addr to 32-bit unsigned for the comparison,
                      // then re-interprets the comparison as signed (because i is signed).
                      // For i = -1 (32'hFFFF_FFFF) vs addr = 4'hF (32'h0000_000F after promotion):
                      // signed comparison says -1 != 15 — FALSE.
                      // But the engineer probably wanted "is the 4-bit value equal to -1?"
                      // which would have been TRUE if both were unsigned.

The IEEE 1364-2005 §4.5 width-extension and signed-vs-unsigned rules are precise but unintuitive. The working-engineer rule: never mix integer with unsigned reg in arithmetic or comparison. If you need a signed counter, use reg signed [N-1:0]. If you need an unsigned counter, use reg [N-1:0]. Picking integer because it's "convenient" is the path that produces the sign-extension bug six months later.

6. Forcing Unsigned Comparison with $unsigned()

When you genuinely need to compare a signed integer with an unsigned value, the IEEE 1364-2005 spec provides $unsigned() and $signed() system functions to convert between the two interpretations:

rtl/unsigned_cast.v — forcing unsigned comparison
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
integer i;
reg [3:0] addr;
 
// ...
 
// Force unsigned comparison: $unsigned(i) treats i's bits as unsigned 32-bit.
//   i = -1 (32'hFFFF_FFFF) → $unsigned(i) = 4,294,967,295
//   addr = 4'hF, promoted to 32-bit = 15.
//   Comparison: 4,294,967,295 != 15 → FALSE (still doesn't match).
// To compare the low 4 bits only:
if (i[3:0] == addr) ...   // bit slice — explicit, no sign-extension issue

The $unsigned() cast is rarely the right answer. More often, the right answer is a bit slicei[3:0] selects the low 4 bits of i regardless of its signedness, and the comparison happens on the 4-bit slice vs the 4-bit addr. Bit slicing is explicit and produces no surprises.

7. Visual Explanation

Three figures illustrate the integer vs reg [N-1:0] decision.

7.1 Visual A — width comparison

Same logical counter, two declarations. integer consumes 32 flops; reg [4:0] consumes 5 flops.

integer vs reg [4:0] — area comparison for a 0–16 counter

data flow
integer vs reg [4:0] — area comparison for a 0–16 counterinteger cnt;32-bit signed32 flops27 bits wastedreg [4:0] cnt;5-bit unsigned5 flopsexact width
For a counter range of 0–16, only 5 bits are needed. integer consumes 32 flops (27 bits wasted as constant 0). reg [4:0] consumes exactly 5. Multiply across the hundreds of counters in a real SoC design and the area cost is significant.

7.2 Visual B — sign-extension trap

A signed integer (-1) assigned to an unsigned reg [7:0] produces 8'hFF because of two's complement representation. Same integer value (-1) compared against an unsigned reg [3:0] value of 4'hF does not match — Verilog promotes the 4-bit value to 32-bit unsigned (32'h0000_000F), then the signed-vs-unsigned comparison rules produce FALSE.

Sign-extension trap with integerinteger i = -132'hFFFF_FFFF (signed)reg [7:0] x = i8'hFF (truncated correctly)i == reg[3:0]=4'hFFALSE (signed vs promoted)12
integer = -1 → 32 bits all 1s in two's complement (signed). When assigned to reg [7:0], truncation gives 8'hFF — looks right. When compared against reg [3:0] = 4'hF, Verilog promotes the 4-bit value to 32'h0000_000F (unsigned promotion, then signed comparison) — the signed -1 is NOT equal to the positive 15. The mental model 'integer holds -1' is correct; the surprise is in how it interacts with unsigned values.

7.3 Visual C — the use-case decision tree

The decision flow for picking the variable type.

integer vs reg use-case decisiontestbench loop?for-loop iteratorinteger i;conventionsynth RTL signal?counter, address, ...reg [N-1:0] q;explicit widthsynth + signed math?rarereg signed [N-1:0]s_q;explicit signed12
The two questions to ask. (1) Is this for loop iteration in a testbench or scratch variable? Use integer. (2) Is this for synthesisable RTL where width and signedness matter? Use reg [N-1:0] (or reg signed [N-1:0] for signed values). For everything in between, prefer the explicit-width form.

8. The Loop Iteration Pattern

The canonical integer use. A testbench initial block iterates over a range of values. The for loop syntax follows the C convention.

tb/loop_iteration.v — the canonical integer loop
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb_register_sweep;
    reg [7:0] cfg_reg [0:15];   // 16-entry × 8-bit configuration table
    integer i;                    // loop iterator
 
    initial begin
        // Initialize every entry to zero
        for (i = 0; i < 16; i = i + 1) begin
            cfg_reg[i] = 8'h00;
        end
 
        // Write a known pattern to each entry
        for (i = 0; i < 16; i = i + 1) begin
            cfg_reg[i] = i[7:0];     // bit slice prevents sign-extension warning
        end
 
        // Verify the pattern
        for (i = 0; i < 16; i = i + 1) begin
            if (cfg_reg[i] !== i[7:0])
                $display("MISMATCH at i=%0d: cfg_reg=%h, expected=%h",
                         i, cfg_reg[i], i[7:0]);
        end
 
        $display("Test complete.");
        $finish;
    end
endmodule

Three patterns visible:

  • for (i = 0; i < N; i = i + 1) — the C-style for loop. Verilog requires i = i + 1 (no ++ operator); SystemVerilog adds i++.
  • i[7:0] — bit-slice the loop iterator to match the target width. Without the slice, cfg_reg[i] = i; would assign a 32-bit value to an 8-bit reg and produce a width-mismatch lint warning.
  • %0d format — print i as a decimal integer with no leading zeros. The %d format would right-align to the integer's natural width (10 digits for 32-bit), making the output noisy.

The pattern scales — testbenches commonly have several integers, each used for one loop dimension. Nested loops (e.g., for stimulus matrices) are written as nested for blocks with separate integer iterators.

9. The Generate Loop Pattern (with genvar)

For synthesisable structural replication, the iterator is genvar, not integer:

rtl/array_inst.v — genvar for synthesis-time iteration
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`default_nettype none
 
module byte_register_array #(parameter N = 8) (
    input  wire          clk,
    input  wire          rst_n,
    input  wire [N-1:0]  wr_en,         // per-byte write enable
    input  wire [7:0]    wr_data,
    output wire [N-1:0][7:0] q_array    // packed array of N × 8-bit
);
    genvar i;            // strictly compile-time
 
    generate
        for (i = 0; i < N; i = i + 1) begin : g_byte
            byte_register u_byte (
                .clk    (clk),
                .rst_n  (rst_n),
                .wr_en  (wr_en[i]),
                .wr_data(wr_data),
                .q      (q_array[i])
            );
        end
    endgenerate
endmodule

The genvar is consumed entirely by the elaborator — by the time simulation starts, i no longer exists; what exists is N copies of the byte_register instance, each connected to wr_en[<literal>], q_array[<literal>], etc. There is no runtime variable.

Using integer instead of genvar for a generate loop is legal in some flows (most simulators accept it) but flagged by lint (use genvar for generate iteration). The Chapter 14.7 generate-block sub-pages cover this in detail.

10. Simulation Behavior

An integer is stored as 32 bits of 4-state data in the simulator's memory. Assignments, reads, and operations behave identically to reg signed [31:0]. The simulator's scheduling rules from §10 of the 5.2.1 reg page apply unchanged:

  • Blocking = runs in Active region; immediate update.
  • Non-blocking <= runs in Active region for the RHS, NBA region for the write.

There is no integer-specific simulator behaviour; the keyword is just a shorter way to declare a 32-bit signed reg.

11. Waveform Analysis

A waveform showing the sign-extension trap visually — same logical counter declared as integer and as reg [4:0], both incrementing through -1 (or its unsigned-wrap equivalent).

integer cnt vs reg [4:0] cnt — sign-extension behaviour

12 cycles
integer cnt vs reg [4:0] cnt — sign-extension behaviourBoth reset to 0 — same value, different storage widthBoth reset to 0 — same…Both increment to 1; integer holds 32'h0000_0001, reg5 holds 5'h01Both increment to 1; i…After many cycles, integer stores values as 32-bit signed; reg5 wraps at 31After many cycles, int…clkrst_nint_cntXXint_cnt_hexXX00..000..000..100..100..200..200..300..300..400..4reg5_cntXXreg5_hexXX0x000x000x010x010x020x020x030x030x040x04t0t1t2t3t4t5t6t7t8t9t10t11
Functionally identical for the positive range — both count 0, 1, 2, ... — but the integer holds 32 bits of state where the reg [4:0] holds 5. If the count reaches 32, the reg [4:0] wraps to 0 (the natural unsigned overflow); the integer continues to 32, 33, ... up to 2^31 - 1. The width difference is invisible in a one-shot test but real in the silicon.

The waveform illustrates the functional equivalence for the common case (positive counting). The sign-extension surprises only appear when the integer's value goes negative — which, for a well-designed RTL counter that never decrements past zero, doesn't happen. For testbenches that genuinely use signed arithmetic (e.g., computing expected ALU results that include sign), the integer semantic is correct.

12. Synthesis Behavior

integer is synthesisable. The synthesis tool maps an integer declaration to a 32-bit signed register, with the same gates as reg signed [31:0].

Three synthesis nuances:

  • Constant-propagation may shrink the register. If the synthesis tool can prove (e.g., from constant initial values and bounded increments) that the integer only uses N bits, it may optimise the register down to N flops. This optimisation is not guaranteed — it depends on the tool, the constraints, and the always-block context.
  • Sign extension on assignments. When an integer's value is assigned to a wider signed reg, the high bits sign-extend (replicate the integer's bit 31). When assigned to an unsigned wider reg, the high bits zero-extend.
  • Comparisons follow the signed rules. integer x; reg [7:0] y; if (x > y) performs a signed comparison after Verilog promotes y to 32-bit. For x = -1, the comparison is (-1) > 0 — TRUE if y is non-negative when interpreted as signed.

The synthesis tool may emit lint warnings on integer use in synthesisable RTL (integer is not the recommended type for this signal — use reg [N-1:0] with explicit width). Production codebases typically allow integer only in generate blocks (as iterator surrogates for genvar) and explicitly forbid it elsewhere.

13. Industry Use Cases

Three patterns where integer is the right tool.

13.1 Testbench loop counters

Every Verilog testbench has at least one for loop in an initial block. The loop iterator is integer i;. Convention is to use single-letter names (i, j, k) for short loops and descriptive names (row, col, byte_idx) for longer loops or nested structures.

13.2 $display and $monitor argument indexing

System tasks like $display, $strobe, and $write take format strings. When iterating to print every element of a memory or every bit of a vector, the iterator is conventionally integer:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer i;
initial begin
    for (i = 0; i < 16; i = i + 1)
        $display("cfg[%0d] = %h", i, cfg_reg[i]);
end

13.3 Configuration scratch variables in testbench initial blocks

Testbenches frequently compute expected values, build stimulus matrices, or track scoreboarding state in integer variables. These are simulation-only constructs that never reach synthesis.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer expected_sum;
integer error_count = 0;
 
initial begin
    for (integer i = 0; i < 100; i = i + 1) begin
        expected_sum = i * (i + 1) / 2;
        // ... apply stimulus, sample DUT output, compare ...
        if (dut_sum !== expected_sum) error_count = error_count + 1;
    end
    $display("Test complete. errors = %0d", error_count);
end

14. Common Mistakes

Five pitfalls that catch engineers using integer.

14.1 Using integer for an unsigned counter

The §1 example pitfall. An 8-bit counter declared as integer consumes 32 flops, the upper 27 bits always zero. Worse, when the counter participates in comparisons or arithmetic with other unsigned values, the signed semantics produce surprises. Use reg [N-1:0] with the exact width.

14.2 Mixing integer and unsigned reg in arithmetic

Verilog's width-extension and signed-vs-unsigned rules (IEEE 1364-2005 §4.5) are precise but counter-intuitive. Mixing integer i and reg [7:0] addr in if (i == addr) triggers signed promotion, and the result for negative i is FALSE even when "the bits look the same." Always pick one type family per expression — either all signed or all unsigned — and use bit-slice or $signed() / $unsigned() for explicit conversion.

14.3 Using integer in a generate block instead of genvar

Legal in some flows but flagged by lint. The genvar keyword is the compile-time iterator; integer is a runtime variable. Even if the simulator accepts the code, the static-elaboration semantics belong to genvar.

14.4 Initial value at declaration in synthesisable RTL

integer cnt = 0; is legal but not synthesisable. The synthesis tool ignores the initial value; the silicon's reset path determines the actual power-on state. Reset the integer in the asynchronous-reset path of a clocked always block.

14.5 Forgetting that integer is 4-state

An integer can hold X and Z values, just like reg. At t = 0, every bit is X until the first procedural assignment. Reading an integer before initialising it propagates X through every downstream operator. The same discipline as for reg — initialise before reading — applies.

15. Debugging Lab

Three integer debug post-mortems

16. Coding Guidelines

  1. Use integer for testbench loop iteration. The canonical use; convention is integer i; for short loops, descriptive names for longer ones.
  2. Use genvar for generate-loop iteration, not integer. The generate loop is compile-time; genvar is the right type.
  3. Use reg [N-1:0] for synthesisable counters, addresses, and data paths. Explicit width matches the actual range; no sign-extension surprises.
  4. Use reg signed [N-1:0] for synthesisable signed arithmetic. Explicit signed declaration is clearer than integer for synthesisable code.
  5. Don't mix integer with unsigned reg in arithmetic or comparison. Pick one type family per expression. Use bit-slice or $signed() / $unsigned() for explicit conversion.
  6. Bit-slice the iterator when assigning to a narrower target. cfg_reg[i] = i[7:0]; is clearer than cfg_reg[i] = i; (which produces a width-mismatch lint warning).
  7. Use %0d for $display of integers. Minimum width, no leading zeros. %d produces 10-digit-wide output for 32-bit integers.
  8. Reset every integer in synthesisable always blocks. Same discipline as reg — initialise before reading.

17. Interview Q&A

18. Exercises

Three exercises that turn the variable-type choice into reflex.

Exercise 1 — Pick the right type

For each signal, recommend integer, reg [N-1:0], reg signed [N-1:0], or genvar.

#Signal description
1A 16-cycle delay counter in synthesisable RTL
2A loop iterator in a testbench initial block
3A 32-bit signed accumulator in a DSP datapath
4A generate loop iterator expanding N copies of a sub-module
5A scoreboard error-count variable in a testbench
6An 8-bit address pointer in an arbiter FSM

Hints. Testbench iteration → integer. Generate iteration → genvar. Synthesisable signed math → reg signed [N-1:0]. Synthesisable unsigned counters/addresses → reg [N-1:0].

Exercise 2 — Spot the sign-extension trap

The following testbench fragment is supposed to iterate 256 times and check every byte value. Identify the bug and fix it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer i;
reg [7:0] data;
 
initial begin
    for (i = 0; i < 256; i = i + 1) begin
        data = i;                       // BUG: width mismatch
        // ... apply data, sample DUT, compare ...
    end
end

What to produce. (a) Identify the width-mismatch warning the lint tool emits. (b) Rewrite the assignment using a bit slice to make the width explicit. (c) Explain why the bit-slice version is clearer about the engineer's intent.

Exercise 3 — Convert integer-based RTL to explicit-width

The following synthesisable counter uses integer. Rewrite it using explicit-width reg [N-1:0] and confirm the gate count drops.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module up_counter (
    input  wire        clk,
    input  wire        rst_n,
    input  wire        en,
    output wire        max_reached
);
    integer cnt;
 
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)     cnt <= 0;
        else if (en)    cnt <= cnt + 1;
    end
 
    assign max_reached = (cnt == 255);
endmodule

What to produce. (a) Determine the exact bit width needed for cnt. (b) Rewrite using reg [N-1:0] cnt;. (c) Identify the gate-count reduction (number of flops saved).

19. Summary

integer is Verilog's convenience type for 32-bit signed variables. It is exactly equivalent to reg signed [31:0] for every purpose; the keyword is just shorter to type.

The properties baked into the keyword:

  • Fixed 32-bit width — no range modifier allowed.
  • Signed by default — two's complement semantics.
  • 4-state value system — same as reg, supports X and Z.
  • Procedural-assignment target — assigned by = or <= inside always / initial.
  • Synthesisable — maps to 32 flops, with possible constant-propagation shrinkage.

The use cases:

  • Loop iteration in testbenches. Canonical use; integer i; for for loops.
  • $display / $monitor argument indexing. Print every element of a memory.
  • Configuration scratch variables in testbench initial blocks. Error counts, expected values, stimulus matrices.

The cases where integer is wrong:

  • Synthesisable RTL counters, addresses, data paths, FSM states. Use reg [N-1:0] with the exact width.
  • generate block iterators. Use genvar (strictly compile-time).
  • Anywhere mixed-signedness arithmetic happens. The signed-vs-unsigned promotion rules produce surprises.

The day-to-day discipline:

  • integer for testbench loops. genvar for generate loops. reg [N-1:0] for everything else.
  • Don't mix integer and unsigned reg in arithmetic. Pick one type family per expression.
  • Use bit-slice for explicit width conversion. i[7:0] is clearer than relying on width-extension.
  • %0d for $display. Minimum width, no leading zeros.
  • Reset every integer in synthesisable RTL. Same as reg.

The next two sub-pages cover the rest of the variable family: 5.2.3 real (testbench-only floating-point) and 5.2.4 Scalar vs Vector vs Arrays (the dimensions every variable can take). After 5.2.4, Chapter 5 closes and Chapter 6 picks up with constant variables.

  • reg — Chapter 5.2.1; the workhorse variable type that integer extends.
  • Register Data Types — Chapter 5.2; the parent overview that motivates this sub-topic.
  • Number Representation — Chapter 4.4; literals and sign-extension rules.
  • Operator Usage — Chapter 4.3; the signed-vs-unsigned operator semantics that integer arithmetic uses.
  • Variables & Data Types — Chapter 5; nets and variables together.