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

Python file to generate register artifacts

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

Click to expand/collapse code.
Python code that parses the example TOML file and generates the VHDL code we need.
 1# Standard libraries
 2import sys
 3from pathlib import Path
 4
 5# First party libraries
 6from hdl_registers.generator.vhdl.axi_lite.wrapper import VhdlAxiLiteWrapperGenerator
 7from hdl_registers.generator.vhdl.record_package import VhdlRecordPackageGenerator
 8from hdl_registers.generator.vhdl.register_package import VhdlRegisterPackageGenerator
 9from hdl_registers.generator.vhdl.simulation.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. mode_clock_cycles) exposed.

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

Click to expand/collapse code.
Implementation of counter example.
  1-- -------------------------------------------------------------------------------------------------
  2-- Regularly send out a 'pulse', with a frequency that is configurable over the register bus.
  3-- -------------------------------------------------------------------------------------------------
  4
  5library ieee;
  6use ieee.std_logic_1164.all;
  7
  8library axi_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.mode is
 97        when mode_clock_cycles =>
 98          count <= count + regs_down.config.increment;
 99
100        when mode_clock_cycles_with_enable =>
101          if clock_enable then
102            count <= count + regs_down.config.increment;
103          end if;
104
105        when mode_enable_edges =>
106          if clock_enable /= clock_enable_p1 then
107            count <= count + regs_down.config.increment;
108          end if;
109      end case;
110
111      if count >= counter_activate_value then
112        count <= 0;
113        pulse <= regs_up.status.enabled;
114      end if;
115
116      clock_enable_p1 <= clock_enable;
117    end process;
118
119  end block;
120
121end architecture;

VHDL example testbench

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

