VHDL code generator

A large ecosystem of VHDL artifacts can be generated that support both implementation and simulation in your project. See the example below for a real-world use case of all these artifacts.

  • VhdlRegisterPackageGenerator generates a VHDL package with register indexes, field indexes, field types, and field conversion functions.

  • VhdlRecordPackageGenerator generates a VHDL package with register records that use native VHDL types for all fields, along with conversion functions for these.

  • VhdlSimulationPackageGenerator generates a VHDL package with procedures for reading/writing register values.

  • VhdlAxiLiteWrapperGenerator generates a VHDL entity that wraps an AXI-Lite general register file, and exposes register values to application using the natively typed records.

The recommended workflow is to generate the register file wrapper from VhdlAxiLiteWrapperGenerator and instantiate it in your VHDL design. With this, registers and their field values are available as native VHDL typed values, requiring no conversion. See the example below for an example of this.

Example

To illustrate the code generators and how to use the code from them in an effective way, there is a complete VHDL entity and testbench below. While the application (a counter that periodically sends out a pulse) is silly, the goal of the example is to showcase as many of the features as possible and how to efficiently use them.

Register definition TOML file

The registers are generated from the TOML file below. Note that there are three registers of different modes: “Read, Write”, “Write-pulse” and “Read”. In the registers there are a few different fields, of type bit, integer and enumeration.

Click to expand/collapse code.
TOML file for example.
 1################################################################################
 2[register.config]
 3
 4mode = "r_w"
 5
 6enumeration.mode.description = "Set mode for how the counter operates."
 7enumeration.mode.element.clock_cycles = "Increment counter each clock cycle."
 8enumeration.mode.element.clock_cycles_with_enable = """
 9Increment counter each clock cycle when **clock_enable** is asserted.
10"""
11enumeration.mode.element.enable_edges = """
12Increment counter each time **clock_enable** changes state.
13"""
14
15integer.increment.description = "How much to increment counter."
16integer.increment.max_value = 15
17
18
19################################################################################
20[register.command]
21
22mode = "wpulse"
23
24bit.start.description = "Write '1' to start operation."
25
26bit.stop.description = "Write '1' to stop operation."
27
28
29################################################################################
30[register.status]
31
32mode = "r"
33
34bit.enabled.description = "Reads as '1' if operation is enabled."
35
36integer.pulse_count.max_value = 255
37integer.pulse_count.description = """
38Number of pulses that have been sent.
39Will wrap around.
40Value will be cleared to zero when **config** register is written.
41"""

Python file to generate register artifacts

The Python code below is used to parse the above TOML file and generate all the VHDL code we need for our VHDL implementation and testbench.

Click to expand/collapse code.
Python code that parses the example TOML file and generates the VHDL code we need.
 1# Standard libraries
 2import sys
 3from pathlib import Path
 4
 5# First party libraries
 6from hdl_registers.generator.vhdl.axi_lite_wrapper import VhdlAxiLiteWrapperGenerator
 7from hdl_registers.generator.vhdl.record_package import VhdlRecordPackageGenerator
 8from hdl_registers.generator.vhdl.register_package import VhdlRegisterPackageGenerator
 9from hdl_registers.generator.vhdl.simulation_package import VhdlSimulationPackageGenerator
10from hdl_registers.parser.toml import from_toml
11
12THIS_DIR = Path(__file__).parent
13
14
15def main(output_folder: Path):
16    """
17    Create register VHDL artifacts from the "counter" example module.
18    """
19    register_list = from_toml(
20        name="counter", toml_file=THIS_DIR.parent / "sim" / "regs_counter.toml"
21    )
22
23    VhdlRegisterPackageGenerator(
24        register_list=register_list, output_folder=output_folder
25    ).create_if_needed()
26
27    VhdlRecordPackageGenerator(
28        register_list=register_list, output_folder=output_folder
29    ).create_if_needed()
30
31    VhdlSimulationPackageGenerator(
32        register_list=register_list, output_folder=output_folder
33    ).create_if_needed()
34
35    VhdlAxiLiteWrapperGenerator(
36        register_list=register_list, output_folder=output_folder
37    ).create_if_needed()
38
39
40if __name__ == "__main__":
41    main(output_folder=Path(sys.argv[1]))

VHDL example implementation

The VHDL below is the implementation of our example counter. Once again, the application is a bit silly, but it does showcase a lot of interesting features.

  1. The entity uses an AXI-Lite register bus and instantiates the register file produced by VhdlAxiLiteWrapperGenerator, which can be seen below.

  2. Register values up and down are record types from the package produced by VhdlRecordPackageGenerator, which can be seen below.

  3. The set_status process shows

    1. How to access bit fields in a “Write-pulse” register and how to set bit fields in a “Read” register.

    2. How to set and update an integer field in a “Read” register.

    3. How to perform an action when a specific register is written on the register bus.

    Note how all the operations are performed using native VHDL types (std_ulogic, integer).

  4. The count process shows

  1. How to take different action depending on an enumeration field in a “Read, Write” register. Note that the field type is a VHDL enum with its elements (e.g. mode_clock_cycles) exposed.

  2. How to use a numeric value from a “Read, Write” register. Since the field is of integer type, it can simply be added to another integer.

Click to expand/collapse code.
Implementation of counter example.
  1-- -------------------------------------------------------------------------------------------------
  2-- Regularly send out a 'pulse', with a frequency that is configurable over the register bus.
  3-- -------------------------------------------------------------------------------------------------
  4
  5library ieee;
  6use ieee.std_logic_1164.all;
  7
  8library axi;
  9use axi.axi_lite_pkg.all;
 10
 11library common;
 12use common.types_pkg.all;
 13
 14use work.counter_regs_pkg.all;
 15use work.counter_register_record_pkg.all;
 16
 17
 18entity counter is
 19  port (
 20    clk : in std_ulogic;
 21    --
 22    regs_m2s : in axi_lite_m2s_t;
 23    regs_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
 24    --
 25    clock_enable : in std_ulogic;
 26    pulse : out std_ulogic := '0'
 27  );
 28end entity;
 29
 30architecture a of counter is
 31
 32  signal regs_up : counter_regs_up_t := counter_regs_up_init;
 33  signal regs_down : counter_regs_down_t := counter_regs_down_init;
 34
 35  signal reg_was_written : counter_reg_was_written_t := counter_reg_was_written_init;
 36
 37begin
 38
 39  ------------------------------------------------------------------------------
 40  counter_reg_file_inst : entity work.counter_reg_file
 41    port map (
 42      clk => clk,
 43      --
 44      axi_lite_m2s => regs_m2s,
 45      axi_lite_s2m => regs_s2m,
 46      --
 47      regs_up => regs_up,
 48      regs_down => regs_down,
 49      --
 50      reg_was_read => open,
 51      reg_was_written => reg_was_written
 52    );
 53
 54
 55  ------------------------------------------------------------------------------
 56  set_status : process
 57  begin
 58    wait until rising_edge(clk);
 59
 60    if regs_down.command.start then
 61      regs_up.status.enabled <= '1';
 62    end if;
 63
 64    if regs_down.command.stop then
 65      regs_up.status.enabled <= '0';
 66    end if;
 67
 68    if reg_was_written.config then
 69      -- Clear value when configuration is changed.
 70      regs_up.status.pulse_count <= 0;
 71    else
 72      regs_up.status.pulse_count <= regs_up.status.pulse_count + to_int(pulse);
 73    end if;
 74  end process;
 75
 76
 77  ------------------------------------------------------------------------------
 78  count_block : block
 79    constant counter_width : positive := 10;
 80    constant counter_activate_value : positive := 2 ** counter_width - 1;
 81    -- One bit extra to avoid overflow.
 82    constant counter_max_value : positive := 2 ** (counter_width + 1) - 1;
 83
 84    signal count : natural range 0 to counter_max_value := 0;
 85
 86    signal clock_enable_p1 : std_ulogic := '0';
 87  begin
 88
 89    ------------------------------------------------------------------------------
 90    count_events : process
 91    begin
 92      wait until rising_edge(clk);
 93
 94      pulse <= '0';
 95
 96      case regs_down.config.mode is
 97        when mode_clock_cycles =>
 98          count <= count + regs_down.config.increment;
 99
