Coverage for hdl_registers/generator/cpp/header.py: 96%
79 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 06:41 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 06:41 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the hdl-registers project, an HDL register generator fast enough to run
5# in real time.
6# https://hdl-registers.com
7# https://github.com/hdl-registers/hdl-registers
8# --------------------------------------------------------------------------------------------------
10from __future__ import annotations
12from typing import TYPE_CHECKING, Any
14from .cpp_generator_common import CppGeneratorCommon
16if TYPE_CHECKING:
17 from pathlib import Path
19 from hdl_registers.register import Register
20 from hdl_registers.register_array import RegisterArray
23class CppHeaderGenerator(CppGeneratorCommon):
24 """
25 Generate a C++ class header.
26 See the :ref:`generator_cpp` article for usage details.
28 The class header will contain:
30 * for each register, signature of getter and setter methods for reading/writing the register as
31 an ``uint``.
33 * for each field in each register, signature of getter and setter methods for reading/writing
34 the field as its native type (enumeration, positive/negative int, etc.).
36 * The setter will read-modify-write the register to update only the specified field,
37 depending on the mode of the register.
38 """
40 __version__ = "1.0.0"
42 SHORT_DESCRIPTION = "C++ header"
44 DEFAULT_INDENTATION_LEVEL = 4
46 @property
47 def output_file(self) -> Path:
48 """
49 Result will be placed in this file.
50 """
51 return self.output_folder / f"{self.name}.h"
53 def get_code(
54 self,
55 **kwargs: Any, # noqa: ANN401, ARG002
56 ) -> str:
57 """
58 Get a complete C++ class header with methods for accessing registers and fields.
59 """
60 public_cpp = ""
61 private_cpp = ""
62 separator = self.get_separator_line()
64 for register, register_array in self.iterate_registers():
65 heading = self._get_register_heading(
66 register=register, register_array=register_array, separator=separator
67 )
68 public_cpp += heading
70 register_has_private_methods = len(register.fields) != 0
71 if register_has_private_methods:
72 private_cpp += heading
74 if register.mode.software_can_read:
75 public_getters, private_getters = self._get_getters(
76 register=register, register_array=register_array
77 )
78 public_cpp += public_getters
79 private_cpp += private_getters
81 if register.mode.software_can_write:
82 # Add empty line between getter and setter interfaces.
83 public_cpp += "\n"
84 if register_has_private_methods:
85 private_cpp += "\n"
87 if register.mode.software_can_write:
88 public_setters, private_setters = self._get_setters(
89 register=register, register_array=register_array
90 )
91 public_cpp += public_setters
92 private_cpp += private_setters
94 public_cpp += separator
95 if register_has_private_methods:
96 private_cpp += separator
98 cpp_code = f"""\
99 class {self._class_name} : public I{self._class_name}
100 {
101 public:
102 /**
103 * Class constructor.
104 * @param base_address Byte address where these registers are memory mapped.
105 * Can be e.g. '0x43C00000' in bare metal, or e.g.
106 * 'reinterpret_cast<uintptr_t>(mmap(...))' in Linux.
107 * When using an operating system, care must be taken to pass the
108 * virtual address, not the physical address.
109 * When using bare metal, these are the same.
110 * @param assertion_handler Function to call when an assertion fails.
111 * Function takes a string pointer as an argument, where the string
112 * will contain an error diagnostic message.
113 * Function must return a boolean 'true'.
114 */
115 {self._constructor_signature()};
117 virtual ~{self._class_name}() { }
118{public_cpp}
119 private:
120 volatile uint32_t *m_registers;
121 bool (*m_assertion_handler) (const std::string*);
122{private_cpp}\
123 } ;
125"""
126 cpp_code_top = f"""\
127#pragma once
129#include "i_{self.name}.h"
131"""
132 return cpp_code_top + self._with_namespace(cpp_code)
134 def _get_getters(
135 self, register: Register, register_array: RegisterArray | None
136 ) -> tuple[str, str]:
137 def get_from_raw_function(comment: str, return_type: str, signature: str) -> str:
138 return f"""\
139{comment}\
140 {return_type} {signature};
141"""
143 public_cpp: list[str] = []
144 private_cpp: list[str] = []
146 register_type = self._get_register_value_type(
147 register=register, register_array=register_array
148 )
149 signature = self._register_getter_signature(
150 register=register, register_array=register_array
151 )
152 public_cpp.append(
153 self._get_override_function(
154 comment=self._get_getter_comment(),
155 return_type=register_type,
156 signature=signature,
157 )
158 )
160 if register.fields:
161 # The main getter will perform type conversion.
162 # Provide a getter that returns the raw value also.
163 signature = self._register_getter_signature(
164 register=register, register_array=register_array, raw=True
165 )
166 public_cpp.append(
167 self._get_override_function(
168 comment=self._get_getter_comment(raw=True),
169 return_type="uint32_t",
170 signature=signature,
171 )
172 )
174 for field in register.fields:
175 field_type = self._get_field_value_type(
176 register=register, register_array=register_array, field=field
177 )
179 signature = self._field_getter_signature(
180 register=register,
181 register_array=register_array,
182 field=field,
183 from_raw=False,
184 )
185 public_cpp.append(
186 self._get_override_function(
187 comment=self._get_getter_comment(field=field),
188 return_type=field_type,
189 signature=signature,
190 )
191 )
193 signature = self._field_getter_signature(
194 register=register,
195 register_array=register_array,
196 field=field,
197 from_raw=True,
198 )
199 private_cpp.append(
200 get_from_raw_function(
201 comment=self._get_from_raw_comment(field=field),
202 return_type=field_type,
203 signature=signature,
204 )
205 )
207 return "\n".join(public_cpp), "\n".join(private_cpp)
209 @staticmethod
210 def _get_override_function(comment: str, return_type: str, signature: str) -> str:
211 return f"""\
212{comment}\
213 virtual {return_type} {signature} override;
214"""
216 def _get_setters(
217 self, register: Register, register_array: RegisterArray | None
218 ) -> tuple[str, str]:
219 def get_to_raw_function(comment: str, signature: str) -> str:
220 return f"""\
221{comment}\
222 uint32_t {signature};
223"""
225 public_cpp: list[str] = []
226 private_cpp: list[str] = []
228 signature = self._register_setter_signature(
229 register=register, register_array=register_array
230 )
231 public_cpp.append(
232 self._get_override_function(
233 comment=self._get_setter_comment(register=register),
234 return_type="void",
235 signature=signature,
236 )
237 )
239 if register.fields:
240 # The main setter will perform type conversion.
241 # Provide a setter that takes a raw value also.
242 signature = self._register_setter_signature(
243 register=register, register_array=register_array, raw=True
244 )
245 public_cpp.append(
246 self._get_override_function(
247 comment=self._get_setter_comment(register=register, raw=True),
248 return_type="void",
249 signature=signature,
250 )
251 )
253 for field in register.fields:
254 signature = self._field_setter_signature(
255 register=register,
256 register_array=register_array,
257 field=field,
258 from_raw=False,
259 )
260 public_cpp.append(
261 self._get_override_function(
262 comment=self._get_setter_comment(register=register, field=field),
263 return_type="void",
264 signature=signature,
265 )
266 )
268 signature = self._field_to_raw_signature(
269 register=register, register_array=register_array, field=field
270 )
271 private_cpp.append(
272 get_to_raw_function(
273 comment=self._get_to_raw_comment(field=field), signature=signature
274 )
275 )
277 return "\n".join(public_cpp), "\n".join(private_cpp)