VHDL generator

A large ecosystem of VHDL artifacts can be generated that support both implementation and simulation in your project. For synthesis:

  • VhdlRegisterPackageGenerator generates the base VHDL package with indexes, modes, types, and conversion functions.

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

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

For simulation:

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

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.
 1import sys
 2from pathlib import Path
 3
 4from hdl_registers.generator.vhdl.axi_lite.wrapper import VhdlAxiLiteWrapperGenerator
 5from hdl_registers.generator.vhdl.record_package import VhdlRecordPackageGenerator
 6from hdl_registers.generator.vhdl.register_package import VhdlRegisterPackageGenerator
 7from hdl_registers.generator.vhdl.simulation.check_package import (
 8    VhdlSimulationCheckPackageGenerator,
 9)
10from hdl_registers.generator.vhdl.simulation.read_write_package import (
11    VhdlSimulationReadWritePackageGenerator,
12)
13from hdl_registers.generator.vhdl.simulation.wait_until_package import (
14    VhdlSimulationWaitUntilPackageGenerator,
15)
16from hdl_registers.parser.toml import from_toml
17
18THIS_DIR = Path(__file__).parent
19
20
21def main(output_folder: Path) -> None:
22    """
23    Create register VHDL artifacts from the "counter" example module.
24    """
25    register_list = from_toml(
26        name="counter", toml_file=THIS_DIR.parent / "example_counter" / "regs_counter.toml"
27    )
28
29    VhdlRegisterPackageGenerator(
30        register_list=register_list, output_folder=output_folder
31    ).create_if_needed()
32
33    VhdlRecordPackageGenerator(
34        register_list=register_list, output_folder=output_folder
35    ).create_if_needed()
36
37    VhdlAxiLiteWrapperGenerator(
38        register_list=register_list, output_folder=output_folder
39    ).create_if_needed()
40
41    VhdlSimulationReadWritePackageGenerator(
42        register_list=register_list, output_folder=output_folder
43    ).create_if_needed()
44
45    VhdlSimulationCheckPackageGenerator(
46        register_list=register_list, output_folder=output_folder
47    ).create_if_needed()
48
49    VhdlSimulationWaitUntilPackageGenerator(
50        register_list=register_list, output_folder=output_folder
51    ).create_if_needed()
52
53
54if __name__ == "__main__":
55    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. condition_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_lite;
  9use axi_lite.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_register_file_axi_lite_inst : entity work.counter_register_file_axi_lite
 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.conf 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.conf.condition is
 97        when condition_clock_cycles =>
 98          count <= count + regs_down.conf.increment;
 99
100        when condition_clock_cycles_with_enable =>
101          if clock_enable then
102            count <= count + regs_down.conf.increment;
103          end if;
104
105        when condition_enable_edges =>
106          if clock_enable /= clock_enable_p1 then
107            count <= count + regs_down.conf.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 example counter implementation above.

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

  2. The testbench uses register wait until procedures from the package produced by VhdlSimulationWaitUntilPackageGenerator, which can be seen below.

    1. For example wait_until_counter_status_pulse_count_equals, which 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 uses register field check procedures from the package produced by VhdlSimulationCheckPackageGenerator, which can be seen below. For example check_counter_status_enabled_equal.

  5. 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 VHDL simulation read/write package.

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

Generated VHDL register package

Below is the generated register package, created from the TOML file above via the VhdlRegisterPackageGenerator class. This is used by the Generated VHDL record package and the Generated VHDL AXI-Lite register file wrapper.

Click to expand/collapse code.
Example register package.
  1-- -----------------------------------------------------------------------------
  2-- This file is automatically generated by hdl-registers version 8.0.1-dev.
  3-- Code generator VhdlRegisterPackageGenerator version 2.0.0.
  4-- Generated 2025-05-05 20:52 from file regs_counter.toml at Git commit f53f19ab11e0.
  5-- Register hash e0b7d8e366d3777f3ef21d8e44540f964701e9be.
  6-- -----------------------------------------------------------------------------
  7
  8library ieee;
  9use ieee.std_logic_1164.all;
 10use ieee.numeric_std.all;
 11use ieee.fixed_pkg.all;
 12
 13library register_file;
 14use register_file.register_file_pkg.all;
 15
 16
 17package counter_regs_pkg is
 18
 19  -- ---------------------------------------------------------------------------
 20  -- The valid range of register indexes.
 21  subtype counter_register_range is natural range 0 to 2;
 22
 23  -- ---------------------------------------------------------------------------
 24  -- The number of bits needed to address all 3 registers on a register bus.
 25  -- Note that this figure includes the lowest two address bits that are assumed zero, since
 26  -- registers are 32-bit and unaligned accesses are not supported.
 27  constant counter_address_width : positive := 4;
 28
 29  -- Register indexes, within the list of registers.
 30  constant counter_conf : natural := 0;
 31  constant counter_command : natural := 1;
 32  constant counter_status : natural := 2;
 33
 34  -- Declare 'register_map' and 'regs_init' constants here but define them in
 35  -- the package body (deferred constants).
 36  -- So that functions have been elaborated when they are called.
 37  -- Needed for ModelSim compilation to pass.
 38
 39  -- To be used as the 'registers' generic of 'axi_lite_register_file.vhd'.
 40  constant counter_register_map : register_definition_vec_t(counter_register_range);
 41
 42  -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_register_file.vhd'.
 43  subtype counter_regs_t is register_vec_t(counter_register_range);
 44  -- To be used as the 'default_values' generic of 'axi_lite_register_file.vhd'.
 45  constant counter_regs_init : counter_regs_t;
 46
 47  -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_register_file.vhd'.
 48  subtype counter_reg_was_accessed_t is std_ulogic_vector(counter_register_range);
 49
 50  -- -----------------------------------------------------------------------------
 51  -- Fields in the 'conf' register.
 52  -- Range of the 'condition' field.
 53  subtype counter_conf_condition is natural range 1 downto 0;
 54  -- Width of the 'condition' field.
 55  constant counter_conf_condition_width : positive := 2;
 56  -- Type for the 'condition' field.
 57  type counter_conf_condition_t is (
 58    condition_clock_cycles,
 59    condition_clock_cycles_with_enable,
 60    condition_enable_edges
 61  );
 62  -- Default value of the 'condition' field.
 63  constant counter_conf_condition_init : counter_conf_condition_t := condition_clock_cycles;
 64  -- Type for the 'condition' field as an SLV.
 65  subtype counter_conf_condition_slv_t is std_ulogic_vector(1 downto 0);
 66  -- Cast a 'condition' field value to SLV.
 67  function to_slv(data : counter_conf_condition_t) return counter_conf_condition_slv_t;
 68  -- Get a 'condition' field value from a register value.
 69  function to_counter_conf_condition(data : register_t) return counter_conf_condition_t;
 70
 71  -- Range of the 'increment' field.
 72  subtype counter_conf_increment is natural range 5 downto 2;
 73  -- Width of the 'increment' field.
 74  constant counter_conf_increment_width : positive := 4;
 75  -- Type for the 'increment' field.
 76  subtype counter_conf_increment_t is integer range 0 to 15;
 77  -- Default value of the 'increment' field.
 78  constant counter_conf_increment_init : counter_conf_increment_t := 0;
 79  -- Type for the 'increment' field as an SLV.
 80  subtype counter_conf_increment_slv_t is std_ulogic_vector(3 downto 0);
 81  -- Cast a 'increment' field value to SLV.
 82  function to_counter_conf_increment_slv(data : counter_conf_increment_t) return counter_conf_increment_slv_t;
 83  -- Get a 'increment' field value from a register value.
 84  function to_counter_conf_increment(data : register_t) return counter_conf_increment_t;
 85
 86  -- -----------------------------------------------------------------------------
 87  -- Fields in the 'command' register.
 88  -- Range of the 'start' field.
 89  constant counter_command_start : natural := 0;
 90  -- Default value of the 'start' field.
 91  constant counter_command_start_init : std_ulogic := '0';
 92
 93  -- Range of the 'stop' field.
 94  constant counter_command_stop : natural := 1;
 95  -- Default value of the 'stop' field.
 96  constant counter_command_stop_init : std_ulogic := '0';
 97
 98  -- -----------------------------------------------------------------------------
 99  -- Fields in the 'status' register.
