Coverage for hdl_registers/generator/vhdl/vhdl_generator_common.py: 92%

92 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-17 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 pathlib import Path 

12from typing import TYPE_CHECKING, Any, Iterator, Optional 

13 

14# First party libraries 

15from hdl_registers.field.bit import Bit 

16from hdl_registers.field.bit_vector import BitVector 

17from hdl_registers.field.enumeration import Enumeration 

18from hdl_registers.field.integer import Integer 

19from hdl_registers.field.numerical_interpretation import Fixed, Signed, Unsigned 

20from hdl_registers.generator.register_code_generator import RegisterCodeGenerator 

21from hdl_registers.register_mode import HardwareAccessDirection, SoftwareAccessDirection 

22 

23if TYPE_CHECKING: 

24 # First party libraries 

25 from hdl_registers.field.register_field import RegisterField 

26 from hdl_registers.register import Register 

27 from hdl_registers.register_array import RegisterArray 

28 

29 

30class VhdlGeneratorCommon(RegisterCodeGenerator): 

31 """ 

32 Common methods for generation of VHDL code. 

33 """ 

34 

35 COMMENT_START = "--" 

36 

37 @staticmethod 

38 def field_to_slv_function_name(field: "RegisterField", field_name: str) -> str: 

39 """ 

40 Name of the function that converts the field's native VHDL representation to SLV. 

41 

42 Arguments: 

43 field: A field. 

44 field_name: The field's qualified name. 

45 """ 

46 if isinstance(field, Integer): 

47 # All integer field values will be sub-type of VHDL integer. 

48 # If many of these functions have the same name "to_slv", that will be a name clash. 

49 # Hence we need to qualify the function name. 

50 return f"to_{field_name}_slv" 

51 

52 if isinstance(field, Enumeration): 

53 # For the enumeration field on the other hand, the type is unambiguous. 

54 return "to_slv" 

55 

56 raise TypeError(f"Field {field} does not have a conversion function.") 

57 

58 def field_to_slv(self, field: "RegisterField", field_name: str, value: str) -> str: 

59 """ 

60 Get a VHDL snippet that converts a value of the given field to SLV. 

61 Via e.g. a function call or a cast. 

62 

63 Arguments: 

64 field: The field. 

65 field_name: The field's qualified name. 

66 value: The name of the variable/constant that holds the field's natively typed value. 

67 """ 

68 if isinstance(field, Bit): 

69 return value 

70 

71 if isinstance(field, BitVector): 

72 if isinstance(field.numerical_interpretation, (Signed, Unsigned)): 

73 # Plain unsigned/signed vector is a subtype of std_logic_vector. 

74 # Hence we can just cast it. 

75 return f"std_logic_vector({value})" 

76 

77 if isinstance(field.numerical_interpretation, Fixed): 

78 # Casting function built into ieee.fixed_pkg. 

79 return f"to_slv({value})" 

80 

81 raise ValueError(f"Unknown bit vector field: {field}") 

82 

83 if isinstance(field, (Enumeration, Integer)): 

84 # Our own conversion functions. 

85 to_slv = self.field_to_slv_function_name(field=field, field_name=field_name) 

86 return f"{to_slv}({value})" 

87 

88 raise ValueError(f"Unknown field: {field}") 

89 

90 def field_type_name( 

91 self, 

92 register: "Register", 

93 field: "RegisterField", 

94 register_array: Optional["RegisterArray"] = None, 

95 ) -> str: 

96 """ 

97 Get the native VHDL type name that will represent the value of the supplied field. 

98 """ 

99 if isinstance(field, Bit): 

100 return "std_ulogic" 

101 

102 if isinstance(field, (BitVector, Enumeration, Integer)): 

103 field_name = self.qualified_field_name( 

104 register=register, register_array=register_array, field=field 

105 ) 

106 return f"{field_name}_t" 

107 

108 raise ValueError(f"Unknown field: {field}") 

109 

110 def has_any_software_accessible_register(self, direction: SoftwareAccessDirection) -> bool: 

111 """ 

112 Return True if the register list contains any register, plain or in array, that is 

113 software-accessible in the given direction. 

114 """ 

115 for register, _ in self.iterate_registers(): 

116 if register.mode.is_software_accessible(direction=direction): 

117 return True 

118 

119 return False 

120 

121 def iterate_software_accessible_registers( 

122 self, direction: SoftwareAccessDirection 

123 ) -> Iterator[tuple["Register", Optional["RegisterArray"]]]: 

124 """ 

125 Iterate all registers in the register list, plain or in array, that are software-accessible 

126 in the given direction. 

127 """ 

128 for register, register_array in self.iterate_registers(): 

129 if register.mode.is_software_accessible(direction=direction): 

