VHDL code generator

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

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

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

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

  • VhdlSimulationReadWritePackageGenerator generates a VHDL simulation support package with procedures for reading/writing register or field values.

  • VhdlSimulationCheckPackageGenerator generates a VHDL simulation support package with procedures for checking current register and field values against a given expected value.

  • VhdlSimulationWaitUntilPackageGenerator generates a VHDL simulation support package with procedures for waiting until a readable register or field assumes a given value.

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

Example

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

Register definition TOML file

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

Click to expand/collapse code.
TOML file for example.
 1################################################################################
 2[config]
 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 **config** 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.
 1# Standard libraries
 2import sys
 3from pathlib import Path
 4
 5# First party libraries
 6from hdl_registers.generator.vhdl.axi_lite.wrapper import VhdlAxiLiteWrapperGenerator
 7from hdl_registers.generator.vhdl.record_package import VhdlRecordPackageGenerator
 8from hdl_registers.generator.vhdl.register_package import VhdlRegisterPackageGenerator
 9from hdl_registers.generator.vhdl.simulation.check_package import (
10    VhdlSimulationCheckPackageGenerator,
11)
12from hdl_registers.generator.vhdl.simulation.read_write_package import (
13    VhdlSimulationReadWritePackageGenerator,
14)
15from hdl_registers.generator.vhdl.simulation.wait_until_package import (
16    VhdlSimulationWaitUntilPackageGenerator,
17)
18from hdl_registers.parser.toml import from_toml
19
20THIS_DIR = Path(__file__).parent
21
22
23def main(output_folder: Path):
24    """
25    Create register VHDL artifacts from the "counter" example module.
26    """
27    register_list = from_toml(
28        name="counter", toml_file=THIS_DIR.parent / "sim" / "regs_counter.toml"
29    )
30
31    VhdlRegisterPackageGenerator(
32        register_list=register_list, output_folder=output_folder
33    ).create_if_needed()
34
35    VhdlRecordPackageGenerator(
36        register_list=register_list, output_folder=output_folder
37    ).create_if_needed()
38
39    VhdlAxiLiteWrapperGenerator(
40        register_list=register_list, output_folder=output_folder
41    ).create_if_needed()
42
43    VhdlSimulationReadWritePackageGenerator(
44        register_list=register_list, output_folder=output_folder
45    ).create_if_needed()
46
47    VhdlSimulationCheckPackageGenerator(
48        register_list=register_list, output_folder=output_folder
49    ).create_if_needed()
50
51    VhdlSimulationWaitUntilPackageGenerator(
52        register_list=register_list, output_folder=output_folder
53    ).create_if_needed()
54
55
56if __name__ == "__main__":
57    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.config then
 69      -- Clear value when configuration is changed.
 70      regs_up.status.pulse_count <= 0;
 71    else
 72      regs_up.status.pulse_count <= regs_up.status.pulse_count + to_int(pulse);
 73    end if;
 74  end process;
 75
 76
 77  ------------------------------------------------------------------------------
 78  count_block : block
 79    constant counter_width : positive := 10;
 80    constant counter_activate_value : positive := 2 ** counter_width - 1;
 81    -- One bit extra to avoid overflow.
 82    constant counter_max_value : positive := 2 ** (counter_width + 1) - 1;
 83
 84    signal count : natural range 0 to counter_max_value := 0;
 85
 86    signal clock_enable_p1 : std_ulogic := '0';
 87  begin
 88
 89    ------------------------------------------------------------------------------
 90    count_events : process
 91    begin
 92      wait until rising_edge(clk);
 93
 94      pulse <= '0';
 95
 96      case regs_down.config.condition is
 97        when condition_clock_cycles =>
 98          count <= count + regs_down.config.increment;
 99
100        when condition_clock_cycles_with_enable =>
101          if clock_enable then
102            count <= count + regs_down.config.increment;
103          end if;
104
105        when condition_enable_edges =>
106          if clock_enable /= clock_enable_p1 then
107            count <= count + regs_down.config.increment;
108          end if;
109      end case;
110
111      if count >= counter_activate_value then
112        count <= 0;
113        pulse <= regs_up.status.enabled;
114      end if;
115
116      clock_enable_p1 <= clock_enable;
117    end process;
118
119  end block;
120
121end architecture;

VHDL example testbench

The VHDL below is the testbench for our 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_config.

  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 config : counter_config_t := counter_config_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      config.condition := condition_clock_cycles;
 55      config.increment := 13;
 56
 57    elsif run("test_count_clock_cycles_with_enable") then
 58      config.condition := condition_clock_cycles_with_enable;
 59      config.increment := 8;
 60
 61      clock_enable <= '1';
 62    end if;
 63
 64    -- Set configuration, which depends on test case.
 65    write_counter_config(net=>net, value=>config);
 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 7.0.2-dev.
  3-- Code generator VhdlRegisterPackageGenerator version 2.0.0.
  4-- Generated 2025-01-21 20:52 from file regs_counter.toml at commit 3c3e6c67d817.
  5-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  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_config : 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 'config' register.
 52  -- Range of the 'condition' field.
 53  subtype counter_config_condition is natural range 1 downto 0;
 54  -- Width of the 'condition' field.
 55  constant counter_config_condition_width : positive := 2;
 56  -- Type for the 'condition' field.
 57  type counter_config_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_config_condition_init : counter_config_condition_t := condition_clock_cycles;
 64  -- Type for the 'condition' field as an SLV.
 65  subtype counter_config_condition_slv_t is std_ulogic_vector(1 downto 0);
 66  -- Cast a 'condition' field value to SLV.
 67  function to_slv(data : counter_config_condition_t) return counter_config_condition_slv_t;
 68  -- Get a 'condition' field value from a register value.
 69  function to_counter_config_condition(data : register_t) return counter_config_condition_t;
 70
 71  -- Range of the 'increment' field.
 72  subtype counter_config_increment is natural range 5 downto 2;
 73  -- Width of the 'increment' field.
 74  constant counter_config_increment_width : positive := 4;
 75  -- Type for the 'increment' field.
 76  subtype counter_config_increment_t is integer range 0 to 15;
 77  -- Default value of the 'increment' field.
 78  constant counter_config_increment_init : counter_config_increment_t := 0;
 79  -- Type for the 'increment' field as an SLV.
 80  subtype counter_config_increment_slv_t is std_ulogic_vector(3 downto 0);
 81  -- Cast a 'increment' field value to SLV.
 82  function to_counter_config_increment_slv(data : counter_config_increment_t) return counter_config_increment_slv_t;
 83  -- Get a 'increment' field value from a register value.
 84  function to_counter_config_increment(data : register_t) return counter_config_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_config, 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_config_condition_t) return counter_config_condition_slv_t is
