C++ code generator
A complete C++ class can be generated with methods to read/write the registers or fields.
CppInterfaceGenerator
creates an abstract interface header that can be used for mocking in a unit test environment. Contains method declarations, register attributes, and register constant values.CppHeaderGenerator
creates a class header which inherits the abstract class.CppImplementationGenerator
creates a class implementation with setters and getters.
Code is generated by running the code below:
1# Standard libraries
2import sys
3from pathlib import Path
4
5# First party libraries
6from hdl_registers.generator.cpp.header import CppHeaderGenerator
7from hdl_registers.generator.cpp.implementation import CppImplementationGenerator
8from hdl_registers.generator.cpp.interface import CppInterfaceGenerator
9from hdl_registers.parser.toml import from_toml
10
11THIS_DIR = Path(__file__).parent
12
13
14def main(output_folder: Path):
15 """
16 Create register C++ artifacts from the TOML example file.
17 """
18 register_list = from_toml(
19 name="example",
20 toml_file=THIS_DIR.parent.parent / "user_guide" / "toml" / "toml_format.toml",
21 )
22
23 CppInterfaceGenerator(
24 register_list=register_list, output_folder=output_folder / "include"
25 ).create()
26
27 CppHeaderGenerator(
28 register_list=register_list, output_folder=output_folder / "include"
29 ).create()
30
31 CppImplementationGenerator(register_list=register_list, output_folder=output_folder).create()
32
33
34if __name__ == "__main__":
35 main(output_folder=Path(sys.argv[1]))
Getters
It can be noted, most clearly in the Interface header below, that there are three ways to read a register field:
The method that reads the whole register, e.g.
get_configuration()
.The method that reads the register and then slices out the field value, e.g.
get_configuration_enable()
.The method that slices out the field value given a previously read register value, e.g.
get_configuration_enable_from_value(register_value)
.
Method (2) is the most convenient in most cases. However if we want to read out more than one field from a register it would be very inefficient to read the register value more than once over the register bus, which would be the result of calling (2) multiple times. Instead we can call (1) once and then (3) multiple times to get our field values.
Setters
Conversely there are three ways to write a register field:
The method that writes the whole register, e.g.
set_configuration()
.The method that reads the register, updates the value of the field, and then writes the register back, e.g.
set_configuration_enable()
.The method that updates the value of the field given a previously read register value, and returns an updated register value, e.g.
set_configuration_enable_from_value(register_value)
.
Method (2) is the most convenient in most cases.
However if we want to update more than one field of a register it would be very inefficient to
read and write the register more than once over the register bus, which would be the result of
calling (2) multiple times.
Instead we can call a register getter once, e.g. get_configuration()
, and then (3) multiple
times to get our updated register value.
This value is then written over the register bus using (1).
Exceptions
The discussion about setters above is valid for “read write” mode registers, which is arguably the
most common type.
However there are three register modes where the previously written register value can not be
read back over the bus and then modified: “write only”, “write pulse”, and “read, write pulse”.
The field setters for registers of this mode will write all bits outside of the current field
as zero.
This can for example be seen in the setter set_channels_configuration_enable()
in the generated
code below.
Interface header
Below is the resulting abstract interface header code, generated from the TOML format example. Note that all register constants as well as field attributes are included here.
1// This file is automatically generated by hdl-registers version 4.1.1-dev2.
2// Code generator CppInterfaceGenerator version 1.0.0.
3// Generated 2023-12-04 10:43 from file toml_format.toml at commit d6ece437d0e3b2bc.
4// Register hash 0d636d16ec9bdadff4d5e144aaacd9416830c91d.
5
6#pragma once
7
8#include <cassert>
9#include <cstdint>
10#include <cstdlib>
11
12namespace fpga_regs
13{
14
15 // Attributes for the "enable" field in the "config" register.
16 namespace example::config::enable
17 {
18 static const auto width = 1;
19 static const auto default_value = 0b1;
20 }
21 // Attributes for the "direction" field in the "config" register.
22 namespace example::config::direction
23 {
24 static const auto width = 2;
25 enum Enumeration
26 {
27 data_in = 0,
28 high_z = 1,
29 data_out = 2,
30 };
31 static const auto default_value = Enumeration::high_z;
32 }
33
34 // Attributes for the "enable" field in the "config" register within the "channels" register array.
35 namespace example::channels::config::enable
36 {
37 static const auto width = 1;
38 static const auto default_value = 0b0;
39 }
40 // Attributes for the "tuser" field in the "config" register within the "channels" register array.
41 namespace example::channels::config::tuser
42 {
43 static const auto width = 8;
44 static const auto default_value = 0b00000000;
45 }
46
47 // Attributes for the "channels" register array.
48 namespace example::channels
49 {
50 // Number of times the registers of the array are repeated.
51 static const auto array_length = 4;
52 };
53
54 class IExample
55 {
56 public:
57 // Register constant.
58 static const int axi_data_width = 64;
59 // Register constant.
60 static constexpr double clock_rate_hz = 156250000.0;
61
62 // Number of registers within this register map.
63 static const size_t num_registers = 10uL;
64
65 virtual ~IExample() {}
66
67 // -------------------------------------------------------------------------
68 // Methods for the 'config' register. Mode "Read, Write".
69
70 // Getter that will read the whole register's value over the register bus.
71 virtual uint32_t get_config() const = 0;
72
73 // Setter that will write the whole register's value over the register bus.
74 virtual void set_config(
75 uint32_t register_value
76 ) const = 0;
77
78 // Getter for the "enable" field in the "config" register,
79 // which will read register value over the register bus.
80 virtual uint32_t get_config_enable() const = 0;
81 // Getter for the "enable" field in the "config" register,
82 // given the register's current value.
83 virtual uint32_t get_config_enable_from_value(
84 uint32_t register_value
85 ) const = 0;
86 // Setter for the "enable" field in the "config" register,
87 // which will read-modify-write over the register bus.
88 virtual void set_config_enable(
89 uint32_t field_value
90 ) const = 0;
91 // Setter for the "enable" field in the "config" register,
92 // given the register's current value, which will return an updated value.
93 virtual uint32_t set_config_enable_from_value(
94 uint32_t register_value,
95 uint32_t field_value
96 ) const = 0;
97
98 // Getter for the "direction" field in the "config" register,
99 // which will read register value over the register bus.
100 virtual example::config::direction::Enumeration get_config_direction() const = 0;
101 // Getter for the "direction" field in the "config" register,
102 // given the register's current value.
103 virtual example::config::direction::Enumeration get_config_direction_from_value(
104 uint32_t register_value
105 ) const = 0;
106 // Setter for the "direction" field in the "config" register,
107 // which will read-modify-write over the register bus.
108 virtual void set_config_direction(
109 example::config::direction::Enumeration field_value
110 ) const = 0;
111 // Setter for the "direction" field in the "config" register,
112 // given the register's current value, which will return an updated value.
113 virtual uint32_t set_config_direction_from_value(
114 uint32_t register_value,
115 example::config::direction::Enumeration field_value
116 ) const = 0;
117
118 // -------------------------------------------------------------------------
119 // Methods for the 'status' register. Mode "Read".
120
121 // Getter that will read the whole register's value over the register bus.
122 virtual uint32_t get_status() const = 0;
123
124 // -------------------------------------------------------------------------
125 // Methods for the 'read_address' register within the 'channels' register array. Mode "Read, Write".
126
127 // Getter that will read the whole register's value over the register bus.
128 virtual uint32_t get_channels_read_address(
129 size_t array_index
130 ) const = 0;
131
132 // Setter that will write the whole register's value over the register bus.
133 virtual void set_channels_read_address(
134 size_t array_index,
135 uint32_t register_value
136 ) const = 0;
137
138 // -------------------------------------------------------------------------
139 // Methods for the 'config' register within the 'channels' register array. Mode "Write".
140
141 // Setter that will write the whole register's value over the register bus.
142 virtual void set_channels_config(
143 size_t array_index,
144 uint32_t register_value
145 ) const = 0;
146
147 // Setter for the "enable" field in the "config" register within the "channels" register array,
148 // which will set the field to the given value, and all other bits to zero.
149 virtual void set_channels_config_enable(
150 size_t array_index,
151 uint32_t field_value
152 ) const = 0;
153 // Setter for the "enable" field in the "config" register within the "channels" register array,
154 // given the register's current value, which will return an updated value.
155 virtual uint32_t set_channels_config_enable_from_value(
156 uint32_t register_value,
157 uint32_t field_value
158 ) const = 0;
159
160 // Setter for the "tuser" field in the "config" register within the "channels" register array,
161 // which will set the field to the given value, and all other bits to zero.
162 virtual void set_channels_config_tuser(
163 size_t array_index,
164 uint32_t field_value
165 ) const = 0;
166 // Setter for the "tuser" field in the "config" register within the "channels" register array,
167 // given the register's current value, which will return an updated value.
168 virtual uint32_t set_channels_config_tuser_from_value(
169 uint32_t register_value,
170 uint32_t field_value
171 ) const = 0;
172
173 };
174
175} /* namespace fpga_regs */
Class header
Below is the generated class header:
1// This file is automatically generated by hdl-registers version 4.1.1-dev2.
2// Code generator CppHeaderGenerator version 1.0.0.
3// Generated 2023-12-04 10:43 from file toml_format.toml at commit d6ece437d0e3b2bc.
4// Register hash 0d636d16ec9bdadff4d5e144aaacd9416830c91d.
5
6#pragma once
7
8#include "i_example.h"
9
10namespace fpga_regs
11{
12
13 class Example : public IExample
14 {
15 private:
16 volatile uint32_t *m_registers;
17
18 public:
19 Example(volatile uint8_t *base_address);
20
21 virtual ~Example() {}
22
23 // -------------------------------------------------------------------------
24 // Methods for the 'config' register.
25 // See interface header for documentation.
26 virtual uint32_t get_config() const override;
27 virtual uint32_t get_config_enable() const override;
28 virtual uint32_t get_config_enable_from_value(
29 uint32_t register_value
30 ) const override;
31 virtual example::config::direction::Enumeration get_config_direction() const override;
32 virtual example::config::direction::Enumeration get_config_direction_from_value(
33 uint32_t register_value
34 ) const override;
35 virtual void set_config(
36 uint32_t register_value
37 ) const override;
38 virtual void set_config_enable(
39 uint32_t field_value
40 ) const override;
41 virtual uint32_t set_config_enable_from_value(
42 uint32_t register_value,
43 uint32_t field_value
44 ) const override;
45 virtual void set_config_direction(
46 example::config::direction::Enumeration field_value
47 ) const override;
48 virtual uint32_t set_config_direction_from_value(
49 uint32_t register_value,
50 example::config::direction::Enumeration field_value
51 ) const override;
52
53 // -------------------------------------------------------------------------
54 // Methods for the 'status' register.
55 // See interface header for documentation.
56 virtual uint32_t get_status() const override;
57
58 // -------------------------------------------------------------------------
59 // Methods for the 'read_address' register within the 'channels' register array.
60 // See interface header for documentation.
61 virtual uint32_t get_channels_read_address(
62 size_t array_index
63 ) const override;
64 virtual void set_channels_read_address(
65 size_t array_index,
66 uint32_t register_value
67 ) const override;
68
69 // -------------------------------------------------------------------------
70 // Methods for the 'config' register within the 'channels' register array.
71 // See interface header for documentation.
72 virtual void set_channels_config(
73 size_t array_index,
74 uint32_t register_value
75 ) const override;
76 virtual void set_channels_config_enable(
77 size_t array_index,
78 uint32_t field_value
79 ) const override;
80 virtual uint32_t set_channels_config_enable_from_value(
81 uint32_t register_value,
82 uint32_t field_value
83 ) const override;
84 virtual void set_channels_config_tuser(
85 size_t array_index,
86 uint32_t field_value
87 ) const override;
88 virtual uint32_t set_channels_config_tuser_from_value(
89 uint32_t register_value,
90 uint32_t field_value
91 ) const override;
92 };
93} /* namespace fpga_regs */
Implementation
Below is the generated class implementation:
1// This file is automatically generated by hdl-registers version 4.1.1-dev2.
2// Code generator CppImplementationGenerator version 1.0.0.
3// Generated 2023-12-04 10:43 from file toml_format.toml at commit d6ece437d0e3b2bc.
4// Register hash 0d636d16ec9bdadff4d5e144aaacd9416830c91d.
5
6#include "include/example.h"
7
8namespace fpga_regs
9{
10
11 Example::Example(volatile uint8_t *base_address)
12 : m_registers(reinterpret_cast<volatile uint32_t *>(base_address))
13 {
14 // Empty
15 }
16
17 // ---------------------------------------------------------------------------
18 // Methods for the 'config' register.
19 // See interface header for documentation.
20
21 uint32_t Example::get_config() const
22 {
23 const size_t index = 0;
24 const uint32_t result = m_registers[index];
25
26 return result;
27 }
28
29 uint32_t Example::get_config_enable() const
30 {
31 const uint32_t register_value = get_config();
32 const uint32_t result = get_config_enable_from_value(register_value);
33
34 return result;
35 }
36
37 uint32_t Example::get_config_enable_from_value(
38 uint32_t register_value
39 ) const
40 {
41 const uint32_t shift = 0uL;
42 const uint32_t mask_at_base = 0b1uL;
43 const uint32_t mask_shifted = mask_at_base << shift;
44
45 const uint32_t result_masked = register_value & mask_shifted;
46 const uint32_t result_shifted = result_masked >> shift;
47
48 return result_shifted;
49 }
50
51 example::config::direction::Enumeration Example::get_config_direction() const
52 {
53 const uint32_t register_value = get_config();
54 const example::config::direction::Enumeration result = get_config_direction_from_value(register_value);
55
56 return result;
57 }
58
59 example::config::direction::Enumeration Example::get_config_direction_from_value(
60 uint32_t register_value
61 ) const
62 {
63 const uint32_t shift = 1uL;
64 const uint32_t mask_at_base = 0b11uL;
65 const uint32_t mask_shifted = mask_at_base << shift;
66
67 const uint32_t result_masked = register_value & mask_shifted;
68 const uint32_t result_shifted = result_masked >> shift;
69
70 const auto result = example::config::direction::Enumeration(result_shifted);
71
72 return result;
73 }
74
75 void Example::set_config(
76 uint32_t register_value
77 ) const
78 {
79 const size_t index = 0;
80 m_registers[index] = register_value;
81 }
82
83 void Example::set_config_enable(
84 uint32_t field_value
85 ) const
86 {
87 // Get the current value of any other fields by reading register on the bus.
88 const uint32_t current_register_value = get_config();
89 const uint32_t result_register_value = set_config_enable_from_value(current_register_value, field_value);
90 set_config(result_register_value);
91 }
92
93 uint32_t Example::set_config_enable_from_value(
94 uint32_t register_value,
95 uint32_t field_value
96 ) const
97 {
98 const uint32_t shift = 0uL;
99 const uint32_t mask_at_base = 0b1uL;
100 const uint32_t mask_shifted = mask_at_base << shift;
101
102 const uint32_t field_value_masked = field_value & mask_at_base;
103 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
104
105 const uint32_t mask_shifted_inverse = ~mask_shifted;
106 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
107
108 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
109
110 return result_register_value;
111 }
112
113 void Example::set_config_direction(
114 example::config::direction::Enumeration field_value
115 ) const
116 {
117 // Get the current value of any other fields by reading register on the bus.
118 const uint32_t current_register_value = get_config();
119 const uint32_t result_register_value = set_config_direction_from_value(current_register_value, field_value);
120 set_config(result_register_value);
121 }
122
123 uint32_t Example::set_config_direction_from_value(
124 uint32_t register_value,
125 example::config::direction::Enumeration field_value
126 ) const
127 {
128 const uint32_t shift = 1uL;
129 const uint32_t mask_at_base = 0b11uL;
130 const uint32_t mask_shifted = mask_at_base << shift;
131
132 const uint32_t field_value_masked = field_value & mask_at_base;
133 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
134
135 const uint32_t mask_shifted_inverse = ~mask_shifted;
136 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
137
138 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
139
140 return result_register_value;
141 }
142
143 // ---------------------------------------------------------------------------
144 // Methods for the 'status' register.
145 // See interface header for documentation.
146
147 uint32_t Example::get_status() const
148 {
149 const size_t index = 1;
150 const uint32_t result = m_registers[index];
151
152 return result;
153 }
154
155 // ---------------------------------------------------------------------------
156 // Methods for the 'read_address' register within the 'channels' register array.
157 // See interface header for documentation.
158
159 uint32_t Example::get_channels_read_address(
160 size_t array_index
161 ) const
162 {
163 assert(array_index < example::channels::array_length);
164 const size_t index = 2 + array_index * 2 + 0;
165 const uint32_t result = m_registers[index];
166
167 return result;
168 }
169
170 void Example::set_channels_read_address(
171 size_t array_index,
172 uint32_t register_value
173 ) const
174 {
175 assert(array_index < example::channels::array_length);
176 const size_t index = 2 + array_index * 2 + 0;
177 m_registers[index] = register_value;
178 }
179
180 // ---------------------------------------------------------------------------
181 // Methods for the 'config' register within the 'channels' register array.
182 // See interface header for documentation.
183
184 void Example::set_channels_config(
185 size_t array_index,
186 uint32_t register_value
187 ) const
188 {
189 assert(array_index < example::channels::array_length);
190 const size_t index = 2 + array_index * 2 + 1;
191 m_registers[index] = register_value;
192 }
193
194 void Example::set_channels_config_enable(
195 size_t array_index,
196 uint32_t field_value
197 ) const
198 {
199 // This register's current value can not be read back due to its mode.
200 // Hence set all bits except for the field to default when writing the value.
201 const uint32_t current_register_value = 0;
202 const uint32_t result_register_value = set_channels_config_enable_from_value(current_register_value, field_value);
203 set_channels_config(array_index, result_register_value);
204 }
205
206 uint32_t Example::set_channels_config_enable_from_value(
207 uint32_t register_value,
208 uint32_t field_value
209 ) const
210 {
211 const uint32_t shift = 0uL;
212 const uint32_t mask_at_base = 0b1uL;
213 const uint32_t mask_shifted = mask_at_base << shift;
214
215 const uint32_t field_value_masked = field_value & mask_at_base;
216 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
217
218 const uint32_t mask_shifted_inverse = ~mask_shifted;
219 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
220
221 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
222
223 return result_register_value;
224 }
225
226 void Example::set_channels_config_tuser(
227 size_t array_index,
228 uint32_t field_value
229 ) const
230 {
231 // This register's current value can not be read back due to its mode.
232 // Hence set all bits except for the field to default when writing the value.
233 const uint32_t current_register_value = 0;
234 const uint32_t result_register_value = set_channels_config_tuser_from_value(current_register_value, field_value);
235 set_channels_config(array_index, result_register_value);
236 }
237
238 uint32_t Example::set_channels_config_tuser_from_value(
239 uint32_t register_value,
240 uint32_t field_value
241 ) const
242 {
243 const uint32_t shift = 1uL;
244 const uint32_t mask_at_base = 0b11111111uL;
245 const uint32_t mask_shifted = mask_at_base << shift;
246
247 const uint32_t field_value_masked = field_value & mask_at_base;
248 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
249
250 const uint32_t mask_shifted_inverse = ~mask_shifted;
251 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
252
253 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
254
255 return result_register_value;
256 }
257
258} /* namespace fpga_regs */
Note that when the register is part of an array, the register setter/getter takes a second
argument array_index
.
There is an assert that the user-provided array index is within the bounds of the array.