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_reg_file_inst : entity work.counter_reg_file
 41    port map (
 42      clk => clk,
 43      --
 44      axi_lite_m2s => regs_m2s,
 45      axi_lite_s2m => regs_s2m,
 46      --
 47      regs_up => regs_up,
 48      regs_down => regs_down,
 49      --
 50      reg_was_read => open,
 51      reg_was_written => reg_was_written
 52    );
 53
 54
 55  ------------------------------------------------------------------------------
 56  set_status : process
 57  begin
 58    wait until rising_edge(clk);
 59
 60    if regs_down.command.start then
 61      regs_up.status.enabled <= '1';
 62    end if;
 63
 64    if regs_down.command.stop then
 65      regs_up.status.enabled <= '0';
 66    end if;
 67
 68    if reg_was_written.config then
 69      -- Clear value when configuration is changed.
 70      regs_up.status.pulse_count <= 0;
 71    else
 72      regs_up.status.pulse_count <= regs_up.status.pulse_count + to_int(pulse);
 73    end if;
 74  end process;
 75
 76
 77  ------------------------------------------------------------------------------
 78  count_block : block
 79    constant counter_width : positive := 10;
 80    constant counter_activate_value : positive := 2 ** counter_width - 1;
 81    -- One bit extra to avoid overflow.
 82    constant counter_max_value : positive := 2 ** (counter_width + 1) - 1;
 83
 84    signal count : natural range 0 to counter_max_value := 0;
 85
 86    signal clock_enable_p1 : std_ulogic := '0';
 87  begin
 88
 89    ------------------------------------------------------------------------------
 90    count_events : process
 91    begin
 92      wait until rising_edge(clk);
 93
 94      pulse <= '0';
 95
 96      case regs_down.config.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 reg_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-- This file is automatically generated by hdl-registers version 6.1.1-dev.
  2-- Code generator VhdlRegisterPackageGenerator version 1.0.0.
  3-- Generated 2024-11-20 20:51 from file regs_counter.toml at commit 547d42cf1aaa5f86.
  4-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  5
  6library ieee;
  7use ieee.std_logic_1164.all;
  8use ieee.numeric_std.all;
  9use ieee.fixed_pkg.all;
 10
 11library reg_file;
 12use reg_file.reg_file_pkg.all;
 13
 14
 15package counter_regs_pkg is
 16
 17  -- ---------------------------------------------------------------------------
 18  -- The valid range of register indexes.
 19  subtype counter_reg_range is natural range 0 to 2;
 20
 21  -- Register indexes, within the list of registers.
 22  constant counter_config : natural := 0;
 23  constant counter_command : natural := 1;
 24  constant counter_status : natural := 2;
 25
 26  -- Declare 'reg_map' and 'regs_init' constants here but define them in body (deferred constants).
 27  -- So that functions have been elaborated when they are called.
 28  -- Needed for ModelSim compilation to pass.
 29
 30  -- To be used as the 'regs' generic of 'axi_lite_reg_file.vhd'.
 31  constant counter_reg_map : reg_definition_vec_t(counter_reg_range);
 32
 33  -- To be used for the 'regs_up' and 'regs_down' ports of 'axi_lite_reg_file.vhd'.
 34  subtype counter_regs_t is reg_vec_t(counter_reg_range);
 35  -- To be used as the 'default_values' generic of 'axi_lite_reg_file.vhd'.
 36  constant counter_regs_init : counter_regs_t;
 37
 38  -- To be used for the 'reg_was_read' and 'reg_was_written' ports of 'axi_lite_reg_file.vhd'.
 39  subtype counter_reg_was_accessed_t is std_ulogic_vector(counter_reg_range);
 40
 41  -- -----------------------------------------------------------------------------
 42  -- Fields in the 'config' register.
 43  -- Range of the 'condition' field.
 44  subtype counter_config_condition is natural range 1 downto 0;
 45  -- Width of the 'condition' field.
 46  constant counter_config_condition_width : positive := 2;
 47  -- Type for the 'condition' field.
 48  type counter_config_condition_t is (
 49    condition_clock_cycles,
 50    condition_clock_cycles_with_enable,
 51    condition_enable_edges
 52  );
 53  -- Default value of the 'condition' field.
 54  constant counter_config_condition_init : counter_config_condition_t := condition_clock_cycles;
 55  -- Type for the 'condition' field as an SLV.
 56  subtype counter_config_condition_slv_t is std_ulogic_vector(1 downto 0);
 57  -- Cast a 'condition' field value to SLV.
 58  function to_slv(data : counter_config_condition_t) return counter_config_condition_slv_t;
 59  -- Get a 'condition' field value from a register value.
 60  function to_counter_config_condition(data : reg_t) return counter_config_condition_t;
 61
 62  -- Range of the 'increment' field.
 63  subtype counter_config_increment is natural range 5 downto 2;
 64  -- Width of the 'increment' field.
 65  constant counter_config_increment_width : positive := 4;
 66  -- Type for the 'increment' field.
 67  subtype counter_config_increment_t is integer range 0 to 15;
 68  -- Default value of the 'increment' field.
 69  constant counter_config_increment_init : counter_config_increment_t := 0;
 70  -- Type for the 'increment' field as an SLV.
 71  subtype counter_config_increment_slv_t is std_ulogic_vector(3 downto 0);
 72  -- Cast a 'increment' field value to SLV.
 73  function to_counter_config_increment_slv(data : counter_config_increment_t) return counter_config_increment_slv_t;
 74  -- Get a 'increment' field value from a register value.
 75  function to_counter_config_increment(data : reg_t) return counter_config_increment_t;
 76
 77  -- -----------------------------------------------------------------------------
 78  -- Fields in the 'command' register.
 79  -- Range of the 'start' field.
 80  constant counter_command_start : natural := 0;
 81  -- Default value of the 'start' field.
 82  constant counter_command_start_init : std_ulogic := '0';
 83
 84  -- Range of the 'stop' field.
 85  constant counter_command_stop : natural := 1;
 86  -- Default value of the 'stop' field.
 87  constant counter_command_stop_init : std_ulogic := '0';
 88
 89  -- -----------------------------------------------------------------------------
 90  -- Fields in the 'status' register.
 91  -- Range of the 'enabled' field.
 92  constant counter_status_enabled : natural := 0;
 93  -- Default value of the 'enabled' field.
 94  constant counter_status_enabled_init : std_ulogic := '0';
 95
 96  -- Range of the 'pulse_count' field.
 97  subtype counter_status_pulse_count is natural range 8 downto 1;
 98  -- Width of the 'pulse_count' field.
 99  constant counter_status_pulse_count_width : positive := 8;