100  -- Range of the 'enabled' field.
101  constant counter_status_enabled : natural := 0;
102  -- Default value of the 'enabled' field.
103  constant counter_status_enabled_init : std_ulogic := '0';
104
105  -- Range of the 'pulse_count' field.
106  subtype counter_status_pulse_count is natural range 8 downto 1;
107  -- Width of the 'pulse_count' field.
108  constant counter_status_pulse_count_width : positive := 8;
109  -- Type for the 'pulse_count' field.
110  subtype counter_status_pulse_count_t is integer range 0 to 255;
111  -- Default value of the 'pulse_count' field.
112  constant counter_status_pulse_count_init : counter_status_pulse_count_t := 0;
113  -- Type for the 'pulse_count' field as an SLV.
114  subtype counter_status_pulse_count_slv_t is std_ulogic_vector(7 downto 0);
115  -- Cast a 'pulse_count' field value to SLV.
116  function to_counter_status_pulse_count_slv(data : counter_status_pulse_count_t) return counter_status_pulse_count_slv_t;
117  -- Get a 'pulse_count' field value from a register value.
118  function to_counter_status_pulse_count(data : register_t) return counter_status_pulse_count_t;
119
120end package;
121
122package body counter_regs_pkg is
123
124  constant counter_register_map : register_definition_vec_t(counter_register_range) := (
125    0 => (index => counter_conf, mode => r_w, utilized_width => 6),
126    1 => (index => counter_command, mode => wpulse, utilized_width => 2),
127    2 => (index => counter_status, mode => r, utilized_width => 9)
128  );
129
130  constant counter_regs_init : counter_regs_t := (
131    0 => "00000000000000000000000000000000",
132    1 => "00000000000000000000000000000000",
133    2 => "00000000000000000000000000000000"
134  );
135
136  -- Cast a 'condition' field value to SLV.
137  function to_slv(data : counter_conf_condition_t) return counter_conf_condition_slv_t is
138    constant data_int : natural := counter_conf_condition_t'pos(data);
139    constant result : counter_conf_condition_slv_t := std_ulogic_vector(
140      to_unsigned(data_int, counter_conf_condition_width)
141    );
142  begin
143    return result;
144  end function;
145
146  -- Get a 'condition' field value from a register value.
147  function to_counter_conf_condition(data : register_t) return counter_conf_condition_t is
148    constant field_slv : counter_conf_condition_slv_t := data(counter_conf_condition);
149    constant field_int : natural := to_integer(unsigned(field_slv));
150    constant result : counter_conf_condition_t := counter_conf_condition_t'val(field_int);
151  begin
152    return result;
153  end function;
154
155  -- Cast a 'increment' field value to SLV.
156  function to_counter_conf_increment_slv(data : counter_conf_increment_t) return counter_conf_increment_slv_t is
157    constant result : counter_conf_increment_slv_t := std_ulogic_vector(to_unsigned(data, counter_conf_increment_width));
158  begin
159    return result;
160  end function;
161
162  -- Get a 'increment' field value from a register value.
163  function to_counter_conf_increment(data : register_t) return counter_conf_increment_t is
164    constant result : integer := to_integer(unsigned(data(counter_conf_increment)));
165  begin
166    return result;
167  end function;
168
169  -- Cast a 'pulse_count' field value to SLV.
170  function to_counter_status_pulse_count_slv(data : counter_status_pulse_count_t) return counter_status_pulse_count_slv_t is
171    constant result : counter_status_pulse_count_slv_t := std_ulogic_vector(to_unsigned(data, counter_status_pulse_count_width));
172  begin
173    return result;
174  end function;
175
176  -- Get a 'pulse_count' field value from a register value.
177  function to_counter_status_pulse_count(data : register_t) return counter_status_pulse_count_t is
178    constant result : integer := to_integer(unsigned(data(counter_status_pulse_count)));
179  begin
180    return result;
181  end function;
182
183end package body;

Generated VHDL record package

Below is the generated record package, created from the TOML file above via the VhdlRecordPackageGenerator class. This is used by the Generated VHDL AXI-Lite register file wrapper as well as the VHDL example implementation and the VHDL example testbench.

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

Click to expand/collapse code.
Example AXI-Lite register file wrapper.
  1-- -----------------------------------------------------------------------------
  2-- This file is automatically generated by hdl-registers version 8.0.1-dev.
  3-- Code generator VhdlAxiLiteWrapperGenerator version 1.0.3.
  4-- Generated 2025-05-05 20:52 from file regs_counter.toml at Git commit f53f19ab11e0.
  5-- Register hash e0b7d8e366d3777f3ef21d8e44540f964701e9be.
  6-- -----------------------------------------------------------------------------
  7
  8-- -----------------------------------------------------------------------------
  9-- AXI-Lite register file for the 'counter' module registers.
 10-- Sets correct generics, and performs conversion to the easy-to-use register record types.
 11-- -----------------------------------------------------------------------------
 12
 13library ieee;
 14use ieee.std_logic_1164.all;
 15
 16-- This VHDL file is a required dependency:
 17-- https://github.com/hdl-modules/hdl-modules/blob/main/modules/axi_lite/src/axi_lite_pkg.vhd
 18-- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
 19library axi_lite;
 20use axi_lite.axi_lite_pkg.all;
 21
 22-- This VHDL file is a required dependency:
 23-- https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/axi_lite_register_file.vhd
 24-- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
 25library register_file;
 26
 27use work.counter_regs_pkg.all;
 28use work.counter_register_record_pkg.all;
 29
 30
 31entity counter_register_file_axi_lite is
 32  port (
 33    clk : in std_ulogic;
 34    -- Active-high synchronous reset.
 35    -- The code in this entity uses initial values so an initial reset is NOT necessary.
 36    -- This port can safely be left unconnected and tied to zero.
 37    -- If asserted, it will reset the AXI-Lite handshaking state as well as all register values.
 38    reset : in std_ulogic := '0';
 39    --# {}
 40    --# Register control bus.
 41    axi_lite_m2s : in axi_lite_m2s_t;
 42    axi_lite_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
 43    --# {}
 44    -- Register values.
 45    regs_up : in counter_regs_up_t := counter_regs_up_init;
 46    regs_down : out counter_regs_down_t := counter_regs_down_init;
 47    --# {}
 48    -- Each bit is pulsed for one cycle when the corresponding register is read/written.
 49    reg_was_read : out counter_reg_was_read_t := counter_reg_was_read_init;
 50    reg_was_written : out counter_reg_was_written_t := counter_reg_was_written_init
 51  );
 52end entity;
 53
 54architecture a of counter_register_file_axi_lite is
 55
 56  signal regs_up_slv, regs_down_slv : counter_regs_t := counter_regs_init;
 57
 58  signal reg_was_read_slv, reg_was_written_slv : counter_reg_was_accessed_t := (
 59    others => '0'
 60  );
 61
 62begin
 63
 64  ------------------------------------------------------------------------------
 65  -- Instantiate the generic register file implementation:
 66  -- https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/axi_lite_register_file.vhd
 67  -- See https://hdl-registers.com/rst/generator/generator_vhdl.html for dependency details.
 68  axi_lite_register_file_inst : entity register_file.axi_lite_register_file
 69    generic map (
 70      registers => counter_register_map,
 71      default_values => counter_regs_init
 72    )
 73    port map(
 74      clk => clk,
 75      reset => reset,
 76      --
 77      axi_lite_m2s => axi_lite_m2s,
 78      axi_lite_s2m => axi_lite_s2m,
 79      --
 80      regs_up => regs_up_slv,
 81      regs_down => regs_down_slv,
 82      --
 83      reg_was_read => reg_was_read_slv,
 84      reg_was_written => reg_was_written_slv
 85    );
 86
 87
 88  ------------------------------------------------------------------------------
 89  -- Combinatorially convert the register record to a list of SLV values that can be handled
 90  -- by the generic register file implementation.
 91  assign_regs_up : process(regs_up)
 92  begin
 93    regs_up_slv <= to_slv(regs_up);
 94  end process;
 95
 96
 97  ------------------------------------------------------------------------------
 98  -- Combinatorially convert the list of SLV values from the generic register file into the record
 99  -- we want to use in our application.
