Coverage for hdl_registers/generator/register_code_generator_helpers.py: 96%
90 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-19 20:50 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-19 20:50 +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.register import Register
15from hdl_registers.register_array import RegisterArray
16from hdl_registers.register_modes import REGISTER_MODES
18if TYPE_CHECKING:
19 from collections.abc import Iterator
21 from hdl_registers.constant.constant import Constant
22 from hdl_registers.field.register_field import RegisterField
23 from hdl_registers.register_list import RegisterList
26class RegisterCodeGeneratorHelpers:
27 """
28 Various helper methods that make register code generation easier.
29 """
31 # Defined in 'RegisterCodeGenerator' class, which shall also be inherited wherever this class
32 # is used.
33 register_list: RegisterList
34 name: str
35 DEFAULT_INDENTATION_LEVEL: int
36 COMMENT_START: str
37 COMMENT_END: str
39 def iterate_constants(self) -> Iterator[Constant]:
40 """
41 Iterate of all constants in the register list.
42 """
43 yield from self.register_list.constants
45 def iterate_register_objects(self) -> Iterator[Register | RegisterArray]:
46 """
47 Iterate over all register objects in the register list.
48 I.e. all plain registers and all register arrays.
49 """
50 yield from iterate_register_objects(register_list=self.register_list)
52 def iterate_registers(self) -> Iterator[tuple[Register, RegisterArray | None]]:
53 """
54 Iterate over all registers, plain or in array, in the register list.
56 Return:
57 If the register is plain, the array return value in the tuple will be ``None``.
58 If the register is in an array, the array return value will conversely be non-``None``.
59 """
60 yield from iterate_registers(register_list=self.register_list)
62 def iterate_plain_registers(self) -> Iterator[Register]:
63 """
64 Iterate over all plain registers (i.e. registers not in array) in the register list.
65 """
66 for register_object in self.iterate_register_objects():
67 if isinstance(register_object, Register):
68 yield register_object
70 def iterate_register_arrays(self) -> Iterator[RegisterArray]:
71 """
72 Iterate over all register arrays in the register list.
73 """
74 for register_object in self.iterate_register_objects():
75 if isinstance(register_object, RegisterArray):
76 yield register_object
78 def qualified_register_name(
79 self, register: Register, register_array: RegisterArray | None = None
80 ) -> str:
81 """
82 Get the qualified register name, e.g. "<module name>_<register name>".
83 To be used where the scope requires it, i.e. outside of records.
84 """
85 return qualified_register_name(
86 register_list=self.register_list, register=register, register_array=register_array
87 )
89 def qualified_register_array_name(self, register_array: RegisterArray) -> str:
90 """
91 Get the qualified register array name, e.g. "<module name>_<register array name>".
92 To be used where the scope requires it, i.e. outside of records.
93 """
94 return qualified_register_array_name(
95 register_list=self.register_list, register_array=register_array
96 )
98 def qualified_field_name(
99 self,
100 register: Register,
101 field: RegisterField,
102 register_array: RegisterArray | None = None,
103 ) -> str:
104 """
105 Get the qualified field name, e.g. "<module name>_<register name>_<field_name>".
106 To be used where the scope requires it, i.e. outside of records.
107 """
108 return qualified_field_name(
109 register_list=self.register_list,
110 register=register,
111 field=field,
112 register_array=register_array,
113 )
115 def get_indentation(self, indent: int | None = None) -> str:
116 """
117 Get the requested indentation in spaces.
118 Will use the default indentation for this generator if not specified.
119 """
120 indent = self.DEFAULT_INDENTATION_LEVEL if indent is None else indent
121 return " " * indent
123 def get_separator_line(self, indent: int | None = None) -> str:
124 """
125 Get a separator line, e.g. ``# ---------------------------------``.
126 """
127 indentation = self.get_indentation(indent=indent)
128 result = f"{indentation}{self.COMMENT_START} "
130 num_dash = 80 - len(result) - len(self.COMMENT_END)
131 result += "-" * num_dash
132 result += f"{self.COMMENT_END}\n"
134 return result
136 def comment(self, comment: str, indent: int | None = None) -> str:
137 """
138 Create a one-line comment.
139 """
140 indentation = self.get_indentation(indent=indent)
141 return f"{indentation}{self.COMMENT_START} {comment}{self.COMMENT_END}\n"
143 def comment_block(self, text: list[str], indent: int | None = None) -> str:
144 """
145 Create a comment block from a list of text lines.
146 """
147 return "".join(self.comment(comment=line, indent=indent) for line in text)
149 @staticmethod
150 def register_description(
151 register: Register, register_array: RegisterArray | None = None
152 ) -> str:
153 """
154 Get a comment describing the register.
155 """
156 result = f"'{register.name}' register"
158 if register_array is None:
159 return result
161 return f"{result} within the '{register_array.name}' register array"
163 def field_description(
164 self,
165 register: Register,
166 field: RegisterField,
167 register_array: RegisterArray | None = None,
168 ) -> str:
169 """
170 Get a comment describing the field.
171 """
172 register_description = self.register_description(
173 register=register, register_array=register_array
174 )
175 return f"'{field.name}' field in the {register_description}"
177 @staticmethod
178 def field_setter_should_read_modify_write(register: Register) -> bool:
179 """
180 Returns True if a field value setter should read-modify-write the register.
182 Is only true if the register is of a writeable type where the software can also read back
183 a previously-written value.
184 Furthermore, read-modify-write only makes sense if there is more than one field, otherwise
185 it is a waste of CPU cycles.
186 """
187 if not register.fields:
188 raise ValueError("Should not end up here if the register has no fields.")
190 if register.mode == REGISTER_MODES["r_w"]:
191 return len(register.fields) > 1
193 if register.mode in [
194 REGISTER_MODES["w"],
195 REGISTER_MODES["wpulse"],
196 REGISTER_MODES["r_wpulse"],
197 ]:
198 return False
200 raise ValueError(f"Got non-writeable register: {register}")
202 @staticmethod
203 def to_pascal_case(snake_string: str) -> str:
204 """
205 Converts e.g., "my_funny_string" to "MyFunnyString".
207 Pascal case is like camel case but with the initial character being capitalized.
208 I.e. how classes are named in Python, C and C++.
209 """
210 return snake_string.title().replace("_", "")
213def iterate_register_objects(register_list: RegisterList) -> Iterator[Register | RegisterArray]:
214 """
215 Iterate over all register objects in the register list.
216 I.e. all plain registers and all register arrays.
217 """
218 yield from register_list.register_objects
221def iterate_registers(
222 register_list: RegisterList,
223) -> Iterator[tuple[Register, RegisterArray | None]]:
224 """
225 Iterate over all registers, plain or in array, in the register list.
227 Return:
228 If the register is plain, the array return value in the tuple will be ``None``.
229 If the register is in an array, the array return value will conversely be non-``None``.
230 """
231 for register_object in iterate_register_objects(register_list=register_list):
232 if isinstance(register_object, Register):
233 yield (register_object, None)
234 else:
235 for register in register_object.registers:
236 yield (register, register_object)
239def qualified_register_name(
240 register_list: RegisterList, register: Register, register_array: RegisterArray | None = None
241) -> str:
242 """
243 Get the qualified register name, e.g. "<module name>_<register name>".
244 To be used where the scope requires it, i.e. outside of records.
245 """
246 if register_array is None:
247 return f"{register_list.name}_{register.name}"
249 register_array_name = qualified_register_array_name(
250 register_list=register_list, register_array=register_array
251 )
252 return f"{register_array_name}_{register.name}"
255def qualified_register_array_name(
256 register_list: RegisterList, register_array: RegisterArray
257) -> str:
258 """
259 Get the qualified register array name, e.g. "<module name>_<register array name>".
260 To be used where the scope requires it, i.e. outside of records.
261 """
262 return f"{register_list.name}_{register_array.name}"
265def qualified_field_name(
266 register_list: RegisterList,
267 register: Register,
268 field: RegisterField,
269 register_array: RegisterArray | None = None,
270) -> str:
271 """
272 Get the qualified field name, e.g. "<module name>_<register name>_<field_name>".
273 To be used where the scope requires it, i.e. outside of records.
274 """
275 register_name = qualified_register_name(
276 register_list=register_list, register=register, register_array=register_array
277 )
278 return f"{register_name}_{field.name}"