100        when mode_clock_cycles_with_enable =>
101          if clock_enable then
102            count <= count + regs_down.config.increment;
103          end if;
104
105        when mode_enable_edges =>
106          if clock_enable /= clock_enable_p1 then
107            count <= count + regs_down.config.increment;
108          end if;
109      end case;
110
111      if count >= counter_activate_value then
112        count <= 0;
113        pulse <= regs_up.status.enabled;
114      end if;
115
116      clock_enable_p1 <= clock_enable;
117    end process;
118
119  end block;
120
121end architecture;

VHDL example testbench

The VHDL below is the testbench for our VHDL example counter implementation above.

  1. The testbench uses register read/write procedures from the package produced by VhdlSimulationPackageGenerator, which can be seen below. For example write_counter_config.

  2. The wait_until_counter_status_pulse_count_equals call will continuously read the status register until the pulse_count field is exactly equal to the supplied value.

  3. The type of the value for each procedure is the native record type for that register.

    1. For example, read_counter_status returns a value of type counter_status_t which is a record that contains a bit enabled and an integer pulse_count.

  4. The testbench instantiates axi_lite_master.vhd which creates AXI-Lite transactions based on the VUnit bus master verification component interface commands created by the generated simulation package.

Click to expand/collapse code.
Testbench for counter example.
  1library ieee;
  2use ieee.std_logic_1164.all;
  3
  4library vunit_lib;
  5context vunit_lib.vc_context;
  6context vunit_lib.vunit_context;
  7
  8library axi;
  9use axi.axi_lite_pkg.all;
 10
 11library bfm;
 12
 13library reg_file;
 14use reg_file.reg_operations_pkg.all;
 15
 16use work.counter_regs_pkg.all;
 17use work.counter_register_record_pkg.all;
 18use work.counter_register_simulation_pkg.all;
 19
 20
 21entity tb_counter is
 22  generic (
 23    runner_cfg : string
 24  );
 25end entity;
 26
 27architecture tb of tb_counter is
 28
 29  signal clk : std_ulogic := '0';
 30
 31  signal regs_m2s : axi_lite_m2s_t := axi_lite_m2s_init;
 32  signal regs_s2m : axi_lite_s2m_t := axi_lite_s2m_init;
 33
 34  signal pulse, clock_enable : std_ulogic := '0';
 35
 36begin
 37
 38  clk <= not clk after 5 ns;
 39  test_runner_watchdog(runner, 1 ms);
 40
 41
 42  ------------------------------------------------------------------------------
 43  main : process
 44    variable config : counter_config_t := counter_config_init;
 45    variable status : counter_status_t := counter_status_init;
 46  begin
 47    test_runner_setup(runner, runner_cfg);
 48
 49    -- Check initial state.
 50    read_counter_status(net=>net, value=>status);
 51    check_equal(status.enabled, '0');
 52    check_equal(status.pulse_count, 0);
 53
 54    if run("test_count_clock_cycles") then
 55      config.mode := mode_clock_cycles;
 56      config.increment := 13;
 57
 58    elsif run("test_count_clock_cycles_with_enable") then
 59      config.mode := mode_clock_cycles_with_enable;
 60      config.increment := 8;
 61
 62      clock_enable <= '1';
 63    end if;
 64
 65    -- Set configuration, which depends on test case.
 66    write_counter_config(net=>net, value=>config);
 67
 68    -- Enable the operation.
 69    write_counter_command_start(net=>net, value=>'1');
 70
 71    -- Check updated status.
 72    read_counter_status(net=>net, value=>status);
 73    check_equal(status.enabled, '1');
 74    check_equal(status.pulse_count, 0);
 75
 76    -- Wait until a number of pulses have passed.
 77    wait_until_counter_status_pulse_count_equals(net=>net, value=>10);
 78
 79    test_runner_cleanup(runner);
 80  end process;
 81
 82
 83  ------------------------------------------------------------------------------
 84  axi_lite_master_inst : entity bfm.axi_lite_master
 85    port map (
 86      clk => clk,
 87      --
 88      axi_lite_m2s => regs_m2s,
 89      axi_lite_s2m => regs_s2m
 90    );
 91
 92
 93  ------------------------------------------------------------------------------
 94  counter_inst : entity work.counter
 95    port map (
 96      clk => clk,
 97      --
 98      regs_m2s => regs_m2s,
 99      regs_s2m => regs_s2m,
100      --
101      clock_enable => clock_enable,
102      pulse => pulse
103    );
104
105end architecture;

Generated VHDL register package

Below is the generated register package, created from the TOML file above via the VhdlRegisterPackageGenerator class.

