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.
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 6.1.1-dev.
2// Code generator CppInterfaceGenerator version 1.0.0.
3// Generated 2024-11-20 20:51 from file toml_format.toml at commit 547d42cf1aaa5f86.
4// Register hash 4df9765ebb584803b583628671e4659579eb85f4.
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 a register 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 a register 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 a register 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 a register 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 everything else to default.
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 a register 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 everything else to default.
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 a register 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 6.1.1-dev.
2// Code generator CppHeaderGenerator version 1.0.0.
3// Generated 2024-11-20 20:51 from file toml_format.toml at commit 547d42cf1aaa5f86.
4// Register hash 4df9765ebb584803b583628671e4659579eb85f4.
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 6.1.1-dev.
2// Code generator CppImplementationGenerator version 1.0.0.
3// Generated 2024-11-20 20:51 from file toml_format.toml at commit 547d42cf1aaa5f86.
4// Register hash 4df9765ebb584803b583628671e4659579eb85f4.
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 field_value = get_config_enable_from_value(register_value);
33
34 return field_value;
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 uint32_t field_value;
49
50 // No casting needed.
51 field_value = result_shifted;
52
53 return field_value;
54 }
55
56 example::config::direction::Enumeration Example::get_config_direction() const
57 {
58 const uint32_t register_value = get_config();
59 const example::config::direction::Enumeration field_value = get_config_direction_from_value(register_value);
60
61 return field_value;
62 }
63
64 example::config::direction::Enumeration Example::get_config_direction_from_value(
65 uint32_t register_value
66 ) const
67 {
68 const uint32_t shift = 1uL;
69 const uint32_t mask_at_base = 0b11uL;
70 const uint32_t mask_shifted = mask_at_base << shift;
71
72 const uint32_t result_masked = register_value & mask_shifted;
73 const uint32_t result_shifted = result_masked >> shift;
74
75 example::config::direction::Enumeration field_value;
76
77 // "Cast" to the enum type.
78 field_value = example::config::direction::Enumeration(result_shifted);
79
80 return field_value;
81 }
82
83 void Example::set_config(
84 uint32_t register_value
85 ) const
86 {
87 const size_t index = 0;
88 m_registers[index] = register_value;
89 }
90
91 void Example::set_config_enable(
92 uint32_t field_value
93 ) const
94 {
95 // Get the current value of other fields by reading register on the bus.
96 const uint32_t current_register_value = get_config();
97 const uint32_t result_register_value = set_config_enable_from_value(current_register_value, field_value);
98 set_config(result_register_value);
99 }
100
101 uint32_t Example::set_config_enable_from_value(
102 uint32_t register_value,
103 uint32_t field_value
104 ) const {
105 const uint32_t shift = 0uL;
106 const uint32_t mask_at_base = 0b1uL;
107 const uint32_t mask_shifted = mask_at_base << shift;
108
109 const uint32_t field_value_masked = field_value & mask_at_base;
110 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
111
112 const uint32_t mask_shifted_inverse = ~mask_shifted;
113 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
114
115 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
116
117 return result_register_value;
118 }
119
120 void Example::set_config_direction(
121 example::config::direction::Enumeration field_value
122 ) const
123 {
124 // Get the current value of other fields by reading register on the bus.
125 const uint32_t current_register_value = get_config();
126 const uint32_t result_register_value = set_config_direction_from_value(current_register_value, field_value);
127 set_config(result_register_value);
128 }
129
130 uint32_t Example::set_config_direction_from_value(
131 uint32_t register_value,
132 example::config::direction::Enumeration field_value
133 ) const {
134 const uint32_t shift = 1uL;
135 const uint32_t mask_at_base = 0b11uL;
136 const uint32_t mask_shifted = mask_at_base << shift;
137
138 const uint32_t field_value_masked = field_value & mask_at_base;
139 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
140
141 const uint32_t mask_shifted_inverse = ~mask_shifted;
142 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
143
144 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
145
146 return result_register_value;
147 }
148
149 // ---------------------------------------------------------------------------
150 // Methods for the 'status' register.
151 // See interface header for documentation.
152
153 uint32_t Example::get_status() const
154 {
155 const size_t index = 1;
156 const uint32_t result = m_registers[index];
157
158 return result;
159 }
160
161 // ---------------------------------------------------------------------------
162 // Methods for the 'read_address' register within the 'channels' register array.
163 // See interface header for documentation.
164
165 uint32_t Example::get_channels_read_address(
166 size_t array_index
167 ) const
168 {
169 assert(array_index < example::channels::array_length);
170 const size_t index = 2 + array_index * 2 + 0;
171 const uint32_t result = m_registers[index];
172
173 return result;
174 }
175
176 void Example::set_channels_read_address(
177 size_t array_index,
178 uint32_t register_value
179 ) const
180 {
181 assert(array_index < example::channels::array_length);
182 const size_t index = 2 + array_index * 2 + 0;
183 m_registers[index] = register_value;
184 }
185
186 // ---------------------------------------------------------------------------
187 // Methods for the 'config' register within the 'channels' register array.
188 // See interface header for documentation.
189
190 void Example::set_channels_config(
191 size_t array_index,
192 uint32_t register_value
193 ) const
194 {
195 assert(array_index < example::channels::array_length);
196 const size_t index = 2 + array_index * 2 + 1;
197 m_registers[index] = register_value;
198 }
199
200 void Example::set_channels_config_enable(
201 size_t array_index,
202 uint32_t field_value
203 ) const
204 {
205 // Set everything except for the field to default when writing the value.
206 const uint32_t current_register_value = 0;
207 const uint32_t result_register_value = set_channels_config_enable_from_value(current_register_value, field_value);
208 set_channels_config(array_index, result_register_value);
209 }
210
211 uint32_t Example::set_channels_config_enable_from_value(
212 uint32_t register_value,
213 uint32_t field_value
214 ) const {
215 const uint32_t shift = 0uL;
216 const uint32_t mask_at_base = 0b1uL;
217 const uint32_t mask_shifted = mask_at_base << shift;
218
219 const uint32_t field_value_masked = field_value & mask_at_base;
220 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
221
222 const uint32_t mask_shifted_inverse = ~mask_shifted;
223 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
224
225 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
226
227 return result_register_value;
228 }
229
230 void Example::set_channels_config_tuser(
231 size_t array_index,
232 uint32_t field_value
233 ) const
234 {
235 // Set everything except for the field to default when writing the value.
236 const uint32_t current_register_value = 0;
237 const uint32_t result_register_value = set_channels_config_tuser_from_value(current_register_value, field_value);
238 set_channels_config(array_index, result_register_value);
239 }
240
241 uint32_t Example::set_channels_config_tuser_from_value(
242 uint32_t register_value,
243 uint32_t field_value
244 ) const {
245 const uint32_t shift = 1uL;
246 const uint32_t mask_at_base = 0b11111111uL;
247 const uint32_t mask_shifted = mask_at_base << shift;
248
249 // Check that field value is within the legal range.
250 const uint32_t mask_at_base_inverse = ~mask_at_base;
251 assert((field_value & mask_at_base_inverse) == 0);
252
253 const uint32_t field_value_masked = field_value & mask_at_base;
254 const uint32_t field_value_masked_and_shifted = field_value_masked << shift;
255
256 const uint32_t mask_shifted_inverse = ~mask_shifted;
257 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
258
259 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
260
261 return result_register_value;
262 }
263
264} /* 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.