C++ 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.

Python code that parses the example TOML file and generates the C++ code we need.
 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 in the Interface header below that there are two ways to read a register field:

  1. The method that reads the whole register, e.g. get_conf(), which returns a struct with all field values.

  2. The method that reads the register and returns the specific field value, e.g. get_conf_enable().

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 select the field values from the struct.

Apart from the methods discussed above, these is also a raw register getter available, e.g. get_conf_raw(). This will read the whole register value and return it without any type conversion. Can be convenient in some special cases, for example when working with interrupt registers.

Setters

Conversely there are two ways to write a register field:

  1. The method that writes the whole register, e.g. set_conf(), which takes a struct of field values as argument.

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

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_conf(), update our field values in the variable, and then call (1) once.

Apart from the methods discussed above, these is also a raw register setter available, e.g. set_conf_raw(). This will write the whole register value without any type conversion. Can be convenient in some special cases, for example when working with interrupt registers.

Exceptions

The discussion about read-modify-write field setters 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 these registers will write all bits outside of the current field as their default value. This can for example be seen in the setter set_channels_conf_enable() in the generated code below.

Assertion macros

There are a few register-related things that can go wrong in an embedded system:

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

  2. Conversely, an error in software might result in writing a field value that is out of bounds.

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

  1. NO_REGISTER_GETTER_ASSERT

  2. NO_REGISTER_SETTER_ASSERT

  3. 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. This contains the public API of the class.

Note that all register constants as well as field attributes are included here. It can also be seen that when a register is part of an array, the setters/getters take a second argument array_index. There is an assert in the Implementation that user-provided array indexes are within bounds.

