Coverage for hdl_registers/generator/cpp/header.py: 96%

79 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, Any 

13 

14from .cpp_generator_common import CppGeneratorCommon 

15 

16if TYPE_CHECKING: 

17 from pathlib import Path 

18 

19 from hdl_registers.register import Register 

20 from hdl_registers.register_array import RegisterArray 

21 

22 

23class CppHeaderGenerator(CppGeneratorCommon): 

24 """ 

25 Generate a C++ class header. 

26 See the :ref:`generator_cpp` article for usage details. 

27 

28 The class header will contain: 

29 

30 * for each register, signature of getter and setter methods for reading/writing the register as 

31 an ``uint``. 

32 

33 * for each field in each register, signature of getter and setter methods for reading/writing 

34 the field as its native type (enumeration, positive/negative int, etc.). 

35 

36 * The setter will read-modify-write the register to update only the specified field, 

37 depending on the mode of the register. 

38 """ 

39 

40 __version__ = "1.0.0" 

41 

42 SHORT_DESCRIPTION = "C++ header" 

43 

44 DEFAULT_INDENTATION_LEVEL = 4 

45 

46 @property 

47 def output_file(self) -> Path: 

48 """ 

49 Result will be placed in this file. 

50 """ 

51 return self.output_folder / f"{self.name}.h" 

52 

53 def get_code( 

54 self, 

55 **kwargs: Any, # noqa: ANN401, ARG002 

56 ) -> str: 

57 """ 

58 Get a complete C++ class header with methods for accessing registers and fields. 

59 """ 

60 public_cpp = "" 

61 private_cpp = "" 

62 separator = self.get_separator_line() 

63 

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

65 heading = self._get_register_heading( 

66 register=register, register_array=register_array, separator=separator 

67 ) 

68 public_cpp += heading 

69 

70 register_has_private_methods = len(register.fields) != 0 

71 if register_has_private_methods: 

72 private_cpp += heading 

73 

74 if register.mode.software_can_read: 

75 public_getters, private_getters = self._get_getters( 

76 register=register, register_array=register_array 

77 ) 

78 public_cpp += public_getters 

79 private_cpp += private_getters 

80 

81 if register.mode.software_can_write: 

82 # Add empty line between getter and setter interfaces. 

83 public_cpp += "\n" 

84 if register_has_private_methods: 

85 private_cpp += "\n" 

86 

87 if register.mode.software_can_write: 

88 public_setters, private_setters = self._get_setters( 

89 register=register, register_array=register_array 

90 ) 

91 public_cpp += public_setters 

92 private_cpp += private_setters 

93 

94 public_cpp += separator 

95 if register_has_private_methods: 

96 private_cpp += separator 

97 

98 cpp_code = f"""\ 

99 class {self._class_name} : public I{self._class_name} 

100 { 

101 public: 

102 /** 

103 * Class constructor. 

104 * @param base_address Byte address where these registers are memory mapped. 

105 * Can be e.g. '0x43C00000' in bare metal, or e.g. 

106 * 'reinterpret_cast<uintptr_t>(mmap(...))' in Linux. 

107 * When using an operating system, care must be taken to pass the 

108 * virtual address, not the physical address. 

109 * When using bare metal, these are the same. 

110 * @param assertion_handler Function to call when an assertion fails. 

111 * Function takes a string pointer as an argument, where the string 

112 * will contain an error diagnostic message. 

113 * Function must return a boolean 'true'. 

114 */ 

115 {self._constructor_signature()}; 

116 

117 virtual ~{self._class_name}() { } 

118{public_cpp} 

119 private: 

120 volatile uint32_t *m_registers; 

121 bool (*m_assertion_handler) (const std::string*); 

122{private_cpp}\ 

123 } ; 

124 

125""" 

126 cpp_code_top = f"""\ 

127#pragma once 

128 

129#include "i_{self.name}.h" 

130 

131""" 

132 return cpp_code_top + self._with_namespace(cpp_code) 

133 

134 def _get_getters( 

135 self, register: Register, register_array: RegisterArray | None 

136 ) -> tuple[str, str]: 

