top of page

VHDL Data Types

So far, you have already seen the different data types used in Verilog and SystemVerilog.

Now, we will go deeper into VHDL data types and understand them in detail.

1. Scalar Types: These are the basic building blocks in VHDL:

1.1 BIT → '0' or '1'


signal a : bit;

variable a : bit;

constant a : bit := '0';

 

1.2 BOOLEAN→ TRUE or FALSE

signal a : boolean;

variable b : boolean := true;

 

1.3 INTEGER (whole numbers (you can also give a range, e.g. INTEGER range 0 to 15) 

signal a : integer range 0 to 15;

​

-- loop example in process

process

begin

    for i in 0 to 15 loop

        a <= i;

    end loop;

end process;

 

1.4 REAL (decimal numbers (used in simulation/testbench, not for synthesis)


signal a : real := 3.14;

 

1.5 CHARACTER (any character like 'A', 'B', '9')

signal ch : character := 'A';

 

1.6 TIME (represents time (e.g. 10 ns, 5 us) used in simulation)
 

signal t_delay : time := 10 ns;

constant clk_period : time := 20 ns;

 

1.7 STD_LOGIC is also considered a scalar type in VHDL because it holds a single bit.
 

ust like the BIT data type, which can have only two values (0 and 1),


the STD_LOGIC (standard logic) type can have 9 different values.

​

We will now go through these 9 values one by one to understand their meaning and use.

​

 '0','1','Z','X','U','L','H','W','-'
 

This makes it more practical for real hardware design.

2. Composite Types (collection of values)

2.1 Array

​

Used to store multiple values of the same type.
 

Example:
 

BIT_VECTOR(7 downto 0)
 

STD_LOGIC_VECTOR(15 downto 0)

 

Similar to reg [7:0] in Verilog/SystemVerilog.
 

2.2 Record

​

A record in VHDL is a custom data type that allows you to group multiple signals or fields into a single variable. It is similar to a struct in programming languages.

​

Purpose:


The main advantage of using records is that when you have a set of related signals—like multiple fields of a register or multiple readings from a sensor—you can organize them into one single signal. This makes your design cleaner and more readable.

​

Example:

​

type REG is record

    F1   : STD_LOGIC;

    F2 : STD_LOGIC;

    F3: STD_LOGIC_VECTOR(7 downto 0);

end record;

 

signal request : REG;

 

Now you can access and assign individual fields of the record:

​

request.F1 <= '1';

request.F2 <= '0';

request.F3(3) <= '1';

 

Request register contains three fields with different data types and with the help of record this is accessed by a single variable.

Why we need dummy variable in Record

​

If you are creating a record and at that time there are no functional signals available or you want to reserve fields for future use, then you should add a dummy field.

​

This ensures that:

​

  • The record is syntactically complete, so the compiler or synthesis tool doesn’t give warnings.
     

  • It’s easy to add real fields later without changing the record structure.
     

  • Bus width or module interface remains consistent.
     

In short, when you create a record, adding a dummy field is necessary if there is no actual signal yet.​​​

VHDL

-- Define a record type with a dummy field

type Block_Regs is record

    Reg1  : STD_LOGIC_VECTOR(7 downto 0);  -- actual register

    Dummy : STD_LOGIC_VECTOR(7 downto 0);  -- dummy field, no functional use yet

end record;

 

-- Declare a signal of this record type

signal block_data : Block_Regs;

 

-- Assign values

block_data.Reg1 <= "00001111";

block_data.Dummy <= (others => '0');  -- dummy initialized to zero

1. Syntactically complete: The record has at least one dummy field, so VHDL tools don’t complain about an empty record.
 

2. Future expansion: Later, you can replace the dummy field with a real signal or add more fields without changing the existing design.

 

For example:​

VHDL

-- Later expansion

type Block_Regs is record

    Reg1  : STD_LOGIC_VECTOR(7 downto 0);

    Reg2  : STD_LOGIC_VECTOR(7 downto 0);  -- new real register added

end record;

The dummy field acted as a placeholder to keep the record ready for future updates.

3. Access Types (like Pointer)

​

The idea of access types in VHDL is a bit like objects in SystemVerilog, but with some limitations.

 

How it’s similar:


In SystemVerilog, you can create objects and store references to them, and even create them dynamically. In VHDL, an access type works like a pointer: it doesn’t hold the actual value, it holds the address of a variable or record, and you can create the object dynamically using new.

​

This means access types let you:

​

  • Dynamically allocate memory, just like creating objects in OOP.
     

  • Pass large records or structures without making a copy, which saves resources.
     

  • Build dynamic data structures like linked lists, queues, or arrays that can grow or shrink.
     

Limitations:


VHDL is not full OOP. You can’t use inheritance or polymorphism like in SystemVerilog. Access types are mostly for pointers and dynamic memory.

​

Easy analogy:

​

  • SystemVerilog object = the actual object
     

  • VHDL access type = a pointer to that object
     

  • .all in VHDL = dereferencing the pointer, like accessing the real object in C++
     

So basically, access types in VHDL give you some object-like behavior, but simpler and focused on dynamic allocation and references.

VHDL

-- 1. Define a record type

type Employee is record

    id   : INTEGER;

    name : STRING(1 to 20);

end record;

 

-- 2. Define an access type for the record

type Employee_Ptr is access Employee;

​

-- 3. Declare a signal (or variable) of access type

signal emp_ref : Employee_Ptr;

 

begin

 

-- 4. Allocate memory dynamically using 'new'

emp_ref := new Employee;

 

-- 5. Assign values to the record fields through the access type

emp_ref.all.id   <= 101;

emp_ref.all.name <= "Mayank Nigam";

 

-- 6. Now emp_ref points to the Employee record and can be used

-- For example, reading values:

-- some_signal <= emp_ref.all.id;

Explanation:

​

1. Employee is a record type with fields id and name.
 

2. Employee_Ptr is an access type pointing to an Employee.
 

3. emp_ref holds a reference to an Employee record.
 

4. Using new, we dynamically allocate memory for the record.
 

5. The .all is used to access the actual record fields through the pointer.

​

​​

System Verilog Analogy

System Verilog

Employee

emp = 

new();

VHDL

emp_ref := new

Employee;

​

System Verilog

emp.id = 101;

VHDL

emp_ref.all.id <= 101;

System Verilog

emp.name = "Mayank";

VHDL

emp_ref.all.name <= "Mayank";

But in VHDL Inheritance and Polymorphism are not available; only dynamic memory allocation is possible.


Note: Rarely used in hardware synthesis, but useful in testbenches and simulations.

4. File Types

In VHDL, a file is a way to read from or write data to external files (like text files). File handling is mostly used in testbench, simulations, or for logging data, because synthesis tools usually don’t implement file operations in actual hardware.

​

VHDL provides different file types to handle different kinds of data. These are mainly categorized based on the type of data the file will store:

​​

  1. Text files (TEXT)
     

    • Used to store lines of text.
       

    • Very common in testbench for reading input vectors or writing output logs.
       

    • Example: reading a list of input values or writing simulation results.
       

  2. File of scalar type
     

    • A file can hold a series of values of a basic VHDL type like BIT, BOOLEAN, INTEGER, or STD_LOGIC.
       

    • Example: file my_file : file of INTEGER;
       

  3. File of composite type
     

    • You can also define a file of records or arrays.
       

    • Example: if you have a record with multiple fields, you can store many such records in a file.

Example: Writing to a text file in a testbench

VHDL

library ieee;

use ieee.std_logic_1164.all;

use std.textio.all;

 

entity tb_file_example is

end entity;

 

architecture sim of tb_file_example is

    file my_file : text open write_mode is "output.txt";

    variable line_buf : line;

begin

    process

    begin

        write(line_buf, string'("Simulation started"));

        writeline(my_file, line_buf);

 

        write(line_buf, string'("Value of signal X = 1"));

        writeline(my_file, line_buf);

 

        wait;

    end process;

end architecture;

​Here,

​

  • file my_file : text → defines a text file.
     

  • write and writeline → write data to the file.
     

  • readline and read → can be used to read data from a file.

 

  • Mostly for testbenches.

5. User-Defined Types

In VHDL, you can create your own enumeration types.
 

Example:
 

type state_type is (IDLE, FETCH, DECODE, EXECUTE, WRITE_BACK);

​

signal state : state_type := IDLE;

 

This is the Instruction cycle of Processors with all States.

image.png

Common IEEE Standard Types (Most Practical in Real Design)

  • STD_LOGIC → A single-bit signal, but unlike BIT, it can take 9 different values:

 

0, 1, Z, X, U, L, H, W, -


(This makes it very useful in simulation and real hardware design.)

​

  • A collection of multiple STD_LOGIC bits.STD_LOGIC_VECTOR →

 

Basically, it represents a bus or wire of many bits (e.g., STD_LOGIC_VECTOR(7 downto 0) is an 8-bit bus).

​

That’s why almost all digital hardware designs in VHDL are written using STD_LOGIC and STD_LOGIC_VECTOR. Both are defined in package std_logic_1164 in ieee Library so we start with package in a VHDL code.

​

​

​

​

STD_LOGIC Value

Meaning

Real Time Sense

'0'

Logic 0 / Strong 0

Digital GND 0 V

'1'

'Z'

'X'

Logic 1 / Strong 1

High Impedance / Tri-state

Unknown / Conflict

VDD   5 V

Open circuit

We can not say 0 or 1

'U'

'L'

Uninitialized

Weak 0 / Pull-down

Value is not known at start

 0 V to 0.8 V(TTL)

 0 V to 1.5 V(CMOS)

'H'

'W'

'-'

Weak 1 / Pull-up

Weak Unknown

Don't Care

2 V to 5 V(TTL)

3.5 V to 5 V(CMOS)

0.8 V to 2.0 V(TTL)

1.5 V to 3.5 V(CMOS)

Used in synthesis in design as K-map

Note: All values are w.r.t.  VDD/VCC i.e 5 V

Strong 0 vs Weak 0 – An Interview Question

  1. Strong 0:

  • Think of it like a door completely closed and locked.
     

  • The output is firmly at 0 volts, no chance of it moving up.
     

  • Example: A transistor directly pulling the output to ground.

2. Weak 0:

  • Think of it like a door that is lightly pressed closed.
     

  • The output is trying to go to 0, but it’s not fully solid.
     

  • This happens because of things like a pull-down resistor or leakage currents.
     

  • If another stronger signal comes along, it can easily push it away from 0.

image.png

Scenario: Weak 0 meets Strong 1

  • Imagine the weak 0 like a door lightly pressed closed.
     

  • Now, suppose a strong force comes along—like someone pushing the door open hard.
     

  • The door (weak 0) gives way, and now it swings fully open.

In electronics terms:

  • The pin was at weak 0 (trying to stay at 0 V but not solid).
     

  • A strong 1 signal comes in (like a strong voltage driving it high).
     

  • The weak 0 cannot resist, so the output becomes strong 1.

Shown in below figure.

image.png
image.png

Floating Pin, Weak 0, and Strong 1

  • When a gate input pin is not connected to VDD or GND, it is called a floating pin.
    → The voltage is undefined (can behave unpredictably).

     

  • If that pin is connected to GND through a resistor, it becomes a Weak 0.
    → The resistor pulls the pin towards 0, but it’s not very strong.

     

  • If you connect VDD (Strong 1) directly to the same pin, it will override the Weak 0.
    → The pin will be read as logic 1 (strong 1)because the strong signal dominates.

Why TTL Weak-0 Voltage < CMOS Weak-0 Voltage

For this, let us step into the world of Electronics.

In TTL technology, the input stage is made using BJTs. Because of this, the input impedance is quite low, and the input pin does not only sense voltage but also sinks current. If you try to pull the input down to logic-0 using only a weak source, the TTL input itself will draw current and resist that weak pull. To guarantee a proper logic-0, the input voltage must therefore be kept very low (below about 0.8 V). At such a low level, the transistor inside the TTL gate is fully turned off, and the logic is clearly recognized as 0.

In
CMOS technology, the input stage is the gate of a MOSFET. A MOSFET gate behaves like a capacitor and has an extremely high impedance, often in the megaohm range. This means almost no current flows into the input. Because of that, the strength of the pull-down current does not matter much; only the voltage at the gate matters. As long as this voltage is below the defined threshold (around 1.5 V), the CMOS input reliably detects a logic-0, even if the source is weak.

Thus, the weak-0 voltage in TTL is lower because TTL inputs are current-sensitive, while the weak-0 voltage in CMOS is higher because CMOS inputs are voltage-sensitive.

Note: These threshold voltages are set during the physical design of the device.

Tri-State Buffer (High Impedance State in VHDL)

A tri-state buffer is a special type of buffer that has one extra control input. This control input decides whether the signal at the input is allowed to pass through the output or not. When the control is active, the buffer works just like a normal buffer: if the input is 0, the output is 0; if the input is 1, the output is 1.

But when the control is inactive, the input is disconnected from the output. In this state the output behaves like an open circuit, as if nothing is connected. This is called the high-impedance state, and in VHDL/Verilog simulation it is represented by the symbol ‘Z’.​

Screenshot (512).png
diagram2.png

The high-impedance state is very useful because it allows multiple devices to share the same output line (like in buses). Only one device drives the bus at a time, while all others remain in the high-impedance state so that they don’t interfere.

STD_LOGIC Weak/Strong Values and Synthesis

In VHDL the type STD_LOGIC allows many different values such as '0', '1', 'L', 'H', 'Z', and 'X'. These extra values are very useful during simulation because they let us describe situations like a weak signal, a floating pin, or even an unknown value. For example, 'L' means a weak logic-0 and 'H' means a weak logic-1. 'Z' represents a high-impedance state, and 'X' represents an unknown state.

Now the natural question arises: are all of these signals both synthesized and simulated?

Let us try to understand this more deeply.

 

During simulation, every one of these values is valid and meaningful. The simulator can show weak 0, weak 1, unknown, high impedance, and so on. But in real hardware (FPGA or ASIC), things are different. Only a few of these values actually exist. There is no such thing as a “weak” zero or a “weak” one coming from a real logic gate. An FPGA output driver or a standard-cell gate always drives a strong digital 0 or a strong digital 1.

Because of this, when we go through synthesis, the tools usually ignore the weak values. If you write 'L' in your code, the synthesis tool will simply treat it as '0'. Similarly, 'H' will be treated as '1'. The only special case is 'Z', which is very important because it corresponds to a real hardware feature: the tri-state buffer. When a signal is assigned 'Z', the synthesis tool generates logic that puts the output in a high-impedance state, just like an open circuit.
 

So the conclusion is simple: weak values like 'L' and 'H' are useful for simulation modeling only, while '0', '1', and 'Z' are the values that can truly appear in hardware after synthesis.

In VHDL the type STD_LOGIC also includes the symbol '-', which is called Don’t Care. This symbol means that the actual value of the signal does not matter for the logic being described. When you use '-', you are telling the simulator that it can freely choose either 0 or 1, because for this situation the exact value is irrelevant. In other words, the design will behave the same way no matter what the signal is. This is especially useful in simulation and synthesis optimizations, because it gives the tools more flexibility to minimize the logic.

Importance and Caution When Using STD_LOGIC

When you take an input of type STD_LOGIC, be careful because it doesn’t just have two values like 0 and 1—it actually has 9 possible values ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-'). Let’s understand this with a simple example.

Imagine you have an input signal inp and you want to decide the output using a case statement:

VHDL

case inp is

  when '0' =>

    outp <= '0';

  when '1' =>

    outp <= '1';

end case;

Here, you only defined behavior for '0' and '1'. But what if the input is 'Z', 'X', or any other value? The simulator or synthesis tool won’t know what to do, and your output becomes undefined.
 

Think of it like a MUX: if you have a 1-bit select line and you only define outputs for 0 and 1, what happens when X, Z…..…..comes? The output is unknown.

​

That’s why in VHDL we use the “others” clause:

VHDL

case inp is

  when '0' =>

    outp <= '0';

  when '1' =>

    outp <= '1';

  when others =>

    outp <= '0';  -- default value for all other STD_LOGIC values

end case;

This way, like a MUX covering all possible select lines, we handle all 9 STD_LOGIC values, making the design safe and avoiding simulator warnings.

​

We will study more about Case statement in further lectures.

VHDL File Structure

VHDL Objects

© Copyright 2025 VLSI Mentor. All Rights Reserved.©

Connect with us

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
bottom of page