Click to expand/collapse code.
Example register package.
  1-- This file is automatically generated by hdl-registers version 4.1.1-dev2.
  2-- Code generator VhdlRegisterPackageGenerator version 1.0.0.
  3-- Generated 2023-12-04 10:43 from file regs_counter.toml at commit d6ece437d0e3b2bc.
  4-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
  5
  6library ieee;
  7use ieee.std_logic_1164.all;
  8use ieee.numeric_std.all;
  9use ieee.fixed_pkg.all;
 10
 11library reg_file;
 12use reg_file.reg_file_pkg.all;
 13
 14
 15package counter_regs_pkg is
 16
 17  -- ---------------------------------------------------------------------------
 18  -- The valid range of register indexes.
 19  subtype counter_reg_range is natural range 0 to 2;
 20
 21  -- Register indexes, within the list of registers.
 22  constant counter_config : natural := 0;
 23  constant counter_command : natural := 1;
 24  constant counter_status : natural := 2;
 25
 26  -- Declare 'reg_map' and 'regs_init' constants here but define them in body (deferred constants).
 27  -- So that functions have been elaborated when they are called.
 28  -- Needed for ModelSim compilation to pass.
 29
 30  -- To be used as the 'regs' generic of 'axi_lite_reg_file.vhd'.
 31  constant counter_reg_map : reg_definition_vec_t(counter_reg_range);
 32
 33  -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_reg_file.vhd'.
 34  subtype counter_regs_t is reg_vec_t(counter_reg_range);
 35  -- To be used as the 'default_values' generic of 'axi_lite_reg_file.vhd'.
 36  constant counter_regs_init : counter_regs_t;
 37
 38  -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_reg_file.vhd'.
 39  subtype counter_reg_was_accessed_t is std_ulogic_vector(counter_reg_range);
 40
 41  -- -----------------------------------------------------------------------------
 42  -- Fields in the 'config' register.
 43  -- Range of the 'mode' field.
 44  subtype counter_config_mode is natural range 1 downto 0;
 45  -- Width of the 'mode' field.
 46  constant counter_config_mode_width : positive := 2;
 47  -- Type for the 'mode' field.
 48  type counter_config_mode_t is (
 49    mode_clock_cycles,
 50    mode_clock_cycles_with_enable,
 51    mode_enable_edges
 52  );
 53  -- Default value of the 'mode' field.
 54  constant counter_config_mode_init : counter_config_mode_t := mode_clock_cycles;
 55  -- Type for the 'mode' field as an SLV.
 56  subtype counter_config_mode_slv_t is std_ulogic_vector(1 downto 0);
 57  -- Cast a 'mode' field value to SLV.
 58  function to_slv(data : counter_config_mode_t) return counter_config_mode_slv_t;
 59  -- Get a 'mode' field value from a register value.
 60  function to_counter_config_mode(data : reg_t) return counter_config_mode_t;
 61
 62  -- Range of the 'increment' field.
 63  subtype counter_config_increment is natural range 5 downto 2;
 64  -- Width of the 'increment' field.
 65  constant counter_config_increment_width : positive := 4;
 66  -- Type for the 'increment' field.
 67  subtype counter_config_increment_t is integer range 0 to 15;
 68  -- Default value of the 'increment' field.
 69  constant counter_config_increment_init : counter_config_increment_t := 0;
 70  -- Type for the 'increment' field as an SLV.
 71  subtype counter_config_increment_slv_t is std_ulogic_vector(3 downto 0);
 72  -- Cast a 'increment' field value to SLV.
 73  function to_counter_config_increment_slv(data : counter_config_increment_t) return counter_config_increment_slv_t;
 74  -- Get a 'increment' field value from a register value.
 75  function to_counter_config_increment(data : reg_t) return counter_config_increment_t;
 76
 77  -- -----------------------------------------------------------------------------
 78  -- Fields in the 'command' register.
 79  -- Range of the 'start' field.
 80  constant counter_command_start : natural := 0;
 81  -- Default value of the 'start' field.
 82  constant counter_command_start_init : std_ulogic := '0';
 83
 84  -- Range of the 'stop' field.
 85  constant counter_command_stop : natural := 1;
 86  -- Default value of the 'stop' field.
 87  constant counter_command_stop_init : std_ulogic := '0';
 88
 89  -- -----------------------------------------------------------------------------
 90  -- Fields in the 'status' register.
 91  -- Range of the 'enabled' field.
 92  constant counter_status_enabled : natural := 0;
 93  -- Default value of the 'enabled' field.
 94  constant counter_status_enabled_init : std_ulogic := '0';
 95
 96  -- Range of the 'pulse_count' field.
 97  subtype counter_status_pulse_count is natural range 8 downto 1;
 98  -- Width of the 'pulse_count' field.
 99  constant counter_status_pulse_count_width : positive := 8;
100  -- Type for the 'pulse_count' field.
101  subtype counter_status_pulse_count_t is integer range 0 to 255;
102  -- Default value of the 'pulse_count' field.
103  constant counter_status_pulse_count_init : counter_status_pulse_count_t := 0;
104  -- Type for the 'pulse_count' field as an SLV.
105  subtype counter_status_pulse_count_slv_t is std_ulogic_vector(7 downto 0);
106  -- Cast a 'pulse_count' field value to SLV.
107  function to_counter_status_pulse_count_slv(data : counter_status_pulse_count_t) return counter_status_pulse_count_slv_t;
108  -- Get a 'pulse_count' field value from a register value.
109  function to_counter_status_pulse_count(data : reg_t) return counter_status_pulse_count_t;
110
111end package;
112
113package body counter_regs_pkg is
114
115  constant counter_reg_map : reg_definition_vec_t(counter_reg_range) := (
116    0 => (idx => counter_config, reg_type => r_w),
117    1 => (idx => counter_command, reg_type => wpulse),
118    2 => (idx => counter_status, reg_type => r)
119  );
120
121  constant counter_regs_init : counter_regs_t := (
122    0 => "00000000000000000000000000000000",
123    1 => "00000000000000000000000000000000",
124    2 => "00000000000000000000000000000000"
125  );
126
127  function to_slv(data : counter_config_mode_t) return counter_config_mode_slv_t is
128    constant data_int : natural := counter_config_mode_t'pos(data);
129    constant result : counter_config_mode_slv_t := std_ulogic_vector(
130      to_unsigned(data_int, counter_config_mode_width)
131    );
132  begin
133    return result;
134  end function;
135
136  function to_counter_config_mode(data : reg_t) return counter_config_mode_t is
137    constant field_slv : counter_config_mode_slv_t := data(counter_config_mode);
138    constant field_int : natural := to_integer(unsigned(field_slv));
139    constant result : counter_config_mode_t := counter_config_mode_t'val(field_int);
140  begin
141    return result;
142  end function;
143
144  function to_counter_config_increment_slv(data : counter_config_increment_t) return counter_config_increment_slv_t is
145    constant result : counter_config_increment_slv_t := std_ulogic_vector(to_unsigned(data, counter_config_increment_width));
146  begin
147    return result;
148  end function;
149
150  function to_counter_config_increment(data : reg_t) return counter_config_increment_t is
151    constant result : integer := to_integer(unsigned(data(counter_config_increment)));
152  begin
153    return result;
154  end function;
155
156  function to_counter_status_pulse_count_slv(data : counter_status_pulse_count_t) return counter_status_pulse_count_slv_t is
157    constant result : counter_status_pulse_count_slv_t := std_ulogic_vector(to_unsigned(data, counter_status_pulse_count_width));
158  begin
159    return result;
160  end function;
161
162  function to_counter_status_pulse_count(data : reg_t) return counter_status_pulse_count_t is
163    constant result : integer := to_integer(unsigned(data(counter_status_pulse_count)));
164  begin
165    return result;
166  end function;
167
168end package body;

Generated VHDL record package

Below is the generated register package, created from the TOML file above via the VhdlRecordPackageGenerator class.

