Coverage for hdl_registers/register.py: 95%
62 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 11:11 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-12 11:11 +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 .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 from .field.numerical_interpretation import NumericalInterpretation
22 from .field.register_field import RegisterField
25class Register:
26 """
27 Used to represent a register and its fields.
28 """
30 def __init__(self, name: str, index: int, mode: RegisterMode, description: str) -> None:
31 """
32 Arguments:
33 name: The name of the register.
34 index: The zero-based index of this register.
35 If this register is part of a register array, the index shall be relative to the
36 start of the array. I.e. the index is zero for the first register in the array.
37 If the register is a plain register, the index shall be relative to the start of
38 the register list.
39 mode: A mode that decides the behavior of this register.
40 See https://hdl-registers.com/rst/basic_feature/basic_feature_register_modes.html
41 for more information about the different modes.
42 description: Textual register description.
43 """
44 if not isinstance(mode, RegisterMode):
45 # This check should be removed eventually.
46 # It is only here to help users during the transition period.
47 raise TypeError(
48 f'Invalid mode: "{mode}". '
49 "Since version 6.0.0, the mode should be a 'RegisterMode' object, not a string."
50 )
52 self.name = name
53 self.index = index
54 self.mode = mode
55 self.description = description
56 self.fields: list[RegisterField] = []
57 self.bit_index = 0
59 @property
60 def utilized_width(self) -> int:
61 """
62 The number of bits that are utilized by the fields in this register.
63 Note that this is not always the same as the width of the register.
64 Some generator implementations can be optimized by only taking into account the
65 bits that are actually utilized.
67 Note that if the register has no fields, we do not really know what the user is doing with
68 it, and we have to assume that the full width is used.
69 """
70 if not self.fields:
71 return 32
73 return self.fields[-1].base_index + self.fields[-1].width
75 def append_bit(self, name: str, description: str, default_value: str) -> Bit:
76 """
77 Append a bit field to this register.
79 See :class:`.Bit` for documentation of the arguments.
81 Return:
82 The bit field object that was created.
83 """
84 bit = Bit(
85 name=name, index=self.bit_index, description=description, default_value=default_value
86 )
87 self._append_field(field=bit)
89 return bit
91 def append_bit_vector(
92 self,
93 name: str,
94 description: str,
95 width: int,
96 default_value: str,
97 numerical_interpretation: NumericalInterpretation | None = None,
98 ) -> BitVector:
99 """
100 Append a bit vector field to this register.
102 See :class:`.BitVector` for documentation of the arguments.
104 Return:
105 The bit vector field object that was created.
106 """
107 bit_vector = BitVector(
108 name=name,
109 base_index=self.bit_index,
110 description=description,
111 width=width,
112 default_value=default_value,
113 numerical_interpretation=numerical_interpretation,
114 )
115 self._append_field(field=bit_vector)
117 return bit_vector
119 def append_enumeration(
120 self, name: str, description: str, elements: dict[str, str], default_value: str
121 ) -> Enumeration:
122 """
123 Append an enumeration field to this register.
125 See :class:`.Enumeration` for documentation of the arguments.
127 Return:
128 The enumeration field object that was created.
129 """
130 field = Enumeration(
131 name=name,
132 base_index=self.bit_index,
133 description=description,
134 elements=elements,
135 default_value=default_value,
136 )
137 self._append_field(field=field)
139 return field
141 def append_integer(
142 self, name: str, description: str, min_value: int, max_value: int, default_value: int
143 ) -> Integer:
144 """
145 Append an integer field to this register.
147 See :class:`.Integer` for documentation of the arguments.
149 Return:
150 The integer field object that was created.
151 """
152 integer = Integer(
153 name=name,
154 base_index=self.bit_index,
155 description=description,
156 min_value=min_value,
157 max_value=max_value,
158 default_value=default_value,
159 )
160 self._append_field(field=integer)
162 return integer
164 def _append_field(self, field: RegisterField) -> None:
165 self.fields.append(field)
167 self.bit_index += field.width
168 if self.bit_index > 32:
169 raise ValueError(f'Maximum width exceeded for register "{self.name}".')
171 @property
172 def default_value(self) -> int:
173 """
174 The default value of this register as an unsigned integer.
175 Depends on the default values of the fields in this register.
176 """
177 default_value = 0
178 for field in self.fields:
179 default_value += field.default_value_uint * 2**field.base_index
181 return default_value
183 def get_field(self, name: str) -> RegisterField:
184 """
185 Get the field within this register that has the given name. Will raise exception if no
186 field matches.
188 Arguments:
189 name: The name of the field.
191 Return:
192 The field.
193 """
194 for field in self.fields:
195 if field.name == name:
196 return field
198 raise ValueError(f'Could not find field "{name}" within register "{self.name}"')
200 @property
201 def address(self) -> int:
202 """
203 Byte address, within the register list, of this register.
204 """
205 return 4 * self.index
207 def __repr__(self) -> str:
208 return f"""{self.__class__.__name__}(\
209name={self.name},\
210index={self.index},\
211mode={self.mode},\
212description={self.description},\
213fields={",".join([repr(field) for field in self.fields])},\
214)"""