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

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# -------------------------------------------------------------------------------------------------- 

9 

10from __future__ import annotations 

11 

12from .numerical_interpretation import NumericalInterpretation, Unsigned 

13from .register_field import RegisterField 

14 

15 

16class BitVector(RegisterField): 

17 """ 

18 Used to represent a bit vector field in a register. 

19 """ 

20 

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 

46 

47 self._numerical_interpretation = ( 

48 Unsigned(bit_width=width) 

49 if numerical_interpretation is None 

50 else numerical_interpretation 

51 ) 

52 

53 self._check_width(width=width) 

54 self._width = width 

55 

56 self._default_value = "" 

57 # Assign self._default_value via setter 

58 self.default_value = default_value 

59 

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`. 

66 

67 Getter for private member. 

68 """ 

69 return self._numerical_interpretation 

70 

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) 

81 

82 if width < 1 or width > 32: 

83 raise ValueError(f'Invalid width for bit vector "{self.name}". Got: "{width}".') 

84 

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 ) 

91 

92 @property 

93 def default_value(self) -> str: 

94 """ 

95 Getter for private member. 

96 """ 

97 return self._default_value 

98 

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) 

110 

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) 

117 

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) 

125 

126 self._default_value = value 

127 

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 ) 

141 

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) 

153 

154 @property 

155 def default_value_uint(self) -> int: 

156 return int(self.default_value, base=2) 

157 

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)"""