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.
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.
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.
The entity uses an AXI-Lite register bus and instantiates the register file produced by
VhdlAxiLiteWrapperGenerator
, which can be seen below.Register values up and down are record types from the package produced by
VhdlRecordPackageGenerator
, which can be seen below.The
set_status
process showsHow to access bit fields in a “Write-pulse” register and how to set bit fields in a “Read” register.
How to set and update an integer field in a “Read” register.
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
).The
count
process shows
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.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.
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.
The testbench uses register read/write procedures from the package produced by
VhdlSimulationReadWritePackageGenerator
, which can be seen below. For examplewrite_counter_config
.The testbench uses register wait until procedures from the package produced by
VhdlSimulationWaitUntilPackageGenerator
, which can be seen below.For example
wait_until_counter_status_pulse_count_equals
, which will continuously read thestatus
register until thepulse_count
field is exactly equal to the supplied value.
The type of the
value
for each procedure is the native record type for that register.For example,
read_counter_status
returns a value of typecounter_status_t
which is a record that contains a bitenabled
and an integerpulse_count
.
The testbench uses register field check procedures from the package produced by
VhdlSimulationCheckPackageGenerator
, which can be seen below. For examplecheck_counter_status_enabled_equal
.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.
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.
1-- This file is automatically generated by hdl-registers version 6.0.2-dev.
2-- Code generator VhdlRegisterPackageGenerator version 1.0.0.
3-- Generated 2024-09-19 20:51 from file regs_counter.toml at commit 25b9eb172eb22827.
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.
1-- This file is automatically generated by hdl-registers version 6.0.2-dev.
2-- Code generator VhdlRecordPackageGenerator version 1.0.0.
3-- Generated 2024-09-19 20:51 from file regs_counter.toml at commit 25b9eb172eb22827.
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.
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.0.2-dev.
11-- Code generator VhdlAxiLiteWrapperGenerator version 1.0.0.
12-- Generated 2024-09-19 20:51 from file regs_counter.toml at commit 25b9eb172eb22827.
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.
1-- This file is automatically generated by hdl-registers version 6.0.2-dev.
2-- Code generator VhdlSimulationReadWritePackageGenerator version 1.0.0.
3-- Generated 2024-09-19 20:51 from file regs_counter.toml at commit 25b9eb172eb22827.
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.
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 'condition' field in the 'config' register.
41 procedure read_counter_config_condition(
42 signal net : inout network_t;
43 value : out counter_config_condition_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 'condition' 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_condition(
67 signal net : inout network_t;
68 value : in counter_config_condition_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 'condition' field in the 'config' register.
165 procedure read_counter_config_condition(
166 signal net : inout network_t;
167 value : out counter_config_condition_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.condition;
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 'condition' 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_condition(
222 signal net : inout network_t;
223 value : in counter_config_condition_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.condition := 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.
1-- This file is automatically generated by hdl-registers version 6.0.2-dev.
2-- Code generator VhdlSimulationCheckPackageGenerator version 1.0.0.
3-- Generated 2024-09-19 20:51 from file regs_counter.toml at commit 25b9eb172eb22827.
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_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 'condition' field in the 'config' register
33 -- equals the given 'expected' value.
34 procedure check_counter_config_condition_equal(
35 signal net : inout network_t;
36 expected : in counter_config_condition_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 'condition' field in the 'config' register
81 -- equals the given 'expected' value.
82 procedure check_counter_config_condition_equal(
83 signal net : inout network_t;
84 expected : in counter_config_condition_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 'condition' 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_condition_t := counter_config_condition_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.condition;
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.
1-- This file is automatically generated by hdl-registers version 6.0.2-dev.
2-- Code generator VhdlSimulationWaitUntilPackageGenerator version 1.0.0.
3-- Generated 2024-09-19 20:51 from file regs_counter.toml at commit 25b9eb172eb22827.
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:
AXI-to-AXI-Lite converter: axi_to_axi_lite.vhd,
AXI/AXI-Lite crossbar: axi_simple_read_crossbar.vhd, axi_simple_write_crossbar.vhd, axi_lite_simple_read_crossbar.vhd, axi_lite_simple_write_crossbar.vhd,
AXI-Lite mux (splitter): axi_lite_mux.vhd,
AXI-Lite clock domain crossing: axi_lite_cdc.vhd,
etc…
See the reg_file library, axi library and axi_lite library for more details.