Coverage for hdl_registers/field/bit_vector.py: 100%
51 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-19 20:51 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-19 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 Optional, Union
13# Local folder libraries
14from .numerical_interpretation import NumericalInterpretation, Unsigned
15from .register_field import RegisterField
18class BitVector(RegisterField):
19 """
20 Used to represent a bit vector field in a register.
21 """
23 def __init__(
24 self,
25 name: str,
26 base_index: int,
27 description: str,
28 width: int,
29 default_value: str,
30 numerical_interpretation: Optional[NumericalInterpretation] = None,
31 ): # pylint: disable=too-many-arguments
32 """
33 Arguments:
34 name: The name of the bit vector.
35 base_index: The zero-based index within the register for the lowest bit of this
36 bit vector.
37 description: Textual field description.
38 width: The width of the bit vector field.
39 default_value: Default value as a string.
40 Must be of length ``width`` and contain only "1" and "0".
41 numerical_interpretation: The mode used when interpreting the bits of this field as
42 a numeric value.
43 Default is unsigned with no fractional bits.
44 """
45 self.name = name
46 self._base_index = base_index
47 self.description = description
49 self._numerical_interpretation = (
50 Unsigned(bit_width=width)
51 if numerical_interpretation is None
52 else numerical_interpretation
53 )
55 self._check_width(width=width)
56 self._width = width
58 self._default_value = ""
59 # Assign self._default_value via setter
60 self.default_value = default_value
62 @property
63 def numerical_interpretation(self) -> NumericalInterpretation:
64 """
65 The mode used when interpreting the bits of this field as a numeric value
66 (E.g. signed, unsigned fixed-point, etc.).
67 Is used by :meth:`get_value` and :meth:`set_value`.
69 Getter for private member.
70 """
71 return self._numerical_interpretation
73 def _check_width(self, width: int) -> None:
74 """
75 Sanity checks for the provided width
76 Will raise exception if something is wrong.
77 """
78 if not isinstance(width, int):
79 message = (
80 f'Bit vector "{self.name}" should have integer value for "width". Got: "{width}".'
81 )
82 raise ValueError(message)
84 if width < 1 or width > 32:
85 raise ValueError(f'Invalid width for bit vector "{self.name}". Got: "{width}".')
87 if width != self.numerical_interpretation.bit_width:
88 raise ValueError(
89 f'Inconsistent width for bit vector "{self.name}". '
90 f'Field is "{width}" bits, numerical interpretation specification is '
91 f'"{self.numerical_interpretation.bit_width}".'
92 )
94 @property # type: ignore[override]
95 def default_value(self) -> str:
96 """
97 Getter for private member.
98 """
99 return self._default_value
101 @default_value.setter
102 def default_value(self, value: str) -> None:
103 """
104 Setter for ``default_value`` that performs sanity checks.
105 """
106 if not isinstance(value, str):
107 message = (
108 f'Bit vector "{self.name}" should have string value for "default_value". '
109 f'Got: "{value}"'
110 )
111 raise ValueError(message)
113 if len(value) != self.width:
114 message = (
115 f'Bit vector "{self.name}" should have "default_value" of length {self.width}. '
116 f'Got: "{value}".'
117 )
118 raise ValueError(message)
120 for character in value:
121 if character not in ["0", "1"]:
122 message = (
123 f'Bit vector "{self.name}" invalid binary value for "default_value". '
124 f'Got: "{value}".'
125 )
126 raise ValueError(message)
128 self._default_value = value
130 def get_value(self, register_value: int) -> Union[int, float]: # type: ignore[override]
131 """
132 See super method for details.
133 This subclass method uses the native numeric representation of the field value
134 (not the raw value of the bits).
135 If the field has a non-zero number of fractional bits, the type of the result
136 will be a ``float``.
137 Otherwise it will be an ``int``.
138 """
139 value_unsigned = super().get_value(register_value=register_value)
140 return self.numerical_interpretation.convert_from_unsigned_binary(
141 unsigned_binary=value_unsigned
142 )
144 def set_value(self, field_value: Union[int, float]) -> int:
145 """
146 See super method for details.
147 This subclass method uses the native numeric representation of the field value
148 (not the raw value of the bits).
149 If the field has a non-zero number of fractional bits, the type of the argument
150 should be a ``float``.
151 Otherwise it should be an ``int``.
152 """
153 unsigned_value = self.numerical_interpretation.convert_to_unsigned_binary(value=field_value)
154 return super().set_value(field_value=unsigned_value)
156 @property
157 def default_value_uint(self) -> int:
158 return int(self.default_value, base=2)
160 def __repr__(self) -> str:
161 return f"""{self.__class__.__name__}(\
162name={self.name},\
163_base_index={self._base_index},\
164description={self.description},
165_width={self._width},\
166_default_value={self._default_value},\
167_numerical_interpretation={self._numerical_interpretation},\
168)"""