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/tsfpga/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 2022-09-29 22:04 from file regs_example.toml at commit 439807c202d6c110.
-- Register hash 9dc2214c9399000bc59897cfc383a3263b00f5b7, generator version 2.1.1-dev.

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_logic_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 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 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 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 unsigned(27 downto 0);

  constant example_constant_axi_data_width : integer := 64;
  constant example_constant_address_alignment : integer := 4096;

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.