Coverage for hdl_registers/register.py: 94%
51 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 06:41 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-29 06:41 +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 def append_bit(self, name: str, description: str, default_value: str) -> Bit:
60 """
61 Append a bit field to this register.
63 See :class:`.Bit` for documentation of the arguments.
65 Return:
66 The bit field object that was created.
67 """
68 bit = Bit(
69 name=name, index=self.bit_index, description=description, default_value=default_value
70 )
71 self._append_field(field=bit)
73 return bit
75 def append_bit_vector(
76 self,
77 name: str,
78 description: str,
79 width: int,
80 default_value: str | float,
81 numerical_interpretation: NumericalInterpretation | None = None,
82 ) -> BitVector:
83 """
84 Append a bit vector field to this register.
86 See :class:`.BitVector` for documentation of the arguments.
88 Return:
89 The bit vector field object that was created.
90 """
91 bit_vector = BitVector(
92 name=name,
93 base_index=self.bit_index,
94 description=description,
95 width=width,
96 default_value=default_value,
97 numerical_interpretation=numerical_interpretation,
98 )
99 self._append_field(field=bit_vector)
101 return bit_vector
103 def append_enumeration(
104 self, name: str, description: str, elements: dict[str, str], default_value: str
105 ) -> Enumeration:
106 """
107 Append an enumeration field to this register.
109 See :class:`.Enumeration` for documentation of the arguments.
111 Return:
112 The enumeration field object that was created.
113 """
114 field = Enumeration(
115 name=name,
116 base_index=self.bit_index,
117 description=description,
118 elements=elements,
119 default_value=default_value,
120 )
121 self._append_field(field=field)
123 return field
125 def append_integer(
126 self, name: str, description: str, min_value: int, max_value: int, default_value: int
127 ) -> Integer:
128 """
129 Append an integer field to this register.
131 See :class:`.Integer` for documentation of the arguments.
133 Return:
134 The integer field object that was created.
135 """
136 integer = Integer(
137 name=name,
138 base_index=self.bit_index,
139 description=description,
140 min_value=min_value,
141 max_value=max_value,
142 default_value=default_value,
143 )
144 self._append_field(field=integer)
146 return integer
148 def _append_field(self, field: RegisterField) -> None:
149 self.fields.append(field)
151 self.bit_index += field.width
152 if self.bit_index > 32:
153 raise ValueError(f'Maximum width exceeded for register "{self.name}".')
155 def get_field(self, name: str) -> RegisterField:
156 """
157 Get the field within this register that has the given name. Will raise exception if no
158 field matches.
160 Arguments:
161 name: The name of the field.
163 Return:
164 The field.
165 """
166 for field in self.fields:
167 if field.name == name:
168 return field
170 raise ValueError(f'Could not find field "{name}" within register "{self.name}"')
172 @property
173 def address(self) -> int:
174 """
175 Byte address, within the register list, of this register.
176 """
177 return 4 * self.index
179 def __repr__(self) -> str:
180 return f"""{self.__class__.__name__}(\
181name={self.name},\
182index={self.index},\
183mode={self.mode},\
184description={self.description},\
185fields={",".join([repr(field) for field in self.fields])},\
186)"""