Coverage for hdl_registers/field/bit_vector.py: 100%

51 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-17 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# -------------------------------------------------------------------------------------------------- 

9 

10# Standard libraries 

11from typing import Optional, Union 

12 

13# Local folder libraries 

14from .numerical_interpretation import NumericalInterpretation, Unsigned 

15from .register_field import RegisterField 

16 

17 

18class BitVector(RegisterField): 

19 """ 

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

21 """ 

22 

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 

48 

49 self._numerical_interpretation = ( 

50 Unsigned(bit_width=width) 

51 if numerical_interpretation is None 

52 else numerical_interpretation 

53 ) 

54 

55 self._check_width(width=width) 

56 self._width = width 

57 

58 self._default_value = "" 

59 # Assign self._default_value via setter 

60 self.default_value = default_value 

61 

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

68 

69 Getter for private member. 

70 """ 

71 return self._numerical_interpretation 

72 

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) 

83 

84 if width < 1 or width > 32: 

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

86 

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 ) 

93 

94 @property # type: ignore[override] 

95 def default_value(self) -> str: 

96 """ 

97 Getter for private member. 

98 """ 

99 return self._default_value 

100 

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) 

112 

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) 

119 

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) 

127 

128 self._default_value = value 

129 

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 ) 

143 

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) 

155 

156 @property 

157 def default_value_uint(self) -> int: 

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

159 

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