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

94 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-15 20:50 +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, Any 

13 

14from hdl_registers.field.bit import Bit 

15from hdl_registers.field.bit_vector import BitVector 

16from hdl_registers.field.enumeration import Enumeration 

17from hdl_registers.field.integer import Integer 

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

19from hdl_registers.generator.register_code_generator import RegisterCodeGenerator 

20 

21if TYPE_CHECKING: 

22 from collections.abc import Iterator 

23 from pathlib import Path 

24 

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 from hdl_registers.register_mode import HardwareAccessDirection, SoftwareAccessDirection 

29 

30 

31class VhdlGeneratorCommon(RegisterCodeGenerator): 

32 """ 

33 Common methods for generation of VHDL code. 

34 """ 

35 

36 COMMENT_START = "--" 

37 

38 @staticmethod 

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

40 """ 

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

42 

43 Arguments: 

44 field: A field. 

45 field_name: The field's qualified name. 

46 """ 

47 if isinstance(field, Integer): 

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

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

50 # Hence we need to qualify the function name. 

51 return f"to_{field_name}_slv" 

52 

53 if isinstance(field, Enumeration): 

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

55 return "to_slv" 

56 

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

58 

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

60 """ 

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

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

63 

64 Arguments: 

65 field: The field. 

66 field_name: The field's qualified name. 

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

68 """ 

69 if isinstance(field, Bit): 

70 return value 

71 

72 if isinstance(field, BitVector): 

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

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

75 # Hence we can just cast it. 

76 return f"std_ulogic_vector({value})" 

77 

78 if isinstance(field.numerical_interpretation, Fixed): 

79 # Casting function built into ieee.fixed_pkg. 

80 return f"to_slv({value})" 

81 

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

83 

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

85 # Our own conversion functions. 

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

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

88 

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

90 

91 def field_type_name( 

92 self, 

93 register: Register, 

94 field: RegisterField, 

95 register_array: RegisterArray | None = None, 

96 ) -> str: 

97 """ 

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

99 """ 

100 if isinstance(field, Bit): 

101 return "std_ulogic" 

102 

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

104 field_name = self.qualified_field_name( 

105 register=register, register_array=register_array, field=field 

106 ) 

107 return f"{field_name}_t" 

108 

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

110 

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

112 """ 

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

114 software-accessible in the given direction. 

115 """ 

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

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

118 return True 

119 

120 return False 

121 

122 def iterate_software_accessible_registers( 

123 self, direction: SoftwareAccessDirection 

124 ) -> Iterator[tuple[Register, RegisterArray | None]]: 

125 """ 

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

127 in the given direction. 

128 """ 

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

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

131 yield register, register_array 

132 

133 def iterate_software_accessible_plain_registers( 

134 self, direction: SoftwareAccessDirection 

135 ) -> Iterator[Register]: 

136 """ 

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

138 given direction. 

139 """ 

140 for register in self.iterate_plain_registers(): 

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

142 yield register 

143 

144 def iterate_software_accessible_array_registers( 

145 self, register_array: RegisterArray, direction: SoftwareAccessDirection 

146 ) -> Iterator[Register]: 

147 """ 

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

149 given direction. 

150 """ 

151 for register in register_array.registers: 

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

153 yield register 

154 

155 def iterate_software_accessible_register_arrays( 

156 self, direction: SoftwareAccessDirection 

157 ) -> Iterator[RegisterArray]: 

158 """ 

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

160 is software-accessible in the given direction. 

161 """ 

162 for register_array in self.iterate_register_arrays(): 

163 accessible_registers = list( 

164 self.iterate_software_accessible_array_registers( 

165 register_array=register_array, direction=direction 

166 ) 

167 ) 

168 if accessible_registers: 

169 yield register_array 

170 

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

172 """ 

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

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

175 """ 

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

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

178 return True 

179 

180 return False 

181 

182 def iterate_hardware_accessible_registers( 

183 self, direction: HardwareAccessDirection 

184 ) -> Iterator[tuple[Register, RegisterArray | None]]: 

185 """ 

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

187 in the given direction. 

188 """ 

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

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

191 yield register, register_array 

192 

193 def iterate_hardware_accessible_plain_registers( 

194 self, direction: HardwareAccessDirection 

195 ) -> Iterator[Register]: 

196 """ 

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

198 given direction. 

199 """ 

200 for register in self.iterate_plain_registers(): 

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

202 yield register 

203 

204 def iterate_hardware_accessible_array_registers( 

205 self, register_array: RegisterArray, direction: HardwareAccessDirection 

206 ) -> Iterator[Register]: 

207 """ 

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

209 given direction. 

210 """ 

211 for register in register_array.registers: 

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

213 yield register 

214 

215 def iterate_hardware_accessible_register_arrays( 

216 self, direction: HardwareAccessDirection 

217 ) -> Iterator[RegisterArray]: 

218 """ 

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

220 is hardware-accessible in the given direction. 

221 """ 

222 for register_array in self.iterate_register_arrays(): 

223 accessible_registers = list( 

224 self.iterate_hardware_accessible_array_registers( 

225 register_array=register_array, direction=direction 

226 ) 

227 ) 

228 if accessible_registers: 

229 yield register_array 

230 

231 def _create_if_there_are_registers_otherwise_delete_file( 

232 self, 

233 **kwargs: Any, # noqa: ANN401 

234 ) -> Path: 

235 """ 

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

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

238 an empty shell. 

239 

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

241 to flood the file system with unnecessary files. 

242 

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

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

245 actively generated. 

246 """ 

247 if self.register_list.register_objects: 

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

249 

250 if self.output_file.exists(): 

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

252 # and we kinda want to back out. 

253 self.output_file.unlink() 

254 

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

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

257 return self.output_file