Fact-checked by Grok 2 weeks ago

numeric_std

The numeric_std package is an IEEE standard library for the , providing predefined numeric types and functions specifically designed for tools in . It defines two primary resolved types: unsigned, an array of std_logic elements representing non-negative integers, and signed, an array of std_logic elements representing integers in form. These types enable portable and standardized operations on vectors of logic values, addressing the limitations of earlier standards that lacked built-in support for such . Developed under IEEE Standard 1076.3, numeric_std ensures interoperability across EDA tools and vendors by offering a consistent set of overloaded operators and functions for arithmetic (+, -, *, /, rem, mod), comparisons (>, <, <=, >=, =, /=), shifts (SHIFT_LEFT, SHIFT_RIGHT), logical operations (and, or, not, etc.), and type conversions (TO_INTEGER, TO_UNSIGNED, TO_SIGNED, RESIZE). Unlike non-standard packages such as std_logic_arith or std_logic_unsigned, which were vendor-specific and prone to portability issues, numeric_std is the recommended approach for modern designs, promoting clarity, synthesizability, and adherence to IEEE guidelines. It is compiled into the IEEE library and must be explicitly declared in VHDL code to access its features, with unresolved subtypes (UNRESOLVED_UNSIGNED and UNRESOLVED_SIGNED) available for advanced applications requiring explicit resolution control.

Introduction

Overview

The numeric_std package is an IEEE-standardized library that defines numeric types and provides overloaded arithmetic, logical, and comparison operations for vector-based representations, enabling reliable numeric processing in hardware description contexts. It is part of the broader IEEE 1076 standard for , specifically tailored for tools while maintaining compatibility with environments. Central to the package are two primary types: UNSIGNED, an array of std_logic elements representing non-negative integers in , and SIGNED, similarly structured but using notation for both positive and negative integers. These types facilitate type-safe operations on bit vectors, avoiding ambiguities inherent in native integer types for hardware modeling. In practice, numeric_std serves as the recommended standard for numeric handling in designs, replacing older non-IEEE packages like std_logic_arith to promote tool interoperability and consistent behavior across and flows. Basic inclusion in a VHDL entity or architecture requires the clauses library ieee; followed by use ieee.numeric_std.all;.

Purpose and Advantages

The numeric_std package serves as the standard library for performing synthesizable operations on vector types, defining specialized numeric types and functions to ensure consistent behavior across and synthesis tools while mitigating ambiguities inherent in vendor-specific or non-standard packages. Its primary purpose is to facilitate portable digital design by providing a unified framework for handling signed and unsigned , promoting reliability in description without relying on ad-hoc interpretations. Key advantages of numeric_std include its rigorous , which enforces distinctions between signed and unsigned operations to prevent common errors like unintended or type mismatches that could lead to - discrepancies. This strict checking enhances design robustness and debuggability, while its full compliance with IEEE Std 1076 ensures broad interoperability and tool independence, supporting seamless integration in multi-vendor environments for both behavioral and FPGA/ASIC . In contrast, legacy packages such as std_logic_arith and std_logic_signed/std_logic_unsigned—originating from and lacking IEEE standardization—are prone to ambiguities in and type coercion, rendering them deprecated in favor of numeric_std's safer, more predictable semantics. In practice, numeric_std is widely employed in core digital hardware components, including blocks for filtering and transformations, binary counters for sequencing and , and arithmetic logic units (ALUs) that execute , , and logical operations in FPGA and ASIC implementations. These use cases benefit from its resolved types—such as UNSIGNED and SIGNED—which enable clear, error-resistant modeling of numeric behaviors essential for high-performance, verifiable designs.

History and Standardization

Development

The numeric_std package originated in the late as part of the IEEE 1076.3-1997 Standard Synthesis Packages, which sought to establish standardized support for numeric operations in designs lacking such capabilities in the core language specification. This development addressed the growing need for reliable, synthesizable arithmetic on vector types like std_logic_vector, enabling consistent hardware description across tools. The primary contributors were members of the IEEE 1076.3 working group, who focused on creating portable, synthesis-oriented data types and functions to facilitate and of digital hardware. Their efforts were driven by the fragmentation caused by packages, such as std_logic_arith from and std_logic_signed/unsigned variants, which led to challenges and non-portable HDL code in multi-tool environments. Over time, numeric_std evolved through integration into the broader , fully incorporated as a core package in IEEE 1076-2008, elevating it from a supplemental to an essential language component. Subsequent revisions, including IEEE 1076-2019, maintained and refined its features, while related packages like fixed_generic_pkg and float_generic_pkg extended support for fixed-point and , building on numeric_std's foundational integer operations.

IEEE Standards Compliance

The numeric_std package is defined in IEEE Std 1076.3-1997, the IEEE Standard for VHDL Synthesis Packages, which establishes numeric types and arithmetic operations tailored for synthesizable hardware descriptions in VHDL. This standard forms part of the broader family of VHDL synthesis packages, ensuring portability across tools by standardizing vector-based integer arithmetic on types derived from std_ulogic. The package achieves full synthesizability for core arithmetic and logical operations, including , , , comparisons, and shifts, allowing direct mapping to elements like adders and multiplexers in FPGA and ASIC flows. However, advanced functions such as division and remainder are conditionally synthesizable, often requiring vendor-specific optimizations or additional resources, as they depend on tool capabilities for efficient implementation. Integration with the core language (IEEE Std 1076) occurs through conversion functions that allow std_logic_vector to be interpreted as signed or unsigned types, enabling arithmetic operations after explicit type conversions in digital designs. Standards evolution has refined this compatibility: VHDL-2008 (IEEE Std 1076-2008) introduced enhanced shift operations for unsigned types and the numeric_std_unsigned package for direct std_logic_vector unsigned math. Development and approval were led by the VHDL Analysis and Standardization Group (VASG) under Accellera, with IEEE ratification ensuring industry-wide adoption. Major EDA tools, including and , fully incorporate numeric_std for synthesis, supporting all defined operators in their VHDL compilers.

Data Types

UNSIGNED Type