Click to expand/collapse code.
Example register record package.
  1-- This file is automatically generated by hdl-registers version 4.1.1-dev2.
  2-- Code generator VhdlRecordPackageGenerator version 1.0.0.
  3-- Generated 2023-12-04 10:43 from file regs_counter.toml at commit d6ece437d0e3b2bc.
  4-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
  5
  6library ieee;
  7use ieee.std_logic_1164.all;
  8use ieee.numeric_std.all;
  9use ieee.fixed_pkg.all;
 10
 11library reg_file;
 12use reg_file.reg_file_pkg.all;
 13
 14use work.counter_regs_pkg.all;
 15
 16
 17package counter_register_record_pkg is
 18
 19  -- -----------------------------------------------------------------------------
 20  -- Record with correctly-typed members for each field in each register.
 21  -- Fields in the 'config' register as a record.
 22  type counter_config_t is record
 23    mode : counter_config_mode_t;
 24    increment : counter_config_increment_t;
 25  end record;
 26  -- Default value for the 'config' register as a record.
 27  constant counter_config_init : counter_config_t := (
 28    mode => counter_config_mode_init,
 29    increment => counter_config_increment_init
 30  );
 31  -- Convert a record of the 'config' register to SLV.
 32  function to_slv(data : counter_config_t) return reg_t;
 33  -- Convert an SLV register value to the record for the 'config' register.
 34  function to_counter_config(data : reg_t) return counter_config_t;
 35
 36  -- Fields in the 'command' register as a record.
 37  type counter_command_t is record
 38    start : std_ulogic;
 39    stop : std_ulogic;
 40  end record;
 41  -- Default value for the 'command' register as a record.
 42  constant counter_command_init : counter_command_t := (
 43    start => counter_command_start_init,
 44    stop => counter_command_stop_init
 45  );
 46  -- Convert a record of the 'command' register to SLV.
 47  function to_slv(data : counter_command_t) return reg_t;
 48  -- Convert an SLV register value to the record for the 'command' register.
 49  function to_counter_command(data : reg_t) return counter_command_t;
 50
 51  -- Fields in the 'status' register as a record.
 52  type counter_status_t is record
 53    enabled : std_ulogic;
 54    pulse_count : counter_status_pulse_count_t;
 55  end record;
 56  -- Default value for the 'status' register as a record.
 57  constant counter_status_init : counter_status_t := (
 58    enabled => counter_status_enabled_init,
 59    pulse_count => counter_status_pulse_count_init
 60  );
 61  -- Convert a record of the 'status' register to SLV.
 62  function to_slv(data : counter_status_t) return reg_t;
 63  -- Convert an SLV register value to the record for the 'status' register.
 64  function to_counter_status(data : reg_t) return counter_status_t;
 65
 66  -- -----------------------------------------------------------------------------
 67  -- Below is a record with correctly typed and ranged members for all registers, register arrays
 68  -- and fields that are in the 'up' direction.
 69  -- Record with everything in the 'up' direction.
 70  type counter_regs_up_t is record
 71    status : counter_status_t;
 72  end record;
 73  -- Default value of the above record.
 74  constant counter_regs_up_init : counter_regs_up_t := (
 75    status => counter_status_init
 76  );
 77  -- Convert record with everything in the 'up' direction to SLV register list.
 78  function to_slv(data : counter_regs_up_t) return counter_regs_t;
 79
 80  -- -----------------------------------------------------------------------------
 81  -- Below is a record with correctly typed and ranged members for all registers, register arrays
 82  -- and fields that are in the 'down' direction.
 83  -- Record with everything in the 'down' direction.
 84  type counter_regs_down_t is record
 85    config : counter_config_t;
 86    command : counter_command_t;
 87  end record;
 88  -- Default value of the above record.
 89  constant counter_regs_down_init : counter_regs_down_t := (
 90    config => counter_config_init,
 91    command => counter_command_init
 92  );
 93  -- Convert SLV register list to record with everything in the 'down' direction.
 94  function to_counter_regs_down(data : counter_regs_t) return counter_regs_down_t;
 95
 96  -- ---------------------------------------------------------------------------
 97  -- Below is a record with a status bit for each readable register in the register map.
 98  -- It can be used for the 'reg_was_read' port of a register file wrapper.
 99  -- Combined status mask record for all readable register.
100  type counter_reg_was_read_t is record
101    config : std_ulogic;
102    status : std_ulogic;
103  end record;
104  -- Default value for the above record.
105  constant counter_reg_was_read_init : counter_reg_was_read_t := (
106    others => '0'
107  );
108  -- Convert an SLV 'reg_was_read' from generic register file to the record above.
109  function to_counter_reg_was_read(
110    data : counter_reg_was_accessed_t
111  ) return counter_reg_was_read_t;
112
113  -- ---------------------------------------------------------------------------
114  -- Below is a record with a status bit for each writeable register in the register map.
115  -- It can be used for the 'reg_was_written' port of a register file wrapper.
116  -- Combined status mask record for all writeable register.
117  type counter_reg_was_written_t is record
118    config : std_ulogic;
119    command : std_ulogic;
120  end record;
121  -- Default value for the above record.
122  constant counter_reg_was_written_init : counter_reg_was_written_t := (
123    others => '0'
124  );
125  -- Convert an SLV 'reg_was_written' from generic register file to the record above.
126  function to_counter_reg_was_written(
127    data : counter_reg_was_accessed_t
128  ) return counter_reg_was_written_t;
129
130end package;
131
132package body counter_register_record_pkg is
133
134  function to_slv(data : counter_config_t) return reg_t is
135    variable result : reg_t := (others => '-');
136  begin
137    result(counter_config_mode) := to_slv(data.mode);
138    result(counter_config_increment) := to_counter_config_increment_slv(data.increment);
139
140    return result;
141  end function;
142
143  function to_counter_config(data : reg_t) return counter_config_t is
144    variable result : counter_config_t := counter_config_init;
145  begin
146    result.mode := to_counter_config_mode(data);
147    result.increment := to_counter_config_increment(data);
148
149    return result;
150  end function;
151
152  function to_slv(data : counter_command_t) return reg_t is
153    variable result : reg_t := (others => '-');
154  begin
155    result(counter_command_start) := data.start;
156    result(counter_command_stop) := data.stop;
157
158    return result;
159  end function;
160
161  function to_counter_command(data : reg_t) return counter_command_t is
162    variable result : counter_command_t := counter_command_init;
163  begin
164    result.start := data(counter_command_start);
165    result.stop := data(counter_command_stop);
166
167    return result;
168  end function;
169
170  function to_slv(data : counter_status_t) return reg_t is
171    variable result : reg_t := (others => '-');
172  begin
173    result(counter_status_enabled) := data.enabled;
174    result(counter_status_pulse_count) := to_counter_status_pulse_count_slv(data.pulse_count);
175
176    return result;
177  end function;
178
179  function to_counter_status(data : reg_t) return counter_status_t is
180    variable result : counter_status_t := counter_status_init;
181  begin
182    result.enabled := data(counter_status_enabled);
183    result.pulse_count := to_counter_status_pulse_count(data);
184
185    return result;
186  end function;
187
188  function to_slv(data : counter_regs_up_t) return counter_regs_t is
189    variable result : counter_regs_t := counter_regs_init;
190  begin
191    result(counter_status) := to_slv(data.status);
192
193    return result;
194  end function;
195
196  function to_counter_regs_down(data : counter_regs_t) return counter_regs_down_t is
197    variable result : counter_regs_down_t := counter_regs_down_init;
198  begin
199    result.config := to_counter_config(data(counter_config));
200    result.command := to_counter_command(data(counter_command));
201
202    return result;
203  end function;
204
205  function to_counter_reg_was_read(
206    data : counter_reg_was_accessed_t
207  ) return counter_reg_was_read_t is
208    variable result : counter_reg_was_read_t := counter_reg_was_read_init;
209  begin
210    result.config := data(counter_config);
211    result.status := data(counter_status);
212
213    return result;
214  end function;
215
216  function to_counter_reg_was_written(
217    data : counter_reg_was_accessed_t
218  ) return counter_reg_was_written_t is
219    variable result : counter_reg_was_written_t := counter_reg_was_written_init;
220  begin
221    result.config := data(counter_config);
222    result.command := data(counter_command);
223
224    return result;
225  end function;
226
227end package body;

Generated VHDL simulation package

Below is the generated register simulation package, created from the TOML file above via the VhdlSimulationPackageGenerator class.

