Enumeration types let you name states or codes explicitly. This is the type used in state machines.
-- Declared in the architecture declarative regiontype t_state is (IDLE, INIT, RUN, DONE, ERROR);type t_color is (RED, ORANGE, GREEN);type t_dir is (NORTH, SOUTH, EAST, WEST);signal r_state : t_state := IDLE;signal r_color : t_color := RED;
-- Used inside a processprocess(i_clk)begin if rising_edge(i_clk) then case r_state is when IDLE => r_state <= INIT; when INIT => r_state <= RUN; when RUN => if i_done = '1' then r_state <= DONE; end if; when others => r_state <= IDLE; end case; end if;end process;
Enumeration types are automatically encoded by the synthesizer (binary or one-hot depending on the tool). Encoding can be forced through synthesis attributes.
User-Defined Array Types
VHDL lets you define multi-dimensional arrays or arrays of custom types.
-- Array of 8 bytes (simple ROM)type t_rom_8x8 is array (0 to 7) of std_logic_vector(7 downto 0);constant c_ROM : t_rom_8x8 := ( x"00", x"1F", x"3C", x"7E", x"FF", x"7E", x"3C", x"1F");-- 2D array (4 × 4 bit matrix)type t_matrix is array (0 to 3, 0 to 3) of std_logic;-- Array of std_logic_vector (inferred RAM)type t_ram is array (0 to 255) of std_logic_vector(7 downto 0);signal r_mem : t_ram;
-- Synchronous RAM inferred from an arrayprocess(i_clk)begin if rising_edge(i_clk) then if i_we = '1' then r_mem(to_integer(unsigned(i_addr))) <= i_data; end if; o_data <= r_mem(to_integer(unsigned(i_addr))); end if;end process;
Unconstrained Arrays
An unconstrained array defines the general shape of the type, but leaves the exact size to the signal, constant or port declaration.
subtype t_byte is std_logic_vector(7 downto 0);type t_byte_array is array (natural range <>) of t_byte;signal r_window : t_byte_array(0 to 3);signal r_fifo : t_byte_array(0 to 15);
The subtype is not mandatory: it mainly gives a clean name to "one byte" and lets you reuse that definition elsewhere.
type t_byte_array is array (natural range <>) of std_logic_vector(7 downto 0);
However, type t_byte is std_logic_vector(7 downto 0); is not the right form. To name a constrained version of std_logic_vector, use a subtype.
t_byte_array can therefore describe a 4-byte window, a 16-byte FIFO or a larger table. The type is the same, only the range changes.
This mechanism is useful with generic, packages and functions that need to accept variable sizes.
Record Type
A record groups multiple fields of different types under one name. Equivalent to a struct in C.
-- Record declaration (in a package or architecture)type t_pixel is record red : unsigned(7 downto 0); green : unsigned(7 downto 0); blue : unsigned(7 downto 0); valid : std_logic;end record t_pixel;-- Usagesignal r_pixel_in : t_pixel;signal r_pixel_out : t_pixel;-- Individual field assignmentr_pixel_in.red <= to_unsigned(255, 8);r_pixel_in.green <= to_unsigned(128, 8);r_pixel_in.blue <= to_unsigned(0, 8);r_pixel_in.valid <= '1';-- Aggregate assignmentr_pixel_out <= (red => x"FF", green => x"80", blue => x"00", valid => '1');-- Full record copyr_pixel_out <= r_pixel_in;
You can also define a complete reset value. This avoids forgetting a field when the record evolves.
constant c_PIXEL_RESET : t_pixel := ( red => (others => '0'), green => (others => '0'), blue => (others => '0'), valid => '0');r_pixel_out <= c_PIXEL_RESET;
Records are very useful for bus interfaces (AXI, structured data) and for passing related signals together through a port map.
When records are used in entity ports, they must be declared in a shared package visible to all files.