Coverage for hdl_registers/register.py: 98%
66 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-10-02 22:01 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-10-02 22:01 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the hdl_registers project, a HDL register generator fast enough to run
5# in real time.
6# https://hdl-registers.com
7# https://gitlab.com/hdl_registers/hdl_registers
8# --------------------------------------------------------------------------------------------------
10# Standard libraries
11from typing import TYPE_CHECKING
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 .field.register_field import DEFAULT_FIELD_TYPE, FieldType
20if TYPE_CHECKING:
21 # Local folder libraries
22 from .field.register_field import RegisterField
25class RegisterMode:
26 def __init__(self, mode_readable, description):
27 """
28 Arguments:
29 mode_readable (str): The readable representation of this mode. E.g. "r" -> "Read-only".
30 description (str): Textual description of mode.
31 """
32 self.mode_readable = mode_readable
33 self.description = description
36REGISTER_MODES = dict(
37 r=RegisterMode("Read", "Bus can read a value that fabric provides."),
38 w=RegisterMode("Write", "Bus can write a value that is available for fabric usage."),
39 r_w=RegisterMode(
40 "Read, Write",
41 "Bus can write a value and read it back. The written value is available for fabric usage.",
42 ),
43 wpulse=RegisterMode(
44 "Write-pulse", "Bus can write a value that is asserted for one clock cycle in fabric."
45 ),
46 r_wpulse=RegisterMode(
47 "Read, Write-pulse",
48 "Bus can read a value that fabric provides. "
49 "Bus can write a value that is asserted for one clock cycle in fabric.",
50 ),
51)
54class Register:
56 """
57 Used to represent a register and its fields.
58 """
60 def __init__(self, name, index, mode, description):
61 """
62 Arguments:
63 name (str): The name of the register.
64 index (int): The zero-based index of this register.
65 If this register is part of a register array, the index shall be relative to the
66 start of the array. I.e. the index is zero for the first register in the array.
67 If the register is a plain register, the index shall be relative to the start of
68 the register list.
69 mode (str): A valid register mode.
70 description (str): Textual register description.
71 """
72 if mode not in REGISTER_MODES:
73 raise ValueError(f'Invalid mode "{mode}" for register "{name}"')
75 self.name = name
76 self.index = index
77 self.mode = mode
78 self.description = description
79 self.fields = []
80 self.bit_index = 0
82 def append_bit(self, name: str, description: str, default_value: str) -> Bit:
83 """
84 Append a bit field to this register.
86 See :class:`.Bit` for documentation of the arguments.
88 Return:
89 The bit field object that was created.
90 """
91 bit = Bit(
92 name=name, index=self.bit_index, description=description, default_value=default_value
93 )
94 self._append_field(field=bit)
96 return bit
98 def append_bit_vector(
99 self,
100 name: str,
101 description: str,
102 width: int,
103 default_value: str,
104 field_type: FieldType = DEFAULT_FIELD_TYPE,
105 ) -> BitVector:
106 """
107 Append a bit vector field to this register.
109 See :class:`.BitVector` for documentation of the arguments.
111 Return:
112 The bit vector field object that was created.
113 """
114 bit_vector = BitVector(
115 name=name,
116 base_index=self.bit_index,
117 description=description,
118 width=width,
119 default_value=default_value,
120 field_type=field_type,
121 )
122 self._append_field(field=bit_vector)
124 return bit_vector
126 def append_enumeration(
127 self, name: str, description: str, elements: dict[str, str], default_value: str
128 ) -> Enumeration:
129 """
130 Append an enumeration field to this register.
132 See :class:`.Enumeration` for documentation of the arguments.
134 Return:
135 The enumeration field object that was created.
136 """
137 field = Enumeration(
138 name=name,
139 base_index=self.bit_index,
140 description=description,
141 elements=elements,
142 default_value=default_value,
143 )
144 self._append_field(field=field)
146 return field
148 def append_integer(
149 self, name: str, description: str, min_value: int, max_value: int, default_value: int
150 ) -> Integer:
151 """
152 Append an integer field to this register.
154 See :class:`.Integer` for documentation of the arguments.
156 Return:
157 The integer field object that was created.
158 """
159 integer = Integer(
160 name=name,
161 base_index=self.bit_index,
162 description=description,
163 min_value=min_value,
164 max_value=max_value,
165 default_value=default_value,
166 )
167 self._append_field(field=integer)
169 return integer
171 def _append_field(self, field: "RegisterField"):
172 self.fields.append(field)
174 self.bit_index += field.width
175 if self.bit_index > 32:
176 raise ValueError(f'Maximum width exceeded for register "{self.name}".')
178 @property
179 def default_value(self) -> int:
180 """
181 The default value of this register as an unsigned integer.
182 Depends on the default values of the fields in this register.
183 """
184 default_value = 0
185 for field in self.fields:
186 default_value += field.default_value_uint * 2**field.base_index
188 return default_value
190 def get_field(self, name):
191 """
192 Get the field within this register that has the given name. Will raise exception if no
193 field matches.
195 Arguments:
196 name (str): The name of the field.
198 Returns:
199 :class:`.RegisterField`: The field.
200 """
201 for field in self.fields:
202 if field.name == name:
203 return field
205 raise ValueError(f'Could not find field "{name}" within register "{self.name}"')
207 @property
208 def address(self):
209 """
210 int: Byte address, within the register list, of this register.
211 """
212 return 4 * self.index
214 @property
215 def is_bus_readable(self):
216 """
217 True if the register is readable by bus. Based on the register type.
218 """
219 return self.mode in ["r", "r_w", "r_wpulse"]
221 @property
222 def is_bus_writeable(self):
223 """
224 True if the register is writeable by bus. Based on the register type.
225 """
226 return self.mode in ["w", "r_w", "wpulse", "r_wpulse"]
228 def __repr__(self):
229 return f"""{self.__class__.__name__}(\
230name={self.name},\
231index={self.index},\
232mode={self.mode},\
233description={self.description},\
234default_value={self.default_value},\
235fields={','.join([repr(field) for field in self.fields])},\
236)"""