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.
C++ code is generated by running the Python code below. Note that it will parse and generate artifacts from the TOML file used in the TOML Format example.
1import sys
2from pathlib import Path
3
4from hdl_registers.generator.cpp.header import CppHeaderGenerator
5from hdl_registers.generator.cpp.implementation import CppImplementationGenerator
6from hdl_registers.generator.cpp.interface import CppInterfaceGenerator
7from hdl_registers.parser.toml import from_toml
8
9THIS_DIR = Path(__file__).parent
10
11
12def main(output_folder: Path) -> None:
13 """
14 Create register C++ artifacts from the TOML example file.
15 """
16 register_list = from_toml(
17 name="example",
18 toml_file=THIS_DIR.parent.parent / "user_guide" / "toml" / "toml_format.toml",
19 )
20
21 CppInterfaceGenerator(
22 register_list=register_list, output_folder=output_folder / "include"
23 ).create()
24
25 CppHeaderGenerator(
26 register_list=register_list, output_folder=output_folder / "include"
27 ).create()
28
29 CppImplementationGenerator(register_list=register_list, output_folder=output_folder).create()
30
31
32if __name__ == "__main__":
33 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.
Assertion macros
There are a few register-related things that can go wrong in an embedded system:
An error in hardware might result in reading a field value that is out of bounds. This is mostly possible for enumeration and integer fields.
Conversely, an error in software might result in writing a field value that is out of bounds.
An error in software might result in a register array read/write with an index that is out of bounds.
The generated C++ implementation checks for these errors using custom assertion macros. Meaning, they can be detected at runtime as well as in unit tests.
If an assertion fails a user-specified handler function is called.
In this function, the user could e.g. call a custom logger, perform a controlled shutdown,
throw exceptions, etc.
One argument is provided that contains a descriptive error message.
The function must return a boolean true
.
This error handler function must be provided to the constructor of the generated class.
Minimal example
A minimal and somewhat unrealistic handler function is shown below. More advanced handler functions are left to the user.
uintptr_t base_address = 0x43C00000;
bool register_assert_fail_handler(const std::string *diagnostic_message)
{
std::cerr << *diagnostic_message << std::endl;
std::exit(EXIT_FAILURE);
return true;
}
fpga_regs::Example example(base_address, register_assert_fail_handler);
Disabling assertions
The assertions add to the binary size and to the runtime of the program.
The user can disable the assertions by defining the following macros
(usually with the -D
compiler flag):
NO_REGISTER_GETTER_ASSERT
NO_REGISTER_SETTER_ASSERT
NO_REGISTER_ARRAY_INDEX_ASSERT
This should result in no overhead.
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// -----------------------------------------------------------------------------
2// This file is automatically generated by hdl-registers version 7.0.4-dev.
3// Code generator CppInterfaceGenerator version 1.0.0.
4// Generated 2025-02-15 20:51 from file toml_format.toml at commit 150d1a2bbe0a.
5// Register hash 4df9765ebb584803b583628671e4659579eb85f4.
6// -----------------------------------------------------------------------------
7
8#pragma once
9
10#include <sstream>
11#include <cstdint>
12#include <cstdlib>
13
14namespace fpga_regs
15{
16
17 // Attributes for the 'enable' field in the 'config' register.
18 namespace example::config::enable
19 {
20 static const auto width = 1;
21 static const auto default_value = 0b1;
22 }
23 // Attributes for the 'direction' field in the 'config' register.
24 namespace example::config::direction
25 {
26 static const auto width = 2;
27 enum Enumeration
28 {
29 data_in = 0,
30 high_z = 1,
31 data_out = 2,
32 };
33 static const auto default_value = Enumeration::high_z;
34 }
35
36 // Attributes for the 'enable' field in the 'config' register within the 'channels' register array.
37 namespace example::channels::config::enable
38 {
39 static const auto width = 1;
40 static const auto default_value = 0b0;
41 }
42 // Attributes for the 'tuser' field in the 'config' register within the 'channels' register array.
43 namespace example::channels::config::tuser
44 {
45 static const auto width = 8;
46 static const auto default_value = 0b00000000;
47 }
48
49 // Attributes for the "channels" register array.
50 namespace example::channels
51 {
52 // Number of times the registers of the array are repeated.
53 static const auto array_length = 4;
54 };
55
56 class IExample
57 {
58 public:
59 // Register constant.
60 static const int axi_data_width = 64;
61 // Register constant.
62 static constexpr double clock_rate_hz = 156250000.0;
63
64 // Number of registers within this register list.
65 static const size_t num_registers = 10uL;
66
67 virtual ~IExample() {}
68
69 // -------------------------------------------------------------------------
70 // Methods for the 'config' register. Mode 'Read, Write'.
71
72 // Getter that will read the whole register's value over the register bus.
73 virtual uint32_t get_config() const = 0;
74
75 // Setter that will write the whole register's value over the register bus.
76 virtual void set_config(
77 uint32_t register_value
78 ) const = 0;
79
80 // Getter for the 'enable' field in the 'config' register,
81 // which will read register value over the register bus.
82 virtual uint32_t get_config_enable() const = 0;
83 // Getter for the 'enable' field in the 'config' register,
84 // given a register value.
85 virtual uint32_t get_config_enable_from_value(
86 uint32_t register_value
87 ) const = 0;
88 // Setter for the 'enable' field in the 'config' register,
89 // which will read-modify-write over the register bus.
90 virtual void set_config_enable(
91 uint32_t field_value
92 ) const = 0;
93 // Setter for the 'enable' field in the 'config' register,
94 // given a register value, which will return an updated value.
95 virtual uint32_t set_config_enable_from_value(
96 uint32_t register_value,
97 uint32_t field_value
98 ) const = 0;
99
100 // Getter for the 'direction' field in the 'config' register,
101 // which will read register value over the register bus.
102 virtual example::config::direction::Enumeration get_config_direction() const = 0;
103 // Getter for the 'direction' field in the 'config' register,
104 // given a register value.
105 virtual example::config::direction::Enumeration get_config_direction_from_value(
106 uint32_t register_value
107 ) const = 0;
108 // Setter for the 'direction' field in the 'config' register,
109 // which will read-modify-write over the register bus.
110 virtual void set_config_direction(
111 example::config::direction::Enumeration field_value
112 ) const = 0;
113 // Setter for the 'direction' field in the 'config' register,
114 // given a register value, which will return an updated value.
115 virtual uint32_t set_config_direction_from_value(
116 uint32_t register_value,
117 example::config::direction::Enumeration field_value
118 ) const = 0;
119
120 // -------------------------------------------------------------------------
121 // Methods for the 'status' register. Mode 'Read'.
122
123 // Getter that will read the whole register's value over the register bus.
124 virtual uint32_t get_status() const = 0;
125
126 // -------------------------------------------------------------------------
127 // Methods for the 'read_address' register within the 'channels' register array. Mode 'Read, Write'.
128
129 // Getter that will read the whole register's value over the register bus.
130 virtual uint32_t get_channels_read_address(
131 size_t array_index
132 ) const = 0;
133
134 // Setter that will write the whole register's value over the register bus.
135 virtual void set_channels_read_address(
136 size_t array_index,
137 uint32_t register_value
138 ) const = 0;
139
140 // -------------------------------------------------------------------------
141 // Methods for the 'config' register within the 'channels' register array. Mode 'Write'.
142
143 // Setter that will write the whole register's value over the register bus.
144 virtual void set_channels_config(
145 size_t array_index,
146 uint32_t register_value
147 ) const = 0;
148
149 // Setter for the 'enable' field in the 'config' register within the 'channels' register array,
150 // which will set the field to the given value, and everything else to default.
151 virtual void set_channels_config_enable(
152 size_t array_index,
153 uint32_t field_value
154 ) const = 0;
155 // Setter for the 'enable' field in the 'config' register within the 'channels' register array,
156 // given a register value, which will return an updated value.
157 virtual uint32_t set_channels_config_enable_from_value(
158 uint32_t register_value,
159 uint32_t field_value
160 ) const = 0;
161
162 // Setter for the 'tuser' field in the 'config' register within the 'channels' register array,
163 // which will set the field to the given value, and everything else to default.
164 virtual void set_channels_config_tuser(
165 size_t array_index,
166 uint32_t field_value
167 ) const = 0;
168 // Setter for the 'tuser' field in the 'config' register within the 'channels' register array,
169 // given a register value, which will return an updated value.
170 virtual uint32_t set_channels_config_tuser_from_value(
171 uint32_t register_value,
172 uint32_t field_value
173 ) const = 0;
174
175 };
176
177} /* namespace fpga_regs */
Class header
Below is the generated class header:
1// -----------------------------------------------------------------------------
2// This file is automatically generated by hdl-registers version 7.0.4-dev.
3// Code generator CppHeaderGenerator version 1.0.0.
4// Generated 2025-02-15 20:51 from file toml_format.toml at commit 150d1a2bbe0a.
5// Register hash 4df9765ebb584803b583628671e4659579eb85f4.
6// -----------------------------------------------------------------------------
7
8#pragma once
9
10#include "i_example.h"
11
12namespace fpga_regs
13{
14
15 class Example : public IExample
16 {
17 private:
18 volatile uint32_t *m_registers;
19 bool (*m_assertion_handler) (const std::string*);
20
21 public:
22 /**
23 * Class constructor.
24 * @param base_address Byte address where these registers are memory mapped.
25 * Can be e.g. '0x43C00000' in bare metal, or e.g.
26 * 'reinterpret_cast<uintptr_t>(mmap(...))' in Linux.
27 * When using an operating system, care must be taken to pass the
28 * virtual address, not the physical address.
29 * When using bare metal, these are the same.
30 * @param assertion_handler Function to call when an assertion fails.
31 * Function takes a string pointer as an argument and must return a
32 * boolean 'true'.
33 */
34 Example(uintptr_t base_address, bool (*assertion_handler) (const std::string*));
35
36 virtual ~Example() {}
37
38 // -------------------------------------------------------------------------
39 // Methods for the 'config' register.
40 // See interface header for documentation.
41 virtual uint32_t get_config() const override;
42 virtual uint32_t get_config_enable() const override;
43 virtual uint32_t get_config_enable_from_value(
44 uint32_t register_value
45 ) const override;
46 virtual example::config::direction::Enumeration get_config_direction() const override;
47 virtual example::config::direction::Enumeration get_config_direction_from_value(
48 uint32_t register_value
49 ) const override;
50 virtual void set_config(
51 uint32_t register_value
52 ) const override;
53 virtual void set_config_enable(
54 uint32_t field_value
55 ) const override;
56 virtual uint32_t set_config_enable_from_value(
57 uint32_t register_value,
58 uint32_t field_value
59 ) const override;
60 virtual void set_config_direction(
61 example::config::direction::Enumeration field_value
62 ) const override;
63 virtual uint32_t set_config_direction_from_value(
64 uint32_t register_value,
65 example::config::direction::Enumeration field_value
66 ) const override;
67
68 // -------------------------------------------------------------------------
69 // Methods for the 'status' register.
70 // See interface header for documentation.
71 virtual uint32_t get_status() const override;
72
73 // -------------------------------------------------------------------------
74 // Methods for the 'read_address' register within the 'channels' register array.
75 // See interface header for documentation.
76 virtual uint32_t get_channels_read_address(
77 size_t array_index
78 ) const override;
79 virtual void set_channels_read_address(
80 size_t array_index,
81 uint32_t register_value
82 ) const override;
83
84 // -------------------------------------------------------------------------
85 // Methods for the 'config' register within the 'channels' register array.
86 // See interface header for documentation.
87 virtual void set_channels_config(
88 size_t array_index,
89 uint32_t register_value
90 ) const override;
91 virtual void set_channels_config_enable(
92 size_t array_index,
93 uint32_t field_value
94 ) const override;
95 virtual uint32_t set_channels_config_enable_from_value(
96 uint32_t register_value,
97 uint32_t field_value
98 ) const override;
99 virtual void set_channels_config_tuser(
100 size_t array_index,
101 uint32_t field_value
102 ) const override;
103 virtual uint32_t set_channels_config_tuser_from_value(
104 uint32_t register_value,
105 uint32_t field_value
106 ) const override;
107 };
108} /* namespace fpga_regs */
Implementation
Below is the generated class implementation:
1// -----------------------------------------------------------------------------
2// This file is automatically generated by hdl-registers version 7.0.4-dev.
3// Code generator CppImplementationGenerator version 2.0.0.
4// Generated 2025-02-15 20:51 from file toml_format.toml at commit 150d1a2bbe0a.
5// Register hash 4df9765ebb584803b583628671e4659579eb85f4.
6// -----------------------------------------------------------------------------
7
8#include "include/example.h"
9
10namespace fpga_regs
11{
12
13#ifdef NO_REGISTER_SETTER_ASSERT
14
15#define _SETTER_ASSERT_TRUE(expression, message) ((void)0)
16
17#else // Not NO_REGISTER_SETTER_ASSERT.
18
19// This macro is called by the register code to check for runtime errors.
20#define _SETTER_ASSERT_TRUE(expression, message) \
21 { \
22 if (!static_cast<bool>(expression)) { \
23 std::ostringstream diagnostics; \
24 diagnostics << "Tried to set value out of range in " << __FILE__ << ":" \
25 << __LINE__ << ", message: " << message << "."; \
26 std::string diagnostic_message = diagnostics.str(); \
27 m_assertion_handler(&diagnostic_message); \
28 } \
29 }
30
31#endif // NO_REGISTER_SETTER_ASSERT.
32
33#ifdef NO_REGISTER_GETTER_ASSERT
34
35#define _GETTER_ASSERT_TRUE(expression, message) ((void)0)
36
37#else // Not NO_REGISTER_GETTER_ASSERT.
38
39// This macro is called by the register code to check for runtime errors.
40#define _GETTER_ASSERT_TRUE(expression, message) \
41 { \
42 if (!static_cast<bool>(expression)) { \
43 std::ostringstream diagnostics; \
44 diagnostics << "Got read value out of range in " << __FILE__ << ":" \
45 << __LINE__ << ", message: " << message << "."; \
46 std::string diagnostic_message = diagnostics.str(); \
47 m_assertion_handler(&diagnostic_message); \
48 } \
49 }
50
51#endif // NO_REGISTER_GETTER_ASSERT.
52
53#ifdef NO_REGISTER_ARRAY_INDEX_ASSERT
54
55#define _ARRAY_INDEX_ASSERT_TRUE(expression, message) ((void)0)
56
57#else // Not NO_REGISTER_ARRAY_INDEX_ASSERT.
58
59// This macro is called by the register code to check for runtime errors.
60#define _ARRAY_INDEX_ASSERT_TRUE(expression, message) \
61 { \
62 if (!static_cast<bool>(expression)) { \
63 std::ostringstream diagnostics; \
64 diagnostics << "Provided array index out of range in " << __FILE__ << ":" \
65 << __LINE__ << ", message: " << message << "."; \
66 std::string diagnostic_message = diagnostics.str(); \
67 m_assertion_handler(&diagnostic_message); \
68 } \
69 }
70
71#endif // NO_REGISTER_ARRAY_INDEX_ASSERT.
72
73 Example::Example(uintptr_t base_address, bool (*assertion_handler) (const std::string*))
74 : m_registers(reinterpret_cast<volatile uint32_t *>(base_address)),
75 m_assertion_handler(assertion_handler)
76 {
77 // Empty
78 }
79
80 // ---------------------------------------------------------------------------
81 // Methods for the 'config' register.
82 // See interface header for documentation.
83
84 uint32_t Example::get_config() const
85 {
86 const size_t index = 0;
87 const uint32_t result = m_registers[index];
88
89 return result;
90 }
91
92 uint32_t Example::get_config_enable() const
93 {
94 const uint32_t register_value = get_config();
95 const uint32_t field_value = get_config_enable_from_value(register_value);
96
97 return field_value;
98 }
99
100 uint32_t Example::get_config_enable_from_value(
101 uint32_t register_value
102 ) const
103 {
104 const uint32_t shift = 0uL;
105 const uint32_t mask_at_base = 0b1uL;
106 const uint32_t mask_shifted = mask_at_base << shift;
107
108 const uint32_t result_masked = register_value & mask_shifted;
109 const uint32_t result_shifted = result_masked >> shift;
110
111 uint32_t field_value;
112
113 // No casting needed.
114 field_value = result_shifted;
115
116 return field_value;
117 }
118
119 example::config::direction::Enumeration Example::get_config_direction() const
120 {
121 const uint32_t register_value = get_config();
122 const example::config::direction::Enumeration field_value = get_config_direction_from_value(register_value);
123
124 return field_value;
125 }
126
127 example::config::direction::Enumeration Example::get_config_direction_from_value(
128 uint32_t register_value
129 ) const
130 {
131 const uint32_t shift = 1uL;
132 const uint32_t mask_at_base = 0b11uL;
133 const uint32_t mask_shifted = mask_at_base << shift;
134
135 const uint32_t result_masked = register_value & mask_shifted;
136 const uint32_t result_shifted = result_masked >> shift;
137
138 example::config::direction::Enumeration field_value;
139
140 // "Cast" to the enum type.
141 field_value = example::config::direction::Enumeration(result_shifted);
142
143 return field_value;
144 }
145
146 void Example::set_config(
147 uint32_t register_value
148 ) const
149 {
150 const size_t index = 0;
151 m_registers[index] = register_value;
152 }
153
154 void Example::set_config_enable(
155 uint32_t field_value
156 ) const
157 {
158 // Get the current value of other fields by reading register on the bus.
159 const uint32_t current_register_value = get_config();
160 const uint32_t result_register_value = set_config_enable_from_value(current_register_value, field_value);
161 set_config(result_register_value);
162 }
163
164 uint32_t Example::set_config_enable_from_value(
165 uint32_t register_value,
166 uint32_t field_value
167 ) const {
168 const uint32_t shift = 0uL;
169 const uint32_t mask_at_base = 0b1uL;
170 const uint32_t mask_shifted = mask_at_base << shift;
171
172 // Check that field value is within the legal range.
173 const uint32_t mask_at_base_inverse = ~mask_at_base;
174 _SETTER_ASSERT_TRUE(
175 (field_value & mask_at_base_inverse) == 0,
176 "'enable' value too many bits used, got '" << field_value << "'"
177 );
178
179 const uint32_t field_value_masked = field_value & mask_at_base;
180 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
181
182 const uint32_t mask_shifted_inverse = ~mask_shifted;
183 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
184
185 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
186
187 return result_register_value;
188 }
189
190 void Example::set_config_direction(
191 example::config::direction::Enumeration field_value
192 ) const
193 {
194 // Get the current value of other fields by reading register on the bus.
195 const uint32_t current_register_value = get_config();
196 const uint32_t result_register_value = set_config_direction_from_value(current_register_value, field_value);
197 set_config(result_register_value);
198 }
199
200 uint32_t Example::set_config_direction_from_value(
201 uint32_t register_value,
202 example::config::direction::Enumeration field_value
203 ) const {
204 const uint32_t shift = 1uL;
205 const uint32_t mask_at_base = 0b11uL;
206 const uint32_t mask_shifted = mask_at_base << shift;
207
208 const uint32_t field_value_masked = field_value & mask_at_base;
209 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
210
211 const uint32_t mask_shifted_inverse = ~mask_shifted;
212 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
213
214 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
215
216 return result_register_value;
217 }
218
219 // ---------------------------------------------------------------------------
220 // Methods for the 'status' register.
221 // See interface header for documentation.
222
223 uint32_t Example::get_status() const
224 {
225 const size_t index = 1;
226 const uint32_t result = m_registers[index];
227
228 return result;
229 }
230
231 // ---------------------------------------------------------------------------
232 // Methods for the 'read_address' register within the 'channels' register array.
233 // See interface header for documentation.
234
235 uint32_t Example::get_channels_read_address(
236 size_t array_index
237 ) const
238 {
239 _ARRAY_INDEX_ASSERT_TRUE(
240 array_index < example::channels::array_length,
241 "'channels' array index out of range, got '" << array_index << "'"
242 );
243
244 const size_t index = 2 + array_index * 2 + 0;
245 const uint32_t result = m_registers[index];
246
247 return result;
248 }
249
250 void Example::set_channels_read_address(
251 size_t array_index,
252 uint32_t register_value
253 ) const
254 {
255 _ARRAY_INDEX_ASSERT_TRUE(
256 array_index < example::channels::array_length,
257 "'channels' array index out of range, got '" << array_index << "'"
258 );
259
260 const size_t index = 2 + array_index * 2 + 0;
261 m_registers[index] = register_value;
262 }
263
264 // ---------------------------------------------------------------------------
265 // Methods for the 'config' register within the 'channels' register array.
266 // See interface header for documentation.
267
268 void Example::set_channels_config(
269 size_t array_index,
270 uint32_t register_value
271 ) const
272 {
273 _ARRAY_INDEX_ASSERT_TRUE(
274 array_index < example::channels::array_length,
275 "'channels' array index out of range, got '" << array_index << "'"
276 );
277
278 const size_t index = 2 + array_index * 2 + 1;
279 m_registers[index] = register_value;
280 }
281
282 void Example::set_channels_config_enable(
283 size_t array_index,
284 uint32_t field_value
285 ) const
286 {
287 // Set everything except for the field to default when writing the value.
288 const uint32_t current_register_value = 0;
289 const uint32_t result_register_value = set_channels_config_enable_from_value(current_register_value, field_value);
290 set_channels_config(array_index, result_register_value);
291 }
292
293 uint32_t Example::set_channels_config_enable_from_value(
294 uint32_t register_value,
295 uint32_t field_value
296 ) const {
297 const uint32_t shift = 0uL;
298 const uint32_t mask_at_base = 0b1uL;
299 const uint32_t mask_shifted = mask_at_base << shift;
300
301 // Check that field value is within the legal range.
302 const uint32_t mask_at_base_inverse = ~mask_at_base;
303 _SETTER_ASSERT_TRUE(
304 (field_value & mask_at_base_inverse) == 0,
305 "'enable' value too many bits used, got '" << field_value << "'"
306 );
307
308 const uint32_t field_value_masked = field_value & mask_at_base;
309 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
310
311 const uint32_t mask_shifted_inverse = ~mask_shifted;
312 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
313
314 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
315
316 return result_register_value;
317 }
318
319 void Example::set_channels_config_tuser(
320 size_t array_index,
321 uint32_t field_value
322 ) const
323 {
324 // Set everything except for the field to default when writing the value.
325 const uint32_t current_register_value = 0;
326 const uint32_t result_register_value = set_channels_config_tuser_from_value(current_register_value, field_value);
327 set_channels_config(array_index, result_register_value);
328 }
329
330 uint32_t Example::set_channels_config_tuser_from_value(
331 uint32_t register_value,
332 uint32_t field_value
333 ) const {
334 const uint32_t shift = 1uL;
335 const uint32_t mask_at_base = 0b11111111uL;
336 const uint32_t mask_shifted = mask_at_base << shift;
337
338 // Check that field value is within the legal range.
339 const uint32_t mask_at_base_inverse = ~mask_at_base;
340 _SETTER_ASSERT_TRUE(
341 (field_value & mask_at_base_inverse) == 0,
342 "'tuser' value too many bits used, got '" << field_value << "'"
343 );
344
345 const uint32_t field_value_masked = field_value & mask_at_base;
346 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
347
348 const uint32_t mask_shifted_inverse = ~mask_shifted;
349 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
350
351 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
352
353 return result_register_value;
354 }
355
356} /* 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.