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:

Python code that parses the example TOML file and generates the C++ code we need.
 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:

  1. The method that reads the whole register, e.g. get_configuration().

  2. The method that reads the register and then slices out the field value, e.g. get_configuration_enable().

  3. 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:

  1. The method that writes the whole register, e.g. set_configuration().

  2. The method that reads the register, updates the value of the field, and then writes the register back, e.g. set_configuration_enable().

  3. 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.

Example interface header
  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:

Example 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:

Example 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.