Coverage for hdl_registers/register.py: 94%

51 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-29 06:41 +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 def append_bit(self, name: str, description: str, default_value: str) -> Bit: 

60 """ 

61 Append a bit field to this register. 

62 

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

64 

65 Return: 

66 The bit field object that was created. 

67 """ 

68 bit = Bit( 

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

70 ) 

71 self._append_field(field=bit) 

72 

73 return bit 

74 

75 def append_bit_vector( 

76 self, 

77 name: str, 

78 description: str, 

79 width: int, 

80 default_value: str | float, 

81 numerical_interpretation: NumericalInterpretation | None = None, 

82 ) -> BitVector: 

83 """ 

84 Append a bit vector field to this register. 

85 

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

87 

88 Return: 

89 The bit vector field object that was created. 

90 """ 

91 bit_vector = BitVector( 

92 name=name, 

93 base_index=self.bit_index, 

94 description=description, 

95 width=width, 

96 default_value=default_value, 

97 numerical_interpretation=numerical_interpretation, 

98 ) 

99 self._append_field(field=bit_vector) 

100 

101 return bit_vector 

102 

103 def append_enumeration( 

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

105 ) -> Enumeration: 

106 """ 

107 Append an enumeration field to this register. 

108 

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

110 

111 Return: 

112 The enumeration field object that was created. 

113 """ 

114 field = Enumeration( 

115 name=name, 

116 base_index=self.bit_index, 

117 description=description, 

118 elements=elements, 

119 default_value=default_value, 

120 ) 

121 self._append_field(field=field) 

122 

123 return field 

124 

125 def append_integer( 

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

127 ) -> Integer: 

128 """ 

129 Append an integer field to this register. 

130 

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

132 

133 Return: 

134 The integer field object that was created. 

135 """ 

136 integer = Integer( 

137 name=name, 

138 base_index=self.bit_index, 

139 description=description, 

140 min_value=min_value, 

141 max_value=max_value, 

142 default_value=default_value, 

143 ) 

144 self._append_field(field=integer) 

145 

146 return integer 

147 

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

149 self.fields.append(field) 

150 

151 self.bit_index += field.width 

152 if self.bit_index > 32: 

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

154 

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

156 """ 

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

158 field matches. 

159 

160 Arguments: 

161 name: The name of the field. 

162 

163 Return: 

164 The field. 

165 """ 

166 for field in self.fields: 

167 if field.name == name: 

168 return field 

169 

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

171 

172 @property 

173 def address(self) -> int: 

174 """ 

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

176 """ 

177 return 4 * self.index 

178 

179 def __repr__(self) -> str: 

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

181name={self.name},\ 

182index={self.index},\ 

183mode={self.mode},\ 

184description={self.description},\ 

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

186)"""