100  -- Type for the 'pulse_count' field.
101  subtype counter_status_pulse_count_t is integer range 0 to 255;
102  -- Default value of the 'pulse_count' field.
103  constant counter_status_pulse_count_init : counter_status_pulse_count_t := 0;
104  -- Type for the 'pulse_count' field as an SLV.
105  subtype counter_status_pulse_count_slv_t is std_ulogic_vector(7 downto 0);
106  -- Cast a 'pulse_count' field value to SLV.
107  function to_counter_status_pulse_count_slv(data : counter_status_pulse_count_t) return counter_status_pulse_count_slv_t;
108  -- Get a 'pulse_count' field value from a register value.
109  function to_counter_status_pulse_count(data : reg_t) return counter_status_pulse_count_t;
110
111end package;
112
113package body counter_regs_pkg is
114
115  constant counter_reg_map : reg_definition_vec_t(counter_reg_range) := (
116    0 => (idx => counter_config, reg_type => r_w),
117    1 => (idx => counter_command, reg_type => wpulse),
118    2 => (idx => counter_status, reg_type => r)
119  );
120
121  constant counter_regs_init : counter_regs_t := (
122    0 => "00000000000000000000000000000000",
123    1 => "00000000000000000000000000000000",
124    2 => "00000000000000000000000000000000"
125  );
126
127  -- Cast a 'condition' field value to SLV.
128  function to_slv(data : counter_config_condition_t) return counter_config_condition_slv_t is
129    constant data_int : natural := counter_config_condition_t'pos(data);
130    constant result : counter_config_condition_slv_t := std_ulogic_vector(
131      to_unsigned(data_int, counter_config_condition_width)
132    );
133  begin
134    return result;
135  end function;
136
137  -- Get a 'condition' field value from a register value.
138  function to_counter_config_condition(data : reg_t) return counter_config_condition_t is
139    constant field_slv : counter_config_condition_slv_t := data(counter_config_condition);
140    constant field_int : natural := to_integer(unsigned(field_slv));
141    constant result : counter_config_condition_t := counter_config_condition_t'val(field_int);
142  begin
143    return result;
144  end function;
145
146  -- Cast a 'increment' field value to SLV.
147  function to_counter_config_increment_slv(data : counter_config_increment_t) return counter_config_increment_slv_t is
148    constant result : counter_config_increment_slv_t := std_ulogic_vector(to_unsigned(data, counter_config_increment_width));
149  begin
150    return result;
151  end function;
152
153  -- Get a 'increment' field value from a register value.
154  function to_counter_config_increment(data : reg_t) return counter_config_increment_t is
155    constant result : integer := to_integer(unsigned(data(counter_config_increment)));
156  begin
157    return result;
158  end function;
159
160  -- Cast a 'pulse_count' field value to SLV.
161  function to_counter_status_pulse_count_slv(data : counter_status_pulse_count_t) return counter_status_pulse_count_slv_t is
162    constant result : counter_status_pulse_count_slv_t := std_ulogic_vector(to_unsigned(data, counter_status_pulse_count_width));
163  begin
164    return result;
165  end function;
166
167  -- Get a 'pulse_count' field value from a register value.
168  function to_counter_status_pulse_count(data : reg_t) return counter_status_pulse_count_t is
169    constant result : integer := to_integer(unsigned(data(counter_status_pulse_count)));
170  begin
171    return result;
172  end function;
173
174end 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-- This file is automatically generated by hdl-registers version 6.1.1-dev.
  2-- Code generator VhdlRecordPackageGenerator version 1.0.0.
  3-- Generated 2024-11-20 20:51 from file regs_counter.toml at commit 547d42cf1aaa5f86.
  4-- Register hash 49f02a8daa9dd4bc3dadeb2a1cfd61dcc38c3864.
  5
  6library ieee;
  7use ieee.fixed_pkg.all;
  8use ieee.std_logic_1164.all;
  9use ieee.numeric_std.all;
 10
 11library reg_file;
 12use reg_file.reg_file_pkg.reg_t;
 13
 14use work.counter_regs_pkg.all;
 15
 16
 17package counter_register_record_pkg is
 18
 19  -- -----------------------------------------------------------------------------
 20  -- Record with correctly-typed members for each field in each register.
 21  -- Fields in the 'config' register as a record.
 22  type counter_config_t is record
 23    condition : counter_config_condition_t;
 24    increment : counter_config_increment_t;
 25  end record;
 26  -- Default value for the 'config' register as a record.
 27  constant counter_config_init : counter_config_t := (
 28    condition => counter_config_condition_init,
 29    increment => counter_config_increment_init
 30  );
 31  -- Convert a record of the 'config' register to SLV.
 32  function to_slv(data : counter_config_t) return reg_t;
 33  -- Convert an SLV register value to the record for the 'config' register.
 34  function to_counter_config(data : reg_t) return counter_config_t;
 35
 36  -- Fields in the 'command' register as a record.
 37  type counter_command_t is record
 38    start : std_ulogic;
 39    stop : std_ulogic;
 40  end record;
 41  -- Default value for the 'command' register as a record.
 42  constant counter_command_init : counter_command_t := (
 43    start => counter_command_start_init,
 44    stop => counter_command_stop_init
 45  );
 46  -- Convert a record of the 'command' register to SLV.
 47  function to_slv(data : counter_command_t) return reg_t;
 48  -- Convert an SLV register value to the record for the 'command' register.
 49  function to_counter_command(data : reg_t) return counter_command_t;
 50
 51  -- Fields in the 'status' register as a record.
 52  type counter_status_t is record
 53    enabled : std_ulogic;
 54    pulse_count : counter_status_pulse_count_t;
 55  end record;
 56  -- Default value for the 'status' register as a record.
 57  constant counter_status_init : counter_status_t := (
 58    enabled => counter_status_enabled_init,
 59    pulse_count => counter_status_pulse_count_init
 60  );
 61  -- Convert a record of the 'status' register to SLV.
 62  function to_slv(data : counter_status_t) return reg_t;
 63  -- Convert an SLV register value to the record for the 'status' register.
 64  function to_counter_status(data : reg_t) return counter_status_t;
 65
 66  -- -----------------------------------------------------------------------------
 67  -- Below is a record with correctly typed and ranged members for all registers, register arrays
 68  -- and fields that are in the 'up' direction.
 69  -- Record with everything in the 'up' direction.
 70  type counter_regs_up_t is record
 71    status : counter_status_t;
 72  end record;
 73  -- Default value of the above record.
 74  constant counter_regs_up_init : counter_regs_up_t := (
 75    status => counter_status_init
 76  );
 77  -- Convert record with everything in the 'up' direction to SLV register list.
 78  function to_slv(data : counter_regs_up_t) return counter_regs_t;
 79
 80  -- -----------------------------------------------------------------------------
 81  -- Below is a record with correctly typed and ranged members for all registers, register arrays
 82  -- and fields that are in the 'down' direction.
 83  -- Record with everything in the 'down' direction.
 84  type counter_regs_down_t is record
 85    config : counter_config_t;
 86    command : counter_command_t;
 87  end record;
 88  -- Default value of the above record.
 89  constant counter_regs_down_init : counter_regs_down_t := (
 90    config => counter_config_init,
 91    command => counter_command_init
 92  );
 93  -- Convert SLV register list to record with everything in the 'down' direction.
 94  function to_counter_regs_down(data : counter_regs_t) return counter_regs_down_t;
 95
 96  -- ---------------------------------------------------------------------------
 97  -- Below is a record with a status bit for each readable register in the register map.
 98  -- It can be used for the 'reg_was_read' port of a register file wrapper.
 99  -- Combined status mask record for all readable register.