Click to expand/collapse code.
Example register simulation package.
  1-- This file is automatically generated by hdl-registers version 4.1.1-dev2.
  2-- Code generator VhdlSimulationPackageGenerator version 1.0.0.
  3-- Generated 2023-12-04 10:43 from file regs_counter.toml at commit d6ece437d0e3b2bc.
  4-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
  5
  6library ieee;
  7use ieee.std_logic_1164.all;
  8use ieee.numeric_std.all;
  9
 10library vunit_lib;
 11context vunit_lib.vc_context;
 12
 13library common;
 14use common.addr_pkg.all;
 15
 16library reg_file;
 17use reg_file.reg_file_pkg.all;
 18use reg_file.reg_operations_pkg.all;
 19
 20use work.counter_regs_pkg.all;
 21use work.counter_register_record_pkg.all;
 22
 23
 24package counter_register_simulation_pkg is
 25
 26  -- ---------------------------------------------------------------------------
 27  -- Read the 'config' register.
 28  procedure read_counter_config(
 29    signal net : inout network_t;
 30    value : out counter_config_t;
 31    base_address : in addr_t := (others => '0');
 32    bus_handle : in bus_master_t := regs_bus_master
 33  );
 34
 35  -- Wait until the 'config' register equals the given 'value'.
 36  procedure wait_until_counter_config_equals(
 37    signal net : inout network_t;
 38    value : in counter_config_t;
 39    base_address : in addr_t := (others => '0');
 40    bus_handle : in bus_master_t := regs_bus_master;
 41    timeout : delay_length := max_timeout;
 42    message : string := ""
 43  );
 44
 45  -- Read the 'mode' field within the 'config' register.
 46  procedure read_counter_config_mode(
 47    signal net : inout network_t;
 48    value : out counter_config_mode_t;
 49    base_address : in addr_t := (others => '0');
 50    bus_handle : in bus_master_t := regs_bus_master
 51  );
 52
 53  -- Wait until the 'mode' field within the 'config' register equals the given 'value'.
 54  procedure wait_until_counter_config_mode_equals(
 55    signal net : inout network_t;
 56    value : in counter_config_mode_t;
 57    base_address : in addr_t := (others => '0');
 58    bus_handle : in bus_master_t := regs_bus_master;
 59    timeout : delay_length := max_timeout;
 60    message : string := ""
 61  );
 62
 63  -- Read the 'increment' field within the 'config' register.
 64  procedure read_counter_config_increment(
 65    signal net : inout network_t;
 66    value : out counter_config_increment_t;
 67    base_address : in addr_t := (others => '0');
 68    bus_handle : in bus_master_t := regs_bus_master
 69  );
 70
 71  -- Wait until the 'increment' field within the 'config' register equals the given 'value'.
 72  procedure wait_until_counter_config_increment_equals(
 73    signal net : inout network_t;
 74    value : in counter_config_increment_t;
 75    base_address : in addr_t := (others => '0');
 76    bus_handle : in bus_master_t := regs_bus_master;
 77    timeout : delay_length := max_timeout;
 78    message : string := ""
 79  );
 80
 81  -- Write the 'config' register.
 82  procedure write_counter_config(
 83    signal net : inout network_t;
 84    value : in counter_config_t;
 85    base_address : in addr_t := (others => '0');
 86    bus_handle : in bus_master_t := regs_bus_master
 87  );
 88
 89  -- Write the 'mode' field within the 'config' register.
 90  -- Will read-modify-write the register to set the field to the supplied 'value'.
 91  procedure write_counter_config_mode(
 92    signal net : inout network_t;
 93    value : in counter_config_mode_t;
 94    base_address : in addr_t := (others => '0');
 95    bus_handle : in bus_master_t := regs_bus_master
 96  );
 97
 98  -- Write the 'increment' field within the 'config' register.
 99  -- Will read-modify-write the register to set the field to the supplied 'value'.
