Coverage for hdl_registers/register.py: 95%

61 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-28 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 @property 

61 def utilized_width(self) -> int: 

62 """ 

63 The number of bits that are utilized by the fields in this register. 

64 Note that this is not always the same as the width of the register. 

65 Some generator implementations can be optimized by only taking into account the 

66 bits that are actually utilized. 

67 

68 Note that if the register has no fields, we do not really know what the user is doing with 

69 it, and we have to assume that the full width is used. 

70 """ 

71 if not self.fields: 

72 return 32 

73 

74 return self.fields[-1].base_index + self.fields[-1].width 

75 

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

77 """ 

78 Append a bit field to this register. 

79 

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

81 

82 Return: 

83 The bit field object that was created. 

84 """ 

85 bit = Bit( 

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

87 ) 

88 self._append_field(field=bit) 

89 

90 return bit 

91 

92 def append_bit_vector( 

93 self, 

94 name: str, 

95 description: str, 

96 width: int, 

97 default_value: str, 

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

99 ) -> BitVector: 

100 """ 

101 Append a bit vector field to this register. 

102 

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

104 

105 Return: 

106 The bit vector field object that was created. 

107 """ 

108 bit_vector = BitVector( 

109 name=name, 

110 base_index=self.bit_index, 

111 description=description, 

112 width=width, 

113 default_value=default_value, 

114 numerical_interpretation=numerical_interpretation, 

115 ) 

116 self._append_field(field=bit_vector) 

117 

118 return bit_vector 

119 

120 def append_enumeration( 

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

122 ) -> Enumeration: 

123 """ 

124 Append an enumeration field to this register. 

125 

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

127 

128 Return: 

129 The enumeration field object that was created. 

130 """ 

131 field = Enumeration( 

132 name=name, 

133 base_index=self.bit_index, 

134 description=description, 

135 elements=elements, 

136 default_value=default_value, 

137 ) 

138 self._append_field(field=field) 

139 

140 return field 

141 

142 def append_integer( 

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

144 ) -> Integer: 

145 """ 

146 Append an integer field to this register. 

147 

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

149 

150 Return: 

151 The integer field object that was created. 

152 """ 

153 integer = Integer( 

154 name=name, 

155 base_index=self.bit_index, 

156 description=description, 

157 min_value=min_value, 

158 max_value=max_value, 

159 default_value=default_value, 

160 ) 

161 self._append_field(field=integer) 

162 

163 return integer 

164 

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

166 self.fields.append(field) 

167 

168 self.bit_index += field.width 

169 if self.bit_index > 32: 

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

171 

172 @property 

173 def default_value(self) -> int: 

174 """ 

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

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

177 """ 

178 default_value = 0 

179 for field in self.fields: 

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

181 

182 return default_value 

183 

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

185 """ 

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

187 field matches. 

188 

189 Arguments: 

190 name: The name of the field. 

191 

192 Return: 

193 The field. 

194 """ 

195 for field in self.fields: 

196 if field.name == name: 

197 return field 

198 

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

200 

201 @property 

202 def address(self) -> int: 

203 """ 

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

205 """ 

206 return 4 * self.index 

207 

208 def __repr__(self) -> str: 

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

210name={self.name},\ 

211index={self.index},\ 

212mode={self.mode},\ 

213description={self.description},\ 

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

215)"""