The UNSIGNED type in the VHDL numeric_std package is a resolved subtype of UNRESOLVED_UNSIGNED, which is defined as an array of STD_ULOGIC elements with a NATURAL range, enabling the representation of non-negative integers in vector form. This type models unsigned numbers, where the value spans from 0 to $2^N - 1 for an N-bit , treating the elements as digits without a dedicated . Unlike the SIGNED type, which supports representation for negative values, UNSIGNED is restricted to positive integers and zero. To declare an UNSIGNED signal or variable, the syntax specifies the range, such as signal my_unsigned : unsigned(7 downto 0);, which creates an 8-bit vector where the leftmost index (7) holds the most significant bit (MSB) with the highest positional weight ($2^7). The MSB is interpreted as the highest-value bit, and the rightmost bit (index 0) is the least significant bit (LSB), following standard binary weighting from left to right. Upon declaration without an explicit initial value, an UNSIGNED signal defaults to all 'U' bits (undefined), similar to other std_logic-based types in VHDL. It supports direct assignment of binary string literals, such as my_unsigned <= "1010";, which sets the value to 10 in decimal (interpreting the string as binary digits). In terms of behaviors, arithmetic operations on UNSIGNED types result in overflow that wraps around modulo $2^N, preserving the vector length without signaling errors, as the package defines modular arithmetic for synthesis compatibility. Additionally, there is no built-in sign extension mechanism; resizing or concatenation operations zero-extend the value to maintain non-negativity rather than propagating a sign bit.

SIGNED Type

The SIGNED type in the numeric_std package is a resolved subtype of UNRESOLVED_SIGNED, which is defined as an array of STD_ULOGIC elements with a NATURAL range, enabling the representation of signed integers in two's complement binary vector form. It provides a standardized way to handle both positive and negative values in VHDL designs, with the range for an N-bit SIGNED vector spanning from -2^{N-1} to $2^{N-1} - 1. This contrasts with the UNSIGNED type, which is limited to non-negative values in the range 0 to $2^N - 1. The declaration of a SIGNED signal follows standard VHDL syntax, such as signal my_signed : signed(7 downto 0);, where the range specifies the bit width, with the leftmost index as the most significant bit (MSB). The MSB serves as the sign bit: a value of '0' indicates a positive number or zero, while '1' denotes a negative number, and the remaining bits encode the magnitude in two's complement form. In this representation, the value of a SIGNED vector is interpreted as the sum of its bit-weighted contributions, with the sign bit contributing negatively. By default, SIGNED signals are uninitialized upon declaration, taking on the undefined state ('U' for each bit) until assigned a value. Initialization can be achieved using functions from the package, such as to_signed(VALUE, WIDTH), which converts an integer to a SIGNED vector of the specified width; for example, to_signed(-5, 8) produces an 8-bit representation of -5. Arithmetic operations on SIGNED types exhibit wrap-around behavior for overflow and underflow, treating the result modulo $2^N. Resizing a SIGNED vector via the resize function employs sign extension—replicating the sign bit—for increases in width, ensuring the value remains correctly represented.

Package Usage

Library and Use Clauses

To include the numeric_std package in VHDL code, the standard approach is to declare the IEEE library and use the package with the all clause, which makes all types, operators, and functions within the package visible in the current scope.
vhdl
library ieee;
use ieee.numeric_std.all;
This declaration brings in the UNSIGNED and SIGNED types, along with arithmetic, comparison, and conversion functions defined in the package. For selective inclusion to minimize namespace pollution or avoid conflicts with legacy packages like std_logic_arith, specific elements can be imported instead of the entire package. For example, only the unsigned subtype or a particular function like to_unsigned can be used:
vhdl
library ieee;
use ieee.numeric_std.unsigned;
-- or
use ieee.numeric_std.to_unsigned;
This targeted approach reduces the risk of name clashes, such as duplicate operator declarations. Library and use clauses must be placed in the declarative region of an entity, architecture, or package, typically at the beginning of the VHDL file before any design unit that relies on them. These clauses apply to the immediate design unit and are inherited by associated secondary units like architectures, but they do not extend to unrelated design units in the same file. A common error occurs when omitting the use ieee.numeric_std.all; clause (or an equivalent selective one), resulting in compilation failures such as "unsigned is not declared" when attempting to use package types or operators. The numeric_std package is universally supported in modern VHDL simulators and synthesizers, including ModelSim/QuestaSim, GHDL, and Vivado, as it forms part of the for synthesis packages.

Type Declarations and Initialization

In the numeric_std package, the UNSIGNED and SIGNED types are defined as arrays of STD_LOGIC elements, enabling vector-based representations for unsigned and two's complement signed numbers, respectively. These types must be used with the appropriate library clause, such as library ieee; use ieee.numeric_std.all;, to access their declarations. For example, a signal can be declared as signal temp : signed(7 downto 0); or a variable as variable cnt : unsigned(15 downto 0);, where the range specifies the bit width. Initialization of SIGNED and UNSIGNED objects occurs during declaration or via assignment, supporting methods like direct bit-string literals, conversion functions, or default values. Direct assignment uses a quoted bit string, such as "00000000" for an 8-bit zero, which is interpreted as unsigned 0 or signed 0 depending on the type. The to_unsigned function converts a non-negative NATURAL to an UNSIGNED vector of specified size, as in variable cnt : unsigned(15 downto 0) := to_unsigned(0, 16);, while to_signed handles INTEGER values for SIGNED, for example, signal temp : signed(7 downto 0) := to_signed(-5, 8);. Alternatively, defaults like (others => '0') provide all-zero initialization, equivalent to unsigned 0 or signed 0. Vector lengths for these types require explicit specification, typically in descending from N-1 downto 0 for an N-bit width, to ensure consistent sizing during and . Dynamic sizing can be achieved using , such as declaring signal data : signed(G-1 downto 0); where G is a defining the width, allowing reusable components with variable bit lengths. This approach promotes modularity while maintaining . Best practices include using constants for fixed initialization values to improve readability and portability, for instance, constant ZERO : unsigned(7 downto 0) := (others => '0'); followed by signal reset_cnt : unsigned(7 downto 0) := ZERO;. Additionally, avoid direct mixing of SIGNED/UNSIGNED with std_logic_vector without explicit conversions, as assignments between these types are illegal and require functions like to_stdlogicvector to prevent compilation errors. Prefer the downto 0 range direction for clarity in most designs. A common pitfall is leaving signals uninitialized, which defaults them to 'U' () in simulation, potentially propagating indeterminate values and causing verification issues or unexpected behavior in waveforms. Always provide explicit initial values to mitigate this, ensuring reliable simulation from the outset.

