Coverage for hdl_registers/field/bit_vector.py: 97%
61 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 .numerical_interpretation import NumericalInterpretation, Unsigned
13from .register_field import RegisterField
16class BitVector(RegisterField):
17 """
18 Used to represent a bit vector field in a register.
19 See :ref:`field_bit_vector` for details.
20 """
22 def __init__(
23 self,
24 name: str,
25 base_index: int,
26 description: str,
27 width: int,
28 default_value: str | float,
29 numerical_interpretation: NumericalInterpretation | None = None,
30 ) -> None:
31 """
32 Arguments:
33 name: The name of the bit vector.
34 base_index: The zero-based index within the register for the lowest bit of this
35 bit vector.
36 description: Textual field description.
37 width: The width of the bit vector field.
38 default_value: Default value.
39 Must be either a string of length ``width`` containing only "1" and "0".
40 Or a numeric value according to the ``numerical_interpretation`` that fits in
41 the ``width``.
42 numerical_interpretation: The mode used when interpreting the bits of this field as
43 a numeric value.
44 Default is unsigned with no fractional bits.
45 """
46 self.name = name
47 self._base_index = base_index
48 self.description = description
50 self._numerical_interpretation = (
51 Unsigned(bit_width=width)
52 if numerical_interpretation is None
53 else numerical_interpretation
54 )
56 self._check_width(width=width)
57 self._width = width
59 self._default_value = ""
60 # Assign self._default_value via setter
61 self.default_value = default_value
63 @property
64 def numerical_interpretation(self) -> NumericalInterpretation:
65 """
66 The mode used when interpreting the bits of this field as a numeric value
67 (E.g. signed, unsigned fixed-point, etc.).
68 Is used by :meth:`get_value` and :meth:`set_value`.
70 Getter for private member.
71 """
72 return self._numerical_interpretation
74 def _check_width(self, width: int) -> None:
75 """
76 Sanity checks for the provided width
77 Will raise exception if something is wrong.
78 """
79 if not isinstance(width, int):
80 message = (
81 f'Bit vector "{self.name}" should have integer value for "width". Got: "{width}".'
82 )
83 raise TypeError(message)
85 if width < 1 or width > 32:
86 raise ValueError(f'Invalid width for bit vector "{self.name}". Got: "{width}".')
88 if width != self.numerical_interpretation.bit_width:
89 raise ValueError(
90 f'Inconsistent width for bit vector "{self.name}". '
91 f'Field is "{width}" bits, numerical interpretation specification is '
92 f'"{self.numerical_interpretation.bit_width}".'
93 )
95 @property
96 def default_value(self) -> str:
97 """
98 Getter for private member.
99 """
100 return self._default_value
102 @default_value.setter
103 def default_value(self, value: str | float) -> None:
104 """
105 Setter for ``default_value`` that performs sanity checks.
106 """
107 if not isinstance(value, (str, int, float)):
108 message = (
109 f'Bit vector "{self.name}" should have string or numeric value '
110 f'for "default_value". Got: "{value}".'
111 )
112 raise TypeError(message)
114 if isinstance(value, str):
115 if len(value) != self.width:
116 message = (
117 f'Bit vector "{self.name}" should have "default_value" of length {self.width}. '
118 f'Got: "{value}".'
119 )
120 raise ValueError(message)
122 for character in value:
123 if character not in ["0", "1"]:
124 message = (
125 f'Bit vector "{self.name}" invalid binary "default_value". Got: "{value}".'
126 )
127 raise ValueError(message)
129 self._default_value = value
130 return
132 try:
133 default_value_uint = self._numerical_interpretation.convert_to_unsigned_binary(
134 value=value
135 )
136 except ValueError as error:
137 message = (
138 f'Bit vector "{self.name}" should have "default_value" that fits in '
139 f'{self.width} {self._numerical_interpretation.name} bits. Got: "{value}".'
140 )
141 raise ValueError(message) from error
143 formatting_string = f"{ :0{self.width}b} "
144 default_value_bin = formatting_string.format(default_value_uint)
146 self._default_value = default_value_bin
148 def get_value(self, register_value: int) -> int | float:
149 """
150 See super method for details.
151 This subclass method uses the native numeric representation of the field value
152 (not the raw value of the bits).
153 If the field has a non-zero number of fractional bits, the type of the result
154 will be a ``float``.
155 Otherwise it will be an ``int``.
156 """
157 value_unsigned = super().get_value(register_value=register_value)
158 return self.numerical_interpretation.convert_from_unsigned_binary(
159 unsigned_binary=value_unsigned
160 )
162 def set_value(self, field_value: float) -> int:
163 """
164 See super method for details.
165 This subclass method uses the native numeric representation of the field value
166 (not the raw value of the bits).
167 If the field has a non-zero number of fractional bits, the type of the argument
168 should be a ``float``.
169 Otherwise it should be an ``int``.
170 """
171 unsigned_value = self.numerical_interpretation.convert_to_unsigned_binary(value=field_value)
172 return super().set_value(field_value=unsigned_value)
174 @property
175 def default_value_uint(self) -> int:
176 return int(self.default_value, base=2)
178 def __repr__(self) -> str:
179 return f"""{self.__class__.__name__}(\
180name={self.name},\
181_base_index={self._base_index},\
182description={self.description},
183_width={self._width},\
184_default_value={self._default_value},\
185_numerical_interpretation={self._numerical_interpretation},\
186)"""