130 yield register, register_array 

131 

132 def iterate_software_accessible_plain_registers( 

133 self, direction: SoftwareAccessDirection 

134 ) -> Iterator["Register"]: 

135 """ 

136 Iterate all plain registers in the register list that are software-accessible in the 

137 given direction. 

138 """ 

139 for register in self.iterate_plain_registers(): 

140 if register.mode.is_software_accessible(direction=direction): 

141 yield register 

142 

143 def iterate_software_accessible_array_registers( 

144 self, register_array: "RegisterArray", direction: SoftwareAccessDirection 

145 ) -> Iterator["Register"]: 

146 """ 

147 Iterate all registers in the register array that are software-accessible in the 

148 given direction. 

149 """ 

150 for register in register_array.registers: 

151 if register.mode.is_software_accessible(direction=direction): 

152 yield register 

153 

154 def iterate_software_accessible_register_arrays( 

155 self, direction: SoftwareAccessDirection 

156 ) -> Iterator["RegisterArray"]: 

157 """ 

158 Iterate all register arrays in the register list that contain at least one register that 

159 is software-accessible in the given direction. 

160 """ 

161 for register_array in self.iterate_register_arrays(): 

162 accessible_registers = list( 

163 self.iterate_software_accessible_array_registers( 

164 register_array=register_array, direction=direction 

165 ) 

166 ) 

167 if accessible_registers: 

168 yield register_array 

169 

170 def has_any_hardware_accessible_register(self, direction: HardwareAccessDirection) -> bool: 

171 """ 

172 Return True if the register list contains at least one register, plain or in array, with a 

173 mode where hardware accesses the value in the given direction. 

174 """ 

175 for register, _ in self.iterate_registers(): 

176 if register.mode.is_hardware_accessible(direction=direction): 

177 return True 

178 

179 return False 

180 

181 def iterate_hardware_accessible_registers( 

182 self, direction: HardwareAccessDirection 

183 ) -> Iterator[tuple["Register", Optional["RegisterArray"]]]: 

184 """ 

185 Iterate all registers in the register list, plain or in array, that are hardware-accessible 

186 in the given direction. 

187 """ 

188 for register, register_array in self.iterate_registers(): 

189 if register.mode.is_hardware_accessible(direction=direction): 

190 yield register, register_array 

191 

192 def iterate_hardware_accessible_plain_registers( 

193 self, direction: HardwareAccessDirection 

194 ) -> Iterator["Register"]: 

195 """ 

196 Iterate all plain registers in the register list that are hardware-accessible in the 

197 given direction. 

198 """ 

199 for register in self.iterate_plain_registers(): 

200 if register.mode.is_hardware_accessible(direction=direction): 

201 yield register 

202 

203 def iterate_hardware_accessible_array_registers( 

204 self, register_array: "RegisterArray", direction: HardwareAccessDirection 

205 ) -> Iterator["Register"]: 

206 """ 

207 Iterate all registers in the register array that are hardware-accessible in the 

208 given direction. 

209 """ 

210 for register in register_array.registers: 

211 if register.mode.is_hardware_accessible(direction=direction): 

212 yield register 

213 

214 def iterate_hardware_accessible_register_arrays( 

215 self, direction: HardwareAccessDirection 

216 ) -> Iterator["RegisterArray"]: 

217 """ 

218 Iterate all register arrays in the register list that contain at least one register that 

219 is hardware-accessible in the given direction. 

220 """ 

221 for register_array in self.iterate_register_arrays(): 

222 accessible_registers = list( 

223 self.iterate_hardware_accessible_array_registers( 

224 register_array=register_array, direction=direction 

225 ) 

226 ) 

227 if accessible_registers: 

228 yield register_array 

229 

230 def _create_if_there_are_registers_otherwise_delete_file(self, **kwargs: Any) -> Path: 

231 """ 

232 Create the code artifact only if the register list actually has any registers. 

233 Convenient to call in generators where no registers would result in the generated file being 

234 an empty shell. 

235 

236 If, for example, the user has a register list with only constants we do not want 

237 to flood the file system with unnecessary files. 

238 

239 If the artifact file exists from a previous run, we delete it since we do not want stray 

240 files laying around and we do not want to give the false impression that this file is being 

241 actively generated. 

242 """ 

243 if self.register_list.register_objects: 

244 return super().create(**kwargs) 

245 

246 if self.output_file.exists(): 

247 # Will not work if it is a directory, but if it is then that is a major user error 

248 # and we kinda want to back out. 

249 self.output_file.unlink() 

250 

251 # Return the path to the output file, which at this point does not exist. 

252 # But do it anyway just to be consistent with the other generators. 

253 return self.output_file