Generated VHDL 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 5.2.1-dev.
 11-- Code generator VhdlAxiLiteWrapperGenerator version 1.0.0.
 12-- Generated 2024-05-21 20:51 from file regs_counter.toml at commit 2f4170d05b13abac.
 13-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
 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 5.2.1-dev.
  2-- Code generator VhdlSimulationReadWritePackageGenerator version 1.0.0.
  3-- Generated 2024-05-21 20:51 from file regs_counter.toml at commit 2f4170d05b13abac.
  4-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
  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.
 33  procedure read_counter_config(
 34    signal net : inout network_t;
 35    value : out counter_config_t;
 36    base_address : in addr_t := (others => '0');
 37    bus_handle : in bus_master_t := regs_bus_master
 38  );
 39
 40  -- Read the 'mode' field in the 'config' register.
 41  procedure read_counter_config_mode(
 42    signal net : inout network_t;
 43    value : out counter_config_mode_t;
 44    base_address : in addr_t := (others => '0');
 45    bus_handle : in bus_master_t := regs_bus_master
 46  );
 47
 48  -- Read the 'increment' field in the 'config' register.
 49  procedure read_counter_config_increment(
 50    signal net : inout network_t;
 51    value : out counter_config_increment_t;
 52    base_address : in addr_t := (others => '0');
 53    bus_handle : in bus_master_t := regs_bus_master
 54  );
 55
 56  -- Write the 'config' register.
 57  procedure write_counter_config(
 58    signal net : inout network_t;
 59    value : in counter_config_t;
 60    base_address : in addr_t := (others => '0');
 61    bus_handle : in bus_master_t := regs_bus_master
 62  );
 63
 64  -- Write the 'mode' field in the 'config' register.
 65  -- Will read-modify-write the register to set the field to the supplied 'value'.
 66  procedure write_counter_config_mode(
 67    signal net : inout network_t;
 68    value : in counter_config_mode_t;
 69    base_address : in addr_t := (others => '0');
 70    bus_handle : in bus_master_t := regs_bus_master
 71  );
 72
 73  -- Write the 'increment' field in the 'config' register.
 74  -- Will read-modify-write the register to set the field to the supplied 'value'.
 75  procedure write_counter_config_increment(
 76    signal net : inout network_t;
 77    value : in counter_config_increment_t;
 78    base_address : in addr_t := (others => '0');
 79    bus_handle : in bus_master_t := regs_bus_master
 80  );
 81  -- ---------------------------------------------------------------------------
 82
 83  -- ---------------------------------------------------------------------------
 84  -- Write the 'command' register.
 85  procedure write_counter_command(
 86    signal net : inout network_t;
 87    value : in counter_command_t;
 88    base_address : in addr_t := (others => '0');
 89    bus_handle : in bus_master_t := regs_bus_master
 90  );
 91
 92  -- Write the 'start' field in the 'command' register.
 93  -- Will write the whole register, with the field set to the 
 94  -- supplied 'value' and everything else set to default.
 95  procedure write_counter_command_start(
 96    signal net : inout network_t;
 97    value : in std_ulogic;
 98    base_address : in addr_t := (others => '0');
 99    bus_handle : in bus_master_t := regs_bus_master
100  );
101
102  -- Write the 'stop' field in the 'command' register.
103  -- Will write the whole register, with the field set to the 
104  -- supplied 'value' and everything else set to default.
105  procedure write_counter_command_stop(
106    signal net : inout network_t;
107    value : in std_ulogic;
108    base_address : in addr_t := (others => '0');
109    bus_handle : in bus_master_t := regs_bus_master
110  );
111  -- ---------------------------------------------------------------------------
112
113  -- ---------------------------------------------------------------------------
114  -- Read the 'status' register.
115  procedure read_counter_status(
116    signal net : inout network_t;
117    value : out counter_status_t;
118    base_address : in addr_t := (others => '0');
119    bus_handle : in bus_master_t := regs_bus_master
120  );
121
122  -- Read the 'enabled' field in the 'status' register.
123  procedure read_counter_status_enabled(
124    signal net : inout network_t;
125    value : out std_ulogic;
126    base_address : in addr_t := (others => '0');
127    bus_handle : in bus_master_t := regs_bus_master
128  );
129
130  -- Read the 'pulse_count' field in the 'status' register.
131  procedure read_counter_status_pulse_count(
132    signal net : inout network_t;
133    value : out counter_status_pulse_count_t;
134    base_address : in addr_t := (others => '0');
135    bus_handle : in bus_master_t := regs_bus_master
136  );
137  -- ---------------------------------------------------------------------------
138
139end package;
140
141package body counter_register_read_write_pkg is
142
143  -- ---------------------------------------------------------------------------
144  -- Read the 'config' register.
145  procedure read_counter_config(
146    signal net : inout network_t;
147    value : out counter_config_t;
148    base_address : in addr_t := (others => '0');
149    bus_handle : in bus_master_t := regs_bus_master
150  ) is
151    constant reg_index : counter_reg_range := counter_config;
152    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
153    variable reg_value : reg_t := (others => '0');
154  begin
155    read_bus(
156      net => net,
157      bus_handle => bus_handle,
158      address => std_logic_vector(reg_address),
159      data => reg_value
160    );
161    value := to_counter_config(reg_value);
162  end procedure;
163
164  -- Read the 'mode' field in the 'config' register.
165  procedure read_counter_config_mode(
166    signal net : inout network_t;
167    value : out counter_config_mode_t;
168    base_address : in addr_t := (others => '0');
169    bus_handle : in bus_master_t := regs_bus_master
170  ) is
171    variable reg_value : counter_config_t := counter_config_init;
172  begin
173    read_counter_config(
174      net => net,
175      value => reg_value,
176      base_address => base_address,
177      bus_handle => bus_handle
178    );
179    value := reg_value.mode;
180  end procedure;
181
182  -- Read the 'increment' field in the 'config' register.
183  procedure read_counter_config_increment(
184    signal net : inout network_t;
185    value : out counter_config_increment_t;
186    base_address : in addr_t := (others => '0');
187    bus_handle : in bus_master_t := regs_bus_master
188  ) is
189    variable reg_value : counter_config_t := counter_config_init;
190  begin
191    read_counter_config(
192      net => net,
193      value => reg_value,
194      base_address => base_address,
195      bus_handle => bus_handle
196    );
197    value := reg_value.increment;
198  end procedure;
199
200  -- Write the 'config' register.
201  procedure write_counter_config(
202    signal net : inout network_t;
203    value : in counter_config_t;
204    base_address : in addr_t := (others => '0');
205    bus_handle : in bus_master_t := regs_bus_master
206  ) is
207    constant reg_index : counter_reg_range := counter_config;
208    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
209    constant reg_value : reg_t := to_slv(value);
210  begin
211    write_bus(
212      net => net,
213      bus_handle => bus_handle,
214      address => std_logic_vector(reg_address),
215      data => reg_value
216    );
217  end procedure;
218
219  -- Write the 'mode' field in the 'config' register.
220  -- Will read-modify-write the register to set the field to the supplied 'value'.
221  procedure write_counter_config_mode(
222    signal net : inout network_t;
223    value : in counter_config_mode_t;
224    base_address : in addr_t := (others => '0');
225    bus_handle : in bus_master_t := regs_bus_master
226  ) is
227    variable reg_value : counter_config_t := counter_config_init;
228  begin
229    read_counter_config(
230      net => net,
231      value => reg_value,
232      base_address => base_address,
233      bus_handle => bus_handle
234    );
235    reg_value.mode := value;
236
237    write_counter_config(
238      net => net,
239      value => reg_value,
240      base_address => base_address,
241      bus_handle => bus_handle
242    );
243  end procedure;
244
245  -- Write the 'increment' field in the 'config' register.
246  -- Will read-modify-write the register to set the field to the supplied 'value'.
247  procedure write_counter_config_increment(
248    signal net : inout network_t;
249    value : in counter_config_increment_t;
250    base_address : in addr_t := (others => '0');
251    bus_handle : in bus_master_t := regs_bus_master
252  ) is
253    variable reg_value : counter_config_t := counter_config_init;
254  begin
255    read_counter_config(
256      net => net,
257      value => reg_value,
258      base_address => base_address,
259      bus_handle => bus_handle
260    );
261    reg_value.increment := value;
262
263    write_counter_config(
264      net => net,
265      value => reg_value,
266      base_address => base_address,
267      bus_handle => bus_handle
268    );
269  end procedure;
270  -- ---------------------------------------------------------------------------
271
272  -- ---------------------------------------------------------------------------
273  -- Write the 'command' register.
274  procedure write_counter_command(
275    signal net : inout network_t;
276    value : in counter_command_t;
277    base_address : in addr_t := (others => '0');
278    bus_handle : in bus_master_t := regs_bus_master
279  ) is
280    constant reg_index : counter_reg_range := counter_command;
281    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
282    constant reg_value : reg_t := to_slv(value);
283  begin
284    write_bus(
285      net => net,
286      bus_handle => bus_handle,
287      address => std_logic_vector(reg_address),
288      data => reg_value
289    );
290  end procedure;
291
292  -- Write the 'start' field in the 'command' register.
293  -- Will write the whole register, with the field set to the 
294  -- supplied 'value' and everything else set to default.
295  procedure write_counter_command_start(
296    signal net : inout network_t;
297    value : in std_ulogic;
298    base_address : in addr_t := (others => '0');
299    bus_handle : in bus_master_t := regs_bus_master
300  ) is
301    variable reg_value : counter_command_t := counter_command_init;
302  begin
303    reg_value.start := value;
304
305    write_counter_command(
306      net => net,
307      value => reg_value,
308      base_address => base_address,
309      bus_handle => bus_handle
310    );
311  end procedure;
312
313  -- Write the 'stop' field in the 'command' register.
314  -- Will write the whole register, with the field set to the 
315  -- supplied 'value' and everything else set to default.
316  procedure write_counter_command_stop(
317    signal net : inout network_t;
318    value : in std_ulogic;
319    base_address : in addr_t := (others => '0');
320    bus_handle : in bus_master_t := regs_bus_master
321  ) is
322    variable reg_value : counter_command_t := counter_command_init;
323  begin
324    reg_value.stop := value;
325
326    write_counter_command(
327      net => net,
328      value => reg_value,
329      base_address => base_address,
330      bus_handle => bus_handle
331    );
332  end procedure;
333  -- ---------------------------------------------------------------------------
334
335  -- ---------------------------------------------------------------------------
336  -- Read the 'status' register.
337  procedure read_counter_status(
338    signal net : inout network_t;
339    value : out counter_status_t;
340    base_address : in addr_t := (others => '0');
341    bus_handle : in bus_master_t := regs_bus_master
342  ) is
343    constant reg_index : counter_reg_range := counter_status;
344    constant reg_address : addr_t := base_address + to_unsigned(4 * reg_index, addr_width);
345    variable reg_value : reg_t := (others => '0');
346  begin
347    read_bus(
348      net => net,
349      bus_handle => bus_handle,
350      address => std_logic_vector(reg_address),
351      data => reg_value
352    );
353    value := to_counter_status(reg_value);
354  end procedure;
355
356  -- Read the 'enabled' field in the 'status' register.
357  procedure read_counter_status_enabled(
358    signal net : inout network_t;
359    value : out std_ulogic;
360    base_address : in addr_t := (others => '0');
361    bus_handle : in bus_master_t := regs_bus_master
362  ) is
363    variable reg_value : counter_status_t := counter_status_init;
364  begin
365    read_counter_status(
366      net => net,
367      value => reg_value,
368      base_address => base_address,
369      bus_handle => bus_handle
370    );
371    value := reg_value.enabled;
372  end procedure;
373
374  -- Read the 'pulse_count' field in the 'status' register.
375  procedure read_counter_status_pulse_count(
376    signal net : inout network_t;
377    value : out counter_status_pulse_count_t;
378    base_address : in addr_t := (others => '0');
379    bus_handle : in bus_master_t := regs_bus_master
380  ) is
381    variable reg_value : counter_status_t := counter_status_init;
382  begin
383    read_counter_status(
384      net => net,
385      value => reg_value,
386      base_address => base_address,
387      bus_handle => bus_handle
388    );
389    value := reg_value.pulse_count;
390  end procedure;
391  -- ---------------------------------------------------------------------------
392
393end 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 5.2.1-dev.
  2-- Code generator VhdlSimulationCheckPackageGenerator version 1.0.0.
  3-- Generated 2024-05-21 20:51 from file regs_counter.toml at commit 2f4170d05b13abac.
  4-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
  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_operations_pkg.regs_bus_master;
 23
 24use work.counter_register_read_write_pkg.all;
 25use work.counter_register_record_pkg.all;
 26use work.counter_regs_pkg.all;
 27
 28
 29package counter_register_check_pkg is
 30
 31  -- ---------------------------------------------------------------------------
 32  -- Check that the current value of the 'mode' field in the 'config' register
 33  -- equals the given 'expected' value.
 34  procedure check_counter_config_mode_equal(
 35    signal net : inout network_t;
 36    expected : in counter_config_mode_t;
 37    base_address : in addr_t := (others => '0');
 38    bus_handle : in bus_master_t := regs_bus_master;
 39    message : in string := ""
 40  );
 41
 42  -- Check that the current value of the 'increment' field in the 'config' register
 43  -- equals the given 'expected' value.
 44  procedure check_counter_config_increment_equal(
 45    signal net : inout network_t;
 46    expected : in counter_config_increment_t;
 47    base_address : in addr_t := (others => '0');
 48    bus_handle : in bus_master_t := regs_bus_master;
 49    message : in string := ""
 50  );
 51  -- ---------------------------------------------------------------------------
 52
 53  -- ---------------------------------------------------------------------------
 54  -- Check that the current value of the 'enabled' field in the 'status' register
 55  -- equals the given 'expected' value.
 56  procedure check_counter_status_enabled_equal(
 57    signal net : inout network_t;
 58    expected : in std_ulogic;
 59    base_address : in addr_t := (others => '0');
 60    bus_handle : in bus_master_t := regs_bus_master;
 61    message : in string := ""
 62  );
 63
 64  -- Check that the current value of the 'pulse_count' field in the 'status' register
 65  -- equals the given 'expected' value.
 66  procedure check_counter_status_pulse_count_equal(
 67    signal net : inout network_t;
 68    expected : in counter_status_pulse_count_t;
 69    base_address : in addr_t := (others => '0');
 70    bus_handle : in bus_master_t := regs_bus_master;
 71    message : in string := ""
 72  );
 73  -- ---------------------------------------------------------------------------
 74
 75end package;
 76
 77package body counter_register_check_pkg is
 78
 79  -- ---------------------------------------------------------------------------
 80  -- Check that the current value of the 'mode' field in the 'config' register
 81  -- equals the given 'expected' value.
 82  procedure check_counter_config_mode_equal(
 83    signal net : inout network_t;
 84    expected : in counter_config_mode_t;
 85    base_address : in addr_t := (others => '0');
 86    bus_handle : in bus_master_t := regs_bus_master;
 87    message : in string := ""
 88  ) is
 89    constant register_array_message : string := "";
 90    function base_address_message return string is
 91    begin
 92      if base_address /= 0 then
 93        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
 94      end if;
 95
 96      return "";
 97    end function;
 98    constant base_message : string := (
 99      "Checking the 'mode' field in the 'config' register"
100      & register_array_message
101      & base_address_message
102      & "."
103    );
104    function get_message return string is
105    begin
106      if message = "" then
107        return base_message;
108      end if;
109
110      return base_message & " " & message & ".";
111    end function;
112
113    variable got_reg : counter_config_t := counter_config_init;
114    variable got : counter_config_mode_t := counter_config_mode_init;
115  begin
116    read_counter_config(
117      net => net,
118      value => got_reg,
119      base_address => base_address,
120      bus_handle => bus_handle
121    );
122    got := got_reg.mode;
123
124    if got /= expected then
125      failing_check(
126        checker => default_checker,
127        msg => p_std_msg(
128          check_result => "Equality check failed",
129          msg => get_message,
130          ctx => (
131            "Got " & to_string(got) & "."
132            & " Expected " & to_string(expected) & "."
133          )
134        )
135      );
136    end if;
137  end procedure;
138
139  -- Check that the current value of the 'increment' field in the 'config' register
140  -- equals the given 'expected' value.
141  procedure check_counter_config_increment_equal(
142    signal net : inout network_t;
143    expected : in counter_config_increment_t;
144    base_address : in addr_t := (others => '0');
145    bus_handle : in bus_master_t := regs_bus_master;
146    message : in string := ""
147  ) is
148    constant register_array_message : string := "";
149    function base_address_message return string is
150    begin
151      if base_address /= 0 then
152        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
153      end if;
154
155      return "";
156    end function;
157    constant base_message : string := (
158      "Checking the 'increment' field in the 'config' register"
159      & register_array_message
160      & base_address_message
161      & "."
162    );
163    function get_message return string is
164    begin
165      if message = "" then
166        return base_message;
167      end if;
168
169      return base_message & " " & message & ".";
170    end function;
171
172    variable got_reg : counter_config_t := counter_config_init;
173    variable got : counter_config_increment_t := counter_config_increment_init;
174  begin
175    read_counter_config(
176      net => net,
177      value => got_reg,
178      base_address => base_address,
179      bus_handle => bus_handle
180    );
181    got := got_reg.increment;
182
183    check_equal(got=>got, expected=>expected, msg=>get_message);
184  end procedure;
185  -- ---------------------------------------------------------------------------
186
187  -- ---------------------------------------------------------------------------
188  -- Check that the current value of the 'enabled' field in the 'status' register
189  -- equals the given 'expected' value.
190  procedure check_counter_status_enabled_equal(
191    signal net : inout network_t;
192    expected : in std_ulogic;
193    base_address : in addr_t := (others => '0');
194    bus_handle : in bus_master_t := regs_bus_master;
195    message : in string := ""
196  ) is
197    constant register_array_message : string := "";
198    function base_address_message return string is
199    begin
200      if base_address /= 0 then
201        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
202      end if;
203
204      return "";
205    end function;
206    constant base_message : string := (
207      "Checking the 'enabled' field in the 'status' register"
208      & register_array_message
209      & base_address_message
210      & "."
211    );
212    function get_message return string is
213    begin
214      if message = "" then
215        return base_message;
216      end if;
217
218      return base_message & " " & message & ".";
219    end function;
220
221    variable got_reg : counter_status_t := counter_status_init;
222    variable got : std_ulogic := counter_status_enabled_init;
223  begin
224    read_counter_status(
225      net => net,
226      value => got_reg,
227      base_address => base_address,
228      bus_handle => bus_handle
229    );
230    got := got_reg.enabled;
231
232    check_equal(got=>got, expected=>expected, msg=>get_message);
233  end procedure;
234
235  -- Check that the current value of the 'pulse_count' field in the 'status' register
236  -- equals the given 'expected' value.
237  procedure check_counter_status_pulse_count_equal(
238    signal net : inout network_t;
239    expected : in counter_status_pulse_count_t;
240    base_address : in addr_t := (others => '0');
241    bus_handle : in bus_master_t := regs_bus_master;
242    message : in string := ""
243  ) is
244    constant register_array_message : string := "";
245    function base_address_message return string is
246    begin
247      if base_address /= 0 then
248        return " (at base address " & hex_image(std_logic_vector(base_address)) & ")";
249      end if;
250
251      return "";
252    end function;
253    constant base_message : string := (
254      "Checking the 'pulse_count' field in the 'status' register"
255      & register_array_message
256      & base_address_message
257      & "."
258    );
259    function get_message return string is
260    begin
261      if message = "" then
262        return base_message;
263      end if;
264
265      return base_message & " " & message & ".";
266    end function;
267
268    variable got_reg : counter_status_t := counter_status_init;
269    variable got : counter_status_pulse_count_t := counter_status_pulse_count_init;
270  begin
271    read_counter_status(
272      net => net,
273      value => got_reg,
274      base_address => base_address,
275      bus_handle => bus_handle
276    );
277    got := got_reg.pulse_count;
278
279    check_equal(got=>got, expected=>expected, msg=>get_message);
280  end procedure;
281  -- ---------------------------------------------------------------------------
282
283end 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 5.2.1-dev.
  2-- Code generator VhdlSimulationWaitUntilPackageGenerator version 1.0.0.
  3-- Generated 2024-05-21 20:51 from file regs_counter.toml at commit 2f4170d05b13abac.
  4-- Register hash 79eb071caf7e3f78ee67fa5265f7ef0edb0ea269.
  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 'mode' field in the 'config' register equals the given 'value'.
 44  procedure wait_until_counter_config_mode_equals(
 45    signal net : inout network_t;
 46    value : in counter_config_mode_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 'mode' field in the 'config' register equals the given 'value'.
152  procedure wait_until_counter_config_mode_equals(
153    signal net : inout network_t;
154    value : in counter_config_mode_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_mode => 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 'mode' 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.