Skip to content
VLSI Mentor

VHDL · Chapter 2.6 · Data Types

Integer Types and Ranges

Not every value in a design is a bus. Indices, counts, loop bounds, and generics are numbers, and VHDL's integer is the abstract whole-number type for them, with no bit positions and no width, just a value with a range. This lesson shows why an unconstrained integer is 32 bits wide, how adding a range constraint shrinks it to exactly the hardware you want, and how it differs from the unsigned type of the numeric library, which is bits that stand for a number. It also covers the gotcha that catches everyone: exceeding an integer's range is a runtime error, not the silent wrap-around an unsigned value would give you, so a counter needs its range chosen with care.

Foundation14 min readVHDLintegerRangesSubtypesnumeric_stdCounters

1. Intuition — an abstract number, not a bus

std_logic_vector is wires; integer is a number. It has no bit positions you can index, no 'Z' or 'X', no width you declare — just a whole-number value. You use it wherever the design needs to count or index rather than route bits: loop variables, array indices, generics, and the value inside a counter.

Because it is abstract, the tool is free to implement it in as many or as few bits as the value needs — if you tell it the range. That single idea, range determines width, is what this lesson is about.

2. Range determines width

An unconstrained integer spans the full 32-bit signed range (about ±2.1 billion). Declared as a signal it implies 32 bits of hardware — almost always far more than you need. Adding a range constraint tells synthesis the real bounds, and it builds only the bits required:

integer range constraints mapping to synthesized bit widthsinteger range 0 to 1516 values4 bitssynthesised widthinteger range 0 to255256 values8 bitssynthesised widthinteger(unconstrained)full 32-bit range32 bitswasteful — avoid12
An integer's range constraint sets its synthesized width. 'integer range 0 to 15' needs 4 bits; 'integer range 0 to 255' needs 8 bits; an unconstrained 'integer' defaults to the full 32-bit signed range and synthesizes to 32 bits — almost always wasteful or rejected. Always constrain an integer's range so it becomes exactly the hardware you intend.
constrained_integers.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
signal idx   : integer range 0 to 15;     -- 4 bits — a small index
signal level : integer range 0 to 255;    -- 8 bits — a byte-sized count
-- signal big : integer;                   -- 32 bits — avoid in real signals
 
-- handy predefined subtypes:
signal n : natural;    -- integer range 0 to integer'high   (non-negative)
signal p : positive;   -- integer range 1 to integer'high   (strictly positive)

natural and positive are just constrained integer subtypes the standard predefines — use them to document intent (a count cannot be negative; a divisor cannot be zero).

3. Where integers belong — indices, loops, generics

integer is the natural type for anything that counts or selects, where you never need to touch individual bits:

integers_in_use.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- loop variable: an integer, ranged by the loop bounds
gen : for i in 0 to 7 generate
  y(i) <= a(i) xor b(i);
end generate;
 
-- array index: integers select elements
data_out <= mem(addr);            -- addr is an integer index into mem
 
-- generic: a compile-time integer parameter
entity fifo is
  generic ( DEPTH : positive := 16 );   -- positive integer, ranged and defaulted
  port ( ... );
end entity;

In each case the value is a number used to count or pick, not a bus to be wired — which is exactly what integer is for.

4. The range bound over time — and the wrap gotcha

A constrained integer counter behaves like any counter, but its range bound is a hard limit you must respect. The waveform shows a range 0 to 5 counter that wraps because the code explicitly resets it — without that reset, hitting 6 is a runtime error, not a wrap:

integer range 0 to 5 counter — counts, then wraps only because the code resets it

14 cycles
integer range 0 to 5 counter — counts, then wraps only because the code resets itcounts 0,1,2,3,4,5 within rangecounts 0,1,2,3,4,5 withinrangeat 5: code forces wrap to 0 (no 6 ever occurs)at 5: code forces wrap to 0(no 6 ever occurs)clkcount00123455001122t0t1t2t3t4t5t6t7t8t9t10t11t12t13
The counter stays inside 0 to 5 because the RTL explicitly sets it back to 0 at the top. If the code instead let it reach 6, the simulator stops with a range error — a constrained integer does NOT silently wrap the way an unsigned does.
ranged_counter.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
signal count : integer range 0 to 5;
process (clk)
begin
  if rising_edge(clk) then
    if count = 5 then
      count <= 0;          -- explicit wrap — REQUIRED to stay in range
    else
      count <= count + 1;
    end if;
  end if;
end process;

5. Debugging example — "value 6 is out of range 0 to 5"

This is the integer gotcha, and the error message is exact:

the overflow that is an error, not a wrap
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
signal count : integer range 0 to 5;
-- count <= count + 1;   -- if count is already 5, this assigns 6
-- → simulation stops: "value 6 is out of the range 0 to 5 of signal count"

Read it literally: you drove a constrained integer past its declared bound, and VHDL treats that as an error — because a value outside the range is meaningless for that type, not a modulo wrap. The fix is to guard the increment (the if count = 5 above) or, if you actually want wrap-around arithmetic, use unsigned from numeric_std, which wraps by design:

if you want wrap-around, use unsigned
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
signal c : unsigned(2 downto 0);   -- 0..7, wraps 7 → 0 automatically
-- c <= c + 1;                      -- legal: unsigned arithmetic wraps, no error

6. Common mistakes & what to watch for

  • Leaving an integer unconstrained. A bare integer signal is 32 bits — wasteful, and often rejected by synthesis. Always give a range.
  • Expecting an integer to wrap. It does not — exceeding the range is a runtime error. Guard it, or use unsigned/signed for wrap-around.
  • Using integer where you need bit access. You cannot index an integer's bits; if you must slice or manipulate bits, it should be a vector (unsigned/signed/ std_logic_vector).
  • Forgetting natural / positive. They are free, documenting constraints (no negatives; no zero) the compiler then enforces.

7. Engineering insight

integer and unsigned are not redundant — they sit on opposite sides of the abstract/concrete line. integer is a pure number with a range: ideal for indices, loop bounds, and generics, where the value matters and the bits do not. unsigned/signed are bits that mean a number: ideal where width, bit access, or wrap-around matter. Constrain your integers so they synthesize small and so out-of-range bugs surface immediately, and reach for numeric_std the moment you need defined-width, wrapping arithmetic.

8. Summary & next step

VHDL's integer is an abstract whole-number type for counts, indices, loop variables, and generics. Unconstrained it is 32 bits; a range constraint shrinks it to the minimal synthesized width and is essentially mandatory in real RTL. It is not a bus — no bit access — and it does not wrap: exceeding the range is a runtime error, so use unsigned/ signed when you need wrap-around. natural and positive are predefined constrained subtypes worth using for intent.

You now have both worlds of scalar value: logic-valued (std_logic) and number-valued (integer). The chapter continues by tying them together — numeric_std's unsigned and signed, the types that let buses do arithmetic — and the conversions that move cleanly between all of them.

Standards & specifications

Governing standard
IEEE Std 1076 (VHDL)(opens IEEE in a new tab)

Defines the VHDL language — types, the simulation cycle, and the semantics a conforming analyser and simulator must implement. Synthesis restrictions and vendor coding rules are tool behaviour, not language rules.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of VHDL Data Types — std_logic, Vectors, Arrays & Records.