Arithmetic Operators and Functions

Sign Changing Operators

In the numeric_std package, sign changing operators are provided specifically for manipulating the sign of values represented by the SIGNED type, which uses arithmetic. These operators include unary and the function, enabling inversion or removal of the while preserving the vector length. They are not defined for the UNSIGNED type in the case of negation, as unsigned values represent non-negative quantities. The unary (-) inverts the of a SIGNED vector by computing its , returning a result of the same length (subtype SIGNED(ARG'LENGTH-1 downto 0)). For example, the syntax for applying is:
vhdl
signal result : signed(my_signed'length-1 downto 0) := -my_signed;
This operation is implemented by bitwise inversion followed by addition of 1, and it handles special values like 'X' by propagating them unchanged. is fully synthesizable in , as it relies on basic bitwise and . The function ([abs](/page/ABS)) returns the of a SIGNED by checking the : if zero (non-negative), it copies the input; if one (negative), it applies unary negation. The result subtype is SIGNED(ARG'LENGTH-1 downto 0), with [abs](/page/ABS)(0) = 0. Syntax example:
vhdl
signal result : signed(my_signed'length-1 downto 0) := [abs](/page/ABS)(my_signed);
A notable behavior in for SIGNED occurs with the minimum representable value (-2^{(N-1)}, where N is the ), such as "" for 4 bits equaling -8: [abs](/page/ABS) yields the same negative value due to wraparound, as the true 2^{(N-1)} exceeds the positive (-2^{(N-1)} to 2^{(N-1)}-1), resulting in tool-dependent handling without explicit error signaling. This function is also fully synthesizable, typically using a for sign-based selection.

Basic Arithmetic Operators

The numeric_std package in provides overloaded arithmetic operators for the UNSIGNED and SIGNED types, enabling synthesizable binary operations on fixed-point vectors represented as arrays of STD_ULOGIC. These operators are defined to handle operands of the same type, with automatic length adjustment for differing bit widths: for and , the result length is the maximum of the operands' lengths, while for , it is the sum of the operands' lengths minus one. Overflow behavior follows —wrapping around for UNSIGNED (modulo $2^n) and wrapping for SIGNED—without built-in detection of carry or flags, which must be implemented separately if needed. Addition (+) performs vector addition, zero-extending the shorter operand as necessary to match the result length. For UNSIGNED, it computes the sum modulo the result bit width, while for SIGNED, it uses two's complement arithmetic to preserve sign extension. The operator is overloaded for pairs of UNRESOLVED_UNSIGNED or UNRESOLVED_SIGNED, as well as combinations with STD_ULOGIC_VECTOR, STD_ULOGIC, NATURAL, or INTEGER, always returning the resolved type with the specified length. An example usage is:
vhdl
library ieee;
use ieee.numeric_std.all;

signal a : unsigned(7 downto 0) := "00000011";  -- 3
signal b : unsigned(5 downto 0) := "000101";   -- 5
signal sum : unsigned(7 downto 0);
...
sum <= a + b;  -- Result: "00001000" (8), length max(8,6)=8
This operation is highly synthesizable, typically mapping to adder logic in hardware. Subtraction (-) mirrors addition in form and length handling but computes the difference, effectively adding the two's complement negation of the right operand for SIGNED to manage borrow propagation. For UNSIGNED, underflow wraps around similarly to addition overflow, yielding a result modulo $2^n. Overloads match those of addition, supporting the same type combinations and returning the resolved vector type. A representative example is:
vhdl
signal c : signed(7 downto 0) := "00000101";  -- +5
signal d : signed(7 downto 0) := "00000011";  -- +3
signal diff : signed(7 downto 0);
...
diff <= c - d;  -- Result: "00000010" (+2), two's complement handling
Like addition, subtraction synthesizes efficiently to subtractor circuits. Multiplication (*) produces a full-precision result without truncation, extending the product to A'\mathrm{length} + B'\mathrm{length} - 1 bits to capture all significant bits. For UNSIGNED, the operation yields a non-negative product; for SIGNED, it applies two's complement rules, accounting for operand signs to produce a correctly signed result. Overloads include pairs of the unresolved types or mixes with NATURAL/INTEGER, with the result always resolved and sign-extended for SIGNED. For instance:
vhdl
signal e : unsigned(3 downto 0) := "0011";  -- 3
signal f : unsigned(3 downto 0) := "0101";  -- 5
signal prod : unsigned(6 downto 0);
...
prod <= e * f;  -- Result: "0001111" (15), length 4+4-1=7
This operator is synthesizable, often inferring dedicated multiplier or DSP block resources in FPGA/ASIC flows for efficiency.

Division and Remainder Functions

The numeric_std package in VHDL defines overloaded division (/), remainder (rem), and modulo (mod) operators for its core numeric types: UNSIGNED (representing non-negative values) and SIGNED (using two's complement representation for signed integers), as well as compatible scalar types like NATURAL and INTEGER. These operators enable integer arithmetic on fixed-width vectors, with results sized to the length of the left operand (dividend) for division. All operations require a non-zero divisor; division by zero raises a runtime assertion error. The division operator / computes the integer quotient of the dividend A divided by the divisor B (A / B), truncating toward zero regardless of operand signs. For UNSIGNED types, the operation is straightforward positive division. For SIGNED types, the behavior accounts for two's complement semantics, ensuring the quotient reflects truncation toward zero (e.g., -7 / 3 = -2, since -7 = 3 * (-2) + (-1)). The result subtype is UNSIGNED(A'length-1 downto 0) or SIGNED(A'length-1 downto 0), potentially truncating if the divisor's bit requirements exceed this length. An example usage is quotient <= dividend / divisor;, where both operands are SIGNED vectors. The remainder operator rem yields the remainder of A divided by B (A rem B), with the sign matching the dividend A and absolute value strictly less than the absolute value of B (|A rem B| < |B|). For UNSIGNED, the result is always non-negative. For SIGNED, negative dividends produce negative remainders (e.g., -9 rem 5 = -4, since -9 = 5 * (-1) + (-4); 9 rem -5 = 4, since 9 = (-5) * (-1) + 4). The result subtype is UNSIGNED(R'length-1 downto 0) or SIGNED(R'length-1 downto 0), and an example is remainder_val <= dividend rem divisor;. This operator complements division by satisfying A = (A / B) * B + (A rem B). The modulo operator mod computes A mod B, producing a result with the sign of the divisor B and absolute value less than |B|. Unlike rem, the result is adjusted to be non-negative when B is positive and non-positive when B is negative, ensuring compatibility with mathematical modulo definitions in two's complement arithmetic (e.g., 9 mod 5 = 4; -9 mod 5 = 1, since -9 = 5 * (-2) + 1; 9 mod -5 = -1, since 9 = (-5) * (-2) + (-1)). The result subtype is UNSIGNED(R'length-1 downto 0) or SIGNED(R'length-1 downto 0), and it satisfies A = (A / B) * B + (A rem B), where (A mod B) aligns with B's sign for periodic interpretations. An example is modulo_val <= dividend mod divisor;. These functions are synthesizable in modern FPGA and ASIC tools, as standardized in IEEE 1076.3, but division and related operations are resource-intensive compared to addition or multiplication, often inferring dedicated divider hardware or requiring behavioral descriptions for optimization; for constant divisors (e.g., powers of two), shifts may be more efficient.

Comparison Operators

Relational Operators

The relational operators in the numeric_std package provide comparisons for magnitude between operands of type UNSIGNED or SIGNED, returning a BOOLEAN result. These operators include greater-than (>), less-than (<), greater-than-or-equal (>=), and less-than-or-equal (<=). They are overloaded to support comparisons between vectors of the same or different lengths, as well as between vectors and integer types (with implicit conversions via TO_UNSIGNED or TO_SIGNED). For UNSIGNED types, comparisons treat the operands as binary representations of non-negative integers, performing a direct magnitude comparison after resizing both to the maximum length by padding the shorter operand with leading zeros. In contrast, for SIGNED types, comparisons interpret the operands in two's complement notation, where the sign bit influences the result; the shorter operand is sign-extended (padded with copies of the sign bit) to match the longer length before comparison. If either operand contains invalid values such as 'X' (undefined) or is a null array, the result is FALSE. These operators are fully synthesizable as combinational logic in hardware descriptions. Consider an example with UNSIGNED vectors: if a is std_logic_vector(3 downto 0) with value "1010" (10 in decimal) and b is std_logic_vector(5 downto 0) with value "001100" (12 in decimal), then a < b evaluates to TRUE after resizing a to six bits as "001010". For SIGNED types, take c as std_logic_vector(3 downto 0) with "0111" (7) and d as std_logic_vector(3 downto 0) with "1000" (-8); here, c > d is TRUE due to the interpretation. Mixed-length operations, such as comparing a four-bit SIGNED to a six-bit SIGNED, automatically handle for the shorter . Edge cases highlight the operators' behavior: two UNSIGNED vectors of all zeros are not greater than each other (e.g., "0000" <= "0000" is TRUE), and for SIGNED, the maximum positive value exceeds the minimum negative value (e.g., in four bits, "0111" > "1000"). These comparisons complement checks but focus solely on relational ordering.

Equality Operators

The operators in the numeric_std package, = and /=, enable exact bit-pattern comparisons for SIGNED and UNSIGNED types, returning a BOOLEAN value indicating equality or inequality, respectively. These operators are overloaded separately for UNSIGNED (treating vectors as non-negative binary numbers) and SIGNED (treating vectors as two's complement signed numbers), but perform a direct bitwise match of the array elements in both cases. The comparison ignores the numerical interpretation inherent to the type, focusing solely on whether the bit patterns are identical; for instance, a SIGNED vector of all '1' bits (representing -1) equals another identical SIGNED vector, even though relational operators would interpret this value differently in numerical contexts. Similarly, this bit pattern corresponds to the maximum value for an UNSIGNED vector of the same length, illustrating that equality is agnostic to signedness when types match. Direct comparison between SIGNED and UNSIGNED requires explicit type conversion, such as via std_logic_vector, to enable bitwise evaluation. Operands may have different lengths, as the operators internally resize the shorter to match the longer one before : zero-padding for UNSIGNED and sign-extension for SIGNED. This ensures consistent handling but may alter the effective numerical value; for precise control, designers should explicitly resize operands using the resize prior to . If lengths differ significantly, the padded bits must align for to hold. In practice, these operators are commonly used in conditional logic for tasks like zero detection or state verification, as in the following example:
vhdl
library ieee;
use ieee.numeric_std.all;

signal counter : unsigned(7 downto 0);
-- ...
if counter = 0 then
    -- Reset or idle state
end if;
if a /= b then
    -- Handle mismatch
end if;
This approach leverages the bitwise nature for efficient checks, contrasting with relational operators (e.g., <, >), which perform value-based comparisons that account for and potential overflows. The operators are fully synthesizable, mapping to simple such as XOR gates followed by AND , or LUT-based comparators in FPGA implementations, with minimal resource overhead for typical widths.

Shift and Rotate Functions

Shift Functions

The shift functions in the numeric_std package enable bit-level manipulation through logical and shifts on SIGNED and UNSIGNED vectors, supporting operations that preserve length while facilitating tasks like data alignment and power-of-two scaling in hardware descriptions. Overloaded for both numeric types, these functions ensure consistent behavior across signed and unsigned contexts, with the shift amount specified as a non-negative of type NATURAL. They are defined such that the result subtype matches the input length, ARG'LENGTH-1 downto 0. The SHIFT_LEFT function performs a left shift on its argument ARG by COUNT positions, filling vacated least significant bits with '0' and discarding the COUNT most significant bits shifted out. Applicable identically to both UNSIGNED and SIGNED types, this maintains the same operation regardless of sign, with the syntax result := SHIFT_LEFT(ARG, COUNT). For an UNSIGNED argument, the behavior equates to multiplying the value of ARG by $2^{\text{COUNT}} $2^{\text{ARG'LENGTH}}, provided COUNT is within the ; if COUNT equals or exceeds the length, the result is all zeros. This equivalence holds conceptually for SIGNED as well, interpreting the value, though sign preservation is not enforced beyond the logical fill. The SHIFT_RIGHT function shifts ARG right by COUNT positions, but its fill mechanism differs by type: for UNSIGNED, it uses logical shifting with '0' fills in vacated most significant bits and discards the COUNT least significant bits, equivalent to integer by $2^{\text{COUNT}}; for SIGNED, it applies arithmetic shifting by replicating the (ARG'LEFT) into vacated positions, preserving the sign while discarding low bits. The syntax is result := SHIFT_RIGHT(ARG, COUNT), and if COUNT is zero, the result is ARG; for COUNT at or beyond the length, UNSIGNED yields all zeros, while SIGNED yields all s. Complementing the functions, numeric_std overloads the infix operators "sll" and "srl", providing logical shifts for more concise usage such as shifted := ARG sll COUNT or shifted := ARG srl COUNT. The "sll" operator always executes logical left shifts, while "srl" performs logical right shifts on both UNSIGNED and SIGNED (for SIGNED, by treating it as UNSIGNED internally). These functions are commonly employed for bit alignment or efficient multiplication/division by powers of two, as in the following example for an 8-bit unsigned value:
vhdl
library ieee;
use ieee.numeric_std.all;

signal a : unsigned(7 downto 0) := x"01";  -- Binary 00000001 (decimal 1)
signal shifted : unsigned(7 downto 0);
...
shifted <= a sll 2;  -- Yields x"04" (binary 00000100, decimal 4 = 1 * 2^2)
Such operations avoid the need for dedicated arithmetic units when scaling by small powers of two. In synthesis, the shift functions infer efficient barrel shifter implementations in target hardware like FPGAs, leveraging logarithmic-depth mux trees for variable COUNT values up to the vector length, with minimal area and delay overhead.

Rotate Functions

The rotate functions in the numeric_std package provide cyclic bit shifting operations for SIGNED and UNSIGNED types, preserving all bits by wrapping them around the vector ends rather than discarding or filling them. These functions treat the operands as logical bit vectors, performing rotations independently of the numeric interpretation. They are defined for both SIGNED and UNSIGNED types, ensuring consistent behavior across numeric representations. The ROTATE_LEFT function cyclically shifts the bits of the input vector to the left by the specified count, moving the leftmost bits to the rightmost positions. Similarly, ROTATE_RIGHT shifts bits to the right, relocating the rightmost bits to the leftmost positions. Both functions return a result of the same subtype as the input argument, with length ARG'LENGTH-1 downto 0. The effective shift amount is reduced modulo the vector length to handle counts larger than the bit width efficiently, ensuring rotations wrap around correctly without altering the vector size. Syntax for these functions is as follows:
vhdl
function ROTATE_LEFT (ARG: UNSIGNED; COUNT: NATURAL) return UNSIGNED;
function ROTATE_LEFT (ARG: SIGNED; COUNT: NATURAL) return SIGNED;
function ROTATE_RIGHT (ARG: UNSIGNED; COUNT: NATURAL) return UNSIGNED;
function ROTATE_RIGHT (ARG: SIGNED; COUNT: NATURAL) return SIGNED;
Overloaded operators rol and ror provide equivalent functionality:
vhdl
result := ARG rol COUNT;  -- Equivalent to ROTATE_LEFT(ARG, COUNT)
result := ARG ror COUNT;  -- Equivalent to ROTATE_RIGHT(ARG, COUNT)
For example, applying rotate_left to an 8-bit UNSIGNED value "10110011" (decimal 179) with COUNT = 2 yields "11001101" (decimal 205), as the two leftmost bits "10" cycle to the right end. The same operation on a SIGNED type behaves identically at the bit level. These rotations differ from shift functions by cycling bits end-to-end, avoiding the introduction of zeros or sign extensions. Rotate functions are synthesizable in modern FPGA and ASIC tools, mapping to efficient barrel shifter circuits, though they are less frequently used than shifts in typical designs. They find application in areas requiring bit preservation, such as cryptographic algorithms where rotations maintain data integrity during processing.

Conversion and Resize Functions

Type Conversion Functions

The numeric_std package in VHDL provides essential type conversion functions for transforming values between integer types (INTEGER, NATURAL, POSITIVE) and vector types (SIGNED, UNSIGNED, STD_LOGIC_VECTOR). These functions facilitate interoperability in digital design by enabling safe and explicit conversions while handling potential range mismatches through truncation and warnings. The TO_INTEGER function converts vector types to scalar integers. It is overloaded for UNSIGNED inputs, returning a NATURAL value representing the binary interpretation of the vector; for example, TO_INTEGER(my_unsigned) yields the decimal equivalent if my_unsigned is an UNSIGNED signal. Similarly, for SIGNED inputs, it returns an INTEGER, accounting for the two's complement representation. If the MSB of the input vector contains a metavalue like 'X' after TO_01 conversion, the function returns 0 and issues a simulation warning: "NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0". Metavalues in lower bits are treated as 0 without warning. Null arrays return 0 with a warning: "NUMERIC_STD.TO_INTEGER: null detected, returning 0". In synthesis, values exceeding INTEGER'HIGH may trigger errors or result in truncation, but the function is fully synthesizable. Conversely, TO_UNSIGNED converts non-negative scalar values to UNSIGNED vectors of a specified length. Its primary overload takes a NATURAL argument and a SIZE parameter, producing an UNSIGNED vector of that length: my_unsigned <= TO_UNSIGNED(42, 8). If the input value exceeds the representable range (i.e., greater than $2^{\text{SIZE}} - 1), the result truncates to the lower bits, accompanied by a simulation warning: "NUMERIC_STD.TO_UNSIGNED: vector truncated". An overload also exists for conversion from STD_LOGIC_VECTOR to UNSIGNED with a specified size, which may resize the input. To preserve length, use the type conversion UNSIGNED(my_slv). Attempting to pass a negative INTEGER to TO_UNSIGNED invokes VHDL's subtype constraint check, raising a runtime error since NATURAL excludes negatives. These functions are synthesizable, though out-of-range inputs during synthesis may produce errors. The TO_SIGNED function mirrors TO_UNSIGNED but targets SIGNED vectors, accepting an INTEGER argument (positive or negative) and a SIZE parameter: my_signed <= TO_SIGNED(-5, 4). For values outside the signed range (-\left(2^{{\text{SIZE}}-1}\right) to $2^{{\text{SIZE}}-1} - 1), it truncates and warns: "NUMERIC_STD.TO_SIGNED: vector truncated". An overload converts STD_LOGIC_VECTOR to SIGNED with a specified size, which may resize. To preserve length, use SIGNED(my_slv). Negative values set the sign bit appropriately within the available bits. Like its unsigned counterpart, synthesis supports this fully, with potential errors for range violations at compile time. Typecasting between numeric vectors and STD_LOGIC_VECTOR uses the overloaded STD_LOGIC_VECTOR function, which performs direct conversions without altering values or ranges. For instance, my_slv <= STD_LOGIC_VECTOR(my_unsigned) or STD_LOGIC_VECTOR(my_signed) reinterprets the bits as STD_LOGIC_VECTOR of the same length. These casts introduce no logic overhead in synthesis, as they are purely type changes, and do not trigger runtime errors or warnings unless underlying values are invalid. Overloads exist for both UNSIGNED and SIGNED inputs. In practice, these conversions often pair with resizing operations for complete type adjustments, though bounds checking remains the designer's responsibility to avoid simulation warnings or synthesis issues.

Resize Function

The RESIZE function in the package is designed to adjust the length of SIGNED or UNSIGNED vectors to a specified number of bits, facilitating compatible operand sizes in arithmetic operations. This function is essential for maintaining numeric integrity during size changes, as it preserves the original value as much as possible through defined extension or truncation rules. The syntax for RESIZE is straightforward: result := RESIZE(arg, new_size);, where arg is either a SIGNED or UNSIGNED vector, and new_size is a positive NATURAL integer specifying the desired bit length of the result. The return type matches the input type, with the result subtype defined as SIGNED(NEW_SIZE-1 downto 0) or UNSIGNED(NEW_SIZE-1 downto 0), respectively. If NEW_SIZE is less than 1, the function returns a null array, though practical usage assumes a valid positive size. For UNSIGNED vectors, extension to a larger size zero-fills the most significant bits (MSBs), while shortening truncates the most significant bits (MSBs) by dropping the leftmost bits, preserving the least significant bits (LSBs). In contrast, for SIGNED vectors, extension sign-extends by replicating the sign bit (ARG'LEFT) into the new MSBs to preserve the signed value, and shortening truncates the most significant bits (MSBs) by dropping the leftmost bits, preserving the least significant bits (LSBs); the new sign bit becomes the original bit at the new MSB position, which may alter the numeric value. These behaviors ensure that the numeric interpretation remains consistent, avoiding unintended value shifts during resizing. A common use case for RESIZE is matching operand lengths before applying arithmetic operators, such as addition or multiplication, to prevent length-mismatch errors during synthesis in FPGA or ASIC designs. For instance, when combining signals of different bit widths from various modules, RESIZE ensures uniform sizing without manual bit manipulation, promoting cleaner and more portable code. Consider an example with an 8-bit UNSIGNED vector short_vec (value 255, binary '11111111') resized to 16 bits: extended := RESIZE(short_vec, 16); yields '00000000000000000011111111', preserving the value through zero-filling. Similarly, for a 4-bit SIGNED vector signed_short (value -1, binary '1111') resized to 8 bits: extended_signed := RESIZE(signed_short, 8); results in '11111111', maintaining the negative value via sign extension. The RESIZE function is fully synthesizable, as it translates to simple hardware logic for padding (zero-fill or sign replication) or truncation, which is efficiently implemented in tools like Xilinx Vivado or Intel Quartus without optimization overhead. This makes it a preferred choice over custom concatenation or slicing for width adjustments in synthesizable RTL code.

Logical Operators

Bitwise Logical Operators

The numeric_std package in VHDL provides overloaded bitwise logical operators that perform bit-by-bit operations on the UNSIGNED and SIGNED types, enabling efficient manipulation of numeric vectors while maintaining their numeric semantics. These operators include the unary not for inversion and the binary and, or, nand, nor, xor, and xnor for conjunction, disjunction, and their complements. Unlike the logical operators in std_logic_1164, which operate on std_logic_vector without inherent numeric interpretation, the numeric_std versions treat UNSIGNED and SIGNED as arrays of bits but return results in the same numeric subtype as the left operand, preserving the vector's length and signed/unsigned nature for subsequent arithmetic or comparison use. The unary not operator inverts each bit of its operand element-wise. It is overloaded as function "not" (L: UNSIGNED) return UNSIGNED and function "not" (L: SIGNED) return SIGNED, producing a result with the same length as the input (L'LENGTH-1 downto 0). For example, applying not to an UNSIGNED vector representing a mask can generate its complement for signal enabling: inverted_mask <= not original_mask;. This operation is synthesizable, mapping directly to inverter gates in hardware implementations. Binary operators such as and, or, nand, nor, xor, and xnor apply their respective logic to corresponding bits of two operands, requiring both to have identical lengths; if lengths differ, explicit resizing (e.g., via the resize function) is necessary before application to avoid synthesis or simulation errors. Each is overloaded separately for UNSIGNED and SIGNED: for instance, function "and" (L, R: UNSIGNED) return UNSIGNED and function "and" (L, R: SIGNED) return SIGNED, with the result matching the left operand's type and length. Common applications include bit masking, where masked_signal <= input_data and mask; selectively clears bits. Bitwise xor chains enable parity computation logic. The xnor operator, introduced for compatibility with and later, computes equivalence but is not supported in VHDL-1987. These operators differ from std_logic_1164 equivalents by ensuring the output retains numeric context, allowing seamless integration into arithmetic expressions without type conversions. All bitwise logical operators in numeric_std are fully synthesizable, translating to primitive logic gates (e.g., AND/OR for and/or, XOR for xor) in FPGA or ASIC flows, with no overhead from resolution functions unlike some std_logic_1164 behaviors. Overloads also support mixing with scalar std_ulogic values, such as result <= vector and '1';, enhancing code conciseness for constant masking. This design prioritizes hardware efficiency and numeric consistency over the purely logical focus of std_logic_1164.

Miscellaneous Functions

Match Function

The STD_MATCH function, part of the IEEE numeric_std package, facilitates the comparison of two logic vectors by treating the don't care value ('-') as a wildcard that matches any corresponding bit value in the other operand. It returns a BOOLEAN value of TRUE if all corresponding bit pairs match under this relaxed rule, and FALSE otherwise. The function requires operands of equal length and performs an element-wise comparison. In terms of bit-level behavior, STD_MATCH evaluates each pair of bits according to the matching semantics defined in IEEE Std 1164 for STD_ULOGIC: it returns TRUE for logically equivalent pairs (e.g., '0' and '0', or '1' and '1'), pairs where at least one is '-' (which matches any STD_ULOGIC value, including 'X' and 'Z'), or pairs with equivalent strengths (e.g., '0' and 'L', '1' and 'H'). It returns FALSE for incompatible pairs, such as 'X' and '0', though compatible metalogical values like 'X' and 'X' or 'Z' and 'Z' return TRUE. Since VHDL-2008 (IEEE Std 1076-2008), STD_MATCH is also defined in the ieee.std_logic_1164 package for broader use, while remaining available in numeric_std for compatibility. This contrasts with the standard equality operator (=), which treats '-' as incompatible with defined values like '0' or '1', often yielding FALSE in direct comparisons. The function is overloaded to support various vector types, including STD_LOGIC_VECTOR, STD_ULOGIC_VECTOR, UNSIGNED, and SIGNED, allowing its use with both raw logic vectors and the numeric types defined in . For instance, the syntax for usage is straightforward:
vhdl
if STD_MATCH(vec1, vec2) then
    -- Vectors match, including any don't cares
end if;
This overloading ensures compatibility without requiring explicit type conversions in numeric contexts. Primarily employed in simulation testbenches, STD_MATCH is valuable for verifying partial or masked bit patterns in expected versus actual results, such as checking specific control bits while ignoring variable fields. An example involves comparing an address bus where only certain bits are relevant:
vhdl
-- Expected pattern with don't cares for irrelevant bits
constant EXPECTED_ADDR : std_logic_vector(7 downto 0) := "1010----";
if STD_MATCH(actual_addr, EXPECTED_ADDR) then
    report "Address matches pattern";
end if;
Here, the function confirms that bits 7-6 and 4-3 are '1' and '0' respectively, while positions 5-0 can be any value. Although defined in the synthesis-oriented package, STD_MATCH is generally not synthesizable due to the non-deterministic nature of don't cares in hardware implementation, limiting its application to simulation and verification environments.

Translation Functions

The TO_01 function in the numeric_std package provides a mechanism to translate elements of UNSIGNED or SIGNED vectors, which are arrays of std_ulogic, into binary values suitable for numeric operations. It operates termwise, mapping 'H' (strong high) to '1' and 'L' (strong low) to '0', while handling other meta-values such as 'U' (uninitialized), 'X' (unknown), 'Z' (high impedance), 'W' (weak unknown), and '-' (don't care). If any element contains a value outside of '0', '1', 'H', or 'L', the entire output vector is set to the specified XMAP value (defaulting to '0'), and a simulation warning is issued to alert the designer to potential issues with non-binary inputs. This function is overloaded for both UNSIGNED and SIGNED types, returning a result of the same subtype and length as the input to preserve vector properties. For example, the syntax for usage is:
vhdl
clean_vec : UNSIGNED(7 downto 0) := TO_01(dirty_vec, XMAP => '0');
Here, dirty_vec might originate from resolved bus signals where values like 'H' or 'Z' arise due to multiple drivers, and the function cleans it for subsequent arithmetic. It is particularly useful in preparing vectors for numeric operations after bus resolution, as internal implementations of operators like (=) in numeric_std invoke TO_01 to map inputs to before conversion to integers for comparison. In terms of synthesizability, TO_01 is behavioral and primarily intended for to detect and flag meta-value propagation, but it can be used in synthesizable code for input cleanup, though tools may issue warnings or conservatively treat non-binary inputs as undefined during optimization. The optional XMAP parameter allows customization, such as mapping to 'X' for explicit in verification scenarios. Related to TO_01, the Is_X function serves as a utility to detect the presence of meta-values ('U', 'X', 'Z', 'W', or '-') in UNSIGNED or SIGNED vectors, returning TRUE if any element matches these conditions, which complements TO_01 in pre-processing checks.

References

  1. [1]
    ieee/numeric_std.vhdl
    Jul 26, 2019 · This package defines numeric types and arithmetic functions for use with synthesis tools. Two numeric types are defined.
  2. [2]
    VHDL Vector Arithmetic using Numeric_std - Doulos
    We cover vector arithmetic extensively on our Comprehensive VHDL course, including two useful diagrams summarising the contents of numeric_std.
  3. [3]
    Numeric_Std Package - HDL Works
    It defines the package numeric_std. Two new numeric data types are declared in the numeric_std package: type unsigned is array (natural range <>) of std_logic; ...
  4. [4]
    VHDL IEEE Packages - 2025.1 English - UG901
    Conversion functions based on these types. numeric_std. Unsigned and signed vector types based on std_logic . Overloaded arithmetic operators, conversion ...
  5. [5]
    numeric_std Vs. std_logic_arith packages - Intel Community
    Feb 2, 2010 · IMHO ieee.numeric_std makes the code more readable. The automated casts of std_logic_arith allow very sloppy code styles, making bugs harder to ...
  6. [6]
  7. [7]
    VHDL code for Arithmetic Logic Unit (ALU) - FPGA4student.com
    An ALU executes logic and arithmetic operations like addition, subtraction, multiplication, division, logical shifts, rotations, AND, OR, XOR, NOR, NAND, XNOR, ...
  8. [8]
    VHDL Math: std_logic_arith vs. numeric_std - Nandland
    Jun 30, 2022 · When using numeric_std, you are not allowed to perform any mathematical operations on signals unless you have first declared them as either type ...
  9. [9]
    [PDF] IEEE Std 1076.3-1997, IEEE Standard VHDL Synthesis Packages
    This standard, IEEE Std 1076.3-1997, supports the synthesis and verification of hardware designs, by defin- ing vector types for representing signed or unsigned ...
  10. [10]
    [PDF] IEEE Standard VHDL Language Reference Manual
    Jan 26, 2009 · VHDL is a formal notation intended for use in all phases of the creation of electronic systems. Because it is both machine readable and human ...Missing: 2019 | Show results with:2019
  11. [11]
    [PDF] IEEE Standard for VHDL Language Reference Manual - 0x04.net
    Dec 23, 2019 · Abstract: VHSIC Hardware Description Language (VHDL) is defined. VHDL is a formal notation intended for use in all phases of the creation of ...
  12. [12]
    IEEE 1076.3-1997 - IEEE SA
    Jun 5, 1997 · This standard provides semantic for the VHDL synthesis domain, and enables formal verification and simulation acceleration in the VHDL based design.
  13. [13]
    [PDF] IEEE 1076-2008 VHDL-200X - SynthWorks
    • Std_logic_1164 Updates. • Numeric_Std Updates. • Numeric_Std_Unsigned. • Biggest Language change since 1076-1993. SynthWorks. 4. Copyright © SynthWorks 2008.
  14. [14]
    2.11.2.2. VHDL Standard Libraries and Packages - Intel
    Sep 26, 2022 · For compatibility with older designs, the Intel® Quartus® Prime software also supports the following vendor-specific packages and libraries:.
  15. [15]
    ieee/numeric_std.vhdl · release · vasg / Packages · GitLab
    ### Summary of Header Comments from numeric_std.vhdl
  16. [16]
    How to use Signed and Unsigned in VHDL - VHDLwhiz
    Sep 2, 2017 · The signed and unsigned types in VHDL are bit vectors which can be used in calculations. They overflow silently, and they get sign-extended by
  17. [17]
    [PDF] VHDL Logic Synthesis Appendix - Inria
    • The use clause may selectively establish visibility. – only the function ... use ieee.numeric_std.all; entity decoder is generic (NbOut : integer ...
  18. [18]
    The scope of VHDL use clauses and VHDL library clauses - Sigasi
    Dec 14, 2011 · The use clauses and library clauses are only written once at the top of the VHDL file. A naïve passer-by would assume that these clauses are valid throughout ...
  19. [19]
    Top 10 Common VHDL Coding Errors and How to Fix Them
    Oct 31, 2024 · This article will cover 10 common VHDL coding errors. For each error, I'll explain why it happens, describe the error message you'll see, and show you how to ...
  20. [20]
    Supported VHDL Types - 2025.1 English - UG901
    Supported VHDL Types - 2025.1 English - UG901. Vivado Design Suite User Guide: Synthesis (UG901). Document ID: UG901; Release Date: 2025-06-11 ...Missing: ModelSim GHDL
  21. [21]
    numeric_std - UMBC
    If a value other than '0'|'1'|'H'|'L' is found, -- the array is set to (others => XMAP), and a warning is -- issued. end NUMERIC_STD;
  22. [22]
    Review of VHDL Signed/Unsigned Data Types - Technical Articles
    Feb 1, 2018 · As shown in Figure 1, the signed/unsigned data types are defined in the “numeric_std” package. This package is included in the “ieee” library.
  23. [23]
  24. [24]
    Is overflow defined for VHDL numeric_std signed/unsigned
    Aug 2, 2012 · Short answer: There is no overflow handling, the overflow carry is simply lost. Thus the result is simply the integer result of your operation modulo 2^MAX.What is the range of a VHDL integer definition VHDL-2019?VHDL numeric_std function ("+") - Stack OverflowMore results from stackoverflow.comMissing: 2019 | Show results with:2019
  25. [25]
    numeric_std.vhd - UMBC
    ... NUMERIC_STD arithmetic package for synthesis -- : Rev. 1.7 (Nov. 23 1994) -- : -- Library : This package shall be compiled into a library symbolically ...<|control11|><|separator|>
  26. [26]
    VHDL Integer Division Problem | Forum for Electronics
    Oct 9, 2011 · In other words the simulator rounds the result toward zero instead of calculating the FLOOR of the result. if i define a and b as ...Missing: numeric_std truncates
  27. [27]
    [PDF] VHDL Fundamentals Lecture 6 Traditional PL HDL
    Highlights of modern HDL: – Encapsulate the concepts of entity, connectivity, concurrency, and timing. – Incorporate propagation delay and timing ...
  28. [28]
    VHDL Synthesis, can`t make code be synthesizable - Stack Overflow
    Nov 1, 2016 · In VHDL/verilog MOD ,REM and DIVISION operators are not synthesizable. Division is only possible when the second operand is a power of 2.
  29. [29]
    ieee/numeric_std-body.vhdl · release · vasg / Packages · GitLab
    Jul 26, 2019 · The following project contains open source materials that will be referenced by the IEEE 1076 standard.
  30. [30]
    Shift Left, Shift Right – VHDL Example - Nandland
    Feb 27, 2025 · Performing shifts in VHDL is done via functions: shift_left() and shift_right(). The functions take two inputs: the first is the signal to shift, the second is ...Missing: specification | Show results with:specification
  31. [31]
    Shifter Design in VHDL - FPGA4student.com
    Fast shifting and rotating functions are critical for cryptographic applications. VHDL code for the shifter will be presented together with its testbench ...Missing: buffer | Show results with:buffer
  32. [32]
    ieee/numeric_std-body.vhdl
    Jul 26, 2019 · This package defines numeric types and arithmetic functions for use with synthesis tools. Two numeric types are defined.
  33. [33]
    [PDF] Vivado Design Suite User Guide: Synthesis
    Nov 16, 2022 · Advantages of VHDL . ... In previous versions of VHDL, the return expression of a function needed be same type as.
  34. [34]
    [PDF] Enhancements to VHDL's Packages - SynthWorks
    This paper will provide details about the new features of std_logic_1164 and numeric_std and ... Logical reduction operators: std_logic_1164, numeric_std. Add ...Missing: differences | Show results with:differences
  35. [35]
    [PDF] VHDL Packages: numeric_std - PLDWorld.com
    IEEE std 1076.6-1999 defines the RTL synthesizeable subset of the language and how language statements should be interpreted for synthesis purposes. – This ...Missing: advantages | Show results with:advantages