Example interface header
  1// -----------------------------------------------------------------------------
  2// This file is automatically generated by hdl-registers version 8.0.2-dev.
  3// Code generator CppInterfaceGenerator version 1.0.0.
  4// Generated 2025-05-29 20:52 from file toml_format.toml at Git commit 76c4bba75025.
  5// Register hash cfcadec603051a8e436b0f0693ccaf3e2ab3188a.
  6// -----------------------------------------------------------------------------
  7
  8#pragma once
  9
 10#include <sstream>
 11#include <cstdint>
 12#include <cstdlib>
 13
 14namespace fpga_regs
 15{
 16
 17  namespace example
 18  {
 19
 20    // Attributes for the 'conf' register.
 21    namespace conf {
 22      // Attributes for the 'enable' field.
 23      namespace enable
 24      {
 25        // The number of bits that the field occupies.
 26        static const size_t width = 1;
 27        // The bit index of the lowest bit in the field.
 28        static const size_t shift = 0;
 29        // The bit mask of the field, at index zero.
 30        static const uint32_t mask_at_base = (1uLL << width) - 1;
 31        // The bit mask of the field, at the field's bit index.
 32        static const uint32_t mask_shifted = mask_at_base << shift;
 33
 34        // Initial value of the field at device startup/reset.
 35        static const bool default_value = true;
 36        // Raw representation of the initial value, at the the field's bit index.
 37        static const uint32_t default_value_raw = 1 << shift;
 38      }
 39
 40      // Attributes for the 'direction' field.
 41      namespace direction
 42      {
 43        // The number of bits that the field occupies.
 44        static const size_t width = 2;
 45        // The bit index of the lowest bit in the field.
 46        static const size_t shift = 1;
 47        // The bit mask of the field, at index zero.
 48        static const uint32_t mask_at_base = (1uLL << width) - 1;
 49        // The bit mask of the field, at the field's bit index.
 50        static const uint32_t mask_shifted = mask_at_base << shift;
 51
 52        // The valid elements for this enumeration field.
 53        enum Enumeration
 54        {
 55          data_in = 0,
 56          high_z = 1,
 57          data_out = 2,
 58        };
 59
 60        // Initial value of the field at device startup/reset.
 61        static const Enumeration default_value = Enumeration::high_z;
 62        // Raw representation of the initial value, at the the field's bit index.
 63        static const uint32_t default_value_raw = 1uL << shift;
 64      }
 65
 66      // Struct that holds the value of each field in the register,
 67      // in a native C++ representation.
 68      struct Value {
 69        bool enable;
 70        direction::Enumeration direction;
 71      };
 72      // Initial value of the register at device startup/reset.
 73      const Value default_value = {
 74        enable::default_value,
 75        direction::default_value,
 76      };
 77    }
 78
 79    // Attributes for the 'channels' register array.
 80    namespace channels
 81    {
 82      // Number of times the registers of the array are repeated.
 83      static const auto array_length = 4;
 84
 85      // Attributes for the 'conf' register.
 86      namespace conf {
 87        // Attributes for the 'enable' field.
 88        namespace enable
 89        {
 90          // The number of bits that the field occupies.
 91          static const size_t width = 1;
 92          // The bit index of the lowest bit in the field.
 93          static const size_t shift = 0;
 94          // The bit mask of the field, at index zero.
 95          static const uint32_t mask_at_base = (1uLL << width) - 1;
 96          // The bit mask of the field, at the field's bit index.
 97          static const uint32_t mask_shifted = mask_at_base << shift;
 98
 99          // Initial value of the field at device startup/reset.
100          static const bool default_value = false;
101          // Raw representation of the initial value, at the the field's bit index.
102          static const uint32_t default_value_raw = 0 << shift;
103        }
104
105        // Attributes for the 'tuser' field.
106        namespace tuser
107        {
108          // The number of bits that the field occupies.
109          static const size_t width = 8;
110          // The bit index of the lowest bit in the field.
111          static const size_t shift = 1;
112          // The bit mask of the field, at index zero.
113          static const uint32_t mask_at_base = (1uLL << width) - 1;
114          // The bit mask of the field, at the field's bit index.
115          static const uint32_t mask_shifted = mask_at_base << shift;
116
117          // Initial value of the field at device startup/reset.
118          static const uint32_t default_value = 0;
119          // Raw representation of the initial value, at the the field's bit index.
120          static const uint32_t default_value_raw = 0b00000000uL << shift;
121        }
122
123        // Struct that holds the value of each field in the register,
124        // in a native C++ representation.
125        struct Value {
126          bool enable;
127          uint32_t tuser;
128        };
129        // Initial value of the register at device startup/reset.
130        const Value default_value = {
131          enable::default_value,
132          tuser::default_value,
133        };
134      }
135    }
136
137  } // namespace example
138
139  class IExample
140  {
141  public:
142    // Register constant.
143    static const int axi_data_width = 64;
144    // Register constant.
145    static constexpr double clock_rate_hz = 156250000.0;
146
147    // Number of registers within this register list.
148    static const size_t num_registers = 10uL;
149
150    virtual ~IExample() {}
151
152    // -------------------------------------------------------------------------
153    // Methods for the 'conf' register.
154    // Mode 'Read, Write'.
155    // -------------------------------------------------------------------------
156    // Read the whole register value over the register bus.
157    virtual example::conf::Value get_conf() const = 0;
158
159    // Read the whole register value over the register bus.
160    // This method returns the raw register value without any type conversion.
161    virtual uint32_t get_conf_raw() const = 0;
162
163    // Read the register and slice out the 'enable' field value.
164    virtual bool get_conf_enable() const = 0;
165
166    // Read the register and slice out the 'direction' field value.
167    virtual example::conf::direction::Enumeration get_conf_direction() const = 0;
168
169    // Write the whole register value over the register bus.
170    virtual void set_conf(
171      example::conf::Value register_value
172    ) const = 0;
173
174    // Write the whole register value over the register bus.
175    // This method takes a raw register value and does not perform any type conversion.
176    virtual void set_conf_raw(
177      uint32_t register_value
178    ) const = 0;
179
180    // Write the 'enable' field value.
181    // Will read-modify-write the register.
182    virtual void set_conf_enable(
183      bool field_value
184    ) const = 0;
185
186    // Write the 'direction' field value.
187    // Will read-modify-write the register.
188    virtual void set_conf_direction(
189      example::conf::direction::Enumeration field_value
190    ) const = 0;
191    // -------------------------------------------------------------------------
192
193    // -------------------------------------------------------------------------
194    // Methods for the 'status' register.
195    // Mode 'Read'.
196    // -------------------------------------------------------------------------
197    // Read the whole register value over the register bus.
198    virtual uint32_t get_status() const = 0;
199    // -------------------------------------------------------------------------
200
201    // -------------------------------------------------------------------------
202    // Methods for the 'read_address' register within the 'channels' register array.
203    // Mode 'Read, Write'.
204    // -------------------------------------------------------------------------
205    // Read the whole register value over the register bus.
206    virtual uint32_t get_channels_read_address(
207      size_t array_index
208    ) const = 0;
209
210    // Write the whole register value over the register bus.
211    virtual void set_channels_read_address(
212      size_t array_index,
213      uint32_t register_value
214    ) const = 0;
215    // -------------------------------------------------------------------------
216
217    // -------------------------------------------------------------------------
218    // Methods for the 'conf' register within the 'channels' register array.
219    // Mode 'Write'.
220    // -------------------------------------------------------------------------
221    // Write the whole register value over the register bus.
222    virtual void set_channels_conf(
223      size_t array_index,
224      example::channels::conf::Value register_value
225    ) const = 0;
226
227    // Write the whole register value over the register bus.
228    // This method takes a raw register value and does not perform any type conversion.
229    virtual void set_channels_conf_raw(
230      size_t array_index,
231      uint32_t register_value
232    ) const = 0;
233
234    // Write the 'enable' field value.
235    // Will write the register with all other fields set as default.
236    virtual void set_channels_conf_enable(
237      size_t array_index,
238      bool field_value
239    ) const = 0;
240
241    // Write the 'tuser' field value.
242    // Will write the register with all other fields set as default.
243    virtual void set_channels_conf_tuser(
244      size_t array_index,
245      uint32_t field_value
246    ) const = 0;
247    // -------------------------------------------------------------------------
248  };
249
250} // namespace fpga_regs

