Coverage for hdl_registers/register.py: 95%
61 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 typing import TYPE_CHECKING, Optional
13# Local folder libraries
14from .field.bit import Bit
15from .field.bit_vector import BitVector
16from .field.enumeration import Enumeration
17from .field.integer import Integer
18from .register_mode import RegisterMode
20if TYPE_CHECKING:
21 # Local folder libraries
22 from .field.numerical_interpretation import NumericalInterpretation
23 from .field.register_field import RegisterField
26class Register:
27 """
28 Used to represent a register and its fields.
29 """
31 def __init__(self, name: str, index: int, mode: "RegisterMode", description: str):
32 """
33 Arguments:
34 name: The name of the register.
35 index: The zero-based index of this register.
36 If this register is part of a register array, the index shall be relative to the
37 start of the array. I.e. the index is zero for the first register in the array.
38 If the register is a plain register, the index shall be relative to the start of
39 the register list.
40 mode: A mode that decides the behavior of this register.
41 See https://hdl-registers.com/rst/basic_feature/basic_feature_register_modes.html
42 for more information about the different modes.
43 description: Textual register description.
44 """
45 if not isinstance(mode, RegisterMode):
46 # This check should be removed eventually.
47 # It is only here to help users during the transition period.
48 raise ValueError(
49 f'Invalid mode: "{mode}". '
50 "Since version 6.0.0, the mode should be a 'RegisterMode' object, not a string."
51 )
53 self.name = name
54 self.index = index
55 self.mode = mode
56 self.description = description
57 self.fields: list["RegisterField"] = []
58 self.bit_index = 0
60 @property
61 def utilized_width(self) -> int:
62 """
63 The number of bits that are utilized by the fields in this register.
64 Note that this is not always the same as the width of the register.
65 Some generator implementations can be optimized by only taking into account the
66 bits that are actually utilized.
68 Note that if the register has no fields, we do not really know what the user is doing with
69 it, and we have to assume that the full width is used.
70 """
71 if not self.fields:
72 return 32
74 return self.fields[-1].base_index + self.fields[-1].width
76 def append_bit(self, name: str, description: str, default_value: str) -> Bit:
77 """
78 Append a bit field to this register.
80 See :class:`.Bit` for documentation of the arguments.
82 Return:
83 The bit field object that was created.
84 """
85 bit = Bit(
86 name=name, index=self.bit_index, description=description, default_value=default_value
87 )
88 self._append_field(field=bit)
90 return bit
92 def append_bit_vector(
93 self,
94 name: str,
95 description: str,
96 width: int,
97 default_value: str,
98 numerical_interpretation: Optional["NumericalInterpretation"] = None,
99 ) -> BitVector:
100 """
101 Append a bit vector field to this register.
103 See :class:`.BitVector` for documentation of the arguments.
105 Return:
106 The bit vector field object that was created.
107 """
108 bit_vector = BitVector(
109 name=name,
110 base_index=self.bit_index,
111 description=description,
112 width=width,
113 default_value=default_value,
114 numerical_interpretation=numerical_interpretation,
115 )
116 self._append_field(field=bit_vector)
118 return bit_vector
120 def append_enumeration(
121 self, name: str, description: str, elements: dict[str, str], default_value: str
122 ) -> Enumeration:
123 """
124 Append an enumeration field to this register.
126 See :class:`.Enumeration` for documentation of the arguments.
128 Return:
129 The enumeration field object that was created.
130 """
131 field = Enumeration(
132 name=name,
133 base_index=self.bit_index,
134 description=description,
135 elements=elements,
136 default_value=default_value,
137 )
138 self._append_field(field=field)
140 return field
142 def append_integer(
143 self, name: str, description: str, min_value: int, max_value: int, default_value: int
144 ) -> Integer:
145 """
146 Append an integer field to this register.
148 See :class:`.Integer` for documentation of the arguments.
150 Return:
151 The integer field object that was created.
152 """
153 integer = Integer(
154 name=name,
155 base_index=self.bit_index,
156 description=description,
157 min_value=min_value,
158 max_value=max_value,
159 default_value=default_value,
160 )
161 self._append_field(field=integer)
163 return integer
165 def _append_field(self, field: "RegisterField") -> None:
166 self.fields.append(field)
168 self.bit_index += field.width
169 if self.bit_index > 32:
170 raise ValueError(f'Maximum width exceeded for register "{self.name}".')
172 @property
173 def default_value(self) -> int:
174 """
175 The default value of this register as an unsigned integer.
176 Depends on the default values of the fields in this register.
177 """
178 default_value = 0
179 for field in self.fields:
180 default_value += field.default_value_uint * 2**field.base_index
182 return default_value
184 def get_field(self, name: str) -> "RegisterField":
185 """
186 Get the field within this register that has the given name. Will raise exception if no
187 field matches.
189 Arguments:
190 name: The name of the field.
192 Return:
193 The field.
194 """
195 for field in self.fields:
196 if field.name == name:
197 return field
199 raise ValueError(f'Could not find field "{name}" within register "{self.name}"')
201 @property
202 def address(self) -> int:
203 """
204 Byte address, within the register list, of this register.
205 """
206 return 4 * self.index
208 def __repr__(self) -> str:
209 return f"""{self.__class__.__name__}(\
210name={self.name},\
211index={self.index},\
212mode={self.mode},\
213description={self.description},\
214fields={','.join([repr(field) for field in self.fields])},\
215)"""