100  type counter_reg_was_read_t is record
101    config : std_ulogic;
102    status : std_ulogic;
103  end record;
104  -- Default value for the above record.
105  constant counter_reg_was_read_init : counter_reg_was_read_t := (
106    others => '0'
107  );
108  -- Convert an SLV 'reg_was_read' from generic register file to the record above.
109  function to_counter_reg_was_read(
110    data : counter_reg_was_accessed_t
111  ) return counter_reg_was_read_t;
112
113  -- ---------------------------------------------------------------------------
114  -- Below is a record with a status bit for each writeable register in the register map.
115  -- It can be used for the 'reg_was_written' port of a register file wrapper.
116  -- Combined status mask record for all writeable register.
117  type counter_reg_was_written_t is record
118    config : std_ulogic;
119    command : std_ulogic;
120  end record;
121  -- Default value for the above record.
122  constant counter_reg_was_written_init : counter_reg_was_written_t := (
123    others => '0'
124  );
125  -- Convert an SLV 'reg_was_written' from generic register file to the record above.
126  function to_counter_reg_was_written(
127    data : counter_reg_was_accessed_t
128  ) return counter_reg_was_written_t;
129
130end package;
131
132package body counter_register_record_pkg is
133
134  function to_slv(data : counter_config_t) return reg_t is
135    variable result : reg_t := (others => '-');
136  begin
137    result(counter_config_condition) := to_slv(data.condition);
138    result(counter_config_increment) := to_counter_config_increment_slv(data.increment);
139
140    return result;
141  end function;
142
143  function to_counter_config(data : reg_t) return counter_config_t is
144    variable result : counter_config_t := counter_config_init;
145  begin
146    result.condition := to_counter_config_condition(data);
147    result.increment := to_counter_config_increment(data);
148
149    return result;
150  end function;
151
152  function to_slv(data : counter_command_t) return reg_t is
153    variable result : reg_t := (others => '-');
154  begin
155    result(counter_command_start) := data.start;
156    result(counter_command_stop) := data.stop;
157
158    return result;
159  end function;
160
161  function to_counter_command(data : reg_t) return counter_command_t is
162    variable result : counter_command_t := counter_command_init;
163  begin
164    result.start := data(counter_command_start);
165    result.stop := data(counter_command_stop);
166
167    return result;
168  end function;
169
170  function to_slv(data : counter_status_t) return reg_t is
171    variable result : reg_t := (others => '-');
172  begin
173    result(counter_status_enabled) := data.enabled;
174    result(counter_status_pulse_count) := to_counter_status_pulse_count_slv(data.pulse_count);
175
176    return result;
177  end function;
178
179  function to_counter_status(data : reg_t) return counter_status_t is
180    variable result : counter_status_t := counter_status_init;
181  begin
182    result.enabled := data(counter_status_enabled);
183    result.pulse_count := to_counter_status_pulse_count(data);
184
185    return result;
186  end function;
187
188  function to_slv(data : counter_regs_up_t) return counter_regs_t is
189    variable result : counter_regs_t := counter_regs_init;
190  begin
191    result(counter_status) := to_slv(data.status);
192
193    return result;
194  end function;
195
196  function to_counter_regs_down(data : counter_regs_t) return counter_regs_down_t is
197    variable result : counter_regs_down_t := counter_regs_down_init;
198  begin
199    result.config := to_counter_config(data(counter_config));
200    result.command := to_counter_command(data(counter_command));
201
202    return result;
203  end function;
204
205  function to_counter_reg_was_read(
206    data : counter_reg_was_accessed_t
207  ) return counter_reg_was_read_t is
208    variable result : counter_reg_was_read_t := counter_reg_was_read_init;
209  begin
210    result.config := data(counter_config);
211    result.status := data(counter_status);
212
213    return result;
214  end function;
215
216  function to_counter_reg_was_written(
217    data : counter_reg_was_accessed_t
218  ) return counter_reg_was_written_t is
219    variable result : counter_reg_was_written_t := counter_reg_was_written_init;
220  begin
221    result.config := data(counter_config);
222    result.command := data(counter_command);
223
224    return result;
225  end function;
226
227end package body;

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

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

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

The VhdlAxiLiteWrapperGenerator package also depends on axi_lite_pkg.vhd.

Unresolved types

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

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

Further tools for simplifying register handling

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

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