Class header

Below is the generated class header. This overrides from the interface header and adds some internal private methods.

Example class header
  1// -----------------------------------------------------------------------------
  2// This file is automatically generated by hdl-registers version 8.0.2-dev.
  3// Code generator CppHeaderGenerator version 1.0.0.
  4// Generated 2025-05-29 20:52 from file toml_format.toml at Git commit 76c4bba75025.
  5// Register hash cfcadec603051a8e436b0f0693ccaf3e2ab3188a.
  6// -----------------------------------------------------------------------------
  7
  8#pragma once
  9
 10#include "i_example.h"
 11
 12namespace fpga_regs
 13{
 14
 15  class Example : public IExample
 16  {
 17  public:
 18    /**
 19     * Class constructor.
 20     * @param base_address Byte address where these registers are memory mapped.
 21     *                     Can be e.g. '0x43C00000' in bare metal, or e.g.
 22     *                     'reinterpret_cast<uintptr_t>(mmap(...))' in Linux.
 23     *                     When using an operating system, care must be taken to pass the
 24     *                     virtual address, not the physical address.
 25     *                     When using bare metal, these are the same.
 26     * @param assertion_handler Function to call when an assertion fails.
 27     *                          Function takes a string pointer as an argument, where the string
 28     *                          will contain an error diagnostic message.
 29     *                          Function must return a boolean 'true'.
 30     */
 31    Example(uintptr_t base_address, bool (*assertion_handler) (const std::string*));
 32
 33    virtual ~Example() {}
 34
 35    // -------------------------------------------------------------------------
 36    // Methods for the 'conf' register.
 37    // Mode 'Read, Write'.
 38    // -------------------------------------------------------------------------
 39    // Read the whole register value over the register bus.
 40    virtual example::conf::Value get_conf() const override;
 41
 42    // Read the whole register value over the register bus.
 43    // This method returns the raw register value without any type conversion.
 44    virtual uint32_t get_conf_raw() const override;
 45
 46    // Read the register and slice out the 'enable' field value.
 47    virtual bool get_conf_enable() const override;
 48
 49    // Read the register and slice out the 'direction' field value.
 50    virtual example::conf::direction::Enumeration get_conf_direction() const override;
 51
 52    // Write the whole register value over the register bus.
 53    virtual void set_conf(
 54      example::conf::Value register_value
 55    ) const override;
 56
 57    // Write the whole register value over the register bus.
 58    // This method takes a raw register value and does not perform any type conversion.
 59    virtual void set_conf_raw(
 60      uint32_t register_value
 61    ) const override;
 62
 63    // Write the 'enable' field value.
 64    // Will read-modify-write the register.
 65    virtual void set_conf_enable(
 66      bool field_value
 67    ) const override;
 68
 69    // Write the 'direction' field value.
 70    // Will read-modify-write the register.
 71    virtual void set_conf_direction(
 72      example::conf::direction::Enumeration field_value
 73    ) const override;
 74    // -------------------------------------------------------------------------
 75
 76    // -------------------------------------------------------------------------
 77    // Methods for the 'status' register.
 78    // Mode 'Read'.
 79    // -------------------------------------------------------------------------
 80    // Read the whole register value over the register bus.
 81    virtual uint32_t get_status() const override;
 82    // -------------------------------------------------------------------------
 83
 84    // -------------------------------------------------------------------------
 85    // Methods for the 'read_address' register within the 'channels' register array.
 86    // Mode 'Read, Write'.
 87    // -------------------------------------------------------------------------
 88    // Read the whole register value over the register bus.
 89    virtual uint32_t get_channels_read_address(
 90      size_t array_index
 91    ) const override;
 92
 93    // Write the whole register value over the register bus.
 94    virtual void set_channels_read_address(
 95      size_t array_index,
 96      uint32_t register_value
 97    ) const override;
 98    // -------------------------------------------------------------------------
 99
100    // -------------------------------------------------------------------------
101    // Methods for the 'conf' register within the 'channels' register array.
102    // Mode 'Write'.
103    // -------------------------------------------------------------------------
104    // Write the whole register value over the register bus.
105    virtual void set_channels_conf(
106      size_t array_index,
107      example::channels::conf::Value register_value
108    ) const override;
109
110    // Write the whole register value over the register bus.
111    // This method takes a raw register value and does not perform any type conversion.
112    virtual void set_channels_conf_raw(
113      size_t array_index,
114      uint32_t register_value
115    ) const override;
116
117    // Write the 'enable' field value.
118    // Will write the register with all other fields set as default.
119    virtual void set_channels_conf_enable(
120      size_t array_index,
121      bool field_value
122    ) const override;
123
124    // Write the 'tuser' field value.
125    // Will write the register with all other fields set as default.
126    virtual void set_channels_conf_tuser(
127      size_t array_index,
128      uint32_t field_value
129    ) const override;
130    // -------------------------------------------------------------------------
131
132  private:
133    volatile uint32_t *m_registers;
134    bool (*m_assertion_handler) (const std::string*);
135
136    // -------------------------------------------------------------------------
137    // Methods for the 'conf' register.
138    // Mode 'Read, Write'.
139    // -------------------------------------------------------------------------
140    // Slice out the 'enable' field value from a given raw register value.
141    // Performs no operation on the register bus.
142    bool get_conf_enable_from_raw(
143      uint32_t register_value
144    ) const;
145
146    // Slice out the 'direction' field value from a given raw register value.
147    // Performs no operation on the register bus.
148    example::conf::direction::Enumeration get_conf_direction_from_raw(
149      uint32_t register_value
150    ) const;
151
152    // Get the raw representation of a given 'enable' field value.
153    // Performs no operation on the register bus.
154    uint32_t get_conf_enable_to_raw(bool field_value) const;
155
156    // Get the raw representation of a given 'direction' field value.
157    // Performs no operation on the register bus.
158    uint32_t get_conf_direction_to_raw(example::conf::direction::Enumeration field_value) const;
159    // -------------------------------------------------------------------------
160
161    // -------------------------------------------------------------------------
162    // Methods for the 'conf' register within the 'channels' register array.
163    // Mode 'Write'.
164    // -------------------------------------------------------------------------
165    // Get the raw representation of a given 'enable' field value.
166    // Performs no operation on the register bus.
167    uint32_t get_channels_conf_enable_to_raw(bool field_value) const;
168
169    // Get the raw representation of a given 'tuser' field value.
170    // Performs no operation on the register bus.
171    uint32_t get_channels_conf_tuser_to_raw(uint32_t field_value) const;
172    // -------------------------------------------------------------------------
173  };
174
175} // namespace fpga_regs

