Coverage for hdl_registers/register.py: 95%
56 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 20:50 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-12-01 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# --------------------------------------------------------------------------------------------------
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 def append_bit(self, name: str, description: str, default_value: str) -> Bit:
61 """
62 Append a bit field to this register.
64 See :class:`.Bit` for documentation of the arguments.
66 Return:
67 The bit field object that was created.
68 """
69 bit = Bit(
70 name=name, index=self.bit_index, description=description, default_value=default_value
71 )
72 self._append_field(field=bit)
74 return bit
76 def append_bit_vector(
77 self,
78 name: str,
79 description: str,
80 width: int,
81 default_value: str,
82 numerical_interpretation: Optional["NumericalInterpretation"] = None,
83 ) -> BitVector:
84 """
85 Append a bit vector field to this register.
87 See :class:`.BitVector` for documentation of the arguments.
89 Return:
90 The bit vector field object that was created.
91 """
92 bit_vector = BitVector(
93 name=name,
94 base_index=self.bit_index,
95 description=description,
96 width=width,
97 default_value=default_value,
98 numerical_interpretation=numerical_interpretation,
99 )
100 self._append_field(field=bit_vector)
102 return bit_vector
104 def append_enumeration(
105 self, name: str, description: str, elements: dict[str, str], default_value: str
106 ) -> Enumeration:
107 """
108 Append an enumeration field to this register.
110 See :class:`.Enumeration` for documentation of the arguments.
112 Return:
113 The enumeration field object that was created.
114 """
115 field = Enumeration(
116 name=name,
117 base_index=self.bit_index,
118 description=description,
119 elements=elements,
120 default_value=default_value,
121 )
122 self._append_field(field=field)
124 return field
126 def append_integer(
127 self, name: str, description: str, min_value: int, max_value: int, default_value: int
128 ) -> Integer:
129 """
130 Append an integer field to this register.
132 See :class:`.Integer` for documentation of the arguments.
134 Return:
135 The integer field object that was created.
136 """
137 integer = Integer(
138 name=name,
139 base_index=self.bit_index,
140 description=description,
141 min_value=min_value,
142 max_value=max_value,
143 default_value=default_value,
144 )
145 self._append_field(field=integer)
147 return integer
149 def _append_field(self, field: "RegisterField") -> None:
150 self.fields.append(field)
152 self.bit_index += field.width
153 if self.bit_index > 32:
154 raise ValueError(f'Maximum width exceeded for register "{self.name}".')
156 @property
157 def default_value(self) -> int:
158 """
159 The default value of this register as an unsigned integer.
160 Depends on the default values of the fields in this register.
161 """
162 default_value = 0
163 for field in self.fields:
164 default_value += field.default_value_uint * 2**field.base_index
166 return default_value
168 def get_field(self, name: str) -> "RegisterField":
169 """
170 Get the field within this register that has the given name. Will raise exception if no
171 field matches.
173 Arguments:
174 name: The name of the field.
176 Return:
177 The field.
178 """
179 for field in self.fields:
180 if field.name == name:
181 return field
183 raise ValueError(f'Could not find field "{name}" within register "{self.name}"')
185 @property
186 def address(self) -> int:
187 """
188 Byte address, within the register list, of this register.
189 """
190 return 4 * self.index
192 def __repr__(self) -> str:
193 return f"""{self.__class__.__name__}(\
194name={self.name},\
195index={self.index},\
196mode={self.mode},\
197description={self.description},\
198fields={','.join([repr(field) for field in self.fields])},\
199)"""