Coverage for hdl_registers/register.py: 98%

66 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-10-02 22:01 +0000

1# -------------------------------------------------------------------------------------------------- 

2# Copyright (c) Lukas Vik. All rights reserved. 

3# 

4# This file is part of the hdl_registers project, a HDL register generator fast enough to run 

5# in real time. 

6# https://hdl-registers.com 

7# https://gitlab.com/hdl_registers/hdl_registers 

8# -------------------------------------------------------------------------------------------------- 

9 

10# Standard libraries 

11from typing import TYPE_CHECKING 

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 .field.register_field import DEFAULT_FIELD_TYPE, FieldType 

19 

20if TYPE_CHECKING: 

21 # Local folder libraries 

22 from .field.register_field import RegisterField 

23 

24 

25class RegisterMode: 

26 def __init__(self, mode_readable, description): 

27 """ 

28 Arguments: 

29 mode_readable (str): The readable representation of this mode. E.g. "r" -> "Read-only". 

30 description (str): Textual description of mode. 

31 """ 

32 self.mode_readable = mode_readable 

33 self.description = description 

34 

35 

36REGISTER_MODES = dict( 

37 r=RegisterMode("Read", "Bus can read a value that fabric provides."), 

38 w=RegisterMode("Write", "Bus can write a value that is available for fabric usage."), 

39 r_w=RegisterMode( 

40 "Read, Write", 

41 "Bus can write a value and read it back. The written value is available for fabric usage.", 

42 ), 

43 wpulse=RegisterMode( 

44 "Write-pulse", "Bus can write a value that is asserted for one clock cycle in fabric." 

45 ), 

46 r_wpulse=RegisterMode( 

47 "Read, Write-pulse", 

48 "Bus can read a value that fabric provides. " 

49 "Bus can write a value that is asserted for one clock cycle in fabric.", 

50 ), 

51) 

52 

53 

54class Register: 

55 

56 """ 

57 Used to represent a register and its fields. 

58 """ 

59 

60 def __init__(self, name, index, mode, description): 

61 """ 

62 Arguments: 

63 name (str): The name of the register. 

64 index (int): The zero-based index of this register. 

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

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

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

68 the register list. 

69 mode (str): A valid register mode. 

70 description (str): Textual register description. 

71 """ 

72 if mode not in REGISTER_MODES: 

73 raise ValueError(f'Invalid mode "{mode}" for register "{name}"') 

74 

75 self.name = name 

76 self.index = index 

77 self.mode = mode 

78 self.description = description 

79 self.fields = [] 

80 self.bit_index = 0 

81 

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

83 """ 

84 Append a bit field to this register. 

85 

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

87 

88 Return: 

89 The bit field object that was created. 

90 """ 

91 bit = Bit( 

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

93 ) 

94 self._append_field(field=bit) 

95 

96 return bit 

97 

98 def append_bit_vector( 

99 self, 

100 name: str, 

101 description: str, 

102 width: int, 

103 default_value: str, 

104 field_type: FieldType = DEFAULT_FIELD_TYPE, 

105 ) -> BitVector: 

106 """ 

107 Append a bit vector field to this register. 

108 

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

110 

111 Return: 

112 The bit vector field object that was created. 

113 """ 

114 bit_vector = BitVector( 

115 name=name, 

116 base_index=self.bit_index, 

117 description=description, 

118 width=width, 

119 default_value=default_value, 

120 field_type=field_type, 

121 ) 

122 self._append_field(field=bit_vector) 

123 

124 return bit_vector 

125 

126 def append_enumeration( 

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

128 ) -> Enumeration: 

129 """ 

130 Append an enumeration field to this register. 

131 

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

133 

134 Return: 

135 The enumeration field object that was created. 

136 """ 

137 field = Enumeration( 

138 name=name, 

139 base_index=self.bit_index, 

140 description=description, 

141 elements=elements, 

142 default_value=default_value, 

143 ) 

144 self._append_field(field=field) 

145 

146 return field 

147 

148 def append_integer( 

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

150 ) -> Integer: 

151 """ 

152 Append an integer field to this register. 

153 

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

155 

156 Return: 

157 The integer field object that was created. 

158 """ 

159 integer = Integer( 

160 name=name, 

161 base_index=self.bit_index, 

162 description=description, 

163 min_value=min_value, 

164 max_value=max_value, 

165 default_value=default_value, 

166 ) 

167 self._append_field(field=integer) 

168 

169 return integer 

170 

171 def _append_field(self, field: "RegisterField"): 

172 self.fields.append(field) 

173 

174 self.bit_index += field.width 

175 if self.bit_index > 32: 

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

177 

178 @property 

179 def default_value(self) -> int: 

180 """ 

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

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

183 """ 

184 default_value = 0 

185 for field in self.fields: 

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

187 

188 return default_value 

189 

190 def get_field(self, name): 

191 """ 

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

193 field matches. 

194 

195 Arguments: 

196 name (str): The name of the field. 

197 

198 Returns: 

199 :class:`.RegisterField`: The field. 

200 """ 

201 for field in self.fields: 

202 if field.name == name: 

203 return field 

204 

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

206 

207 @property 

208 def address(self): 

209 """ 

210 int: Byte address, within the register list, of this register. 

211 """ 

212 return 4 * self.index 

213 

214 @property 

215 def is_bus_readable(self): 

216 """ 

217 True if the register is readable by bus. Based on the register type. 

218 """ 

219 return self.mode in ["r", "r_w", "r_wpulse"] 

220 

221 @property 

222 def is_bus_writeable(self): 

223 """ 

224 True if the register is writeable by bus. Based on the register type. 

225 """ 

226 return self.mode in ["w", "r_w", "wpulse", "r_wpulse"] 

227 

228 def __repr__(self): 

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

230name={self.name},\ 

231index={self.index},\ 

232mode={self.mode},\ 

233description={self.description},\ 

234default_value={self.default_value},\ 

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

236)"""