Every VHDL file starts with the same ritual: two lines of library declarations copied from the last project. Most engineers type them from muscle memory, without knowing what they are actually importing — or how choosing the wrong package can cause two perfectly valid lines of code to refuse to compile together.
Unlike Verilog, which has primitive types baked into the language itself, VHDL’s most fundamental type — the ubiquitous std_logic — is not part of the core language at all. It lives in a package. Understanding what those packages actually contain, and more importantly what they should not contain, separates clean portable RTL from a legacy maintenance nightmare.
1. The Anatomy of a VHDL Library Declaration
VHDL organizes reusable code into a three-level hierarchy: libraries contain packages, and packages contain type definitions, functions, and constants. The library keyword makes a compiled library visible to the design unit. The use keyword imports specific packages from that library into the current scope.
The IEEE library is the industry-standard collection maintained by the IEEE. The ALL suffix imports every declaration from a package — the correct practice for RTL design files.
2. IEEE.STD_LOGIC_1164: The Nine-Value Type System
The most important thing this package provides is not a wire — it is a philosophy. Real hardware signals are not binary. A bus line can be driven high, driven low, left floating, driven by two sources simultaneously, or simply unknown after power-on. The STD_LOGIC_1164 package models this reality with nine distinct values.
- ‘0’ and ‘1’ — strong logic levels, driven by a source
- ‘Z’ — high impedance, the bus is not being driven (tri-state)
- ‘X’ — unknown, two drivers are fighting or the value is undefined
- ‘U’ — uninitialized, signal has never been assigned (simulation only)
- ‘L’, ‘H’ — weak ‘0’ and ‘1’, used for pull-up/pull-down resistor modeling
- ‘W’, ‘-‘ — weak unknown and don’t-care
STD_LOGIC_VECTOR exclusively for port definitions (the interface boundary of your module). Inside the architecture body, convert to UNSIGNED or SIGNED as soon as arithmetic is needed. This keeps interfaces clean while giving the compiler full type information about your intent.
3. IEEE.NUMERIC_STD: The Right Tool for Arithmetic
Here is the critical insight most beginners miss: STD_LOGIC_VECTOR has no arithmetic meaning. It is just an array of bits. When you write a + b where a and b are STD_LOGIC_VECTOR, the compiler has no idea if you mean unsigned addition, signed two’s complement, or something else entirely.
NUMERIC_STD solves this by defining two typed wrappers over STD_LOGIC_VECTOR: UNSIGNED for unsigned integers and SIGNED for two’s complement signed integers. Arithmetic operators defined on these types synthesize to the correct hardware automatically.
Conversions between types are explicit and deliberate — the compiler forces you to state your intent. There is no silent truncation or implicit sign extension hiding in your design.
4. The Packages You Must Never Use
In the 1990s, before NUMERIC_STD was mature and widely supported, Synopsys distributed three proprietary packages that allowed arithmetic directly on STD_LOGIC_VECTOR. These packages spread through the industry and became entrenched in legacy codebases. They are not IEEE standard, and they will silently corrupt a project if mixed with modern libraries.
STD_LOGIC_VECTOR ports — the neutral type both libraries understand. Never let STD_LOGIC_ARITH leak into your own design files.
5. IEEE.MATH_REAL: Elaboration-Time Power Tool
MATH_REAL provides floating-point mathematical functions: LOG2, CEIL, FLOOR, SQRT, trigonometric functions, and more. There is a critical rule that cannot be broken: MATH_REAL is not synthesizable. It is resolved entirely at elaboration time — before the synthesis tool ever sees your design.
Its correct use is in generic calculations: computing the address bus width from a memory depth parameter, for example. The result is a constant that the synthesizer uses to size ports and signals — the functions themselves never touch hardware.
Final Thoughts: Know What You Are Importing
The two-line header at the top of every VHDL file is not boilerplate — it is a contract. STD_LOGIC_1164 says “I am describing hardware signals with real electrical meaning.” NUMERIC_STD says “when I write arithmetic, I mean it explicitly.” Together they give the synthesis tool everything it needs to make correct decisions.
The next time you open a legacy file and see STD_LOGIC_ARITH at the top, you will know exactly what you are dealing with: a design written before the standard matured, carrying 30 years of technical debt in two lines. Replace it with NUMERIC_STD, fix the type conversions, and the design will be cleaner, more portable, and far easier to maintain.
Happy coding.
fpgawizard.com

