Coverage for hdl_registers/generator/cpp/cpp_generator_common.py: 93%
145 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
14from hdl_registers.field.bit import Bit
15from hdl_registers.field.bit_vector import BitVector
16from hdl_registers.field.enumeration import Enumeration
17from hdl_registers.field.integer import Integer
18from hdl_registers.field.numerical_interpretation import (
19 Signed,
20 SignedFixedPoint,
21 Unsigned,
22 UnsignedFixedPoint,
23)
24from hdl_registers.generator.register_code_generator import RegisterCodeGenerator
26if TYPE_CHECKING:
27 from pathlib import Path
29 from hdl_registers.field.register_field import RegisterField
30 from hdl_registers.register import Register
31 from hdl_registers.register_array import RegisterArray
32 from hdl_registers.register_list import RegisterList
35class CppGeneratorCommon(RegisterCodeGenerator):
36 """
37 Class with common methods for generating C++ code.
38 """
40 COMMENT_START = "//"
42 def __init__(self, register_list: RegisterList, output_folder: Path) -> None:
43 super().__init__(register_list=register_list, output_folder=output_folder)
45 self._class_name = self.to_pascal_case(snake_string=self.name)
47 @staticmethod
48 def _with_namespace(cpp_code_body: str) -> str:
49 return f"""\
50namespace fpga_regs
51{
53{cpp_code_body}\
54} // namespace fpga_regs
55"""
57 def _constructor_signature(self) -> str:
58 return (
59 f"{self._class_name}(uintptr_t base_address, "
60 "bool (*assertion_handler) (const std::string*))"
61 )
63 def _get_register_heading(
64 self,
65 register: Register,
66 register_array: RegisterArray | None,
67 separator: str,
68 indent: int | None = None,
69 ) -> str:
70 indentation = self.get_indentation(indent=indent)
71 description = self._get_methods_description(
72 register=register, register_array=register_array
73 )
74 return f"""
75{separator}\
76{indentation}// {description}
77{indentation}// Mode '{register.mode.name}'.
78{separator}\
79"""
81 def _get_methods_description(
82 self, register: Register, register_array: RegisterArray | None
83 ) -> str:
84 register_description = self.register_description(
85 register=register, register_array=register_array
86 )
87 return f"Methods for the {register_description}."
89 def _get_namespace(
90 self,
91 register: Register,
92 register_array: RegisterArray | None = None,
93 field: RegisterField | None = None,
94 ) -> str:
95 """
96 Get namespace to use within the class for the attributes of the field or register that is
97 pointed out by the arguments.
98 """
99 register_array_namespace = f"{register_array.name}::" if register_array else ""
100 field_namespace = f"{field.name}::" if field else ""
101 return f"{self.name}::{register_array_namespace}{register.name}::{field_namespace}"
103 def _get_register_value_type(
104 self, register: Register, register_array: RegisterArray | None
105 ) -> str:
106 """
107 The name of the type used to represent the register value.
108 """
109 if register.fields:
110 namespace = self._get_namespace(register=register, register_array=register_array)
111 return f"{namespace}Value"
113 return "uint32_t"
115 def _get_field_value_type(
116 self,
117 register: Register,
118 register_array: RegisterArray | None,
119 field: RegisterField,
120 include_namespace: bool = True,
121 ) -> str:
122 """
123 The name of the type used to represent the field value.
124 """
125 if isinstance(field, Bit):
126 return "bool"
128 if isinstance(field, BitVector):
129 if isinstance(field.numerical_interpretation, Unsigned):
130 return "uint32_t"
132 if isinstance(field.numerical_interpretation, Signed):
133 return "int32_t"
135 if isinstance(field.numerical_interpretation, (UnsignedFixedPoint, SignedFixedPoint)):
136 # This assumes that the target platform/compiler uses IEEE-754 single-precision for
137 # 'float' and IEEE-754 double-precision for 'double'.
138 # This is NOT guaranteed by the C++ standard.
139 # The user is encouraged to check their platform's limits
140 # with e.g. 'std::numeric_limits<float>::is_iec559'.
141 # https://stackoverflow.com/questions/24157094
142 return "double" if field.width > 24 else "float"
144 raise ValueError(
145 f"Unsupported numerical interpretation: {field.numerical_interpretation}"
146 )
148 if isinstance(field, Enumeration):
149 # The name of an enum available in this field's attributes.
150 namespace = (
151 self._get_namespace(register=register, register_array=register_array, field=field)
152 if include_namespace
153 else ""
154 )
155 return f"{namespace}Enumeration"
157 if isinstance(field, Integer):
158 return "int32_t" if field.is_signed else "uint32_t"
160 raise ValueError(f"Got unknown field type: {field}")
162 @staticmethod
163 def _register_getter_name(
164 register: Register, register_array: RegisterArray | None, raw: bool
165 ) -> str:
166 register_array_name = f"_{register_array.name}" if register_array else ""
167 raw_name = "_raw" if raw else ""
168 return f"get{register_array_name}_{register.name}{raw_name}"
170 def _register_getter_signature(
171 self,
172 register: Register,
173 register_array: RegisterArray | None,
174 raw: bool = False,
175 indent: int | None = None,
176 ) -> str:
177 function_name = self._register_getter_name(
178 register=register, register_array=register_array, raw=raw
179 )
181 if register_array:
182 indentation = self.get_indentation(indent=indent)
183 array_index = f"""
184{indentation} size_t array_index
185{indentation}\
186"""
187 else:
188 array_index = ""
190 return f"{function_name}({array_index}) const"
192 @staticmethod
193 def _field_getter_name(
194 register: Register,
195 register_array: RegisterArray | None,
196 field: RegisterField,
197 from_raw: bool,
198 ) -> str:
199 register_array_name = f"_{register_array.name}" if register_array else ""
200 raw_name = "_from_raw" if from_raw else ""
201 return f"get{register_array_name}_{register.name}_{field.name}{raw_name}"
203 def _field_getter_signature(
204 self,
205 register: Register,
206 register_array: RegisterArray | None,
207 field: RegisterField,
208 from_raw: bool,
209 indent: int | None = None,
210 ) -> str:
211 indentation = self.get_indentation(indent=indent)
213 function_name = self._field_getter_name(
214 register=register, register_array=register_array, field=field, from_raw=from_raw
215 )
216 result = f"{function_name}("
218 if from_raw:
219 # Value is supplied by user
220 result += f"\n{indentation} uint32_t register_value\n{indentation}"
221 elif register_array:
222 # Value shall be read from bus, in which case we need to know array index if this
223 # is an array
224 result += f"\n{indentation} size_t array_index\n{indentation}"
226 result += ") const"
228 return result
230 @staticmethod
231 def _field_to_raw_name(
232 register: Register, register_array: RegisterArray | None, field: RegisterField
233 ) -> str:
234 array = f"{register_array.name}_" if register_array else ""
235 return f"get_{array}{register.name}_{field.name}_to_raw"
237 def _field_to_raw_signature(
238 self, register: Register, register_array: RegisterArray | None, field: RegisterField
239 ) -> str:
240 field_type = self._get_field_value_type(
241 register=register, register_array=register_array, field=field
242 )
243 function_name = self._field_to_raw_name(
244 register=register, register_array=register_array, field=field
245 )
246 return f"{function_name}({field_type} field_value) const"
248 @staticmethod
249 def _register_setter_name(
250 register: Register, register_array: RegisterArray | None, raw: bool
251 ) -> str:
252 array_name = f"_{register_array.name}" if register_array else ""
253 raw_name = "_raw" if raw else ""
255 return f"set{array_name}_{register.name}{raw_name}"
257 def _register_setter_signature(
258 self,
259 register: Register,
260 register_array: RegisterArray | None,
261 raw: bool = False,
262 indent: int | None = None,
263 ) -> str:
264 indentation = self.get_indentation(indent=indent)
266 function_name = self._register_setter_name(
267 register=register, register_array=register_array, raw=raw
268 )
270 array_index = f"{indentation} size_t array_index,\n" if register_array else ""
271 value_type = (
272 "uint32_t"
273 if raw
274 else self._get_register_value_type(register=register, register_array=register_array)
275 )
277 return f"""\
278{function_name}(
279{array_index}\
280{indentation} {value_type} register_value
281{indentation}) const\
282"""
284 @staticmethod
285 def _field_setter_name(
286 register: Register,
287 register_array: RegisterArray | None,
288 field: RegisterField,
289 from_raw: bool,
290 ) -> str:
291 register_array_name = f"_{register_array.name}" if register_array else ""
292 from_raw_name = "_from_raw" if from_raw else ""
293 return f"set{register_array_name}_{register.name}_{field.name}{from_raw_name}"
295 def _field_setter_signature(
296 self,
297 register: Register,
298 register_array: RegisterArray | None,
299 field: RegisterField,
300 from_raw: bool,
301 indent: int | None = None,
302 ) -> str:
303 indentation = self.get_indentation(indent=indent)
305 function_name = self._field_setter_name(
306 register=register, register_array=register_array, field=field, from_raw=from_raw
307 )
308 result = f"{function_name}(\n"
310 if from_raw:
311 # Current register value is supplied by user
312 result += f"{indentation} uint32_t register_value,\n"
313 elif register_array:
314 # Current register value shall be read from bus, in which case we need to know array
315 # index if this is an array
316 result += f"{indentation} size_t array_index,\n"
318 field_type = self._get_field_value_type(
319 register=register, register_array=register_array, field=field
320 )
321 result += f"{indentation} {field_type} field_value\n{indentation}) const"
323 return result
325 def _get_getter_comment(self, field: RegisterField | None = None, raw: bool = False) -> str:
326 """
327 Generate a comment for getter method documentation.
328 """
329 if field:
330 if raw:
331 raise ValueError("Invalid arguments")
333 return self.comment(
334 comment=f"Read the register and slice out the '{field.name}' field value.",
335 )
337 comment = ["Read the whole register value over the register bus."]
338 if raw:
339 comment.append(
340 "This method returns the raw register value without any type conversion."
341 )
343 return self.comment_block(text=comment)
345 def _get_setter_comment(
346 self, register: Register, field: RegisterField | None = None, raw: bool = False
347 ) -> str:
348 """
349 Generate a comment for setter method documentation.
350 """
351 if field:
352 if raw:
353 raise ValueError("Invalid arguments")
355 comment = [f"Write the '{field.name}' field value."]
356 if self.field_setter_should_read_modify_write(register=register):
357 comment.append("Will read-modify-write the register.")
358 else:
359 comment.append("Will write the register with all other fields set as default.")
361 return self.comment_block(text=comment)
363 comment = ["Write the whole register value over the register bus."]
365 if raw:
366 comment.append(
367 "This method takes a raw register value and does not perform any type conversion."
368 )
370 return self.comment_block(text=comment)
372 def _get_from_raw_comment(self, field: RegisterField) -> str:
373 """
374 Generate a comment for a ``get_<field>_from_raw`` method documentation.
375 """
376 return self.comment_block(
377 text=[
378 f"Slice out the '{field.name}' field value from a given raw register value.",
379 "Performs no operation on the register bus.",
380 ]
381 )
383 def _get_to_raw_comment(self, field: RegisterField) -> str:
384 """
385 Generate a comment for a ``get_<field>_to_raw`` method documentation.
386 """
387 return self.comment_block(
388 text=[
389 f"Get the raw representation of a given '{field.name}' field value.",
390 "Performs no operation on the register bus.",
391 ]
392 )