Coverage for hdl_registers/generator/cpp/implementation.py: 97%
142 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-28 20:51 +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# --------------------------------------------------------------------------------------------------
10# Standard libraries
11from pathlib import Path
12from typing import TYPE_CHECKING, Any, Optional
14# First party libraries
15from hdl_registers.field.bit import Bit
16from hdl_registers.field.bit_vector import BitVector
17from hdl_registers.field.enumeration import Enumeration
18from hdl_registers.field.integer import Integer
20# Local folder libraries
21from .cpp_generator_common import CppGeneratorCommon
23if TYPE_CHECKING:
24 # First party libraries
25 from hdl_registers.field.register_field import RegisterField
26 from hdl_registers.register import Register
27 from hdl_registers.register_array import RegisterArray
30class CppImplementationGenerator(CppGeneratorCommon):
31 """
32 Generate a C++ class implementation.
33 See the :ref:`generator_cpp` article for usage details.
35 The class implementation will contain:
37 * for each register, implementation of getter and setter methods for reading/writing the
38 register as an ``uint``.
40 * for each field in each register, implementation of getter and setter methods for
41 reading/writing the field as its native type (enumeration, positive/negative int, etc.).
43 * The setter will read-modify-write the register to update only the specified field,
44 depending on the mode of the register.
45 """
47 __version__ = "2.0.0"
49 SHORT_DESCRIPTION = "C++ implementation"
51 DEFAULT_INDENTATION_LEVEL = 4
53 @property
54 def output_file(self) -> Path:
55 """
56 Result will be placed in this file.
57 """
58 return self.output_folder / f"{self.name}.cpp"
60 def get_code(self, **kwargs: Any) -> str:
61 """
62 Get a complete C++ class implementation with all methods.
63 """
64 cpp_code = f"""\
65{self._macros()}\
66 {self._class_name}::{self._constructor_signature()}
67 : m_registers(reinterpret_cast<volatile uint32_t *>(base_address)),
68 m_assertion_handler(assertion_handler)
69 {
70 // Empty
71 }
73"""
75 for register, register_array in self.iterate_registers():
76 cpp_code += f"{self.get_separator_line(indent=2)}"
78 description = self._get_methods_description(
79 register=register, register_array=register_array
80 )
81 cpp_code += self.comment_block(
82 text=[description, "See interface header for documentation."], indent=2
83 )
84 cpp_code += "\n"
86 if register.mode.software_can_read:
87 cpp_code += self._register_getter_function(register, register_array)
89 for field in register.fields:
90 cpp_code += self._field_getter_function(register, register_array, field=field)
91 cpp_code += self._field_getter_function_from_value(
92 register, register_array, field=field
93 )
95 if register.mode.software_can_write:
96 cpp_code += self._register_setter_function(register, register_array)
98 for field in register.fields:
99 cpp_code += self._field_setter_function(register, register_array, field=field)
100 cpp_code += self._field_setter_function_from_value(
101 register, register_array, field=field
102 )
104 cpp_code_top = f'#include "include/{self.name}.h"\n\n'
106 return cpp_code_top + self._with_namespace(cpp_code)
108 def _macros(self) -> str:
109 def get_macro(name: str, message: str) -> str:
110 macro_name = f"_{name}_ASSERT_TRUE"
111 guard_name = f"NO_REGISTER_{name}_ASSERT"
112 name_space = " " * (38 - len(name))
113 message_space = " " * (21 - len(message))
114 base = """\
115#ifdef {guard_name}
117#define {macro_name}(expression, message) ((void)0)
119#else // Not {guard_name}.
121// This macro is called by the register code to check for runtime errors.
122#define {macro_name}(expression, message) {name_space}\\
123 {{ \\
124 if (!static_cast<bool>(expression)) {{ \\
125 std::ostringstream diagnostics; \\
126 diagnostics << "{message} out of range in " << __FILE__ << ":" {message_space}\\
127 << __LINE__ << ", message: " << message << "."; \\
128 std::string diagnostic_message = diagnostics.str(); \\
129 m_assertion_handler(&diagnostic_message); \\
130 }} \\
131 }}
133#endif // {guard_name}.
134"""
135 return base.format(
136 guard_name=guard_name,
137 macro_name=macro_name,
138 name=name,
139 name_space=name_space,
140 message=message,
141 message_space=message_space,
142 )
144 setter_assert = get_macro(name="SETTER", message="Tried to set value")
145 getter_assert = get_macro(name="GETTER", message="Got read value")
146 array_index_assert = get_macro(name="ARRAY_INDEX", message="Provided array index")
147 return f"""\
148{setter_assert}
149{getter_assert}
150{array_index_assert}
151"""
153 def _register_setter_function(
154 self, register: "Register", register_array: Optional["RegisterArray"]
155 ) -> str:
156 signature = self._register_setter_function_signature(
157 register=register, register_array=register_array, indent=2
158 )
159 cpp_code = f" void {self._class_name}::{signature} const\n"
160 cpp_code += " {\n"
162 if register_array:
163 cpp_code += f"""\
164 _ARRAY_INDEX_ASSERT_TRUE(
165 array_index < {self.name}::{register_array.name}::array_length,
166 "'{register_array.name}' array index out of range, got '" << array_index << "'"
167 );
169"""
170 cpp_code += (
171 f" const size_t index = {register_array.base_index} "
172 f"+ array_index * {len(register_array.registers)} + {register.index};\n"
173 )
174 else:
175 cpp_code += f" const size_t index = {register.index};\n"
177 cpp_code += " m_registers[index] = register_value;\n"
178 cpp_code += " }\n\n"
179 return cpp_code
181 def _field_setter_function(
182 self,
183 register: "Register",
184 register_array: Optional["RegisterArray"],
185 field: "RegisterField",
186 ) -> str:
187 signature = self._field_setter_function_signature(
188 register=register,
189 register_array=register_array,
190 field=field,
191 from_value=False,
192 indent=2,
193 )
195 cpp_code = f" void {self._class_name}::{signature} const\n"
196 cpp_code += " {\n"
198 if self.field_setter_should_read_modify_write(register=register):
199 register_getter_function_name = self._register_getter_function_name(
200 register=register, register_array=register_array
201 )
202 cpp_code += self.comment(
203 comment="Get the current value of other fields by reading register on the bus."
204 )
205 current_register_value = f"{register_getter_function_name}("
206 if register_array:
207 current_register_value += "array_index"
208 current_register_value += ")"
210 else:
211 cpp_code += self.comment(
212 "Set everything except for the field to default when writing the value."
213 )
214 current_register_value = str(register.default_value)
216 cpp_code += f" const uint32_t current_register_value = {current_register_value};\n"
218 signature = self._field_setter_function_name(
219 register=register, register_array=register_array, field=field, from_value=True
220 )
221 cpp_code += (
222 " const uint32_t result_register_value = "
223 f"{signature}(current_register_value, field_value);\n"
224 )
226 register_setter_function_name = self._register_setter_function_name(
227 register=register, register_array=register_array
228 )
229 cpp_code += f" {register_setter_function_name}("
230 if register_array:
231 cpp_code += "array_index, "
232 cpp_code += "result_register_value);\n"
234 cpp_code += " }\n\n"
236 return cpp_code
238 def _field_setter_function_from_value(
239 self,
240 register: "Register",
241 register_array: Optional["RegisterArray"],
242 field: "RegisterField",
243 ) -> str:
244 signature = self._field_setter_function_signature(
245 register=register, register_array=register_array, field=field, from_value=True, indent=2
246 )
248 return f"""\
249 uint32_t {self._class_name}::{signature} const\
250 {
251{self._get_field_shift_and_mask(field=field)}\
252{self._get_field_value_checker(field=field, setter_or_getter="setter")}\
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;
256 const uint32_t mask_shifted_inverse = ~mask_shifted;
257 const uint32_t register_value_masked = register_value & mask_shifted_inverse;
259 const uint32_t result_register_value = register_value_masked | field_value_masked_and_shifted;
261 return result_register_value;
262 }
264"""
266 @staticmethod
267 def _get_field_shift_and_mask(field: "RegisterField") -> str:
268 return f"""\
269 const uint32_t shift = {field.base_index}uL;
270 const uint32_t mask_at_base = 0b{"1" * field.width}uL;
271 const uint32_t mask_shifted = mask_at_base << shift;
273"""
275 @staticmethod
276 def _get_field_value_checker(field: "RegisterField", setter_or_getter: str) -> str:
277 comment = "// Check that field value is within the legal range."
278 assertion = f"_{setter_or_getter.upper()}_ASSERT_TRUE"
280 if isinstance(field, Integer):
281 return f"""\
282 {comment}
283 {assertion}(
284 field_value >= {field.min_value},
285 "'{field.name}' value too small, got '" << field_value << "'"
286 );
287 {assertion}(
288 field_value <= {field.max_value},
289 "'{field.name}' value too large, got '" << field_value << "'"
290 );
292"""
294 if isinstance(field, (Bit, BitVector)):
295 return f"""\
296 {comment}
297 const uint32_t mask_at_base_inverse = ~mask_at_base;
298 {assertion}(
299 (field_value & mask_at_base_inverse) == 0,
300 "'{field.name}' value too many bits used, got '" << field_value << "'"
301 );
303"""
305 return ""
307 def _get_field_getter_value_checker(self, field: "RegisterField") -> str:
308 if isinstance(field, Integer):
309 return self._get_field_value_checker(field=field, setter_or_getter="getter")
311 return ""
313 def _register_getter_function(
314 self, register: "Register", register_array: Optional["RegisterArray"]
315 ) -> str:
316 signature = self._register_getter_function_signature(
317 register=register, register_array=register_array, indent=2
318 )
319 cpp_code = f" uint32_t {self._class_name}::{signature} const\n"
320 cpp_code += " {\n"
322 if register_array:
323 cpp_code += f"""\
324 _ARRAY_INDEX_ASSERT_TRUE(
325 array_index < {self.name}::{register_array.name}::array_length,
326 "'{register_array.name}' array index out of range, got '" << array_index << "'"
327 );
329"""
330 cpp_code += (
331 f" const size_t index = {register_array.base_index} "
332 f"+ array_index * {len(register_array.registers)} + {register.index};\n"
333 )
334 else:
335 cpp_code += f" const size_t index = {register.index};\n"
337 cpp_code += " const uint32_t result = m_registers[index];\n\n"
338 cpp_code += " return result;\n"
339 cpp_code += " }\n\n"
340 return cpp_code
342 def _field_getter_function(
343 self,
344 register: "Register",
345 register_array: Optional["RegisterArray"],
346 field: "RegisterField",
347 ) -> str:
348 signature = self._field_getter_function_signature(
349 register=register,
350 register_array=register_array,
351 field=field,
352 from_value=False,
353 indent=2,
354 )
356 field_type_name = self._field_value_type_name(
357 register=register, register_array=register_array, field=field
358 )
360 cpp_code = f" {field_type_name} {self._class_name}::{signature} const\n"
361 cpp_code += " {\n"
363 register_getter_function_name = self._register_getter_function_name(
364 register=register, register_array=register_array
365 )
367 field_getter_from_value_function_name = self._field_getter_function_name(
368 register=register, register_array=register_array, field=field, from_value=True
369 )
371 cpp_code += f" const uint32_t register_value = {register_getter_function_name}("
372 if register_array:
373 cpp_code += "array_index"
374 cpp_code += ");\n"
376 cpp_code += (
377 f" const {field_type_name} field_value = "
378 f"{field_getter_from_value_function_name}(register_value);\n"
379 )
380 cpp_code += "\n return field_value;\n"
381 cpp_code += " }\n\n"
383 return cpp_code
385 def _field_getter_function_from_value(
386 self,
387 register: "Register",
388 register_array: Optional["RegisterArray"],
389 field: "RegisterField",
390 ) -> str:
391 signature = self._field_getter_function_signature(
392 register=register, register_array=register_array, field=field, from_value=True, indent=2
393 )
395 type_name = self._field_value_type_name(
396 register=register, register_array=register_array, field=field
397 )
399 cpp_code = f"""\
400 {type_name} {self._class_name}::{signature} const
401 {
402{self._get_field_shift_and_mask(field=field)}\
403 const uint32_t result_masked = register_value & mask_shifted;
404 const uint32_t result_shifted = result_masked >> shift;
406 {type_name} field_value;
408"""
410 if type_name == "uint32_t":
411 cpp_code += """\
412 // No casting needed.
413 field_value = result_shifted;
414"""
416 else:
417 if isinstance(field, Enumeration):
418 cpp_code += f"""\
419 // "Cast" to the enum type.
420 field_value = {type_name}(result_shifted);
421"""
423 elif isinstance(field, Integer) and field.is_signed:
424 cpp_code += f"""\
425 const {type_name} sign_bit_mask = 1 << {field.width - 1};
427 if (result_shifted & sign_bit_mask)
428 {
429 // Value is to be interpreted as negative.
430 // Sign extend it from the width of the field to the width of the return type.
431 field_value = result_shifted - 2 * sign_bit_mask;
432 }
433 else
434 {
435 // Value is positive.
436 field_value = result_shifted;
437 }
438"""
439 else:
440 raise ValueError(f"Got unexpected field type: {type_name}")
442 cpp_code += f"""
443{self._get_field_getter_value_checker(field=field)}\
444 return field_value;
445 }
447"""
449 return cpp_code