138    constant data_int : natural := counter_config_condition_t'pos(data);
139    constant result : counter_config_condition_slv_t := std_ulogic_vector(
140      to_unsigned(data_int, counter_config_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_config_condition(data : register_t) return counter_config_condition_t is
148    constant field_slv : counter_config_condition_slv_t := data(counter_config_condition);
149    constant field_int : natural := to_integer(unsigned(field_slv));
150    constant result : counter_config_condition_t := counter_config_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_config_increment_slv(data : counter_config_increment_t) return counter_config_increment_slv_t is
157    constant result : counter_config_increment_slv_t := std_ulogic_vector(to_unsigned(data, counter_config_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_config_increment(data : register_t) return counter_config_increment_t is
164    constant result : integer := to_integer(unsigned(data(counter_config_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 7.0.2-dev.
  3-- Code generator VhdlRecordPackageGenerator version 1.0.0.
  4-- Generated 2025-01-21 20:52 from file regs_counter.toml at commit 3c3e6c67d817.
  5-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  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 'config' register as a record.
 24  type counter_config_t is record
 25    condition : counter_config_condition_t;
 26    increment : counter_config_increment_t;
 27  end record;
 28  -- Default value for the 'config' register as a record.
 29  constant counter_config_init : counter_config_t := (
 30    condition => counter_config_condition_init,
 31    increment => counter_config_increment_init
 32  );
 33  -- Convert a record of the 'config' register to SLV.
 34  function to_slv(data : counter_config_t) return register_t;
 35  -- Convert an SLV register value to the record for the 'config' register.
 36  function to_counter_config(data : register_t) return counter_config_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    config : counter_config_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    config => counter_config_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    config : 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    config : 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_config_t) return register_t is
137    variable result : register_t := (others => '-');
138  begin
139    result(counter_config_condition) := to_slv(data.condition);
140    result(counter_config_increment) := to_counter_config_increment_slv(data.increment);
141
142    return result;
143  end function;
144
145  function to_counter_config(data : register_t) return counter_config_t is
146    variable result : counter_config_t := counter_config_init;
147  begin
148    result.condition := to_counter_config_condition(data);
149    result.increment := to_counter_config_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.config := to_counter_config(data(counter_config));
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.config := data(counter_config);
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.config := data(counter_config);
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 7.0.2-dev.
  3-- Code generator VhdlAxiLiteWrapperGenerator version 1.0.0.
  4-- Generated 2025-01-21 20:52 from file regs_counter.toml at commit 3c3e6c67d817.
  5-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  6-- -----------------------------------------------------------------------------
  7
  8-- -----------------------------------------------------------------------------
  9-- AXI-Lite register file for the 'counter' module registers.
 10--
 11-- Is a wrapper around the generic AXI-Lite register file from hdl-modules:
 12-- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
 13-- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/axi_lite_register_file.vhd
 14--
 15-- Sets correct generics, and performs conversion to the easy-to-use register record types.
 16-- -----------------------------------------------------------------------------
 17
 18library ieee;
 19use ieee.std_logic_1164.all;
 20
 21library axi_lite;
 22use axi_lite.axi_lite_pkg.all;
 23
 24library register_file;
 25use register_file.register_file_pkg.all;
 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    --# {}
 35    --# Register control bus.
 36    axi_lite_m2s : in axi_lite_m2s_t;
 37    axi_lite_s2m : out axi_lite_s2m_t := axi_lite_s2m_init;
 38    --# {}
 39    -- Register values.
 40    regs_up : in counter_regs_up_t := counter_regs_up_init;
 41    regs_down : out counter_regs_down_t := counter_regs_down_init;
 42    --# {}
 43    -- Each bit is pulsed for one cycle when the corresponding register is read/written.
 44    reg_was_read : out counter_reg_was_read_t := counter_reg_was_read_init;
 45    reg_was_written : out counter_reg_was_written_t := counter_reg_was_written_init
 46  );
 47end entity;
 48
 49architecture a of counter_register_file_axi_lite is
 50
 51  signal regs_up_slv, regs_down_slv : counter_regs_t := counter_regs_init;
 52
 53  signal reg_was_read_slv, reg_was_written_slv : counter_reg_was_accessed_t := (
 54    others => '0'
 55  );
 56
 57begin
 58
 59  ------------------------------------------------------------------------------
 60  -- Instantiate the generic AXI-Lite register file from
 61  -- * https://hdl-modules.com/modules/register_file/register_file.html#axi-lite-reg-file-vhd
 62  -- * https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/axi_lite_register_file.vhd
 63  axi_lite_register_file_inst : entity register_file.axi_lite_register_file
 64    generic map (
 65      registers => counter_register_map,
 66      default_values => counter_regs_init
 67    )
 68    port map(
 69      clk => clk,
 70      --
 71      axi_lite_m2s => axi_lite_m2s,
 72      axi_lite_s2m => axi_lite_s2m,
 73      --
 74      regs_up => regs_up_slv,
 75      regs_down => regs_down_slv,
 76      --
 77      reg_was_read => reg_was_read_slv,
 78      reg_was_written => reg_was_written_slv
 79    );
 80
 81
 82  ------------------------------------------------------------------------------
 83  -- Combinatorially convert the register record to a list of SLV values that can be handled
 84  -- by the generic register file implementation.
 85  assign_regs_up : process(regs_up)
 86  begin
 87    regs_up_slv <= to_slv(regs_up);
 88  end process;
 89
 90
 91  ------------------------------------------------------------------------------
 92  -- Combinatorially convert the list of SLV values from the generic register file into the record
 93  -- we want to use in our application.
 94  assign_regs_down : process(regs_down_slv)
 95  begin
 96    regs_down <= to_counter_regs_down(regs_down_slv);
 97  end process;
 98
 99
100  ------------------------------------------------------------------------------
101  -- Combinatorially convert status mask to a record where only the applicable registers are present.
102  assign_reg_was_read : process(reg_was_read_slv)
103  begin
104    reg_was_read <= to_counter_reg_was_read(reg_was_read_slv);
105  end process;
106
107
108  ------------------------------------------------------------------------------
109  -- Combinatorially convert status mask to a record where only the applicable registers are present.
110  assign_reg_was_written : process(reg_was_written_slv)
111  begin
112    reg_was_written <= to_counter_reg_was_written(reg_was_written_slv);
113  end process;
114
115end 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 7.0.2-dev.
  3-- Code generator VhdlSimulationReadWritePackageGenerator version 1.1.0.
  4-- Generated 2025-01-21 20:52 from file regs_counter.toml at commit 3c3e6c67d817.
  5-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  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 common;
 19use common.addr_pkg.addr_t;
 20use common.addr_pkg.addr_width;
 21
 22library register_file;
 23use register_file.register_file_pkg.register_t;
 24use register_file.register_file_pkg.register_width;
 25use register_file.register_operations_pkg.register_bus_master;
 26
 27use work.counter_regs_pkg.all;
 28use work.counter_register_record_pkg.all;
 29
 30
 31package counter_register_read_write_pkg is
 32
 33  -- ---------------------------------------------------------------------------
 34  -- Read the 'config' register as a plain 'register_t'.
 35  procedure read_counter_config(
 36    signal net : inout network_t;
 37    value : out register_t;
 38    base_address : in addr_t := (others => '0');
 39    bus_handle : in bus_master_t := register_bus_master
 40  );
 41
 42  -- Read the 'config' register as an 'integer'.
 43  procedure read_counter_config(
 44    signal net : inout network_t;
 45    value : out integer;
 46    base_address : in addr_t := (others => '0');
 47    bus_handle : in bus_master_t := register_bus_master
 48  );
 49
 50  -- Read the 'config' register.
 51  procedure read_counter_config(
 52    signal net : inout network_t;
 53    value : out counter_config_t;
 54    base_address : in addr_t := (others => '0');
 55    bus_handle : in bus_master_t := register_bus_master
 56  );
 57
 58  -- Read the 'condition' field in the 'config' register.
 59  procedure read_counter_config_condition(
 60    signal net : inout network_t;
 61    value : out counter_config_condition_t;
 62    base_address : in addr_t := (others => '0');
 63    bus_handle : in bus_master_t := register_bus_master
 64  );
 65
 66  -- Read the 'increment' field in the 'config' register.
 67  procedure read_counter_config_increment(
 68    signal net : inout network_t;
 69    value : out counter_config_increment_t;
 70    base_address : in addr_t := (others => '0');
 71    bus_handle : in bus_master_t := register_bus_master
 72  );
 73
 74  -- Write the 'config' register as an 'integer'.
 75  procedure write_counter_config(
 76    signal net : inout network_t;
 77    value : in integer;
 78    base_address : in addr_t := (others => '0');
 79    bus_handle : in bus_master_t := register_bus_master
 80  );
 81
 82  -- Write the 'config' register.
 83  procedure write_counter_config(
 84    signal net : inout network_t;
 85    value : in counter_config_t;
 86    base_address : in addr_t := (others => '0');
 87    bus_handle : in bus_master_t := register_bus_master
 88  );
 89
 90  -- Write the 'condition' field in the 'config' register.
 91  -- Will read-modify-write the register to set the field to the supplied 'value'.
 92  procedure write_counter_config_condition(
 93    signal net : inout network_t;
 94    value : in counter_config_condition_t;
 95    base_address : in addr_t := (others => '0');
 96    bus_handle : in bus_master_t := register_bus_master
 97  );
 98
 99  -- Write the 'increment' field in the 'config' register.
100  -- Will read-modify-write the register to set the field to the supplied 'value'.
101  procedure write_counter_config_increment(
102    signal net : inout network_t;
103    value : in counter_config_increment_t;
104    base_address : in addr_t := (others => '0');
105    bus_handle : in bus_master_t := register_bus_master
106  );
107  -- ---------------------------------------------------------------------------
108
109  -- ---------------------------------------------------------------------------
110  -- Write the 'command' register as an 'integer'.
111  procedure write_counter_command(
112    signal net : inout network_t;
113    value : in integer;
114    base_address : in addr_t := (others => '0');
115    bus_handle : in bus_master_t := register_bus_master
116  );
117
118  -- Write the 'command' register.
119  procedure write_counter_command(
120    signal net : inout network_t;
121    value : in counter_command_t;
122    base_address : in addr_t := (others => '0');
123    bus_handle : in bus_master_t := register_bus_master
124  );
125
126  -- Write the 'start' field in the 'command' register.
127  -- Will write the whole register, with the field set to the 
128  -- supplied 'value' and everything else set to default.
129  procedure write_counter_command_start(
130    signal net : inout network_t;
131    value : in std_ulogic;
132    base_address : in addr_t := (others => '0');
133    bus_handle : in bus_master_t := register_bus_master
134  );
135
136  -- Write the 'stop' field in the 'command' register.
137  -- Will write the whole register, with the field set to the 
138  -- supplied 'value' and everything else set to default.
139  procedure write_counter_command_stop(
140    signal net : inout network_t;
141    value : in std_ulogic;
142    base_address : in addr_t := (others => '0');
143    bus_handle : in bus_master_t := register_bus_master
144  );
145  -- ---------------------------------------------------------------------------
146
147  -- ---------------------------------------------------------------------------
148  -- Read the 'status' register as a plain 'register_t'.
149  procedure read_counter_status(
150    signal net : inout network_t;
151    value : out register_t;
152    base_address : in addr_t := (others => '0');
153    bus_handle : in bus_master_t := register_bus_master
154  );
155
156  -- Read the 'status' register as an 'integer'.
157  procedure read_counter_status(
158    signal net : inout network_t;
159    value : out integer;
160    base_address : in addr_t := (others => '0');
161    bus_handle : in bus_master_t := register_bus_master
162  );
163
164  -- Read the 'status' register.
165  procedure read_counter_status(
166    signal net : inout network_t;
167    value : out counter_status_t;
168    base_address : in addr_t := (others => '0');
169    bus_handle : in bus_master_t := register_bus_master
170  );
171
172  -- Read the 'enabled' field in the 'status' register.
173  procedure read_counter_status_enabled(
174    signal net : inout network_t;
175    value : out std_ulogic;
176    base_address : in addr_t := (others => '0');
177    bus_handle : in bus_master_t := register_bus_master
178  );
179
180  -- Read the 'pulse_count' field in the 'status' register.
181  procedure read_counter_status_pulse_count(
182    signal net : inout network_t;
183    value : out counter_status_pulse_count_t;
184    base_address : in addr_t := (others => '0');
185    bus_handle : in bus_master_t := register_bus_master
186  );
187  -- ---------------------------------------------------------------------------
188
189end package;
190
191package body counter_register_read_write_pkg is
192
193  -- ---------------------------------------------------------------------------
194  -- Read the 'config' register as a plain 'register_t'.
195  procedure read_counter_config(
196    signal net : inout network_t;
197    value : out register_t;
198    base_address : in addr_t := (others => '0');
199    bus_handle : in bus_master_t := register_bus_master
200  ) is
201    constant reg_index : counter_register_range := counter_config;
202    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
203    variable reg_value : register_t := (others => '0');
204  begin
205    read_bus(
206      net => net,
207      bus_handle => bus_handle,
208      address => std_logic_vector(reg_address),
209      data => reg_value
210    );
211    value := reg_value;
212  end procedure;
213
214  -- Read the 'config' register as an 'integer'.
215  procedure read_counter_config(
216    signal net : inout network_t;
217    value : out integer;
218    base_address : in addr_t := (others => '0');
219    bus_handle : in bus_master_t := register_bus_master
220  ) is
221    constant reg_index : counter_register_range := counter_config;
222    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
223    variable reg_value : register_t := (others => '0');
224  begin
225    read_bus(
226      net => net,
227      bus_handle => bus_handle,
228      address => std_logic_vector(reg_address),
229      data => reg_value
230    );
231    value := to_integer(unsigned(reg_value));
232  end procedure;
233
234  -- Read the 'config' register.
235  procedure read_counter_config(
236    signal net : inout network_t;
237    value : out counter_config_t;
238    base_address : in addr_t := (others => '0');
239    bus_handle : in bus_master_t := register_bus_master
240  ) is
241    constant reg_index : counter_register_range := counter_config;
242    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
243    variable reg_value : register_t := (others => '0');
244  begin
245    read_bus(
246      net => net,
247      bus_handle => bus_handle,
248      address => std_logic_vector(reg_address),
249      data => reg_value
250    );
251    value := to_counter_config(reg_value);
252  end procedure;
253
254  -- Read the 'condition' field in the 'config' register.
255  procedure read_counter_config_condition(
256    signal net : inout network_t;
257    value : out counter_config_condition_t;
258    base_address : in addr_t := (others => '0');
259    bus_handle : in bus_master_t := register_bus_master
260  ) is
261    variable reg_value : counter_config_t := counter_config_init;
262  begin
263    read_counter_config(
264      net => net,
265      value => reg_value,
266      base_address => base_address,
267      bus_handle => bus_handle
268    );
269    value := reg_value.condition;
270  end procedure;
271
272  -- Read the 'increment' field in the 'config' register.
273  procedure read_counter_config_increment(
274    signal net : inout network_t;
275    value : out counter_config_increment_t;
276    base_address : in addr_t := (others => '0');
277    bus_handle : in bus_master_t := register_bus_master
278  ) is
279    variable reg_value : counter_config_t := counter_config_init;
280  begin
281    read_counter_config(
282      net => net,
283      value => reg_value,
284      base_address => base_address,
285      bus_handle => bus_handle
286    );
287    value := reg_value.increment;
288  end procedure;
289
290  -- Write the 'config' register as an 'integer'.
291  procedure write_counter_config(
292    signal net : inout network_t;
293    value : in integer;
294    base_address : in addr_t := (others => '0');
295    bus_handle : in bus_master_t := register_bus_master
296  ) is
297    constant reg_index : counter_register_range := counter_config;
298    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
299    constant reg_value : register_t := std_ulogic_vector(to_unsigned(value, register_width));
300  begin
301    write_bus(
302      net => net,
303      bus_handle => bus_handle,
304      address => std_logic_vector(reg_address),
305      data => reg_value
306    );
307  end procedure;
308
309  -- Write the 'config' register.
310  procedure write_counter_config(
311    signal net : inout network_t;
312    value : in counter_config_t;
313    base_address : in addr_t := (others => '0');
314    bus_handle : in bus_master_t := register_bus_master
315  ) is
316    constant reg_index : counter_register_range := counter_config;
317    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
318    constant reg_value : register_t := to_slv(value);
319  begin
320    write_bus(
321      net => net,
322      bus_handle => bus_handle,
323      address => std_logic_vector(reg_address),
324      data => reg_value
325    );
326  end procedure;
327
328  -- Write the 'condition' field in the 'config' register.
329  -- Will read-modify-write the register to set the field to the supplied 'value'.
330  procedure write_counter_config_condition(
331    signal net : inout network_t;
332    value : in counter_config_condition_t;
333    base_address : in addr_t := (others => '0');
334    bus_handle : in bus_master_t := register_bus_master
335  ) is
336    variable reg_value : counter_config_t := counter_config_init;
337  begin
338    read_counter_config(
339      net => net,
340      value => reg_value,
341      base_address => base_address,
342      bus_handle => bus_handle
343    );
344    reg_value.condition := value;
345
346    write_counter_config(
347      net => net,
348      value => reg_value,
349      base_address => base_address,
350      bus_handle => bus_handle
351    );
352  end procedure;
353
354  -- Write the 'increment' field in the 'config' register.
355  -- Will read-modify-write the register to set the field to the supplied 'value'.
356  procedure write_counter_config_increment(
357    signal net : inout network_t;
358    value : in counter_config_increment_t;
359    base_address : in addr_t := (others => '0');
360    bus_handle : in bus_master_t := register_bus_master
361  ) is
362    variable reg_value : counter_config_t := counter_config_init;
363  begin
364    read_counter_config(
365      net => net,
366      value => reg_value,
367      base_address => base_address,
368      bus_handle => bus_handle
369    );
370    reg_value.increment := value;
371
372    write_counter_config(
373      net => net,
374      value => reg_value,
375      base_address => base_address,
376      bus_handle => bus_handle
377    );
378  end procedure;
379  -- ---------------------------------------------------------------------------
380
381  -- ---------------------------------------------------------------------------
382  -- Write the 'command' register as an 'integer'.
383  procedure write_counter_command(
384    signal net : inout network_t;
385    value : in integer;
386    base_address : in addr_t := (others => '0');
387    bus_handle : in bus_master_t := register_bus_master
388  ) is
389    constant reg_index : counter_register_range := counter_command;
390    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
391    constant reg_value : register_t := std_ulogic_vector(to_unsigned(value, register_width));
392  begin
393    write_bus(
394      net => net,
395      bus_handle => bus_handle,
396      address => std_logic_vector(reg_address),
397      data => reg_value
398    );
399  end procedure;
400
401  -- Write the 'command' register.
402  procedure write_counter_command(
403    signal net : inout network_t;
404    value : in counter_command_t;
405    base_address : in addr_t := (others => '0');
406    bus_handle : in bus_master_t := register_bus_master
407  ) is
408    constant reg_index : counter_register_range := counter_command;
409    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
410    constant reg_value : register_t := to_slv(value);
411  begin
412    write_bus(
413      net => net,
414      bus_handle => bus_handle,
415      address => std_logic_vector(reg_address),
416      data => reg_value
417    );
418  end procedure;
419
420  -- Write the 'start' field in the 'command' register.
421  -- Will write the whole register, with the field set to the 
422  -- supplied 'value' and everything else set to default.
423  procedure write_counter_command_start(
424    signal net : inout network_t;
425    value : in std_ulogic;
426    base_address : in addr_t := (others => '0');
427    bus_handle : in bus_master_t := register_bus_master
428  ) is
429    variable reg_value : counter_command_t := counter_command_init;
430  begin
431    reg_value.start := value;
432
433    write_counter_command(
434      net => net,
435      value => reg_value,
436      base_address => base_address,
437      bus_handle => bus_handle
438    );
439  end procedure;
440
441  -- Write the 'stop' field in the 'command' register.
442  -- Will write the whole register, with the field set to the 
443  -- supplied 'value' and everything else set to default.
444  procedure write_counter_command_stop(
445    signal net : inout network_t;
446    value : in std_ulogic;
447    base_address : in addr_t := (others => '0');
448    bus_handle : in bus_master_t := register_bus_master
449  ) is
450    variable reg_value : counter_command_t := counter_command_init;
451  begin
452    reg_value.stop := value;
453
454    write_counter_command(
455      net => net,
456      value => reg_value,
457      base_address => base_address,
458      bus_handle => bus_handle
459    );
460  end procedure;
461  -- ---------------------------------------------------------------------------
462
463  -- ---------------------------------------------------------------------------
464  -- Read the 'status' register as a plain 'register_t'.
465  procedure read_counter_status(
466    signal net : inout network_t;
467    value : out register_t;
468    base_address : in addr_t := (others => '0');
469    bus_handle : in bus_master_t := register_bus_master
470  ) is
471    constant reg_index : counter_register_range := counter_status;
472    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
473    variable reg_value : register_t := (others => '0');
474  begin
475    read_bus(
476      net => net,
477      bus_handle => bus_handle,
478      address => std_logic_vector(reg_address),
479      data => reg_value
480    );
481    value := reg_value;
482  end procedure;
483
484  -- Read the 'status' register as an 'integer'.
485  procedure read_counter_status(
486    signal net : inout network_t;
487    value : out integer;
488    base_address : in addr_t := (others => '0');
489    bus_handle : in bus_master_t := register_bus_master
490  ) is
491    constant reg_index : counter_register_range := counter_status;
492    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
493    variable reg_value : register_t := (others => '0');
494  begin
495    read_bus(
496      net => net,
497      bus_handle => bus_handle,
498      address => std_logic_vector(reg_address),
499      data => reg_value
500    );
501    value := to_integer(unsigned(reg_value));
502  end procedure;
503
504  -- Read the 'status' register.
505  procedure read_counter_status(
506    signal net : inout network_t;
507    value : out counter_status_t;
508    base_address : in addr_t := (others => '0');
509    bus_handle : in bus_master_t := register_bus_master
510  ) is
511    constant reg_index : counter_register_range := counter_status;
512    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
513    variable reg_value : register_t := (others => '0');
514  begin
515    read_bus(
516      net => net,
517      bus_handle => bus_handle,
518      address => std_logic_vector(reg_address),
519      data => reg_value
520    );
521    value := to_counter_status(reg_value);
522  end procedure;
523
524  -- Read the 'enabled' field in the 'status' register.
525  procedure read_counter_status_enabled(
526    signal net : inout network_t;
527    value : out std_ulogic;
528    base_address : in addr_t := (others => '0');
529    bus_handle : in bus_master_t := register_bus_master
530  ) is
531    variable reg_value : counter_status_t := counter_status_init;
532  begin
533    read_counter_status(
534      net => net,
535      value => reg_value,
536      base_address => base_address,
537      bus_handle => bus_handle
538    );
539    value := reg_value.enabled;
540  end procedure;
541
542  -- Read the 'pulse_count' field in the 'status' register.
543  procedure read_counter_status_pulse_count(
544    signal net : inout network_t;
545    value : out counter_status_pulse_count_t;
546    base_address : in addr_t := (others => '0');
547    bus_handle : in bus_master_t := register_bus_master
548  ) is
549    variable reg_value : counter_status_t := counter_status_init;
550  begin
551    read_counter_status(
552      net => net,
553      value => reg_value,
554      base_address => base_address,
555      bus_handle => bus_handle
556    );
557    value := reg_value.pulse_count;
558  end procedure;
559  -- ---------------------------------------------------------------------------
560
561end 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 7.0.2-dev.
  3-- Code generator VhdlSimulationCheckPackageGenerator version 1.2.0.
  4-- Generated 2025-01-21 20:52 from file regs_counter.toml at commit 3c3e6c67d817.
  5-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  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 common;
 21use common.addr_pkg.addr_t;
 22
 23library register_file;
 24use register_file.register_file_pkg.register_t;
 25use register_file.register_operations_pkg.register_bus_master;
 26
 27use work.counter_register_read_write_pkg.all;
 28use work.counter_register_record_pkg.all;
 29use work.counter_regs_pkg.all;
 30
 31
 32package counter_register_check_pkg is
 33
 34  -- ---------------------------------------------------------------------------
 35  -- Check that the current value of the 'config' register
 36  -- equals the given 'expected' value as a plain SLV casted to integer.
 37  procedure check_counter_config_equal(
 38    signal net : inout network_t;
 39    expected : in integer;
 40    base_address : in addr_t := (others => '0');
 41    bus_handle : in bus_master_t := register_bus_master;
 42    message : in string := ""
 43  );
 44
 45  -- Check that the current value of the 'config' register
 46  -- equals the given 'expected' value.
 47  procedure check_counter_config_equal(
 48    signal net : inout network_t;
 49    expected : in counter_config_t;
 50    base_address : in addr_t := (others => '0');
 51    bus_handle : in bus_master_t := register_bus_master;
 52    message : in string := ""
 53  );
 54
 55  -- Check that the current value of the 'condition' field in the 'config' register
 56  -- equals the given 'expected' value.
 57  procedure check_counter_config_condition_equal(
 58    signal net : inout network_t;
 59    expected : in counter_config_condition_t;
 60    base_address : in addr_t := (others => '0');
 61    bus_handle : in bus_master_t := register_bus_master;
 62    message : in string := ""
 63  );
 64
 65  -- Check that the current value of the 'increment' field in the 'config' register
 66  -- equals the given 'expected' value.
 67  procedure check_counter_config_increment_equal(
 68    signal net : inout network_t;
 69    expected : in counter_config_increment_t;
 70    base_address : in addr_t := (others => '0');
 71    bus_handle : in bus_master_t := register_bus_master;
 72    message : in string := ""
 73  );
 74  -- ---------------------------------------------------------------------------
 75
 76  -- ---------------------------------------------------------------------------
 77  -- Check that the current value of the 'status' register
 78  -- equals the given 'expected' value as a plain SLV casted to integer.
 79  procedure check_counter_status_equal(
 80    signal net : inout network_t;
 81    expected : in integer;
 82    base_address : in addr_t := (others => '0');
 83    bus_handle : in bus_master_t := register_bus_master;
 84    message : in string := ""
 85  );
 86
 87  -- Check that the current value of the 'status' register
 88  -- equals the given 'expected' value.
 89  procedure check_counter_status_equal(
 90    signal net : inout network_t;
 91    expected : in counter_status_t;
 92    base_address : in addr_t := (others => '0');
 93    bus_handle : in bus_master_t := register_bus_master;
 94    message : in string := ""
 95  );
 96
 97  -- Check that the current value of the 'enabled' field in the 'status' register
 98  -- equals the given 'expected' value.
 99  procedure check_counter_status_enabled_equal(
100    signal net : inout network_t;
101    expected : in std_ulogic;
102    base_address : in addr_t := (others => '0');
103    bus_handle : in bus_master_t := register_bus_master;
104    message : in string := ""
105  );
106
107  -- Check that the current value of the 'pulse_count' field in the 'status' register
108  -- equals the given 'expected' value.
109  procedure check_counter_status_pulse_count_equal(
110    signal net : inout network_t;
111    expected : in counter_status_pulse_count_t;
112    base_address : in addr_t := (others => '0');
113    bus_handle : in bus_master_t := register_bus_master;
114    message : in string := ""
115  );
116  -- ---------------------------------------------------------------------------
117
118end package;
119
120package body counter_register_check_pkg is
121
122  -- ---------------------------------------------------------------------------
123  -- Check that the current value of the 'config' register
124  -- equals the given 'expected' value as a plain SLV casted to integer.
125  procedure check_counter_config_equal(
126    signal net : inout network_t;
127    expected : in integer;
128    base_address : in addr_t := (others => '0');
129    bus_handle : in bus_master_t := register_bus_master;
130    message : in string := ""
131  ) is
132    constant register_array_message : string := "";
133    function base_address_message return string is
134    begin
135      if base_address /= 0 then
136        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
137      end if;
138
139      return "";
140    end function;
141    constant base_message : string := (
142      "Checking the 'config' register"
143      & register_array_message
144      & base_address_message
145      & "."
146    );
147    function get_message return string is
148    begin
149      if message = "" then
150        return base_message;
151      end if;
152
153      return base_message & " " & message & ".";
154    end function;
155
156    variable got : integer;
157  begin
158    read_counter_config(
159      net => net,
160      value => got,
161      base_address => base_address,
162      bus_handle => bus_handle
163    );
164
165    check_equal(got=>got, expected=>expected, msg=>get_message);
166  end procedure;
167
168  -- Check that the current value of the 'config' register
169  -- equals the given 'expected' value.
170  procedure check_counter_config_equal(
171    signal net : inout network_t;
172    expected : in counter_config_t;
173    base_address : in addr_t := (others => '0');
174    bus_handle : in bus_master_t := register_bus_master;
175    message : in string := ""
176  ) is
177    constant register_array_message : string := "";
178    function base_address_message return string is
179    begin
180      if base_address /= 0 then
181        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
182      end if;
183
184      return "";
185    end function;
186    constant base_message : string := (
187      "Checking the 'config' register"
188      & register_array_message
189      & base_address_message
190      & "."
191    );
192    function get_message return string is
193    begin
194      if message = "" then
195        return base_message;
196      end if;
197
198      return base_message & " " & message & ".";
199    end function;
200
201    variable got : counter_config_t;
202  begin
203    read_counter_config(
204      net => net,
205      value => got,
206      base_address => base_address,
207      bus_handle => bus_handle
208    );
209
210    if got /= expected then
211      failing_check(
212        checker => default_checker,
213        msg => p_std_msg(
214          check_result => "Equality check failed",
215          msg => get_message,
216          ctx => (
217            "Got " & to_string(to_slv(got)) & ". Expected " & to_string(to_slv(expected)) & "."
218          )
219        )
220      );
221    end if;
222  end procedure;
223
224  -- Check that the current value of the 'condition' field in the 'config' register
225  -- equals the given 'expected' value.
226  procedure check_counter_config_condition_equal(
227    signal net : inout network_t;
228    expected : in counter_config_condition_t;
229    base_address : in addr_t := (others => '0');
230    bus_handle : in bus_master_t := register_bus_master;
231    message : in string := ""
232  ) is
233    constant register_array_message : string := "";
234    function base_address_message return string is
235    begin
236      if base_address /= 0 then
237        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
238      end if;
239
240      return "";
241    end function;
242    constant base_message : string := (
243      "Checking the 'condition' field in the 'config' register"
244      & register_array_message
245      & base_address_message
246      & "."
247    );
248    function get_message return string is
249    begin
250      if message = "" then
251        return base_message;
252      end if;
253
254      return base_message & " " & message & ".";
255    end function;
256
257    variable got_reg : counter_config_t := counter_config_init;
258    variable got : counter_config_condition_t := counter_config_condition_init;
259  begin
260    read_counter_config(
261      net => net,
262      value => got_reg,
263      base_address => base_address,
264      bus_handle => bus_handle
265    );
266    got := got_reg.condition;
267
268    if got /= expected then
269      failing_check(
270        checker => default_checker,
271        msg => p_std_msg(
272          check_result => "Equality check failed",
273          msg => get_message,
274          ctx => (
275            "Got " & to_string(got) & "."
276            & " Expected " & to_string(expected) & "."
277          )
278        )
279      );
280    end if;
281  end procedure;
282
283  -- Check that the current value of the 'increment' field in the 'config' register
284  -- equals the given 'expected' value.
285  procedure check_counter_config_increment_equal(
286    signal net : inout network_t;
287    expected : in counter_config_increment_t;
288    base_address : in addr_t := (others => '0');
289    bus_handle : in bus_master_t := register_bus_master;
290    message : in string := ""
291  ) is
292    constant register_array_message : string := "";
293    function base_address_message return string is
294    begin
295      if base_address /= 0 then
296        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
297      end if;
298
299      return "";
300    end function;
301    constant base_message : string := (
302      "Checking the 'increment' field in the 'config' register"
303      & register_array_message
304      & base_address_message
305      & "."
306    );
307    function get_message return string is
308    begin
309      if message = "" then
310        return base_message;
311      end if;
312
313      return base_message & " " & message & ".";
314    end function;
315
316    variable got_reg : counter_config_t := counter_config_init;
317    variable got : counter_config_increment_t := counter_config_increment_init;
318  begin
319    read_counter_config(
320      net => net,
321      value => got_reg,
322      base_address => base_address,
323      bus_handle => bus_handle
324    );
325    got := got_reg.increment;
326
327    check_equal(got=>got, expected=>expected, msg=>get_message);
328  end procedure;
329  -- ---------------------------------------------------------------------------
330
331  -- ---------------------------------------------------------------------------
332  -- Check that the current value of the 'status' register
333  -- equals the given 'expected' value as a plain SLV casted to integer.
334  procedure check_counter_status_equal(
335    signal net : inout network_t;
336    expected : in integer;
337    base_address : in addr_t := (others => '0');
338    bus_handle : in bus_master_t := register_bus_master;
339    message : in string := ""
340  ) is
341    constant register_array_message : string := "";
342    function base_address_message return string is
343    begin
344      if base_address /= 0 then
345        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
346      end if;
347
348      return "";
349    end function;
350    constant base_message : string := (
351      "Checking the 'status' register"
352      & register_array_message
353      & base_address_message
354      & "."
355    );
356    function get_message return string is
357    begin
358      if message = "" then
359        return base_message;
360      end if;
361
362      return base_message & " " & message & ".";
363    end function;
364
365    variable got : integer;
366  begin
367    read_counter_status(
368      net => net,
369      value => got,
370      base_address => base_address,
371      bus_handle => bus_handle
372    );
373
374    check_equal(got=>got, expected=>expected, msg=>get_message);
375  end procedure;
376
377  -- Check that the current value of the 'status' register
378  -- equals the given 'expected' value.
379  procedure check_counter_status_equal(
380    signal net : inout network_t;
381    expected : in counter_status_t;
382    base_address : in addr_t := (others => '0');
383    bus_handle : in bus_master_t := register_bus_master;
384    message : in string := ""
385  ) is
386    constant register_array_message : string := "";
387    function base_address_message return string is
388    begin
389      if base_address /= 0 then
390        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
391      end if;
392
393      return "";
394    end function;
395    constant base_message : string := (
396      "Checking the 'status' register"
397      & register_array_message
398      & base_address_message
399      & "."
400    );
401    function get_message return string is
402    begin
403      if message = "" then
404        return base_message;
405      end if;
406
407      return base_message & " " & message & ".";
408    end function;
409
410    variable got : counter_status_t;
411  begin
412    read_counter_status(
413      net => net,
414      value => got,
415      base_address => base_address,
416      bus_handle => bus_handle
417    );
418
419    if got /= expected then
420      failing_check(
421        checker => default_checker,
422        msg => p_std_msg(
423          check_result => "Equality check failed",
424          msg => get_message,
425          ctx => (
426            "Got " & to_string(to_slv(got)) & ". Expected " & to_string(to_slv(expected)) & "."
427          )
428        )
429      );
430    end if;
431  end procedure;
432
433  -- Check that the current value of the 'enabled' field in the 'status' register
434  -- equals the given 'expected' value.
435  procedure check_counter_status_enabled_equal(
436    signal net : inout network_t;
437    expected : in std_ulogic;
438    base_address : in addr_t := (others => '0');
439    bus_handle : in bus_master_t := register_bus_master;
440    message : in string := ""
441  ) is
442    constant register_array_message : string := "";
443    function base_address_message return string is
444    begin
445      if base_address /= 0 then
446        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
447      end if;
448
449      return "";
450    end function;
451    constant base_message : string := (
452      "Checking the 'enabled' field in the 'status' register"
453      & register_array_message
454      & base_address_message
455      & "."
456    );
457    function get_message return string is
458    begin
459      if message = "" then
460        return base_message;
461      end if;
462
463      return base_message & " " & message & ".";
464    end function;
465
466    variable got_reg : counter_status_t := counter_status_init;
467    variable got : std_ulogic := counter_status_enabled_init;
468  begin
469    read_counter_status(
470      net => net,
471      value => got_reg,
472      base_address => base_address,
473      bus_handle => bus_handle
474    );
475    got := got_reg.enabled;
476
477    check_equal(got=>got, expected=>expected, msg=>get_message);
478  end procedure;
479
480  -- Check that the current value of the 'pulse_count' field in the 'status' register
481  -- equals the given 'expected' value.
482  procedure check_counter_status_pulse_count_equal(
483    signal net : inout network_t;
484    expected : in counter_status_pulse_count_t;
485    base_address : in addr_t := (others => '0');
486    bus_handle : in bus_master_t := register_bus_master;
487    message : in string := ""
488  ) is
489    constant register_array_message : string := "";
490    function base_address_message return string is
491    begin
492      if base_address /= 0 then
493        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
494      end if;
495
496      return "";
497    end function;
498    constant base_message : string := (
499      "Checking the 'pulse_count' field in the 'status' register"
500      & register_array_message
501      & base_address_message
502      & "."
503    );
504    function get_message return string is
505    begin
506      if message = "" then
507        return base_message;
508      end if;
509
510      return base_message & " " & message & ".";
511    end function;
512
513    variable got_reg : counter_status_t := counter_status_init;
514    variable got : counter_status_pulse_count_t := counter_status_pulse_count_init;
515  begin
516    read_counter_status(
517      net => net,
518      value => got_reg,
519      base_address => base_address,
520      bus_handle => bus_handle
521    );
522    got := got_reg.pulse_count;
523
524    check_equal(got=>got, expected=>expected, msg=>get_message);
525  end procedure;
526  -- ---------------------------------------------------------------------------
527
528end 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 7.0.2-dev.
  3-- Code generator VhdlSimulationWaitUntilPackageGenerator version 1.0.0.
  4-- Generated 2025-01-21 20:52 from file regs_counter.toml at commit 3c3e6c67d817.
  5-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  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 common;
 21use common.addr_pkg.addr_t;
 22use common.addr_pkg.addr_width;
 23
 24library register_file;
 25use register_file.register_file_pkg.register_t;
 26use register_file.register_operations_pkg.register_bus_master;
 27
 28use work.counter_regs_pkg.all;
 29use work.counter_register_record_pkg.all;
 30
 31
 32package counter_register_wait_until_pkg is
 33
 34  -- ---------------------------------------------------------------------------
 35  -- Wait until the 'config' register equals the given 'value'.
 36  procedure wait_until_counter_config_equals(
 37    signal net : inout network_t;
 38    value : in counter_config_t;
 39    base_address : in addr_t := (others => '0');
 40    bus_handle : in bus_master_t := register_bus_master;
 41    timeout : delay_length := max_timeout;
 42    message : string := ""
 43  );
 44
 45  -- Wait until the 'condition' field in the 'config' register equals the given 'value'.
 46  procedure wait_until_counter_config_condition_equals(
 47    signal net : inout network_t;
 48    value : in counter_config_condition_t;
 49    base_address : in addr_t := (others => '0');
 50    bus_handle : in bus_master_t := register_bus_master;
 51    timeout : delay_length := max_timeout;
 52    message : string := ""
 53  );
 54
 55  -- Wait until the 'increment' field in the 'config' register equals the given 'value'.
 56  procedure wait_until_counter_config_increment_equals(
 57    signal net : inout network_t;
 58    value : in counter_config_increment_t;
 59    base_address : in addr_t := (others => '0');
 60    bus_handle : in bus_master_t := register_bus_master;
 61    timeout : delay_length := max_timeout;
 62    message : string := ""
 63  );
 64  -- ---------------------------------------------------------------------------
 65
 66  -- ---------------------------------------------------------------------------
 67  -- Wait until the 'status' register equals the given 'value'.
 68  procedure wait_until_counter_status_equals(
 69    signal net : inout network_t;
 70    value : in counter_status_t;
 71    base_address : in addr_t := (others => '0');
 72    bus_handle : in bus_master_t := register_bus_master;
 73    timeout : delay_length := max_timeout;
 74    message : string := ""
 75  );
 76
 77  -- Wait until the 'enabled' field in the 'status' register equals the given 'value'.
 78  procedure wait_until_counter_status_enabled_equals(
 79    signal net : inout network_t;
 80    value : in std_ulogic;
 81    base_address : in addr_t := (others => '0');
 82    bus_handle : in bus_master_t := register_bus_master;
 83    timeout : delay_length := max_timeout;
 84    message : string := ""
 85  );
 86
 87  -- Wait until the 'pulse_count' field in the 'status' register equals the given 'value'.
 88  procedure wait_until_counter_status_pulse_count_equals(
 89    signal net : inout network_t;
 90    value : in counter_status_pulse_count_t;
 91    base_address : in addr_t := (others => '0');
 92    bus_handle : in bus_master_t := register_bus_master;
 93    timeout : delay_length := max_timeout;
 94    message : string := ""
 95  );
 96  -- ---------------------------------------------------------------------------
 97
 98end package;
 99
100package body counter_register_wait_until_pkg is
101
102  -- ---------------------------------------------------------------------------
103  -- Wait until the 'config' register equals the given 'value'.
104  procedure wait_until_counter_config_equals(
105    signal net : inout network_t;
106    value : in counter_config_t;
107    base_address : in addr_t := (others => '0');
108    bus_handle : in bus_master_t := register_bus_master;
109    timeout : delay_length := max_timeout;
110    message : string := ""
111  ) is
112    constant reg_value : register_t := to_slv(value);
113
114    constant reg_index : counter_register_range := counter_config;
115    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
116
117    constant register_array_message : string := "";
118    function base_address_message return string is
119    begin
120      if base_address /= 0 then
121        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
122      end if;
123
124      return "";
125    end function;
126    constant base_message : string := (
127      "Timeout while waiting for the 'config' register"
128      & register_array_message
129      & base_address_message
130      & " to equal the given value: "
131      & to_string(reg_value)
132      & "."
133    );
134    function get_message return string is
135    begin
136      if message = "" then
137        return base_message;
138      end if;
139
140      return base_message & " " & message & ".";
141    end function;
142  begin
143    wait_until_read_equals(
144      net => net,
145      bus_handle => bus_handle,
146      addr => std_ulogic_vector(reg_address),
147      value => reg_value,
148      timeout => timeout,
149      msg => get_message
150    );
151  end procedure;
152
153  -- Wait until the 'condition' field in the 'config' register equals the given 'value'.
154  procedure wait_until_counter_config_condition_equals(
155    signal net : inout network_t;
156    value : in counter_config_condition_t;
157    base_address : in addr_t := (others => '0');
158    bus_handle : in bus_master_t := register_bus_master;
159    timeout : delay_length := max_timeout;
160    message : string := ""
161  ) is
162    constant reg_value : register_t := (
163      counter_config_condition => to_slv(value),
164      others => '-'
165    );
166
167    constant reg_index : counter_register_range := counter_config;
168    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
169
170    constant register_array_message : string := "";
171    function base_address_message return string is
172    begin
173      if base_address /= 0 then
174        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
175      end if;
176
177      return "";
178    end function;
179    constant base_message : string := (
180      "Timeout while waiting for the 'condition' field in the 'config' register"
181      & register_array_message
182      & base_address_message
183      & " to equal the given value: "
184      & to_string(reg_value)
185      & "."
186    );
187    function get_message return string is
188    begin
189      if message = "" then
190        return base_message;
191      end if;
192
193      return base_message & " " & message & ".";
194    end function;
195  begin
196    wait_until_read_equals(
197      net => net,
198      bus_handle => bus_handle,
199      addr => std_ulogic_vector(reg_address),
200      value => reg_value,
201      timeout => timeout,
202      msg => get_message
203    );
204  end procedure;
205
206  -- Wait until the 'increment' field in the 'config' register equals the given 'value'.
207  procedure wait_until_counter_config_increment_equals(
208    signal net : inout network_t;
209    value : in counter_config_increment_t;
210    base_address : in addr_t := (others => '0');
211    bus_handle : in bus_master_t := register_bus_master;
212    timeout : delay_length := max_timeout;
213    message : string := ""
214  ) is
215    constant reg_value : register_t := (
216      counter_config_increment => to_counter_config_increment_slv(value),
217      others => '-'
218    );
219
220    constant reg_index : counter_register_range := counter_config;
221    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
222
223    constant register_array_message : string := "";
224    function base_address_message return string is
225    begin
226      if base_address /= 0 then
227        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
228      end if;
229
230      return "";
231    end function;
232    constant base_message : string := (
233      "Timeout while waiting for the 'increment' field in the 'config' register"
234      & register_array_message
235      & base_address_message
236      & " to equal the given value: "
237      & to_string(reg_value)
238      & "."
239    );
240    function get_message return string is
241    begin
242      if message = "" then
243        return base_message;
244      end if;
245
246      return base_message & " " & message & ".";
247    end function;
248  begin
249    wait_until_read_equals(
250      net => net,
251      bus_handle => bus_handle,
252      addr => std_ulogic_vector(reg_address),
253      value => reg_value,
254      timeout => timeout,
255      msg => get_message
256    );
257  end procedure;
258  -- ---------------------------------------------------------------------------
259
260  -- ---------------------------------------------------------------------------
261  -- Wait until the 'status' register equals the given 'value'.
262  procedure wait_until_counter_status_equals(
263    signal net : inout network_t;
264    value : in counter_status_t;
265    base_address : in addr_t := (others => '0');
266    bus_handle : in bus_master_t := register_bus_master;
267    timeout : delay_length := max_timeout;
268    message : string := ""
269  ) is
270    constant reg_value : register_t := to_slv(value);
271
272    constant reg_index : counter_register_range := counter_status;
273    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
274
275    constant register_array_message : string := "";
276    function base_address_message return string is
277    begin
278      if base_address /= 0 then
279        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
280      end if;
281
282      return "";
283    end function;
284    constant base_message : string := (
285      "Timeout while waiting for the 'status' register"
286      & register_array_message
287      & base_address_message
288      & " to equal the given value: "
289      & to_string(reg_value)
290      & "."
291    );
292    function get_message return string is
293    begin
294      if message = "" then
295        return base_message;
296      end if;
297
298      return base_message & " " & message & ".";
299    end function;
300  begin
301    wait_until_read_equals(
302      net => net,
303      bus_handle => bus_handle,
304      addr => std_ulogic_vector(reg_address),
305      value => reg_value,
306      timeout => timeout,
307      msg => get_message
308    );
309  end procedure;
310
311  -- Wait until the 'enabled' field in the 'status' register equals the given 'value'.
312  procedure wait_until_counter_status_enabled_equals(
313    signal net : inout network_t;
314    value : in std_ulogic;
315    base_address : in addr_t := (others => '0');
316    bus_handle : in bus_master_t := register_bus_master;
317    timeout : delay_length := max_timeout;
318    message : string := ""
319  ) is
320    constant reg_value : register_t := (
321      counter_status_enabled => value,
322      others => '-'
323    );
324
325    constant reg_index : counter_register_range := counter_status;
326    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
327
328    constant register_array_message : string := "";
329    function base_address_message return string is
330    begin
331      if base_address /= 0 then
332        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
333      end if;
334
335      return "";
336    end function;
337    constant base_message : string := (
338      "Timeout while waiting for the 'enabled' field in the 'status' register"
339      & register_array_message
340      & base_address_message
341      & " to equal the given value: "
342      & to_string(reg_value)
343      & "."
344    );
345    function get_message return string is
346    begin
347      if message = "" then
348        return base_message;
349      end if;
350
351      return base_message & " " & message & ".";
352    end function;
353  begin
354    wait_until_read_equals(
355      net => net,
356      bus_handle => bus_handle,
357      addr => std_ulogic_vector(reg_address),
358      value => reg_value,
359      timeout => timeout,
360      msg => get_message
361    );
362  end procedure;
363
364  -- Wait until the 'pulse_count' field in the 'status' register equals the given 'value'.
365  procedure wait_until_counter_status_pulse_count_equals(
366    signal net : inout network_t;
367    value : in counter_status_pulse_count_t;
368    base_address : in addr_t := (others => '0');
369    bus_handle : in bus_master_t := register_bus_master;
370    timeout : delay_length := max_timeout;
371    message : string := ""
372  ) is
373    constant reg_value : register_t := (
374      counter_status_pulse_count => to_counter_status_pulse_count_slv(value),
375      others => '-'
376    );
377
378    constant reg_index : counter_register_range := counter_status;
379    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
380
381    constant register_array_message : string := "";
382    function base_address_message return string is
383    begin
384      if base_address /= 0 then
385        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
386      end if;
387
388      return "";
389    end function;
390    constant base_message : string := (
391      "Timeout while waiting for the 'pulse_count' field in the 'status' register"
392      & register_array_message
393      & base_address_message
394      & " to equal the given value: "
395      & to_string(reg_value)
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  begin
407    wait_until_read_equals(
408      net => net,
409      bus_handle => bus_handle,
410      addr => std_ulogic_vector(reg_address),
411      value => reg_value,
412      timeout => timeout,
413      msg => get_message
414    );
415  end procedure;
416  -- ---------------------------------------------------------------------------
417
418end 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

Most of the generated code depends on VHDL packages from hdl-modules version 4.0.0 or greater.

The VhdlRegisterPackageGenerator and VhdlRecordPackageGenerator packages depend on register_file_pkg.vhd. Can be downloaded from GitHub here: https://github.com/hdl-modules/hdl-modules/blob/main/modules/register_file/src/register_file_pkg.vhd

The VhdlSimulationReadWritePackageGenerator and VhdlSimulationWaitUntilPackageGenerator packages furthermore depend on register_operations_pkg.vhd and addr_pkg.vhd.

The VhdlAxiLiteWrapperGenerator package also depends on axi_lite_pkg.vhd.

Unresolved types

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

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

Further tools for simplifying register handling

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

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