VHDL code generator

A VHDL package can be generated with a call to RegisterList.create_vhdl_package(). The VHDL package file is designed to be used with the generic AXI-Lite register file available in the hdl_modules project: axi_lite_reg_file.vhd.

Since generation of VHDL packages is usually run in real time (e.g. before running a simulation) the speed of the tool is important. In order the save time, RegisterList.create_vhdl_package() maintains a hash of the register definitions, and will only generate the VHDL file when necessary.

Requirements

The VHDL package depends on reg_file_pkg.vhd from the reg_file module of hdl_modules. Can be downloaded from gitlab here: https://gitlab.com/hdl_modules/hdl_modules/-/blob/main/modules/reg_file/src/reg_file_pkg.vhd

Example

Below is the resulting code from the TOML format example.

example_regs_pkg.vhd
-- This file is automatically generated by hdl_registers.
-- Generated 2023-01-29 22:04 from file regs_example.toml at commit 38485d1f4fcdd4f6.
-- Register hash e01909937059ec3af01caa222d6efa92b3053813, generator version 2.1.1-dev2.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.fixed_pkg.all;

library reg_file;
use reg_file.reg_file_pkg.all;


package example_regs_pkg is

  constant example_configuration : natural := 0;
  constant example_status : natural := 1;
  constant example_command : natural := 2;
  function example_base_addresses_read_address(array_index : natural) return natural;
  function example_base_addresses_write_address(array_index : natural) return natural;

  constant example_base_addresses_array_length : natural := 3;

  -- Declare register map constants here, but define them in body.
  -- This is done so that functions have been elaborated when they are called.
  subtype example_reg_range is natural range 0 to 8;
  constant example_reg_map : reg_definition_vec_t(example_reg_range);

  subtype example_regs_t is reg_vec_t(example_reg_range);
  constant example_regs_init : example_regs_t;

  subtype example_reg_was_accessed_t is std_ulogic_vector(example_reg_range);

  constant example_configuration_enable : natural := 0;
  subtype example_configuration_data_tag is natural range 4 downto 1;
  constant example_configuration_data_tag_width : positive := 4;
  subtype example_configuration_data_tag_t is u_unsigned(3 downto 0);

  constant example_status_idle : natural := 0;
  constant example_status_stalling : natural := 1;
  subtype example_status_counter is natural range 9 downto 2;
  constant example_status_counter_width : positive := 8;
  subtype example_status_counter_t is u_unsigned(7 downto 0);

  constant example_command_start : natural := 0;
  constant example_command_flush : natural := 1;

  subtype example_base_addresses_read_address_address is natural range 27 downto 0;
  constant example_base_addresses_read_address_address_width : positive := 28;
  subtype example_base_addresses_read_address_address_t is u_unsigned(27 downto 0);

  subtype example_base_addresses_write_address_address is natural range 27 downto 0;
  constant example_base_addresses_write_address_address_width : positive := 28;
  subtype example_base_addresses_write_address_address_t is u_unsigned(27 downto 0);

  constant example_constant_axi_data_width : integer := 64;
  constant example_constant_clock_rate_hz : real := 156250000.0;
  constant example_constant_supports_pre_filtering : boolean := true;

end package;

package body example_regs_pkg is

  function example_base_addresses_read_address(array_index : natural) return natural is
  begin
    assert array_index < example_base_addresses_array_length
      report "Array index out of bounds: " & natural'image(array_index)
      severity failure;
    return 3 + array_index * 2 + 0;
  end function;

  function example_base_addresses_write_address(array_index : natural) return natural is
  begin
    assert array_index < example_base_addresses_array_length
      report "Array index out of bounds: " & natural'image(array_index)
      severity failure;
    return 3 + array_index * 2 + 1;
  end function;

  constant example_reg_map : reg_definition_vec_t(example_reg_range) := (
    0 => (idx => example_configuration, reg_type => r_w),
    1 => (idx => example_status, reg_type => r),
    2 => (idx => example_command, reg_type => wpulse),
    3 => (idx => example_base_addresses_read_address(0), reg_type => r_w),
    4 => (idx => example_base_addresses_write_address(0), reg_type => r_w),
    5 => (idx => example_base_addresses_read_address(1), reg_type => r_w),
    6 => (idx => example_base_addresses_write_address(1), reg_type => r_w),
    7 => (idx => example_base_addresses_read_address(2), reg_type => r_w),
    8 => (idx => example_base_addresses_write_address(2), reg_type => r_w)
  );

  constant example_regs_init : example_regs_t := (
    0 => "00000000000000000000000000001011",
    1 => "00000000000000000000000000000001",
    2 => "00000000000000000000000000000000",
    3 => "00000000000000000000000000000000",
    4 => "00000000000000000000000000000000",
    5 => "00000000000000000000000000000000",
    6 => "00000000000000000000000000000000",
    7 => "00000000000000000000000000000000",
    8 => "00000000000000000000000000000000"
  );

end package body;

For the plain register (configuration) the register index is simply a natural (example_configuration, where “example” is the name of the module). For the register arrays it is instead a function, e.g. example_base_addresses_read_address. The function takes an array index argument and will assert if it is out of bounds of the array.

Note that there is a large eco-system of register related components in the hdl_modules project. Firstly there are wrappers available for easier working with VUnit verification components. See the bfm library and reg_operations_pkg.vhd. Furthermore there is a large number of synthesizable AXI components available that enable the register bus: AXI-to-AXI-Lite converter, AXI/AXI-Lite interconnect, AXI-Lite mux (splitter), AXI-Lite clock domain crossing, etc. See the reg_file library and axi library for more details.

Unresolved types

The generated VHDL uses unresolved types (e.g. std_ulogic_vector instead of std_logic_vector) consistently. This means that accidental multiple drivers of a signal will result in an error when simulating or synthesizing the design.

Since e.g. std_logic is a sub-type of std_ulogic in VHDL-2008, it is no problem if hdl_registers components are integrated in a code base that still uses the resolved types. E.g. a std_logic signal can be assigned to a hdl_registers signal of type std_ulogic, and vice versa, without problem.