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