137 def get_from_raw_function(comment: str, return_type: str, signature: str) -> str: 

138 return f"""\ 

139{comment}\ 

140 {return_type} {signature}; 

141""" 

142 

143 public_cpp: list[str] = [] 

144 private_cpp: list[str] = [] 

145 

146 register_type = self._get_register_value_type( 

147 register=register, register_array=register_array 

148 ) 

149 signature = self._register_getter_signature( 

150 register=register, register_array=register_array 

151 ) 

152 public_cpp.append( 

153 self._get_override_function( 

154 comment=self._get_getter_comment(), 

155 return_type=register_type, 

156 signature=signature, 

157 ) 

158 ) 

159 

160 if register.fields: 

161 # The main getter will perform type conversion. 

162 # Provide a getter that returns the raw value also. 

163 signature = self._register_getter_signature( 

164 register=register, register_array=register_array, raw=True 

165 ) 

166 public_cpp.append( 

167 self._get_override_function( 

168 comment=self._get_getter_comment(raw=True), 

169 return_type="uint32_t", 

170 signature=signature, 

171 ) 

172 ) 

173 

174 for field in register.fields: 

175 field_type = self._get_field_value_type( 

176 register=register, register_array=register_array, field=field 

177 ) 

178 

179 signature = self._field_getter_signature( 

180 register=register, 

181 register_array=register_array, 

182 field=field, 

183 from_raw=False, 

184 ) 

185 public_cpp.append( 

186 self._get_override_function( 

187 comment=self._get_getter_comment(field=field), 

188 return_type=field_type, 

189 signature=signature, 

190 ) 

191 ) 

192 

193 signature = self._field_getter_signature( 

194 register=register, 

195 register_array=register_array, 

196 field=field, 

197 from_raw=True, 

198 ) 

199 private_cpp.append( 

200 get_from_raw_function( 

201 comment=self._get_from_raw_comment(field=field), 

202 return_type=field_type, 

203 signature=signature, 

204 ) 

205 ) 

206 

207 return "\n".join(public_cpp), "\n".join(private_cpp) 

208 

209 @staticmethod 

210 def _get_override_function(comment: str, return_type: str, signature: str) -> str: 

211 return f"""\ 

212{comment}\ 

213 virtual {return_type} {signature} override; 

214""" 

215 

216 def _get_setters( 

217 self, register: Register, register_array: RegisterArray | None 

218 ) -> tuple[str, str]: 

219 def get_to_raw_function(comment: str, signature: str) -> str: 

220 return f"""\ 

221{comment}\ 

222 uint32_t {signature}; 

223""" 

224 

225 public_cpp: list[str] = [] 

226 private_cpp: list[str] = [] 

227 

228 signature = self._register_setter_signature( 

229 register=register, register_array=register_array 

230 ) 

231 public_cpp.append( 

232 self._get_override_function( 

233 comment=self._get_setter_comment(register=register), 

234 return_type="void", 

235 signature=signature, 

236 ) 

237 ) 

238 

239 if register.fields: 

240 # The main setter will perform type conversion. 

241 # Provide a setter that takes a raw value also. 

242 signature = self._register_setter_signature( 

243 register=register, register_array=register_array, raw=True 

244 ) 

245 public_cpp.append( 

246 self._get_override_function( 

247 comment=self._get_setter_comment(register=register, raw=True), 

248 return_type="void", 

249 signature=signature, 

250 ) 

251 ) 

252 

253 for field in register.fields: 

254 signature = self._field_setter_signature( 

255 register=register, 

256 register_array=register_array, 

257 field=field, 

258 from_raw=False, 

259 ) 

260 public_cpp.append( 

261 self._get_override_function( 

262 comment=self._get_setter_comment(register=register, field=field), 

263 return_type="void", 

264 signature=signature, 

265 ) 

266 ) 

267 

268 signature = self._field_to_raw_signature( 

269 register=register, register_array=register_array, field=field 

270 ) 

271 private_cpp.append( 

272 get_to_raw_function( 

273 comment=self._get_to_raw_comment(field=field), signature=signature 

274 ) 

275 ) 

276 

277 return "\n".join(public_cpp), "\n".join(private_cpp)