100  assign_regs_down : process(regs_down_slv)
101  begin
102    regs_down <= to_counter_regs_down(regs_down_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_read : process(reg_was_read_slv)
109  begin
110    reg_was_read <= to_counter_reg_was_read(reg_was_read_slv);
111  end process;
112
113
114  ------------------------------------------------------------------------------
115  -- Combinatorially convert status mask to a record where only the applicable registers are present.
116  assign_reg_was_written : process(reg_was_written_slv)
117  begin
118    reg_was_written <= to_counter_reg_was_written(reg_was_written_slv);
119  end process;
120
121end architecture;

Generated VHDL simulation read/write package

Below is the generated register simulation read/write package, created from the TOML file above via the VhdlSimulationReadWritePackageGenerator class. It is used by the VHDL example testbench to read/write registers in a compact way.

Click to expand/collapse code.
Example register simulation read/write package.
  1-- -----------------------------------------------------------------------------
  2-- This file is automatically generated by hdl-registers version 8.0.1-dev.
  3-- Code generator VhdlSimulationReadWritePackageGenerator version 1.1.1.
  4-- Generated 2025-05-05 20:52 from file regs_counter.toml at Git commit f53f19ab11e0.
  5-- Register hash e0b7d8e366d3777f3ef21d8e44540f964701e9be.
  6-- -----------------------------------------------------------------------------
  7
  8library ieee;
  9use ieee.numeric_std.all;
 10use ieee.std_logic_1164.all;
 11
 12library vunit_lib;
 13use vunit_lib.bus_master_pkg.bus_master_t;
 14use vunit_lib.bus_master_pkg.read_bus;
 15use vunit_lib.bus_master_pkg.write_bus;
 16use vunit_lib.com_types_pkg.network_t;
 17
 18library register_file;
 19use register_file.register_file_pkg.register_t;
 20use register_file.register_file_pkg.register_width;
 21use register_file.register_operations_pkg.register_bus_master;
 22
 23use work.counter_regs_pkg.all;
 24use work.counter_register_record_pkg.all;
 25
 26
 27package counter_register_read_write_pkg is
 28
 29  -- ---------------------------------------------------------------------------
 30  -- Read the 'conf' register as a plain 'register_t'.
 31  procedure read_counter_conf(
 32    signal net : inout network_t;
 33    value : out register_t;
 34    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 35    bus_handle : in bus_master_t := register_bus_master
 36  );
 37
 38  -- Read the 'conf' register as an 'integer'.
 39  procedure read_counter_conf(
 40    signal net : inout network_t;
 41    value : out integer;
 42    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 43    bus_handle : in bus_master_t := register_bus_master
 44  );
 45
 46  -- Read the 'conf' register.
 47  procedure read_counter_conf(
 48    signal net : inout network_t;
 49    value : out counter_conf_t;
 50    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 51    bus_handle : in bus_master_t := register_bus_master
 52  );
 53
 54  -- Read the 'condition' field in the 'conf' register.
 55  procedure read_counter_conf_condition(
 56    signal net : inout network_t;
 57    value : out counter_conf_condition_t;
 58    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 59    bus_handle : in bus_master_t := register_bus_master
 60  );
 61
 62  -- Read the 'increment' field in the 'conf' register.
 63  procedure read_counter_conf_increment(
 64    signal net : inout network_t;
 65    value : out counter_conf_increment_t;
 66    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 67    bus_handle : in bus_master_t := register_bus_master
 68  );
 69
 70  -- Write the 'conf' register as an 'integer'.
 71  procedure write_counter_conf(
 72    signal net : inout network_t;
 73    value : in integer;
 74    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 75    bus_handle : in bus_master_t := register_bus_master
 76  );
 77
 78  -- Write the 'conf' register.
 79  procedure write_counter_conf(
 80    signal net : inout network_t;
 81    value : in counter_conf_t;
 82    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 83    bus_handle : in bus_master_t := register_bus_master
 84  );
 85
 86  -- Write the 'condition' field in the 'conf' register.
 87  -- Will read-modify-write the register to set the field to the supplied 'value'.
 88  procedure write_counter_conf_condition(
 89    signal net : inout network_t;
 90    value : in counter_conf_condition_t;
 91    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 92    bus_handle : in bus_master_t := register_bus_master
 93  );
 94
 95  -- Write the 'increment' field in the 'conf' register.
 96  -- Will read-modify-write the register to set the field to the supplied 'value'.
 97  procedure write_counter_conf_increment(
 98    signal net : inout network_t;
 99    value : in counter_conf_increment_t;
100    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
101    bus_handle : in bus_master_t := register_bus_master
102  );
103  -- ---------------------------------------------------------------------------
104
105  -- ---------------------------------------------------------------------------
106  -- Write the 'command' register as an 'integer'.
107  procedure write_counter_command(
108    signal net : inout network_t;
109    value : in integer;
110    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
111    bus_handle : in bus_master_t := register_bus_master
112  );
113
114  -- Write the 'command' register.
115  procedure write_counter_command(
116    signal net : inout network_t;
117    value : in counter_command_t;
118    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
119    bus_handle : in bus_master_t := register_bus_master
120  );
121
122  -- Write the 'start' field in the 'command' register.
123  -- Will write the whole register, with the field set to the 
124  -- supplied 'value' and everything else set to default.
125  procedure write_counter_command_start(
126    signal net : inout network_t;
127    value : in std_ulogic;
128    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
129    bus_handle : in bus_master_t := register_bus_master
130  );
131
132  -- Write the 'stop' field in the 'command' register.
133  -- Will write the whole register, with the field set to the 
134  -- supplied 'value' and everything else set to default.
135  procedure write_counter_command_stop(
136    signal net : inout network_t;
137    value : in std_ulogic;
138    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
139    bus_handle : in bus_master_t := register_bus_master
140  );
141  -- ---------------------------------------------------------------------------
142
143  -- ---------------------------------------------------------------------------
144  -- Read the 'status' register as a plain 'register_t'.
145  procedure read_counter_status(
146    signal net : inout network_t;
147    value : out register_t;
148    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
149    bus_handle : in bus_master_t := register_bus_master
150  );
151
152  -- Read the 'status' register as an 'integer'.
153  procedure read_counter_status(
154    signal net : inout network_t;
155    value : out integer;
156    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
157    bus_handle : in bus_master_t := register_bus_master
158  );
159
160  -- Read the 'status' register.
161  procedure read_counter_status(
162    signal net : inout network_t;
163    value : out counter_status_t;
164    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
165    bus_handle : in bus_master_t := register_bus_master
166  );
167
168  -- Read the 'enabled' field in the 'status' register.
169  procedure read_counter_status_enabled(
170    signal net : inout network_t;
171    value : out std_ulogic;
172    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
173    bus_handle : in bus_master_t := register_bus_master
174  );
175
176  -- Read the 'pulse_count' field in the 'status' register.
177  procedure read_counter_status_pulse_count(
178    signal net : inout network_t;
179    value : out counter_status_pulse_count_t;
180    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
181    bus_handle : in bus_master_t := register_bus_master
182  );
183  -- ---------------------------------------------------------------------------
184
185end package;
186
187package body counter_register_read_write_pkg is
188
189  -- ---------------------------------------------------------------------------
190  -- Read the 'conf' register as a plain 'register_t'.
191  procedure read_counter_conf(
192    signal net : inout network_t;
193    value : out register_t;
194    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
195    bus_handle : in bus_master_t := register_bus_master
196  ) is
197    constant reg_index : counter_register_range := counter_conf;
198    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
199    variable reg_value : register_t := (others => '0');
200  begin
201    read_bus(
202      net => net,
203      bus_handle => bus_handle,
204      address => std_logic_vector(reg_address),
205      data => reg_value
206    );
207    value := reg_value;
208  end procedure;
209
210  -- Read the 'conf' register as an 'integer'.
211  procedure read_counter_conf(
212    signal net : inout network_t;
213    value : out integer;
214    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
215    bus_handle : in bus_master_t := register_bus_master
216  ) is
217    constant reg_index : counter_register_range := counter_conf;
218    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
219    variable reg_value : register_t := (others => '0');
220  begin
221    read_bus(
222      net => net,
223      bus_handle => bus_handle,
224      address => std_logic_vector(reg_address),
225      data => reg_value
226    );
227    value := to_integer(unsigned(reg_value));
228  end procedure;
229
230  -- Read the 'conf' register.
231  procedure read_counter_conf(
232    signal net : inout network_t;
233    value : out counter_conf_t;
234    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
235    bus_handle : in bus_master_t := register_bus_master
236  ) is
237    constant reg_index : counter_register_range := counter_conf;
238    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
239    variable reg_value : register_t := (others => '0');
240  begin
241    read_bus(
242      net => net,
243      bus_handle => bus_handle,
244      address => std_logic_vector(reg_address),
245      data => reg_value
246    );
247    value := to_counter_conf(reg_value);
248  end procedure;
249
250  -- Read the 'condition' field in the 'conf' register.
251  procedure read_counter_conf_condition(
252    signal net : inout network_t;
253    value : out counter_conf_condition_t;
254    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
255    bus_handle : in bus_master_t := register_bus_master
256  ) is
257    variable reg_value : counter_conf_t := counter_conf_init;
258  begin
259    read_counter_conf(
260      net => net,
261      value => reg_value,
262      base_address => base_address,
263      bus_handle => bus_handle
264    );
265    value := reg_value.condition;
266  end procedure;
267
268  -- Read the 'increment' field in the 'conf' register.
269  procedure read_counter_conf_increment(
270    signal net : inout network_t;
271    value : out counter_conf_increment_t;
272    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
273    bus_handle : in bus_master_t := register_bus_master
274  ) is
275    variable reg_value : counter_conf_t := counter_conf_init;
276  begin
277    read_counter_conf(
278      net => net,
279      value => reg_value,
280      base_address => base_address,
281      bus_handle => bus_handle
282    );
283    value := reg_value.increment;
284  end procedure;
285
286  -- Write the 'conf' register as an 'integer'.
287  procedure write_counter_conf(
288    signal net : inout network_t;
289    value : in integer;
290    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
291    bus_handle : in bus_master_t := register_bus_master
292  ) is
293    constant reg_index : counter_register_range := counter_conf;
294    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
295    constant reg_value : register_t := std_ulogic_vector(to_unsigned(value, register_width));
296  begin
297    write_bus(
298      net => net,
299      bus_handle => bus_handle,
300      address => std_logic_vector(reg_address),
301      data => reg_value
302    );
303  end procedure;
304
305  -- Write the 'conf' register.
306  procedure write_counter_conf(
307    signal net : inout network_t;
308    value : in counter_conf_t;
309    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
310    bus_handle : in bus_master_t := register_bus_master
311  ) is
312    constant reg_index : counter_register_range := counter_conf;
313    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
314    constant reg_value : register_t := to_slv(value);
315  begin
316    write_bus(
317      net => net,
318      bus_handle => bus_handle,
319      address => std_logic_vector(reg_address),
320      data => reg_value
321    );
322  end procedure;
323
324  -- Write the 'condition' field in the 'conf' register.
325  -- Will read-modify-write the register to set the field to the supplied 'value'.
326  procedure write_counter_conf_condition(
327    signal net : inout network_t;
328    value : in counter_conf_condition_t;
329    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
330    bus_handle : in bus_master_t := register_bus_master
331  ) is
332    variable reg_value : counter_conf_t := counter_conf_init;
333  begin
334    read_counter_conf(
335      net => net,
336      value => reg_value,
337      base_address => base_address,
338      bus_handle => bus_handle
339    );
340    reg_value.condition := value;
341
342    write_counter_conf(
343      net => net,
344      value => reg_value,
345      base_address => base_address,
346      bus_handle => bus_handle
347    );
348  end procedure;
349
350  -- Write the 'increment' field in the 'conf' register.
351  -- Will read-modify-write the register to set the field to the supplied 'value'.
352  procedure write_counter_conf_increment(
353    signal net : inout network_t;
354    value : in counter_conf_increment_t;
355    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
356    bus_handle : in bus_master_t := register_bus_master
357  ) is
358    variable reg_value : counter_conf_t := counter_conf_init;
359  begin
360    read_counter_conf(
361      net => net,
362      value => reg_value,
363      base_address => base_address,
364      bus_handle => bus_handle
365    );
366    reg_value.increment := value;
367
368    write_counter_conf(
369      net => net,
370      value => reg_value,
371      base_address => base_address,
372      bus_handle => bus_handle
373    );
374  end procedure;
375  -- ---------------------------------------------------------------------------
376
377  -- ---------------------------------------------------------------------------
378  -- Write the 'command' register as an 'integer'.
379  procedure write_counter_command(
380    signal net : inout network_t;
381    value : in integer;
382    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
383    bus_handle : in bus_master_t := register_bus_master
384  ) is
385    constant reg_index : counter_register_range := counter_command;
386    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
387    constant reg_value : register_t := std_ulogic_vector(to_unsigned(value, register_width));
388  begin
389    write_bus(
390      net => net,
391      bus_handle => bus_handle,
392      address => std_logic_vector(reg_address),
393      data => reg_value
394    );
395  end procedure;
396
397  -- Write the 'command' register.
398  procedure write_counter_command(
399    signal net : inout network_t;
400    value : in counter_command_t;
401    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
402    bus_handle : in bus_master_t := register_bus_master
403  ) is
404    constant reg_index : counter_register_range := counter_command;
405    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
406    constant reg_value : register_t := to_slv(value);
407  begin
408    write_bus(
409      net => net,
410      bus_handle => bus_handle,
411      address => std_logic_vector(reg_address),
412      data => reg_value
413    );
414  end procedure;
415
416  -- Write the 'start' field in the 'command' register.
417  -- Will write the whole register, with the field set to the 
418  -- supplied 'value' and everything else set to default.
419  procedure write_counter_command_start(
420    signal net : inout network_t;
421    value : in std_ulogic;
422    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
423    bus_handle : in bus_master_t := register_bus_master
424  ) is
425    variable reg_value : counter_command_t := counter_command_init;
426  begin
427    reg_value.start := value;
428
429    write_counter_command(
430      net => net,
431      value => reg_value,
432      base_address => base_address,
433      bus_handle => bus_handle
434    );
435  end procedure;
436
437  -- Write the 'stop' field in the 'command' register.
438  -- Will write the whole register, with the field set to the 
439  -- supplied 'value' and everything else set to default.
440  procedure write_counter_command_stop(
441    signal net : inout network_t;
442    value : in std_ulogic;
443    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
444    bus_handle : in bus_master_t := register_bus_master
445  ) is
446    variable reg_value : counter_command_t := counter_command_init;
447  begin
448    reg_value.stop := value;
449
450    write_counter_command(
451      net => net,
452      value => reg_value,
453      base_address => base_address,
454      bus_handle => bus_handle
455    );
456  end procedure;
457  -- ---------------------------------------------------------------------------
458
459  -- ---------------------------------------------------------------------------
460  -- Read the 'status' register as a plain 'register_t'.
461  procedure read_counter_status(
462    signal net : inout network_t;
463    value : out register_t;
464    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
465    bus_handle : in bus_master_t := register_bus_master
466  ) is
467    constant reg_index : counter_register_range := counter_status;
468    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
469    variable reg_value : register_t := (others => '0');
470  begin
471    read_bus(
472      net => net,
473      bus_handle => bus_handle,
474      address => std_logic_vector(reg_address),
475      data => reg_value
476    );
477    value := reg_value;
478  end procedure;
479
480  -- Read the 'status' register as an 'integer'.
481  procedure read_counter_status(
482    signal net : inout network_t;
483    value : out integer;
484    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
485    bus_handle : in bus_master_t := register_bus_master
486  ) is
487    constant reg_index : counter_register_range := counter_status;
488    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
489    variable reg_value : register_t := (others => '0');
490  begin
491    read_bus(
492      net => net,
493      bus_handle => bus_handle,
494      address => std_logic_vector(reg_address),
495      data => reg_value
496    );
497    value := to_integer(unsigned(reg_value));
498  end procedure;
499
500  -- Read the 'status' register.
501  procedure read_counter_status(
502    signal net : inout network_t;
503    value : out counter_status_t;
504    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
505    bus_handle : in bus_master_t := register_bus_master
506  ) is
507    constant reg_index : counter_register_range := counter_status;
508    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
509    variable reg_value : register_t := (others => '0');
510  begin
511    read_bus(
512      net => net,
513      bus_handle => bus_handle,
514      address => std_logic_vector(reg_address),
515      data => reg_value
516    );
517    value := to_counter_status(reg_value);
518  end procedure;
519
520  -- Read the 'enabled' field in the 'status' register.
521  procedure read_counter_status_enabled(
522    signal net : inout network_t;
523    value : out std_ulogic;
524    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
525    bus_handle : in bus_master_t := register_bus_master
526  ) is
527    variable reg_value : counter_status_t := counter_status_init;
528  begin
529    read_counter_status(
530      net => net,
531      value => reg_value,
532      base_address => base_address,
533      bus_handle => bus_handle
534    );
535    value := reg_value.enabled;
536  end procedure;
537
538  -- Read the 'pulse_count' field in the 'status' register.
539  procedure read_counter_status_pulse_count(
540    signal net : inout network_t;
541    value : out counter_status_pulse_count_t;
542    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
543    bus_handle : in bus_master_t := register_bus_master
544  ) is
545    variable reg_value : counter_status_t := counter_status_init;
546  begin
547    read_counter_status(
548      net => net,
549      value => reg_value,
550      base_address => base_address,
551      bus_handle => bus_handle
552    );
553    value := reg_value.pulse_count;
554  end procedure;
555  -- ---------------------------------------------------------------------------
556
557end package body;

Generated VHDL simulation check package

Below is the generated register simulation check package, created from the TOML file above via the VhdlSimulationCheckPackageGenerator class. It is used by the VHDL example testbench to check that the status register has the expected value.

Click to expand/collapse code.
Example register simulation check package.
  1-- -----------------------------------------------------------------------------
  2-- This file is automatically generated by hdl-registers version 8.0.1-dev.
  3-- Code generator VhdlSimulationCheckPackageGenerator version 1.2.1.
  4-- Generated 2025-05-05 20:52 from file regs_counter.toml at Git commit f53f19ab11e0.
  5-- Register hash e0b7d8e366d3777f3ef21d8e44540f964701e9be.
  6-- -----------------------------------------------------------------------------
  7
  8library ieee;
  9use ieee.fixed_pkg.all;
 10use ieee.numeric_std.all;
 11use ieee.std_logic_1164.all;
 12
 13library vunit_lib;
 14use vunit_lib.bus_master_pkg.bus_master_t;
 15use vunit_lib.check_pkg.all;
 16use vunit_lib.checker_pkg.all;
 17use vunit_lib.com_types_pkg.network_t;
 18use vunit_lib.string_ops.hex_image;
 19
 20library register_file;
 21use register_file.register_file_pkg.register_t;
 22use register_file.register_operations_pkg.register_bus_master;
 23
 24use work.counter_register_read_write_pkg.all;
 25use work.counter_register_record_pkg.all;
 26use work.counter_regs_pkg.all;
 27
 28
 29package counter_register_check_pkg is
 30
 31  -- ---------------------------------------------------------------------------
 32  -- Check that the current value of the 'conf' register
 33  -- equals the given 'expected' value as a plain SLV casted to integer.
 34  procedure check_counter_conf_equal(
 35    signal net : inout network_t;
 36    expected : in integer;
 37    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 38    bus_handle : in bus_master_t := register_bus_master;
 39    message : in string := ""
 40  );
 41
 42  -- Check that the current value of the 'conf' register
 43  -- equals the given 'expected' value.
 44  procedure check_counter_conf_equal(
 45    signal net : inout network_t;
 46    expected : in counter_conf_t;
 47    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 48    bus_handle : in bus_master_t := register_bus_master;
 49    message : in string := ""
 50  );
 51
 52  -- Check that the current value of the 'condition' field in the 'conf' register
 53  -- equals the given 'expected' value.
 54  procedure check_counter_conf_condition_equal(
 55    signal net : inout network_t;
 56    expected : in counter_conf_condition_t;
 57    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 58    bus_handle : in bus_master_t := register_bus_master;
 59    message : in string := ""
 60  );
 61
 62  -- Check that the current value of the 'increment' field in the 'conf' register
 63  -- equals the given 'expected' value.
 64  procedure check_counter_conf_increment_equal(
 65    signal net : inout network_t;
 66    expected : in counter_conf_increment_t;
 67    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 68    bus_handle : in bus_master_t := register_bus_master;
 69    message : in string := ""
 70  );
 71  -- ---------------------------------------------------------------------------
 72
 73  -- ---------------------------------------------------------------------------
 74  -- Check that the current value of the 'status' register
 75  -- equals the given 'expected' value as a plain SLV casted to integer.
 76  procedure check_counter_status_equal(
 77    signal net : inout network_t;
 78    expected : in integer;
 79    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 80    bus_handle : in bus_master_t := register_bus_master;
 81    message : in string := ""
 82  );
 83
 84  -- Check that the current value of the 'status' register
 85  -- equals the given 'expected' value.
 86  procedure check_counter_status_equal(
 87    signal net : inout network_t;
 88    expected : in counter_status_t;
 89    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 90    bus_handle : in bus_master_t := register_bus_master;
 91    message : in string := ""
 92  );
 93
 94  -- Check that the current value of the 'enabled' field in the 'status' register
 95  -- equals the given 'expected' value.
 96  procedure check_counter_status_enabled_equal(
 97    signal net : inout network_t;
 98    expected : in std_ulogic;
 99    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
100    bus_handle : in bus_master_t := register_bus_master;
101    message : in string := ""
102  );
103
104  -- Check that the current value of the 'pulse_count' field in the 'status' register
105  -- equals the given 'expected' value.
106  procedure check_counter_status_pulse_count_equal(
107    signal net : inout network_t;
108    expected : in counter_status_pulse_count_t;
109    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
110    bus_handle : in bus_master_t := register_bus_master;
111    message : in string := ""
112  );
113  -- ---------------------------------------------------------------------------
114
115end package;
116
117package body counter_register_check_pkg is
118
119  -- ---------------------------------------------------------------------------
120  -- Check that the current value of the 'conf' register
121  -- equals the given 'expected' value as a plain SLV casted to integer.
122  procedure check_counter_conf_equal(
123    signal net : inout network_t;
124    expected : in integer;
125    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
126    bus_handle : in bus_master_t := register_bus_master;
127    message : in string := ""
128  ) is
129    constant register_array_message : string := "";
130    function base_address_message return string is
131    begin
132      if base_address /= 0 then
133        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
134      end if;
135
136      return "";
137    end function;
138    constant base_message : string := (
139      "Checking the 'conf' register"
140      & register_array_message
141      & base_address_message
142      & "."
143    );
144    function get_message return string is
145    begin
146      if message = "" then
147        return base_message;
148      end if;
149
150      return base_message & " " & message & ".";
151    end function;
152
153    variable got : integer;
154  begin
155    read_counter_conf(
156      net => net,
157      value => got,
158      base_address => base_address,
159      bus_handle => bus_handle
160    );
161
162    check_equal(got=>got, expected=>expected, msg=>get_message);
163  end procedure;
164
165  -- Check that the current value of the 'conf' register
166  -- equals the given 'expected' value.
167  procedure check_counter_conf_equal(
168    signal net : inout network_t;
169    expected : in counter_conf_t;
170    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
171    bus_handle : in bus_master_t := register_bus_master;
172    message : in string := ""
173  ) is
174    constant register_array_message : string := "";
175    function base_address_message return string is
176    begin
177      if base_address /= 0 then
178        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
179      end if;
180
181      return "";
182    end function;
183    constant base_message : string := (
184      "Checking the 'conf' register"
185      & register_array_message
186      & base_address_message
187      & "."
188    );
189    function get_message return string is
190    begin
191      if message = "" then
192        return base_message;
193      end if;
194
195      return base_message & " " & message & ".";
196    end function;
197
198    variable got : counter_conf_t;
199  begin
200    read_counter_conf(
201      net => net,
202      value => got,
203      base_address => base_address,
204      bus_handle => bus_handle
205    );
206
207    if got /= expected then
208      failing_check(
209        checker => default_checker,
210        msg => p_std_msg(
211          check_result => "Equality check failed",
212          msg => get_message,
213          ctx => (
214            "Got " & to_string(to_slv(got)) & ". Expected " & to_string(to_slv(expected)) & "."
215          )
216        )
217      );
218    end if;
219  end procedure;
220
221  -- Check that the current value of the 'condition' field in the 'conf' register
222  -- equals the given 'expected' value.
223  procedure check_counter_conf_condition_equal(
224    signal net : inout network_t;
225    expected : in counter_conf_condition_t;
226    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
227    bus_handle : in bus_master_t := register_bus_master;
228    message : in string := ""
229  ) is
230    constant register_array_message : string := "";
231    function base_address_message return string is
232    begin
233      if base_address /= 0 then
234        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
235      end if;
236
237      return "";
238    end function;
239    constant base_message : string := (
240      "Checking the 'condition' field in the 'conf' register"
241      & register_array_message
242      & base_address_message
243      & "."
244    );
245    function get_message return string is
246    begin
247      if message = "" then
248        return base_message;
249      end if;
250
251      return base_message & " " & message & ".";
252    end function;
253
254    variable got_reg : counter_conf_t := counter_conf_init;
255    variable got : counter_conf_condition_t := counter_conf_condition_init;
256  begin
257    read_counter_conf(
258      net => net,
259      value => got_reg,
260      base_address => base_address,
261      bus_handle => bus_handle
262    );
263    got := got_reg.condition;
264
265    if got /= expected then
266      failing_check(
267        checker => default_checker,
268        msg => p_std_msg(
269          check_result => "Equality check failed",
270          msg => get_message,
271          ctx => (
272            "Got " & to_string(got) & "."
273            & " Expected " & to_string(expected) & "."
274          )
275        )
276      );
277    end if;
278  end procedure;
279
280  -- Check that the current value of the 'increment' field in the 'conf' register
281  -- equals the given 'expected' value.
282  procedure check_counter_conf_increment_equal(
283    signal net : inout network_t;
284    expected : in counter_conf_increment_t;
285    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
286    bus_handle : in bus_master_t := register_bus_master;
287    message : in string := ""
288  ) is
289    constant register_array_message : string := "";
290    function base_address_message return string is
291    begin
292      if base_address /= 0 then
293        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
294      end if;
295
296      return "";
297    end function;
298    constant base_message : string := (
299      "Checking the 'increment' field in the 'conf' register"
300      & register_array_message
301      & base_address_message
302      & "."
303    );
304    function get_message return string is
305    begin
306      if message = "" then
307        return base_message;
308      end if;
309
310      return base_message & " " & message & ".";
311    end function;
312
313    variable got_reg : counter_conf_t := counter_conf_init;
314    variable got : counter_conf_increment_t := counter_conf_increment_init;
315  begin
316    read_counter_conf(
317      net => net,
318      value => got_reg,
319      base_address => base_address,
320      bus_handle => bus_handle
321    );
322    got := got_reg.increment;
323
324    check_equal(got=>got, expected=>expected, msg=>get_message);
325  end procedure;
326  -- ---------------------------------------------------------------------------
327
328  -- ---------------------------------------------------------------------------
329  -- Check that the current value of the 'status' register
330  -- equals the given 'expected' value as a plain SLV casted to integer.
331  procedure check_counter_status_equal(
332    signal net : inout network_t;
333    expected : in integer;
334    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
335    bus_handle : in bus_master_t := register_bus_master;
336    message : in string := ""
337  ) is
338    constant register_array_message : string := "";
339    function base_address_message return string is
340    begin
341      if base_address /= 0 then
342        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
343      end if;
344
345      return "";
346    end function;
347    constant base_message : string := (
348      "Checking the 'status' register"
349      & register_array_message
350      & base_address_message
351      & "."
352    );
353    function get_message return string is
354    begin
355      if message = "" then
356        return base_message;
357      end if;
358
359      return base_message & " " & message & ".";
360    end function;
361
362    variable got : integer;
363  begin
364    read_counter_status(
365      net => net,
366      value => got,
367      base_address => base_address,
368      bus_handle => bus_handle
369    );
370
371    check_equal(got=>got, expected=>expected, msg=>get_message);
372  end procedure;
373
374  -- Check that the current value of the 'status' register
375  -- equals the given 'expected' value.
376  procedure check_counter_status_equal(
377    signal net : inout network_t;
378    expected : in counter_status_t;
379    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
380    bus_handle : in bus_master_t := register_bus_master;
381    message : in string := ""
382  ) is
383    constant register_array_message : string := "";
384    function base_address_message return string is
385    begin
386      if base_address /= 0 then
387        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
388      end if;
389
390      return "";
391    end function;
392    constant base_message : string := (
393      "Checking the 'status' register"
394      & register_array_message
395      & base_address_message
396      & "."
397    );
398    function get_message return string is
399    begin
400      if message = "" then
401        return base_message;
402      end if;
403
404      return base_message & " " & message & ".";
405    end function;
406
407    variable got : counter_status_t;
408  begin
409    read_counter_status(
410      net => net,
411      value => got,
412      base_address => base_address,
413      bus_handle => bus_handle
414    );
415
416    if got /= expected then
417      failing_check(
418        checker => default_checker,
419        msg => p_std_msg(
420          check_result => "Equality check failed",
421          msg => get_message,
422          ctx => (
423            "Got " & to_string(to_slv(got)) & ". Expected " & to_string(to_slv(expected)) & "."
424          )
425        )
426      );
427    end if;
428  end procedure;
429
430  -- Check that the current value of the 'enabled' field in the 'status' register
431  -- equals the given 'expected' value.
432  procedure check_counter_status_enabled_equal(
433    signal net : inout network_t;
434    expected : in std_ulogic;
435    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
436    bus_handle : in bus_master_t := register_bus_master;
437    message : in string := ""
438  ) is
439    constant register_array_message : string := "";
440    function base_address_message return string is
441    begin
442      if base_address /= 0 then
443        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
444      end if;
445
446      return "";
447    end function;
448    constant base_message : string := (
449      "Checking the 'enabled' field in the 'status' register"
450      & register_array_message
451      & base_address_message
452      & "."
453    );
454    function get_message return string is
455    begin
456      if message = "" then
457        return base_message;
458      end if;
459
460      return base_message & " " & message & ".";
461    end function;
462
463    variable got_reg : counter_status_t := counter_status_init;
464    variable got : std_ulogic := counter_status_enabled_init;
465  begin
466    read_counter_status(
467      net => net,
468      value => got_reg,
469      base_address => base_address,
470      bus_handle => bus_handle
471    );
472    got := got_reg.enabled;
473
474    check_equal(got=>got, expected=>expected, msg=>get_message);
475  end procedure;
476
477  -- Check that the current value of the 'pulse_count' field in the 'status' register
478  -- equals the given 'expected' value.
479  procedure check_counter_status_pulse_count_equal(
480    signal net : inout network_t;
481    expected : in counter_status_pulse_count_t;
482    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
483    bus_handle : in bus_master_t := register_bus_master;
484    message : in string := ""
485  ) is
486    constant register_array_message : string := "";
487    function base_address_message return string is
488    begin
489      if base_address /= 0 then
490        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
491      end if;
492
493      return "";
494    end function;
495    constant base_message : string := (
496      "Checking the 'pulse_count' field in the 'status' register"
497      & register_array_message
498      & base_address_message
499      & "."
500    );
501    function get_message return string is
502    begin
503      if message = "" then
504        return base_message;
505      end if;
506
507      return base_message & " " & message & ".";
508    end function;
509
510    variable got_reg : counter_status_t := counter_status_init;
511    variable got : counter_status_pulse_count_t := counter_status_pulse_count_init;
512  begin
513    read_counter_status(
514      net => net,
515      value => got_reg,
516      base_address => base_address,
517      bus_handle => bus_handle
518    );
519    got := got_reg.pulse_count;
520
521    check_equal(got=>got, expected=>expected, msg=>get_message);
522  end procedure;
523  -- ---------------------------------------------------------------------------
524
525end package body;

Generated VHDL simulation wait until package

Below is the generated register simulation wait until package, created from the TOML file above via the VhdlSimulationWaitUntilPackageGenerator class. It is used by the VHDL example testbench to wait for registers to assume a give value.

Click to expand/collapse code.
Example register simulation wait until package.
  1-- -----------------------------------------------------------------------------
  2-- This file is automatically generated by hdl-registers version 8.0.1-dev.
  3-- Code generator VhdlSimulationWaitUntilPackageGenerator version 1.0.2.
  4-- Generated 2025-05-05 20:52 from file regs_counter.toml at Git commit f53f19ab11e0.
  5-- Register hash e0b7d8e366d3777f3ef21d8e44540f964701e9be.
  6-- -----------------------------------------------------------------------------
  7
  8library ieee;
  9use ieee.fixed_pkg.all;
 10use ieee.std_logic_1164.all;
 11use ieee.numeric_std.all;
 12
 13library vunit_lib;
 14use vunit_lib.bus_master_pkg.bus_master_t;
 15use vunit_lib.bus_master_pkg.wait_until_read_equals;
 16use vunit_lib.com_types_pkg.max_timeout;
 17use vunit_lib.com_types_pkg.network_t;
 18use vunit_lib.string_ops.hex_image;
 19
 20library register_file;
 21use register_file.register_file_pkg.register_t;
 22use register_file.register_operations_pkg.register_bus_master;
 23
 24use work.counter_regs_pkg.all;
 25use work.counter_register_record_pkg.all;
 26
 27
 28package counter_register_wait_until_pkg is
 29
 30  -- ---------------------------------------------------------------------------
 31  -- Wait until the 'conf' register equals the given 'value'.
 32  procedure wait_until_counter_conf_equals(
 33    signal net : inout network_t;
 34    value : in counter_conf_t;
 35    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 36    bus_handle : in bus_master_t := register_bus_master;
 37    timeout : delay_length := max_timeout;
 38    message : string := ""
 39  );
 40
 41  -- Wait until the 'condition' field in the 'conf' register equals the given 'value'.
 42  procedure wait_until_counter_conf_condition_equals(
 43    signal net : inout network_t;
 44    value : in counter_conf_condition_t;
 45    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 46    bus_handle : in bus_master_t := register_bus_master;
 47    timeout : delay_length := max_timeout;
 48    message : string := ""
 49  );
 50
 51  -- Wait until the 'increment' field in the 'conf' register equals the given 'value'.
 52  procedure wait_until_counter_conf_increment_equals(
 53    signal net : inout network_t;
 54    value : in counter_conf_increment_t;
 55    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 56    bus_handle : in bus_master_t := register_bus_master;
 57    timeout : delay_length := max_timeout;
 58    message : string := ""
 59  );
 60  -- ---------------------------------------------------------------------------
 61
 62  -- ---------------------------------------------------------------------------
 63  -- Wait until the 'status' register equals the given 'value'.
 64  procedure wait_until_counter_status_equals(
 65    signal net : inout network_t;
 66    value : in counter_status_t;
 67    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 68    bus_handle : in bus_master_t := register_bus_master;
 69    timeout : delay_length := max_timeout;
 70    message : string := ""
 71  );
 72
 73  -- Wait until the 'enabled' field in the 'status' register equals the given 'value'.
 74  procedure wait_until_counter_status_enabled_equals(
 75    signal net : inout network_t;
 76    value : in std_ulogic;
 77    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 78    bus_handle : in bus_master_t := register_bus_master;
 79    timeout : delay_length := max_timeout;
 80    message : string := ""
 81  );
 82
 83  -- Wait until the 'pulse_count' field in the 'status' register equals the given 'value'.
 84  procedure wait_until_counter_status_pulse_count_equals(
 85    signal net : inout network_t;
 86    value : in counter_status_pulse_count_t;
 87    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
 88    bus_handle : in bus_master_t := register_bus_master;
 89    timeout : delay_length := max_timeout;
 90    message : string := ""
 91  );
 92  -- ---------------------------------------------------------------------------
 93
 94end package;
 95
 96package body counter_register_wait_until_pkg is
 97
 98  -- ---------------------------------------------------------------------------
 99  -- Wait until the 'conf' register equals the given 'value'.
100  procedure wait_until_counter_conf_equals(
101    signal net : inout network_t;
102    value : in counter_conf_t;
103    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
104    bus_handle : in bus_master_t := register_bus_master;
105    timeout : delay_length := max_timeout;
106    message : string := ""
107  ) is
108    constant reg_value : register_t := to_slv(value);
109
110    constant reg_index : counter_register_range := counter_conf;
111    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
112
113    constant register_array_message : string := "";
114    function base_address_message return string is
115    begin
116      if base_address /= 0 then
117        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
118      end if;
119
120      return "";
121    end function;
122    constant base_message : string := (
123      "Timeout while waiting for the 'conf' register"
124      & register_array_message
125      & base_address_message
126      & " to equal the given value: "
127      & to_string(reg_value)
128      & "."
129    );
130    function get_message return string is
131    begin
132      if message = "" then
133        return base_message;
134      end if;
135
136      return base_message & " " & message & ".";
137    end function;
138  begin
139    wait_until_read_equals(
140      net => net,
141      bus_handle => bus_handle,
142      addr => std_ulogic_vector(reg_address),
143      value => reg_value,
144      timeout => timeout,
145      msg => get_message
146    );
147  end procedure;
148
149  -- Wait until the 'condition' field in the 'conf' register equals the given 'value'.
150  procedure wait_until_counter_conf_condition_equals(
151    signal net : inout network_t;
152    value : in counter_conf_condition_t;
153    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
154    bus_handle : in bus_master_t := register_bus_master;
155    timeout : delay_length := max_timeout;
156    message : string := ""
157  ) is
158    constant reg_value : register_t := (
159      counter_conf_condition => to_slv(value),
160      others => '-'
161    );
162
163    constant reg_index : counter_register_range := counter_conf;
164    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
165
166    constant register_array_message : string := "";
167    function base_address_message return string is
168    begin
169      if base_address /= 0 then
170        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
171      end if;
172
173      return "";
174    end function;
175    constant base_message : string := (
176      "Timeout while waiting for the 'condition' field in the 'conf' register"
177      & register_array_message
178      & base_address_message
179      & " to equal the given value: "
180      & to_string(reg_value)
181      & "."
182    );
183    function get_message return string is
184    begin
185      if message = "" then
186        return base_message;
187      end if;
188
189      return base_message & " " & message & ".";
190    end function;
191  begin
192    wait_until_read_equals(
193      net => net,
194      bus_handle => bus_handle,
195      addr => std_ulogic_vector(reg_address),
196      value => reg_value,
197      timeout => timeout,
198      msg => get_message
199    );
200  end procedure;
201
202  -- Wait until the 'increment' field in the 'conf' register equals the given 'value'.
203  procedure wait_until_counter_conf_increment_equals(
204    signal net : inout network_t;
205    value : in counter_conf_increment_t;
206    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
207    bus_handle : in bus_master_t := register_bus_master;
208    timeout : delay_length := max_timeout;
209    message : string := ""
210  ) is
211    constant reg_value : register_t := (
212      counter_conf_increment => to_counter_conf_increment_slv(value),
213      others => '-'
214    );
215
216    constant reg_index : counter_register_range := counter_conf;
217    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
218
219    constant register_array_message : string := "";
220    function base_address_message return string is
221    begin
222      if base_address /= 0 then
223        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
224      end if;
225
226      return "";
227    end function;
228    constant base_message : string := (
229      "Timeout while waiting for the 'increment' field in the 'conf' register"
230      & register_array_message
231      & base_address_message
232      & " to equal the given value: "
233      & to_string(reg_value)
234      & "."
235    );
236    function get_message return string is
237    begin
238      if message = "" then
239        return base_message;
240      end if;
241
242      return base_message & " " & message & ".";
243    end function;
244  begin
245    wait_until_read_equals(
246      net => net,
247      bus_handle => bus_handle,
248      addr => std_ulogic_vector(reg_address),
249      value => reg_value,
250      timeout => timeout,
251      msg => get_message
252    );
253  end procedure;
254  -- ---------------------------------------------------------------------------
255
256  -- ---------------------------------------------------------------------------
257  -- Wait until the 'status' register equals the given 'value'.
258  procedure wait_until_counter_status_equals(
259    signal net : inout network_t;
260    value : in counter_status_t;
261    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
262    bus_handle : in bus_master_t := register_bus_master;
263    timeout : delay_length := max_timeout;
264    message : string := ""
265  ) is
266    constant reg_value : register_t := to_slv(value);
267
268    constant reg_index : counter_register_range := counter_status;
269    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
270
271    constant register_array_message : string := "";
272    function base_address_message return string is
273    begin
274      if base_address /= 0 then
275        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
276      end if;
277
278      return "";
279    end function;
280    constant base_message : string := (
281      "Timeout while waiting for the 'status' register"
282      & register_array_message
283      & base_address_message
284      & " to equal the given value: "
285      & to_string(reg_value)
286      & "."
287    );
288    function get_message return string is
289    begin
290      if message = "" then
291        return base_message;
292      end if;
293
294      return base_message & " " & message & ".";
295    end function;
296  begin
297    wait_until_read_equals(
298      net => net,
299      bus_handle => bus_handle,
300      addr => std_ulogic_vector(reg_address),
301      value => reg_value,
302      timeout => timeout,
303      msg => get_message
304    );
305  end procedure;
306
307  -- Wait until the 'enabled' field in the 'status' register equals the given 'value'.
308  procedure wait_until_counter_status_enabled_equals(
309    signal net : inout network_t;
310    value : in std_ulogic;
311    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
312    bus_handle : in bus_master_t := register_bus_master;
313    timeout : delay_length := max_timeout;
314    message : string := ""
315  ) is
316    constant reg_value : register_t := (
317      counter_status_enabled => value,
318      others => '-'
319    );
320
321    constant reg_index : counter_register_range := counter_status;
322    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
323
324    constant register_array_message : string := "";
325    function base_address_message return string is
326    begin
327      if base_address /= 0 then
328        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
329      end if;
330
331      return "";
332    end function;
333    constant base_message : string := (
334      "Timeout while waiting for the 'enabled' field in the 'status' register"
335      & register_array_message
336      & base_address_message
337      & " to equal the given value: "
338      & to_string(reg_value)
339      & "."
340    );
341    function get_message return string is
342    begin
343      if message = "" then
344        return base_message;
345      end if;
346
347      return base_message & " " & message & ".";
348    end function;
349  begin
350    wait_until_read_equals(
351      net => net,
352      bus_handle => bus_handle,
353      addr => std_ulogic_vector(reg_address),
354      value => reg_value,
355      timeout => timeout,
356      msg => get_message
357    );
358  end procedure;
359
360  -- Wait until the 'pulse_count' field in the 'status' register equals the given 'value'.
361  procedure wait_until_counter_status_pulse_count_equals(
362    signal net : inout network_t;
363    value : in counter_status_pulse_count_t;
364    base_address : in unsigned(32 - 1 downto 0) := (others => '0');
365    bus_handle : in bus_master_t := register_bus_master;
366    timeout : delay_length := max_timeout;
367    message : string := ""
368  ) is
369    constant reg_value : register_t := (
370      counter_status_pulse_count => to_counter_status_pulse_count_slv(value),
371      others => '-'
372    );
373
374    constant reg_index : counter_register_range := counter_status;
375    constant reg_address : unsigned(32 - 1 downto 0) := base_address + to_unsigned(4 * reg_index, 32);
376
377    constant register_array_message : string := "";
378    function base_address_message return string is
379    begin
380      if base_address /= 0 then
381        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
382      end if;
383
384      return "";
385    end function;
386    constant base_message : string := (
387      "Timeout while waiting for the 'pulse_count' field in the 'status' register"
388      & register_array_message
389      & base_address_message
390      & " to equal the given value: "
391      & to_string(reg_value)
392      & "."
393    );
394    function get_message return string is
395    begin
396      if message = "" then
397        return base_message;
398      end if;
399
400      return base_message & " " & message & ".";
401    end function;
402  begin
403    wait_until_read_equals(
404      net => net,
405      bus_handle => bus_handle,
406      addr => std_ulogic_vector(reg_address),
407      value => reg_value,
408      timeout => timeout,
409      msg => get_message
410    );
411  end procedure;
412  -- ---------------------------------------------------------------------------
413
414end package body;

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.

See here for a comparison with the performance of other tools.

Dependencies

Generated VHDL code depends on files from hdl-modules version 6.2.0 or greater:

  1. axi_lite_pkg.vhd and axi_lite_register_file.vhd in a library called axi_lite.

  2. register_file_pkg.vhd in a library called register_file.

The simulation code is furthermore dependent on the file register_operations_pkg.vhd in the library register_file, and access to VUnit’s VHDL libraries.

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/AXI-Lite components available that enable the register bus:

See the register_file library, axi library and axi_lite library for more details.