100  procedure write_counter_config_increment(
101    signal net : inout network_t;
102    value : in counter_config_increment_t;
103    base_address : in addr_t := (others => '0');
104    bus_handle : in bus_master_t := regs_bus_master
105  );
106  -- ---------------------------------------------------------------------------
107
108  -- ---------------------------------------------------------------------------
109  -- Write the 'command' register.
110  procedure write_counter_command(
111    signal net : inout network_t;
112    value : in counter_command_t;
113    base_address : in addr_t := (others => '0');
114    bus_handle : in bus_master_t := regs_bus_master
115  );
116
117  -- Write the 'start' field within the 'command' register.
118  -- Will write the whole register, with the field set to the 
119  -- supplied 'value' and all other fields to their default values.
120  procedure write_counter_command_start(
121    signal net : inout network_t;
122    value : in std_ulogic;
123    base_address : in addr_t := (others => '0');
124    bus_handle : in bus_master_t := regs_bus_master
125  );
126
127  -- Write the 'stop' field within the 'command' register.
128  -- Will write the whole register, with the field set to the 
129  -- supplied 'value' and all other fields to their default values.
130  procedure write_counter_command_stop(
131    signal net : inout network_t;
132    value : in std_ulogic;
133    base_address : in addr_t := (others => '0');
134    bus_handle : in bus_master_t := regs_bus_master
135  );
136  -- ---------------------------------------------------------------------------
137
138  -- ---------------------------------------------------------------------------
139  -- Read the 'status' register.
140  procedure read_counter_status(
141    signal net : inout network_t;
142    value : out counter_status_t;
143    base_address : in addr_t := (others => '0');
144    bus_handle : in bus_master_t := regs_bus_master
145  );
146
147  -- Wait until the 'status' register equals the given 'value'.
148  procedure wait_until_counter_status_equals(
149    signal net : inout network_t;
150    value : in counter_status_t;
151    base_address : in addr_t := (others => '0');
152    bus_handle : in bus_master_t := regs_bus_master;
153    timeout : delay_length := max_timeout;
154    message : string := ""
155  );
156
157  -- Read the 'enabled' field within the 'status' register.
158  procedure read_counter_status_enabled(
159    signal net : inout network_t;
160    value : out std_ulogic;
161    base_address : in addr_t := (others => '0');
162    bus_handle : in bus_master_t := regs_bus_master
163  );
164
165  -- Wait until the 'enabled' field within the 'status' register equals the given 'value'.
166  procedure wait_until_counter_status_enabled_equals(
167    signal net : inout network_t;
168    value : in std_ulogic;
169    base_address : in addr_t := (others => '0');
170    bus_handle : in bus_master_t := regs_bus_master;
171    timeout : delay_length := max_timeout;
172    message : string := ""
173  );
174
175  -- Read the 'pulse_count' field within the 'status' register.
176  procedure read_counter_status_pulse_count(
177    signal net : inout network_t;
178    value : out counter_status_pulse_count_t;
179    base_address : in addr_t := (others => '0');
180    bus_handle : in bus_master_t := regs_bus_master
181  );
182
183  -- Wait until the 'pulse_count' field within the 'status' register equals the given 'value'.
184  procedure wait_until_counter_status_pulse_count_equals(
185    signal net : inout network_t;
186    value : in counter_status_pulse_count_t;
187    base_address : in addr_t := (others => '0');
188    bus_handle : in bus_master_t := regs_bus_master;
189    timeout : delay_length := max_timeout;
190    message : string := ""
191  );
192  -- ---------------------------------------------------------------------------
193
194end package;
195
196package body counter_register_simulation_pkg is
197
198  -- ---------------------------------------------------------------------------
199  -- Read the 'config' register.
200  procedure read_counter_config(
201    signal net : inout network_t;
202    value : out counter_config_t;
203    base_address : in addr_t := (others => '0');
204    bus_handle : in bus_master_t := regs_bus_master
205  ) is
206    constant reg_index : counter_reg_range := counter_config;
207    variable reg_value : reg_t := (others => '0');
208  begin
209    read_reg(
210      net => net,
211      reg_index => reg_index,
212      value => reg_value,
213      base_address => base_address,
214      bus_handle => bus_handle
215    );
216    value := to_counter_config(reg_value);
217  end procedure;
218
219  -- Wait until the 'config' register equals the given 'value'.
220  procedure wait_until_counter_config_equals(
221    signal net : inout network_t;
222    value : in counter_config_t;
223    base_address : in addr_t := (others => '0');
224    bus_handle : in bus_master_t := regs_bus_master;
225    timeout : delay_length := max_timeout;
226    message : string := ""
227  ) is
228    constant reg_value : reg_t := to_slv(value);
229    constant reg_index : counter_reg_range := counter_config;
230    constant address : addr_t := base_address or to_unsigned(4 * reg_index, addr_t'length);
231
232    constant base_timeout_message : string := (
233      "Timeout while waiting for the 'config' register to equal the given value."
234      & " value: " & to_string(reg_value)
235      & " - register index: " & to_string(reg_index)
236      & " - base address: " & to_string(base_address)
237    );
238    function get_timeout_message return string is
239    begin
240      if message = "" then
241        return base_timeout_message;
242      end if;
243
244      return base_timeout_message & " - message: " & message;
245    end function;
246    constant timeout_message : string := get_timeout_message;
247  begin
248    wait_until_read_equals(
249      net=>net,
250      bus_handle=>bus_handle,
251      addr=>std_ulogic_vector(address),
252      value=>reg_value,
253      timeout=>timeout,
254      msg=>timeout_message
255    );
256  end procedure;
257
258  -- Read the 'mode' field within the 'config' register.
259  procedure read_counter_config_mode(
260    signal net : inout network_t;
261    value : out counter_config_mode_t;
262    base_address : in addr_t := (others => '0');
263    bus_handle : in bus_master_t := regs_bus_master
264  ) is
265    variable reg_value : counter_config_t := counter_config_init;
266  begin
267    read_counter_config(
268      net => net,
269      value => reg_value,
270      base_address => base_address,
271      bus_handle => bus_handle
272    );
273    value := reg_value.mode;
274  end procedure;
275
276  -- Wait until the 'mode' field within the 'config' register equals the given 'value'.
277  procedure wait_until_counter_config_mode_equals(
278    signal net : inout network_t;
279    value : in counter_config_mode_t;
280    base_address : in addr_t := (others => '0');
281    bus_handle : in bus_master_t := regs_bus_master;
282    timeout : delay_length := max_timeout;
283    message : string := ""
284  ) is
285    constant reg_value : reg_t := (
286      counter_config_mode => to_slv(value),
287      others => '-'
288    );
289    constant reg_index : counter_reg_range := counter_config;
290    constant address : addr_t := base_address or to_unsigned(4 * reg_index, addr_t'length);
291
292    constant base_timeout_message : string := (
293      "Timeout while waiting for the 'config' register to equal the given value."
294      & " value: " & to_string(reg_value)
295      & " - register index: " & to_string(reg_index)
296      & " - base address: " & to_string(base_address)
297    );
298    function get_timeout_message return string is
299    begin
300      if message = "" then
301        return base_timeout_message;
302      end if;
303
304      return base_timeout_message & " - message: " & message;
305    end function;
306    constant timeout_message : string := get_timeout_message;
307  begin
308    wait_until_read_equals(
309      net=>net,
310      bus_handle=>bus_handle,
311      addr=>std_ulogic_vector(address),
312      value=>reg_value,
313      timeout=>timeout,
314      msg=>timeout_message
315    );
316  end procedure;
317
318  -- Read the 'increment' field within the 'config' register.
319  procedure read_counter_config_increment(
320    signal net : inout network_t;
321    value : out counter_config_increment_t;
322    base_address : in addr_t := (others => '0');
323    bus_handle : in bus_master_t := regs_bus_master
324  ) is
325    variable reg_value : counter_config_t := counter_config_init;
326  begin
327    read_counter_config(
328      net => net,
329      value => reg_value,
330      base_address => base_address,
331      bus_handle => bus_handle
332    );
333    value := reg_value.increment;
334  end procedure;
335
336  -- Wait until the 'increment' field within the 'config' register equals the given 'value'.
337  procedure wait_until_counter_config_increment_equals(
338    signal net : inout network_t;
339    value : in counter_config_increment_t;
340    base_address : in addr_t := (others => '0');
341    bus_handle : in bus_master_t := regs_bus_master;
342    timeout : delay_length := max_timeout;
343    message : string := ""
344  ) is
345    constant reg_value : reg_t := (
346      counter_config_increment => to_counter_config_increment_slv(value),
347      others => '-'
348    );
349    constant reg_index : counter_reg_range := counter_config;
350    constant address : addr_t := base_address or to_unsigned(4 * reg_index, addr_t'length);
351
352    constant base_timeout_message : string := (
353      "Timeout while waiting for the 'config' register to equal the given value."
354      & " value: " & to_string(reg_value)
355      & " - register index: " & to_string(reg_index)
356      & " - base address: " & to_string(base_address)
357    );
358    function get_timeout_message return string is
359    begin
360      if message = "" then
361        return base_timeout_message;
362      end if;
363
364      return base_timeout_message & " - message: " & message;
365    end function;
366    constant timeout_message : string := get_timeout_message;
367  begin
368    wait_until_read_equals(
369      net=>net,
370      bus_handle=>bus_handle,
371      addr=>std_ulogic_vector(address),
372      value=>reg_value,
373      timeout=>timeout,
374      msg=>timeout_message
375    );
376  end procedure;
377
378  -- Write the 'config' register.
379  procedure write_counter_config(
380    signal net : inout network_t;
381    value : in counter_config_t;
382    base_address : in addr_t := (others => '0');
383    bus_handle : in bus_master_t := regs_bus_master
384  ) is
385    constant reg_index : counter_reg_range := counter_config;
386    constant reg_value : reg_t := to_slv(value);
387  begin
388    write_reg(
389      net => net,
390      reg_index => reg_index,
391      value => reg_value,
392      base_address => base_address,
393      bus_handle => bus_handle
394    );
395  end procedure;
396
397  -- Write the 'mode' field within the 'config' register.
398  -- Will read-modify-write the register to set the field to the supplied 'value'.
399  procedure write_counter_config_mode(
400    signal net : inout network_t;
401    value : in counter_config_mode_t;
402    base_address : in addr_t := (others => '0');
403    bus_handle : in bus_master_t := regs_bus_master
404  ) is
405    variable reg_value : counter_config_t := counter_config_init;
406  begin
407    read_counter_config(
408      net => net,
409      value => reg_value,
410      base_address => base_address,
411      bus_handle => bus_handle
412    );
413    reg_value.mode := value;
414
415    write_counter_config(
416      net => net,
417      value => reg_value,
418      base_address => base_address,
419      bus_handle => bus_handle
420    );
421  end procedure;
422
423  -- Write the 'increment' field within the 'config' register.
424  -- Will read-modify-write the register to set the field to the supplied 'value'.
425  procedure write_counter_config_increment(
426    signal net : inout network_t;
427    value : in counter_config_increment_t;
428    base_address : in addr_t := (others => '0');
429    bus_handle : in bus_master_t := regs_bus_master
430  ) is
431    variable reg_value : counter_config_t := counter_config_init;
432  begin
433    read_counter_config(
434      net => net,
435      value => reg_value,
436      base_address => base_address,
437      bus_handle => bus_handle
438    );
439    reg_value.increment := value;
440
441    write_counter_config(
442      net => net,
443      value => reg_value,
444      base_address => base_address,
445      bus_handle => bus_handle
446    );
447  end procedure;
448  -- ---------------------------------------------------------------------------
449
450  -- ---------------------------------------------------------------------------
451  -- Write the 'command' register.
452  procedure write_counter_command(
453    signal net : inout network_t;
454    value : in counter_command_t;
455    base_address : in addr_t := (others => '0');
456    bus_handle : in bus_master_t := regs_bus_master
457  ) is
458    constant reg_index : counter_reg_range := counter_command;
459    constant reg_value : reg_t := to_slv(value);
460  begin
461    write_reg(
462      net => net,
463      reg_index => reg_index,
464      value => reg_value,
465      base_address => base_address,
466      bus_handle => bus_handle
467    );
468  end procedure;
469
470  -- Write the 'start' field within the 'command' register.
471  -- Will write the whole register, with the field set to the 
472  -- supplied 'value' and all other fields to their default values.
473  procedure write_counter_command_start(
474    signal net : inout network_t;
475    value : in std_ulogic;
476    base_address : in addr_t := (others => '0');
477    bus_handle : in bus_master_t := regs_bus_master
478  ) is
479    variable reg_value : counter_command_t := counter_command_init;
480  begin
481    reg_value.start := value;
482
483    write_counter_command(
484      net => net,
485      value => reg_value,
486      base_address => base_address,
487      bus_handle => bus_handle
488    );
489  end procedure;
490
491  -- Write the 'stop' field within the 'command' register.
492  -- Will write the whole register, with the field set to the 
493  -- supplied 'value' and all other fields to their default values.
494  procedure write_counter_command_stop(
495    signal net : inout network_t;
496    value : in std_ulogic;
497    base_address : in addr_t := (others => '0');
498    bus_handle : in bus_master_t := regs_bus_master
499  ) is
500    variable reg_value : counter_command_t := counter_command_init;
501  begin
502    reg_value.stop := value;
503
504    write_counter_command(
505      net => net,
506      value => reg_value,
507      base_address => base_address,
508      bus_handle => bus_handle
509    );
510  end procedure;
511  -- ---------------------------------------------------------------------------
512
513  -- ---------------------------------------------------------------------------
514  -- Read the 'status' register.
515  procedure read_counter_status(
516    signal net : inout network_t;
517    value : out counter_status_t;
518    base_address : in addr_t := (others => '0');
519    bus_handle : in bus_master_t := regs_bus_master
520  ) is
521    constant reg_index : counter_reg_range := counter_status;
522    variable reg_value : reg_t := (others => '0');
523  begin
524    read_reg(
525      net => net,
526      reg_index => reg_index,
527      value => reg_value,
528      base_address => base_address,
529      bus_handle => bus_handle
530    );
531    value := to_counter_status(reg_value);
532  end procedure;
533
534  -- Wait until the 'status' register equals the given 'value'.
535  procedure wait_until_counter_status_equals(
536    signal net : inout network_t;
537    value : in counter_status_t;
538    base_address : in addr_t := (others => '0');
539    bus_handle : in bus_master_t := regs_bus_master;
540    timeout : delay_length := max_timeout;
541    message : string := ""
542  ) is
543    constant reg_value : reg_t := to_slv(value);
544    constant reg_index : counter_reg_range := counter_status;
545    constant address : addr_t := base_address or to_unsigned(4 * reg_index, addr_t'length);
546
547    constant base_timeout_message : string := (
548      "Timeout while waiting for the 'status' register to equal the given value."
549      & " value: " & to_string(reg_value)
550      & " - register index: " & to_string(reg_index)
551      & " - base address: " & to_string(base_address)
552    );
553    function get_timeout_message return string is
554    begin
555      if message = "" then
556        return base_timeout_message;
557      end if;
558
559      return base_timeout_message & " - message: " & message;
560    end function;
561    constant timeout_message : string := get_timeout_message;
562  begin
563    wait_until_read_equals(
564      net=>net,
565      bus_handle=>bus_handle,
566      addr=>std_ulogic_vector(address),
567      value=>reg_value,
568      timeout=>timeout,
569      msg=>timeout_message
570    );
571  end procedure;
572
573  -- Read the 'enabled' field within the 'status' register.
574  procedure read_counter_status_enabled(
575    signal net : inout network_t;
576    value : out std_ulogic;
577    base_address : in addr_t := (others => '0');
578    bus_handle : in bus_master_t := regs_bus_master
579  ) is
580    variable reg_value : counter_status_t := counter_status_init;
581  begin
582    read_counter_status(
583      net => net,
584      value => reg_value,
585      base_address => base_address,
586      bus_handle => bus_handle
587    );
588    value := reg_value.enabled;
589  end procedure;
590
591  -- Wait until the 'enabled' field within the 'status' register equals the given 'value'.
592  procedure wait_until_counter_status_enabled_equals(
593    signal net : inout network_t;
594    value : in std_ulogic;
595    base_address : in addr_t := (others => '0');
596    bus_handle : in bus_master_t := regs_bus_master;
597    timeout : delay_length := max_timeout;
598    message : string := ""
599  ) is
600    constant reg_value : reg_t := (
601      counter_status_enabled => value,
602      others => '-'
603    );
604    constant reg_index : counter_reg_range := counter_status;
605    constant address : addr_t := base_address or to_unsigned(4 * reg_index, addr_t'length);
606
607    constant base_timeout_message : string := (
608      "Timeout while waiting for the 'status' register to equal the given value."
609      & " value: " & to_string(reg_value)
610      & " - register index: " & to_string(reg_index)
611      & " - base address: " & to_string(base_address)
612    );
613    function get_timeout_message return string is
614    begin
615      if message = "" then
616        return base_timeout_message;
617      end if;
618
619      return base_timeout_message & " - message: " & message;
620    end function;
621    constant timeout_message : string := get_timeout_message;
622  begin
623    wait_until_read_equals(
624      net=>net,
625      bus_handle=>bus_handle,
626      addr=>std_ulogic_vector(address),
627      value=>reg_value,
628      timeout=>timeout,
629      msg=>timeout_message
630    );
631  end procedure;
632
633  -- Read the 'pulse_count' field within the 'status' register.
634  procedure read_counter_status_pulse_count(
635    signal net : inout network_t;
636    value : out counter_status_pulse_count_t;
637    base_address : in addr_t := (others => '0');
638    bus_handle : in bus_master_t := regs_bus_master
639  ) is
640    variable reg_value : counter_status_t := counter_status_init;
641  begin
642    read_counter_status(
643      net => net,
644      value => reg_value,
645      base_address => base_address,
646      bus_handle => bus_handle
647    );
648    value := reg_value.pulse_count;
649  end procedure;
650
651  -- Wait until the 'pulse_count' field within the 'status' register equals the given 'value'.
652  procedure wait_until_counter_status_pulse_count_equals(
653    signal net : inout network_t;
654    value : in counter_status_pulse_count_t;
655    base_address : in addr_t := (others => '0');
656    bus_handle : in bus_master_t := regs_bus_master;
657    timeout : delay_length := max_timeout;
658    message : string := ""
659  ) is
660    constant reg_value : reg_t := (
661      counter_status_pulse_count => to_counter_status_pulse_count_slv(value),
662      others => '-'
663    );
664    constant reg_index : counter_reg_range := counter_status;
665    constant address : addr_t := base_address or to_unsigned(4 * reg_index, addr_t'length);
666
667    constant base_timeout_message : string := (
668      "Timeout while waiting for the 'status' register to equal the given value."
669      & " value: " & to_string(reg_value)
670      & " - register index: " & to_string(reg_index)
671      & " - base address: " & to_string(base_address)
672    );
673    function get_timeout_message return string is
674    begin
675      if message = "" then
676        return base_timeout_message;
677      end if;
678
679      return base_timeout_message & " - message: " & message;
680    end function;
681    constant timeout_message : string := get_timeout_message;
682  begin
683    wait_until_read_equals(
684      net=>net,
685      bus_handle=>bus_handle,
686      addr=>std_ulogic_vector(address),
687      value=>reg_value,
688      timeout=>timeout,
689      msg=>timeout_message
690    );
691  end procedure;
692  -- ---------------------------------------------------------------------------
693
694end package body;

Generated VHDL AXI-Lite register file wrapper

Below is the generated AXI-Lite register file wrapper, created from the TOML file above via the VhdlAxiLiteWrapperGenerator class.

Click to expand/collapse code.
Example AXI-Lite register file wrapper.
  1-- -----------------------------------------------------------------------------
  2-- AXI-Lite register file for the 'counter' module registers.
  3--
  4-- Is a wrapper around the generic AXI-Lite register file from hdl-modules:
  5-- * https://hdl-modules.com/modules/reg_file/reg_file.html#axi-lite-reg-file-vhd
  6-- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/reg_file/src/axi_lite_reg_file.vhd
  7--
  8-- Sets correct generics, and performs conversion to the easy-to-use register record types.
  9-- -----------------------------------------------------------------------------
 10-- This file is automatically generated by hdl-registers version 4.1.1-dev2.
 11-- Code generator VhdlAxiLiteWrapperGenerator version 1.0.0.
 12-- Generated 2023-12-04 10:43 from file regs_counter.toml at commit d6ece437d0e3b2bc.
 13-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
 14-- -----------------------------------------------------------------------------
 15
 16library ieee;
 17use ieee.std_logic_1164.all;
 18
 19library axi;
 20use axi.axi_lite_pkg.all;
 21
 22library reg_file;
 23use reg_file.reg_file_pkg.all;
 24
 25use work.counter_regs_pkg.all;
 26use work.counter_register_record_pkg.all;
 27
 28
 29entity counter_reg_file is
 30  port (
 31    clk : in std_ulogic;
 32    --# {}
 33    --# Register control bus.
 34    axi_lite_m2s : in axi_lite_m2s_t;
 35    axi_lite_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
 36    --# {}
 37    -- Register values.
 38    regs_up : in counter_regs_up_t := counter_regs_up_init;
 39    regs_down : out counter_regs_down_t := counter_regs_down_init;
 40    --# {}
 41    -- Each bit is pulsed for one cycle when the corresponding register is read/written.
 42    reg_was_read : out counter_reg_was_read_t := counter_reg_was_read_init;
 43    reg_was_written : out counter_reg_was_written_t := counter_reg_was_written_init
 44  );
 45end entity;
 46
 47architecture a of counter_reg_file is
 48
 49  signal regs_up_slv, regs_down_slv : counter_regs_t := counter_regs_init;
 50
 51  signal reg_was_read_slv, reg_was_written_slv : counter_reg_was_accessed_t := (
 52    others => '0'
 53  );
 54
 55begin
 56
 57  ------------------------------------------------------------------------------
 58  -- Instantiate the generic AXI-Lite register file from
 59  -- * https://hdl-modules.com/modules/reg_file/reg_file.html#axi-lite-reg-file-vhd
 60  -- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/reg_file/src/axi_lite_reg_file.vhd
 61  axi_lite_reg_file_inst : entity reg_file.axi_lite_reg_file
 62    generic map (
 63      regs => counter_reg_map,
 64      default_values => counter_regs_init
 65    )
 66    port map(
 67      clk => clk,
 68      --
 69      axi_lite_m2s => axi_lite_m2s,
 70      axi_lite_s2m => axi_lite_s2m,
 71      --
 72      regs_up => regs_up_slv,
 73      regs_down => regs_down_slv,
 74      --
 75      reg_was_read => reg_was_read_slv,
 76      reg_was_written => reg_was_written_slv
 77    );
 78
 79
 80  ------------------------------------------------------------------------------
 81  -- Combinatorially convert the register record to a list of SLV values that can be handled
 82  -- by the generic register file implementation.
 83  assign_regs_up : process(regs_up)
 84  begin
 85    regs_up_slv <= to_slv(regs_up);
 86  end process;
 87
 88
 89  ------------------------------------------------------------------------------
 90  -- Combinatorially convert the list of SLV values from the generic register file into the record
 91  -- we want to use in our application.
 92  assign_regs_down : process(regs_down_slv)
 93  begin
 94    regs_down <= to_counter_regs_down(regs_down_slv);
 95  end process;
 96
 97
 98  ------------------------------------------------------------------------------
 99  -- Combinatorially convert status mask to a record where only the applicable registers are present.
100  assign_reg_was_read : process(reg_was_read_slv)
101  begin
102    reg_was_read <= to_counter_reg_was_read(reg_was_read_slv);
103  end process;
104
105
106  ------------------------------------------------------------------------------
107  -- Combinatorially convert status mask to a record where only the applicable registers are present.
108  assign_reg_was_written : process(reg_was_written_slv)
109  begin
110    reg_was_written <= to_counter_reg_was_written(reg_was_written_slv);
111  end process;
112
113end architecture;

Performance

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, RegisterCodeGenerator.create_if_needed() maintains a hash of the register definitions, and will only generate the VHDL file when necessary. Hence it is recommended to call this function as opposed to RegisterCodeGenerator.create() which will waste time by always re-creating, even when it is not necessary.

Dependencies

The VhdlRegisterPackageGenerator and VhdlRecordPackageGenerator packages depend on reg_file_pkg.vhd from the reg_file module of hdl-modules. Can be downloaded from github here: https://github.com/hdl-modules/hdl-modules/blob/main/modules/reg_file/src/reg_file_pkg.vhd

The VhdlSimulationPackageGenerator package furthermore depends on reg_operations_pkg.vhd and addr_pkg.vhd.

The VhdlAxiLiteWrapperGenerator package also depends on axi_lite_pkg.vhd.

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. I.e. a std_logic signal can be assigned to a hdl-registers signal of type std_ulogic, and vice versa, without problem.

Further tools for simplifying register handling

There is a large eco-system of register-related components in the hdl-modules project. Firstly there are wrappers in the bfm library for easier working with VUnit verification components. Furthermore there is a large number of synthesizable AXI components available that enable the register bus:

See the reg_file library and axi library for more details.