VHDL data types and their ranges
VHDL is strongly typed. Every signal, variable and port has one type, which determines the values it can hold and the operators that apply to it, and influences how synthesis represents it in hardware. This page covers the types used in synthesizable designs. Converting between them is covered in VHDL type conversions.
Quick reference
| Type | Defined in | Values / range | Synthesizable | Use for |
|---|---|---|---|---|
std_logic | ieee.std_logic_1164 | 9 values: 'U' 'X' '0' '1' 'Z' 'W' 'L' 'H' '-' | yes | single bits: clocks, resets, enables |
std_ulogic | ieee.std_logic_1164 | same 9 values, unresolved | yes | single bits with exactly one driver |
std_logic_vector | ieee.std_logic_1164 | N bits, no numeric value | yes | ports, buses, raw data |
unsigned | ieee.numeric_std | 0 to 2N − 1 | yes | counters, addresses, unsigned arithmetic |
signed | ieee.numeric_std | −2N−1 to 2N−1 − 1 | yes | signed arithmetic |
integer | std.standard | implementation-dependent; 32-bit in most tools (see below) | yes | constants, generics, indices |
natural | std.standard | 0 to integer'high | yes | sizes, counts, indices |
positive | std.standard | 1 to integer'high | yes | widths and depths |
boolean | std.standard | false, true | yes | conditions, generics |
bit | std.standard | '0', '1' | yes | rarely used; superseded by std_logic |
| enumeration | user-defined | the listed names | yes | FSM states |
real | std.standard | IEEE 754 floating point, at least 64 bits | constant calculations only | computing constants and generics |
time | std.standard | physical type, e.g. 10 ns | no; some tools allow it in constant calculations | testbenches |
The IEEE types require these clauses. The std.standard types are always
visible.
library ieee;
use ieee.std_logic_1164.all; -- std_logic, std_ulogic, std_logic_vector
use ieee.numeric_std.all; -- unsigned, signed
std_logic and std_ulogic
std_logic has nine values. Most exist so that simulation can show faults.
| Value | Meaning | In simulation | Written as a literal in synthesizable code |
|---|---|---|---|
'U' | uninitialized | never assigned, e.g. a register with no reset | not meaningful |
'X' | forcing unknown | conflicting drivers or unknown input | don’t-care in most tools |
'0', '1' | forcing 0, forcing 1 | logic levels | logic levels |
'Z' | high impedance | undriven, e.g. an idle tri-state bus | tri-state buffer (FPGA: device pins only) |
'W' | weak unknown | conflicting weak drivers | not meaningful |
'L', 'H' | weak 0, weak 1 | pull-down, pull-up | read as 0 and 1 |
'-' | don’t care | optimizer chooses 0 or 1 |
The last column describes values written in the code, and tool behaviour
varies. A value that appears during simulation, such as an 'X' from two
conflicting drivers, has no hardware equivalent.
Resolution
std_logic is a resolved subtype of std_ulogic. When several drivers
assign the same signal, the resolution function in std_logic_1164
combines them using this table:
U | X | 0 | 1 | Z | W | L | H | - | |
|---|---|---|---|---|---|---|---|---|---|
U | U | U | U | U | U | U | U | U | U |
X | U | X | X | X | X | X | X | X | X |
0 | U | X | 0 | X | 0 | 0 | 0 | 0 | X |
1 | U | X | X | 1 | 1 | 1 | 1 | 1 | X |
Z | U | X | 0 | 1 | Z | W | L | H | X |
W | U | X | 0 | 1 | W | W | W | W | X |
L | U | X | 0 | 1 | L | W | L | W | X |
H | U | X | 0 | 1 | H | W | W | H | X |
- | U | X | X | X | X | X | X | X | X |
'U'dominates every other value.- A forcing value (
'0','1') overrides'Z'and the weak values. Two different forcing values give'X'. 'Z'gives way to any other value.'-'resolves like'X'.
Resolution determines how multiple drivers behave:
std_logicallows multiple drivers. This is required for tri-state buses, but an accidental second driver only appears as'X'in simulation.std_ulogicallows one driver. A second driver is an elaboration error, so the tool reports the mistake.std_logicis the usual convention.
std_logic_vector
An array of std_logic. It has a width and an index direction, but no
numeric value: the bits alone do not say whether "1000" is 8 or −8.
signal data : std_logic_vector(7 downto 0); -- data(7) is the leftmost element
signal lane : std_logic_vector(0 to 7); -- lane(0) is the leftmost element
When a vector is interpreted as unsigned or signed, the leftmost
element is the MSB.
- Arithmetic (
+,-,*) is not defined forstd_logic_vector. Adding two vectors fails to compile:error: no function declarations for operator "+". - Relational operators (
<,<=,>,>=) are predefined for all one-dimensional arrays, so they compile. They compare element by element from the left, not as numbers. For vectors of different lengths the result is numerically wrong:"100" < "0111"is false, although 4 < 7. For signed values the result is wrong even at equal lengths. - Equality (
=) is false for vectors of different lengths:"100" = "0100"is false.
Convert to unsigned or signed for any arithmetic or numeric comparison.
unsigned and signed
Both are defined in numeric_std as arrays of std_logic. In hardware
they are N wires, like std_logic_vector; the type sets the
interpretation. The leftmost bit is the MSB.
unsignedis plain binary: 0 to 2N − 1.signedis two’s complement: −2N−1 to 2N−1 − 1.
| Width | unsigned | signed |
|---|---|---|
| 4 | 0 to 15 | −8 to 7 |
| 8 | 0 to 255 | −128 to 127 |
| 16 | 0 to 65,535 | −32,768 to 32,767 |
| 32 | 0 to 4,294,967,295 | −2,147,483,648 to 2,147,483,647 |
Arithmetic rules:
+and-return the width of the wider operand. The carry is discarded: an 8-bitx"FF" + 1givesx"00".*returns the sum of the operand widths (M + N bits), so the full product cannot overflow. It is still truncated if assigned to a narrower signal.- For operands of equal width,
+and-produce identical bits forsignedandunsigned. Comparison, resizing, right shifts, multiplication and division differ. For example,"1000" < "0111"is false forunsigned(8 < 7) and true forsigned(−8 < 7).
integer, natural and positive
integer is a numeric type, not a vector: it cannot be sliced or indexed
by bit. Its range is implementation-dependent:
- VHDL-2008 guarantees at least −2,147,483,647 to +2,147,483,647.
- In practice, most tools implement 32-bit two’s complement: −2,147,483,648 to 2,147,483,647.
- VHDL-2019 raises the minimum to 64 bits (−263 to 263 − 1). Some tools apply this in 2019 mode, including Vivado synthesis and the nvc simulator.
natural and positive are subtypes of integer:
subtype natural is integer range 0 to integer'high;
subtype positive is integer range 1 to integer'high;
A constrained range reduces the synthesized width
The language does not define how many bits an integer occupies in
hardware. Synthesis tools derive the width from the range: Vivado and
Synplify, for example, implement an unconstrained integer as 32 bits (64
in Vivado’s VHDL-2019 mode), and integer range 0 to 9 as 4 bits.
Interactive schematic by RapidRTL
In this schematic (GHDL and Yosys, 32-bit integer), the register is 4 bits.
The addition n + 1 is drawn at integer width: the register value is
zero-extended (0x0 into bits 4:31), added, and truncated back to 4 bits
by the 0:3 slice. Other tools may build the adder at 4 bits directly.
Exercise: delete range 0 to 9 from both declarations and click Run.
The register, adder and comparator become 32 bits wide. With plain
natural, they become 31 bits, since no sign bit is needed.
Range checks are simulation-only
- Simulation checks every assignment: assigning 10 to
nis a fatal error. - Synthesis generates no range-checking hardware, and the language does not define what the hardware does with a value outside the declared range. Do not rely on it: a tool may optimize on the assumption that the value stays in range.
- With a 32-bit integer, values cannot exceed 231 − 1. Use
unsignedfor wider values.
Types and subtypes
- A subtype constrains an existing type. It is compatible with its base type; the constraint is checked in simulation.
- A type declaration creates a distinct type. It is incompatible with every other type, even one with an identical definition, and needs an explicit conversion.
subtype byte is std_logic_vector(7 downto 0); -- subtype
subtype count_t is natural range 0 to 9; -- subtype
type word_t is array (15 downto 0) of std_logic; -- new type
signal b : byte;
signal v : std_logic_vector(7 downto 0);
signal w : word_t;
signal c : count_t;
signal i : integer;
b <= v; -- legal: byte is a std_logic_vector
c <= i; -- legal: count_t is an integer (range checked in simulation)
w <= v & v; -- error: word_t is not a std_logic_vector
w <= word_t(v & v); -- legal: explicit conversion
Applied to the standard types:
std_logicis a subtype ofstd_ulogic;naturalandpositiveare subtypes ofinteger. Each mixes freely with its base type.- In VHDL-2008,
std_logic_vectoris a subtype ofstd_ulogic_vector. Before VHDL-2008 they were separate types. std_logic_vector,unsignedandsignedare separate types. They are closely related (same element type), so a type-name cast converts between them at no hardware cost. See VHDL type conversions.
Enumerations and boolean
Enumerations define a set of named values. Use them for FSM states:
type state_t is (idle, load, run, done);
signal state : state_t;
The compiler rejects undefined names, and synthesis selects the encoding (binary, one-hot or Gray). See FSM design.
boolean is the type of conditions: comparisons return it, and if
and while require it. It synthesizes to one bit. In VHDL-2008, if en then is accepted for a std_logic signal en, and is true when en is
'1' or 'H'.
Choosing a type
| Purpose | Type |
|---|---|
| Top-level ports, vendor IP interfaces | std_logic, std_logic_vector |
| Ports between your own entities | any; unsigned or signed documents numeric data |
| Arithmetic and comparison | unsigned, or signed if values can be negative |
| Generics, constants, loop and array indices | integer, natural, positive |
| Counters up to 31 bits | constrained integer or natural, or unsigned |
| Counters over 31 bits, or where individual bits are needed | unsigned |
| FSM states | enumeration |
| Constant calculations only | real |
| Testbenches only | time |
Non-standard packages: std_logic_arith and std_logic_unsigned
use ieee.std_logic_arith.all; -- Synopsys, not IEEE
use ieee.std_logic_unsigned.all; -- Synopsys, not IEEE
These are Synopsys packages that predate numeric_std (1997). Vendors
install them in library ieee, but they are not IEEE standards. Avoid them:
- They conflict with
numeric_std.std_logic_arithdeclares its ownunsignedandsigned, so using both packages makesunsignedambiguous:error: no declaration for "unsigned" (due to conflicts). - They apply file-wide.
std_logic_unsignedtreats everystd_logic_vectorin the file as unsigned, andstd_logic_signedas signed. The choice is not visible where the arithmetic occurs. - They are not portable. Tools need not provide them, and vendor
versions differ. GHDL requires the
-fsynopsysoption.
Use numeric_std. For unsigned arithmetic directly on std_logic_vector,
VHDL-2008 provides the standard package ieee.numeric_std_unsigned. There
is no standard signed equivalent; use signed.
References
Current standard:
- IEEE Std 1076-2019, IEEE Standard for VHDL Language Reference Manual (IEEE). Introduced the 64-bit minimum INTEGER range through LCS-2016-026c, “Long Integers” (archived copy), and requires floating-point types to conform to IEEE Std 754-2019.
Previous edition (clause numbers on this page refer to it):
- IEEE Std 1076-2008, IEEE Standard VHDL Language Reference Manual
(IEEE): §5.2.3 integer
types (INTEGER range: §5.2.3.2), §5.2.5 floating-point types, §6.2 type
declarations, §6.3 subtype declarations, §9.3.6 type conversions and
closely related types, Clause 16 predefined packages (STANDARD: §16.3).
This edition incorporated
std_logic_1164andnumeric_stdinto the language standard.
Historical origins:
- IEEE Std 1164-1993, IEEE Standard Multivalue Logic System for VHDL Model
Interoperability (Std_logic_1164)
(IEEE): origin of
std_logic. - IEEE Std 1076.3-1997, IEEE Standard VHDL Synthesis Packages
(IEEE): origin of
numeric_std.
Package sources and tools:
- IEEE package sources (
std_logic_1164, including the resolution table;numeric_std;numeric_std_unsigned), VHDL Analysis and Standardization Group: opensource.ieee.org/vasg/Packages. - GHDL documentation, “IEEE library pitfalls” (ghdl.github.io).
- AMD UG901, Vivado Design Suite User Guide: Synthesis: “VHDL Integer Types” (docs.amd.com) and “VHDL Predefined IEEE Real Type and IEEE Math_Real Packages” (docs.amd.com).
- nvc release notes, version 1.10.0: “The
integertype is now 64-bit in VHDL-2019 mode” (github.com/nickg/nvc).