Organize VHDL with reusable packages, functions and procedures.
A package gives files a common definition. It can share types, subtypes, constants, component declarations and function or procedure interfaces.
Expose and use definitions
The package declaration exposes elements to other design units:
library IEEE;use IEEE.STD_LOGIC_1164.ALL
;
use IEEE.NUMERIC_STD.ALL;
package video_pkg is
subtype t_color is unsigned(7 downto 0);
type t_rgb is record
red : t_color;
green : t_color;
blue : t_color;
valid : std_logic;
end record t_rgb;
constant c_RGB_BLACK : t_rgb := (
red => (others => '0'),
green => (others => '0'),
blue => (others => '0'),
valid => '0'
);
function is_valid_pixel(i_pixel : t_rgb) return boolean;
end package video_pkg;
The package body implements declared subprograms:
package body video_pkg is function is_valid_pixel(i_pixel : t_rgb) return boolean is begin return i_pixel.valid = '1'; end function is_valid_pixel;end package body video_pkg;
Types and constants can be fully defined in the declaration. A package supplies reusable definitions; importing it does not by itself create a hardware block.
Import it before the unit that uses its elements:
library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;library work;use work.video_pkg.ALL;entity pixel_gate is port ( i_pixel : in t_rgb; o_red : out std_logic_vector(7 downto 0) );end entity pixel_gate;architecture rtl of pixel_gate isbegin o_red <= std_logic_vector(i_pixel.red) when is_valid_pixel(i_pixel) else x"00";end architecture rtl;
A type used in ports must be visible before the entity declaration. Sharing it avoids maintaining divergent definitions of the same interface.
Return a value with a function
A function returns a result. This one calculates even parity:
function parity_even(i_data : std_logic_vector) return std_logic is variable v_parity : std_logic := '0';begin for i in i_data'range loop v_parity := v_parity xor i_data(i); end loop; return v_parity;end function parity_even;
w_parity <= parity_even(i_data);
For RTL, keep functions simple, deterministic and free of side effects. They express calculations without creating simulation delays or extra clock cycles. A function cannot contain wait.
Factor actions with a procedure
A procedure can modify out or inout parameters rather than return one value:
procedure drive_byte( signal o_bus : out std_logic_vector(7 downto 0); constant i_value : in natural) isbegin o_bus <= std_logic_vector(to_unsigned(i_value, o_bus'length));end procedure drive_byte;
In a testbench, this call drives a byte and then waits for an edge:
drive_byte(w_data, 16#A5#);wait until rising_edge(i_clk);
In RTL, factored statements take the combinational or sequential context of their call. Synthesizable code must remain compatible with the target hardware; timed waits belong in testbench code.
Need
Choice
Compute one value
Function
Factor a testbench check
Procedure
Modify several outputs
Procedure
Share types and constants
Package
Organize packages around clear roles, such as video_pkg, spi_pkg or test_utils_pkg. Separate test helpers from hardware interfaces where that eases use, and import only required dependencies.