Implementation

Below is the generated class implementation. This implements all the methods of the class, whether public from the Interface header or private from the Class header.

Example class implementation
  1// -----------------------------------------------------------------------------
  2// This file is automatically generated by hdl-registers version 8.0.2-dev.
  3// Code generator CppImplementationGenerator version 2.0.2.
  4// Generated 2025-05-29 20:52 from file toml_format.toml at Git commit 76c4bba75025.
  5// Register hash cfcadec603051a8e436b0f0693ccaf3e2ab3188a.
  6// -----------------------------------------------------------------------------
  7
  8#include "include/example.h"
  9
 10namespace fpga_regs
 11{
 12
 13// Macros called by the register code below to check for runtime errors.
 14#ifdef NO_REGISTER_SETTER_ASSERT
 15  #define _SETTER_ASSERT_TRUE(expression, message) ((void)0)
 16#else
 17  #define _SETTER_ASSERT_TRUE(expression, message) (_ASSERT_TRUE(expression, message))
 18#endif
 19
 20#ifdef NO_REGISTER_GETTER_ASSERT
 21  #define _GETTER_ASSERT_TRUE(expression, message) ((void)0)
 22#else
 23  #define _GETTER_ASSERT_TRUE(expression, message) (_ASSERT_TRUE(expression, message))
 24#endif
 25
 26#ifdef NO_REGISTER_ARRAY_INDEX_ASSERT
 27  #define _ARRAY_INDEX_ASSERT_TRUE(expression, message) ((void)0)
 28#else
 29  #define _ARRAY_INDEX_ASSERT_TRUE(expression, message) (_ASSERT_TRUE(expression, message))
 30#endif
 31
 32// Base macro called by the other macros.
 33#define _ASSERT_TRUE(expression, message)                                                      \
 34  {                                                                                            \
 35    if (!static_cast<bool>(expression)) {                                                      \
 36      std::ostringstream diagnostics;                                                          \
 37      diagnostics << "example.cpp:" << __LINE__ << ": " << message << ".";                     \
 38      std::string diagnostic_message = diagnostics.str();                                      \
 39      m_assertion_handler(&diagnostic_message);                                                \
 40    }                                                                                          \
 41  }
 42
 43  Example::Example(uintptr_t base_address, bool (*assertion_handler) (const std::string*))
 44      : m_registers(reinterpret_cast<volatile uint32_t *>(base_address)),
 45        m_assertion_handler(assertion_handler)
 46  {
 47    // Empty
 48  }
 49
 50  // ---------------------------------------------------------------------------
 51  // Methods for the 'conf' register.
 52  // Mode 'Read, Write'.
 53  // ---------------------------------------------------------------------------
 54  // Read the whole register value over the register bus.
 55  example::conf::Value Example::get_conf() const
 56  {
 57    const uint32_t raw_value = get_conf_raw();
 58
 59    const bool enable_value = get_conf_enable_from_raw(raw_value);
 60    const example::conf::direction::Enumeration direction_value = get_conf_direction_from_raw(raw_value);
 61
 62    return {enable_value, direction_value};
 63  }
 64
 65  // Read the whole register value over the register bus.
 66  // This method returns the raw register value without any type conversion.
 67  uint32_t Example::get_conf_raw() const
 68  {
 69    const size_t index = 0;
 70    const uint32_t raw_value = m_registers[index];
 71
 72    return raw_value;
 73  }
 74
 75  // Read the register and slice out the 'enable' field value.
 76  bool Example::get_conf_enable() const
 77  {
 78    const uint32_t raw_value = get_conf_raw();
 79
 80    return get_conf_enable_from_raw(raw_value);
 81  }
 82
 83  // Slice out the 'enable' field value from a given raw register value.
 84  // Performs no operation on the register bus.
 85  bool Example::get_conf_enable_from_raw(
 86    uint32_t register_value
 87  ) const
 88  {
 89    const uint32_t result_masked = register_value & example::conf::enable::mask_shifted;
 90    const uint32_t result_shifted = result_masked >> example::conf::enable::shift;
 91
 92    // Convert to the result type.
 93    const bool field_value = static_cast<bool>(result_shifted);
 94
 95    return field_value;
 96  }
 97
 98  // Read the register and slice out the 'direction' field value.
 99  example::conf::direction::Enumeration Example::get_conf_direction() const
100  {
101    const uint32_t raw_value = get_conf_raw();
102
103    return get_conf_direction_from_raw(raw_value);
104  }
105
106  // Slice out the 'direction' field value from a given raw register value.
107  // Performs no operation on the register bus.
108  example::conf::direction::Enumeration Example::get_conf_direction_from_raw(
109    uint32_t register_value
110  ) const
111  {
112    const uint32_t result_masked = register_value & example::conf::direction::mask_shifted;
113    const uint32_t result_shifted = result_masked >> example::conf::direction::shift;
114
115    // "Cast" to the enum type.
116    const auto field_value = example::conf::direction::Enumeration(result_shifted);
117
118    _GETTER_ASSERT_TRUE(
119      field_value <= 2,
120      "Got 'direction' value out of range: " << field_value
121    );
122
123    return field_value;
124  }
125
126  // Write the whole register value over the register bus.
127  void Example::set_conf(
128    example::conf::Value register_value
129  ) const
130  {
131    const uint32_t enable_value = get_conf_enable_to_raw(register_value.enable);
132    const uint32_t direction_value = get_conf_direction_to_raw(register_value.direction);
133    const uint32_t raw_value = enable_value | direction_value;
134
135    set_conf_raw(raw_value);
136  }
137
138  // Write the whole register value over the register bus.
139  // This method takes a raw register value and does not perform any type conversion.
140  void Example::set_conf_raw(
141    uint32_t register_value
142  ) const
143  {
144    const size_t index = 0;
145    m_registers[index] = register_value;
146  }
147
148  // Write the 'enable' field value.
149  // Will read-modify-write the register.
150  void Example::set_conf_enable(
151    bool field_value
152  ) const
153  {
154    const size_t index = 0;
155
156    const uint32_t raw_value = m_registers[index];
157    const uint32_t mask_shifted_inverse = ~example::conf::enable::mask_shifted;
158    const uint32_t base_value = raw_value & mask_shifted_inverse;
159
160    const uint32_t field_value_raw = get_conf_enable_to_raw(field_value);
161    const uint32_t register_value = base_value | field_value_raw;
162
163    m_registers[index] = register_value;
164  }
165
166  // Get the raw representation of a given 'enable' field value.
167  // Performs no operation on the register bus.
168  uint32_t Example::get_conf_enable_to_raw(bool field_value) const
169  {
170    const uint32_t field_value_casted = static_cast<uint32_t>(field_value);
171    const uint32_t field_value_shifted = field_value_casted << example::conf::enable::shift;
172
173    return field_value_shifted;
174  }
175
176  // Write the 'direction' field value.
177  // Will read-modify-write the register.
178  void Example::set_conf_direction(
179    example::conf::direction::Enumeration field_value
180  ) const
181  {
182    const size_t index = 0;
183
184    const uint32_t raw_value = m_registers[index];
185    const uint32_t mask_shifted_inverse = ~example::conf::direction::mask_shifted;
186    const uint32_t base_value = raw_value & mask_shifted_inverse;
187
188    const uint32_t field_value_raw = get_conf_direction_to_raw(field_value);
189    const uint32_t register_value = base_value | field_value_raw;
190
191    m_registers[index] = register_value;
192  }
193
194  // Get the raw representation of a given 'direction' field value.
195  // Performs no operation on the register bus.
196  uint32_t Example::get_conf_direction_to_raw(example::conf::direction::Enumeration field_value) const
197  {
198    _SETTER_ASSERT_TRUE(
199      field_value <= 2,
200      "Got 'direction' value out of range: " << field_value
201    );
202
203    const uint32_t field_value_casted = static_cast<uint32_t>(field_value);
204    const uint32_t field_value_shifted = field_value_casted << example::conf::direction::shift;
205
206    return field_value_shifted;
207  }
208  // ---------------------------------------------------------------------------
209
210  // ---------------------------------------------------------------------------
211  // Methods for the 'status' register.
212  // Mode 'Read'.
213  // ---------------------------------------------------------------------------
214  // Read the whole register value over the register bus.
215  uint32_t Example::get_status() const
216  {
217    const size_t index = 1;
218    const uint32_t raw_value = m_registers[index];
219
220    return raw_value;
221  }
222  // ---------------------------------------------------------------------------
223
224  // ---------------------------------------------------------------------------
225  // Methods for the 'read_address' register within the 'channels' register array.
226  // Mode 'Read, Write'.
227  // ---------------------------------------------------------------------------
228  // Read the whole register value over the register bus.
229  uint32_t Example::get_channels_read_address(
230    size_t array_index
231  ) const
232  {
233    _ARRAY_INDEX_ASSERT_TRUE(
234      array_index < example::channels::array_length,
235      "Got 'channels' array index out of range: " << array_index
236    );
237    const size_t index = 2 + array_index * 2 + 0;
238    const uint32_t raw_value = m_registers[index];
239
240    return raw_value;
241  }
242
243  // Write the whole register value over the register bus.
244  void Example::set_channels_read_address(
245    size_t array_index,
246    uint32_t register_value
247  ) const
248  {
249    _ARRAY_INDEX_ASSERT_TRUE(
250      array_index < example::channels::array_length,
251      "Got 'channels' array index out of range: " << array_index
252    );
253    const size_t index = 2 + array_index * 2 + 0;
254    m_registers[index] = register_value;
255  }
256  // ---------------------------------------------------------------------------
257
258  // ---------------------------------------------------------------------------
259  // Methods for the 'conf' register within the 'channels' register array.
260  // Mode 'Write'.
261  // ---------------------------------------------------------------------------
262  // Write the whole register value over the register bus.
263  void Example::set_channels_conf(
264    size_t array_index,
265    example::channels::conf::Value register_value
266  ) const
267  {
268    const uint32_t enable_value = get_channels_conf_enable_to_raw(register_value.enable);
269    const uint32_t tuser_value = get_channels_conf_tuser_to_raw(register_value.tuser);
270    const uint32_t raw_value = enable_value | tuser_value;
271
272    set_channels_conf_raw(array_index, raw_value);
273  }
274
275  // Write the whole register value over the register bus.
276  // This method takes a raw register value and does not perform any type conversion.
277  void Example::set_channels_conf_raw(
278    size_t array_index,
279    uint32_t register_value
280  ) const
281  {
282    _ARRAY_INDEX_ASSERT_TRUE(
283      array_index < example::channels::array_length,
284      "Got 'channels' array index out of range: " << array_index
285    );
286    const size_t index = 2 + array_index * 2 + 1;
287    m_registers[index] = register_value;
288  }
289
290  // Write the 'enable' field value.
291  // Will write the register with all other fields set as default.
292  void Example::set_channels_conf_enable(
293    size_t array_index,
294    bool field_value
295  ) const
296  {
297    _ARRAY_INDEX_ASSERT_TRUE(
298      array_index < example::channels::array_length,
299      "Got 'channels' array index out of range: " << array_index
300    );
301    const size_t index = 2 + array_index * 2 + 1;
302
303    const uint32_t base_value = example::channels::conf::tuser::default_value_raw;
304
305    const uint32_t field_value_raw = get_channels_conf_enable_to_raw(field_value);
306    const uint32_t register_value = base_value | field_value_raw;
307
308    m_registers[index] = register_value;
309  }
310
311  // Get the raw representation of a given 'enable' field value.
312  // Performs no operation on the register bus.
313  uint32_t Example::get_channels_conf_enable_to_raw(bool field_value) const
314  {
315    const uint32_t field_value_casted = static_cast<uint32_t>(field_value);
316    const uint32_t field_value_shifted = field_value_casted << example::channels::conf::enable::shift;
317
318    return field_value_shifted;
319  }
320
321  // Write the 'tuser' field value.
322  // Will write the register with all other fields set as default.
323  void Example::set_channels_conf_tuser(
324    size_t array_index,
325    uint32_t field_value
326  ) const
327  {
328    _ARRAY_INDEX_ASSERT_TRUE(
329      array_index < example::channels::array_length,
330      "Got 'channels' array index out of range: " << array_index
331    );
332    const size_t index = 2 + array_index * 2 + 1;
333
334    const uint32_t base_value = example::channels::conf::enable::default_value_raw;
335
336    const uint32_t field_value_raw = get_channels_conf_tuser_to_raw(field_value);
337    const uint32_t register_value = base_value | field_value_raw;
338
339    m_registers[index] = register_value;
340  }
341
342  // Get the raw representation of a given 'tuser' field value.
343  // Performs no operation on the register bus.
344  uint32_t Example::get_channels_conf_tuser_to_raw(uint32_t field_value) const
345  {
346    _SETTER_ASSERT_TRUE(
347      field_value <= 255,
348      "Got 'tuser' value out of range: " << field_value
349    );
350
351    const uint32_t field_value_shifted = field_value << example::channels::conf::tuser::shift;
352
353    return field_value_shifted;
354  }
355  // ---------------------------------------------------------------------------
356
357} // namespace fpga_regs