Coverage for hdl_registers/register.py: 95%

56 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-07 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 TYPE_CHECKING, Optional 

12 

13# Local folder libraries 

14from .field.bit import Bit 

15from .field.bit_vector import BitVector 

16from .field.enumeration import Enumeration 

17from .field.integer import Integer 

18from .register_mode import RegisterMode 

19 

20if TYPE_CHECKING: 

21 # Local folder libraries 

22 from .field.numerical_interpretation import NumericalInterpretation 

23 from .field.register_field import RegisterField 

24 

25 

26class Register: 

27 """ 

28 Used to represent a register and its fields. 

29 """ 

30 

31 def __init__(self, name: str, index: int, mode: "RegisterMode", description: str): 

32 """ 

33 Arguments: 

34 name: The name of the register. 

35 index: The zero-based index of this register. 

36 If this register is part of a register array, the index shall be relative to the 

37 start of the array. I.e. the index is zero for the first register in the array. 

38 If the register is a plain register, the index shall be relative to the start of 

39 the register list. 

40 mode: A mode that decides the behavior of this register. 

41 See https://hdl-registers.com/rst/basic_feature/basic_feature_register_modes.html 

42 for more information about the different modes. 

43 description: Textual register description. 

44 """ 

45 if not isinstance(mode, RegisterMode): 

46 # This check should be removed eventually. 

47 # It is only here to help users during the transition period. 

48 raise ValueError( 

49 f'Invalid mode: "{mode}". ' 

50 "Since version 6.0.0, the mode should be a 'RegisterMode' object, not a string." 

51 ) 

52 

53 self.name = name 

54 self.index = index 

55 self.mode = mode 

56 self.description = description 

57 self.fields: list["RegisterField"] = [] 

58 self.bit_index = 0 

59 

60 def append_bit(self, name: str, description: str, default_value: str) -> Bit: 

61 """ 

62 Append a bit field to this register. 

63 

64 See :class:`.Bit` for documentation of the arguments. 

65 

66 Return: 

67 The bit field object that was created. 

68 """ 

69 bit = Bit( 

70 name=name, index=self.bit_index, description=description, default_value=default_value 

71 ) 

72 self._append_field(field=bit) 

73 

74 return bit 

75 

76 def append_bit_vector( 

77 self, 

78 name: str, 

79 description: str, 

80 width: int, 

81 default_value: str, 

82 numerical_interpretation: Optional["NumericalInterpretation"] = None, 

83 ) -> BitVector: 

84 """ 

85 Append a bit vector field to this register. 

86 

87 See :class:`.BitVector` for documentation of the arguments. 

88 

89 Return: 

90 The bit vector field object that was created. 

91 """ 

92 bit_vector = BitVector( 

93 name=name, 

94 base_index=self.bit_index, 

95 description=description, 

96 width=width, 

97 default_value=default_value, 

98 numerical_interpretation=numerical_interpretation, 

99 ) 

100 self._append_field(field=bit_vector) 

101 

102 return bit_vector 

103 

104 def append_enumeration( 

105 self, name: str, description: str, elements: dict[str, str], default_value: str 

106 ) -> Enumeration: 

107 """ 

108 Append an enumeration field to this register. 

109 

110 See :class:`.Enumeration` for documentation of the arguments. 

111 

112 Return: 

113 The enumeration field object that was created. 

114 """ 

115 field = Enumeration( 

116 name=name, 

117 base_index=self.bit_index, 

118 description=description, 

119 elements=elements, 

120 default_value=default_value, 

121 ) 

122 self._append_field(field=field) 

123 

124 return field 

125 

126 def append_integer( 

127 self, name: str, description: str, min_value: int, max_value: int, default_value: int 

128 ) -> Integer: 

129 """ 

130 Append an integer field to this register. 

131 

132 See :class:`.Integer` for documentation of the arguments. 

133 

134 Return: 

135 The integer field object that was created. 

136 """ 

137 integer = Integer( 

138 name=name, 

139 base_index=self.bit_index, 

140 description=description, 

141 min_value=min_value, 

142 max_value=max_value, 

143 default_value=default_value, 

144 ) 

145 self._append_field(field=integer) 

146 

147 return integer 

148 

149 def _append_field(self, field: "RegisterField") -> None: 

150 self.fields.append(field) 

151 

152 self.bit_index += field.width 

153 if self.bit_index > 32: 

154 raise ValueError(f'Maximum width exceeded for register "{self.name}".') 

155 

156 @property 

157 def default_value(self) -> int: 

158 """ 

159 The default value of this register as an unsigned integer. 

160 Depends on the default values of the fields in this register. 

161 """ 

162 default_value = 0 

163 for field in self.fields: 

164 default_value += field.default_value_uint * 2**field.base_index 

165 

166 return default_value 

167 

168 def get_field(self, name: str) -> "RegisterField": 

169 """ 

170 Get the field within this register that has the given name. Will raise exception if no 

171 field matches. 

172 

173 Arguments: 

174 name: The name of the field. 

175 

176 Return: 

177 The field. 

178 """ 

179 for field in self.fields: 

180 if field.name == name: 

181 return field 

182 

183 raise ValueError(f'Could not find field "{name}" within register "{self.name}"') 

184 

185 @property 

186 def address(self) -> int: 

187 """ 

188 Byte address, within the register list, of this register. 

189 """ 

190 return 4 * self.index 

191 

192 def __repr__(self) -> str: 

193 return f"""{self.__class__.__name__}(\ 

194name={self.name},\ 

195index={self.index},\ 

196mode={self.mode},\ 

197description={self.description},\ 

198fields={','.join([repr(field) for field in self.fields])},\ 

199)"""