Coverage for hdl_registers/register.py: 95%

62 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 typing import TYPE_CHECKING 

13 

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 from .field.numerical_interpretation import NumericalInterpretation 

22 from .field.register_field import RegisterField 

23 

24 

25class Register: 

26 """ 

27 Used to represent a register and its fields. 

28 """ 

29 

30 def __init__(self, name: str, index: int, mode: RegisterMode, description: str) -> None: 

31 """ 

32 Arguments: 

33 name: The name of the register. 

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

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

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

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

38 the register list. 

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

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

41 for more information about the different modes. 

42 description: Textual register description. 

43 """ 

44 if not isinstance(mode, RegisterMode): 

45 # This check should be removed eventually. 

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

47 raise TypeError( 

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

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

50 ) 

51 

52 self.name = name 

53 self.index = index 

54 self.mode = mode 

55 self.description = description 

56 self.fields: list[RegisterField] = [] 

57 self.bit_index = 0 

58 

59 @property 

60 def utilized_width(self) -> int: 

61 """ 

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

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

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

65 bits that are actually utilized. 

66 

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

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

69 """ 

70 if not self.fields: 

71 return 32 

72 

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

74 

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

76 """ 

77 Append a bit field to this register. 

78 

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

80 

81 Return: 

82 The bit field object that was created. 

83 """ 

84 bit = Bit( 

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

86 ) 

87 self._append_field(field=bit) 

88 

89 return bit 

90 

91 def append_bit_vector( 

92 self, 

93 name: str, 

94 description: str, 

95 width: int, 

96 default_value: str, 

97 numerical_interpretation: NumericalInterpretation | None = None, 

98 ) -> BitVector: 

99 """ 

100 Append a bit vector field to this register. 

101 

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

103 

104 Return: 

105 The bit vector field object that was created. 

106 """ 

107 bit_vector = BitVector( 

108 name=name, 

109 base_index=self.bit_index, 

110 description=description, 

111 width=width, 

112 default_value=default_value, 

113 numerical_interpretation=numerical_interpretation, 

114 ) 

115 self._append_field(field=bit_vector) 

116 

117 return bit_vector 

118 

119 def append_enumeration( 

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

121 ) -> Enumeration: 

122 """ 

123 Append an enumeration field to this register. 

124 

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

126 

127 Return: 

128 The enumeration field object that was created. 

129 """ 

130 field = Enumeration( 

131 name=name, 

132 base_index=self.bit_index, 

133 description=description, 

134 elements=elements, 

135 default_value=default_value, 

136 ) 

137 self._append_field(field=field) 

138 

139 return field 

140 

141 def append_integer( 

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

143 ) -> Integer: 

144 """ 

145 Append an integer field to this register. 

146 

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

148 

149 Return: 

150 The integer field object that was created. 

151 """ 

152 integer = Integer( 

153 name=name, 

154 base_index=self.bit_index, 

155 description=description, 

156 min_value=min_value, 

157 max_value=max_value, 

158 default_value=default_value, 

159 ) 

160 self._append_field(field=integer) 

161 

162 return integer 

163 

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

165 self.fields.append(field) 

166 

167 self.bit_index += field.width 

168 if self.bit_index > 32: 

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

170 

171 @property 

172 def default_value(self) -> int: 

173 """ 

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

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

176 """ 

177 default_value = 0 

178 for field in self.fields: 

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

180 

181 return default_value 

182 

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

184 """ 

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

186 field matches. 

187 

188 Arguments: 

189 name: The name of the field. 

190 

191 Return: 

192 The field. 

193 """ 

194 for field in self.fields: 

195 if field.name == name: 

196 return field 

197 

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

199 

200 @property 

201 def address(self) -> int: 

202 """ 

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

204 """ 

205 return 4 * self.index 

206 

207 def __repr__(self) -> str: 

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

209name={self.name},\ 

210index={self.index},\ 

211mode={self.mode},\ 

212description={self.description},\ 

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

214)"""