Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

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

3# 

4# This file is part of the tsfpga project. 

5# https://tsfpga.com 

6# https://gitlab.com/tsfpga/tsfpga 

7# -------------------------------------------------------------------------------------------------- 

8 

9from .register import REGISTER_MODES 

10from .register_array import RegisterArray 

11from .register_code_generator import RegisterCodeGenerator 

12 

13 

14class RegisterCppGenerator(RegisterCodeGenerator): 

15 """ 

16 Generate a C++ class with register definitions and methods. 

17 

18 There is no unit test of this class that checks the generated code. It is instead functionally 

19 tested in the file test_register_compilation.py. That test generates C++ code from an example 

20 register set, compiles it and performs some run-time assertions in a C++ program. 

21 That test is considered more meaningful and exhaustive than a unit test would be. 

22 """ 

23 

24 def __init__(self, module_name, generated_info): 

25 self.module_name = module_name 

26 self.generated_info = generated_info 

27 self._class_name = self._to_pascal_case(module_name) 

28 

29 def get_interface(self, register_objects, constants): 

30 cpp_code = f"class I{self._class_name}\n" 

31 cpp_code += "{\n" 

32 cpp_code += "public:\n" 

33 

34 for constant in constants: 

35 cpp_code += self._comment("Register constant.", indentation=2) 

36 cpp_code += self._comment_block(constant.description, indentation=2) 

37 

38 cpp_code += f" static const int {constant.name} = {constant.value}L;\n" 

39 if constants: 

40 cpp_code += "\n" 

41 

42 cpp_code += self._num_registers(register_objects) 

43 

44 for register_object in register_objects: 

45 if isinstance(register_object, RegisterArray): 

46 cpp_code += self._comment( 

47 f'Length of the "{register_object.name}" register array', indentation=2 

48 ) 

49 constant_name = self._array_length_constant_name(register_object) 

50 cpp_code += ( 

51 f" static const size_t {constant_name} = {register_object.length}uL;\n\n" 

52 ) 

53 

54 cpp_code += f" virtual ~I{self._class_name}() {{ }}\n" 

55 

56 for register, register_array in self._iterate_registers(register_objects): 

57 cpp_code += "\n" 

58 

59 if register_array: 

60 description = ( 

61 f'Register "{register.name}" within the "{register_array.name}" register array.' 

62 ) 

63 else: 

64 description = f'Register "{register.name}".' 

65 description += f' Mode "{REGISTER_MODES[register.mode].mode_readable}".' 

66 

67 cpp_code += self._comment(description, indentation=2) 

68 cpp_code += self._comment_block(register.description, indentation=2) 

69 

70 if register.is_bus_readable: 

71 cpp_code += ( 

72 " virtual uint32_t " 

73 f"{self._getter_function_signature(register, register_array)} const = 0;\n" 

74 ) 

75 if register.is_bus_writeable: 

76 cpp_code += ( 

77 " virtual void " 

78 f"{self._setter_function_signature(register, register_array)} const = 0;\n" 

79 ) 

80 

81 cpp_code += self._field_constants(register, register_array) 

82 

83 cpp_code += "};\n" 

84 

85 cpp_code_top = f"""\ 

86{self._file_header()} 

87#pragma once 

88 

89#include <cassert> 

90#include <cstdint> 

91#include <cstdlib> 

92 

93""" 

94 return cpp_code_top + self._with_namespace(cpp_code) 

95 

96 def _num_registers(self, register_objects): 

97 # It is possible that we have constants but no registers 

98 num_registers = 0 

99 if register_objects: 

100 num_registers = register_objects[-1].index + 1 

101 

102 cpp_code = self._comment("Number of registers within this register map.", indentation=2) 

103 cpp_code += f" static const size_t num_registers = {num_registers}uL;\n\n" 

104 return cpp_code 

105 

106 def get_header(self, register_objects): 

107 cpp_code = f"class {self._class_name} : public I{self._class_name}\n" 

108 cpp_code += "{\n" 

109 

110 cpp_code += "private:\n" 

111 cpp_code += " volatile uint32_t *m_registers;\n\n" 

112 

113 cpp_code += "public:\n" 

114 cpp_code += f" {self._constructor_signature()};\n\n" 

115 cpp_code += f" virtual ~{self._class_name}() {{ }}\n" 

116 

117 for register, register_array in self._iterate_registers(register_objects): 

118 cpp_code += "\n" 

119 if register.is_bus_readable: 

120 cpp_code += ( 

121 " virtual uint32_t " 

122 f"{self._getter_function_signature(register, register_array)} const override;\n" 

123 ) 

124 if register.is_bus_writeable: 

125 cpp_code += ( 

126 " virtual void " 

127 f"{self._setter_function_signature(register, register_array)} const override;\n" 

128 ) 

129 

130 cpp_code += "};\n" 

131 

132 cpp_code_top = f"""\ 

133{self._file_header()} 

134#pragma once 

135 

136#include "i_{self.module_name}.h" 

137 

138""" 

139 return cpp_code_top + self._with_namespace(cpp_code) 

140 

141 def get_implementation(self, register_objects): 

142 cpp_code = f"{self._class_name}::{self._constructor_signature()}\n" 

143 cpp_code += " : m_registers(reinterpret_cast<volatile uint32_t *>(base_address))\n" 

144 cpp_code += "{\n" 

145 cpp_code += " // Empty\n" 

146 cpp_code += "}\n\n" 

147 

148 for register, register_array in self._iterate_registers(register_objects): 

149 if register.is_bus_readable: 

150 cpp_code += self._getter_function(register, register_array) 

151 if register.is_bus_writeable: 

152 cpp_code += self._setter_function(register, register_array) 

153 

154 cpp_code_top = f"{self._file_header()}\n" 

155 cpp_code_top += f'#include "include/{self.module_name}.h"\n\n' 

156 

157 return cpp_code_top + self._with_namespace(cpp_code) 

158 

159 def _field_constants(self, register, register_array): 

160 cpp_code = "" 

161 for field in register.fields: 

162 description = f'Bitmask for the "{field.name}" field in the "{register.name}" register' 

163 if register_array is None: 

164 description += "." 

165 field_constant_name = f"{register.name}_{field.name}" 

166 else: 

167 description += f' within the "{register_array.name}" register array.' 

168 field_constant_name = f"{register_array.name}_{register.name}_{field.name}" 

169 

170 cpp_code += self._comment(description, indentation=2) 

171 cpp_code += self._comment_block(field.description, indentation=2) 

172 

173 cpp_code += ( 

174 f" static const uint32_t {field_constant_name}_shift = {field.base_index}uL;\n" 

175 ) 

176 cpp_code += ( 

177 f" static const uint32_t {field_constant_name}_mask = " 

178 f'0b{"1" * field.width}uL << {field.base_index}uL;\n' 

179 ) 

180 

181 return cpp_code 

182 

183 @staticmethod 

184 def _array_length_constant_name(register_array): 

185 return f"{register_array.name}_array_length" 

186 

187 @staticmethod 

188 def _with_namespace(cpp_code_body): 

189 cpp_code = "namespace fpga_regs\n" 

190 cpp_code += "{\n\n" 

191 cpp_code += f"{cpp_code_body}" 

192 cpp_code += "\n} /* namespace fpga_regs */\n" 

193 return cpp_code 

194 

195 @staticmethod 

196 def _comment(comment, indentation=0): 

197 indent = " " * indentation 

198 return f"{indent}// {comment}\n" 

199 

200 def _file_header(self): 

201 return "".join([self._comment(header_line) for header_line in self.generated_info]) 

202 

203 def _constructor_signature(self): 

204 return f"{self._class_name}(volatile uint8_t *base_address)" 

205 

206 def _setter_function(self, register, register_array): 

207 cpp_code = ( 

208 "void " 

209 f"{self._class_name}::{self._setter_function_signature(register, register_array)} " 

210 "const\n" 

211 ) 

212 cpp_code += "{\n" 

213 if register_array is None: 

214 cpp_code += f" m_registers[{register.index}] = value;\n" 

215 else: 

216 cpp_code += ( 

217 f" assert(array_index < {self._array_length_constant_name(register_array)});\n" 

218 ) 

219 cpp_code += ( 

220 f" const size_t index = {register_array.base_index} " 

221 f"+ array_index * {len(register_array.registers)} + {register.index};\n" 

222 ) 

223 cpp_code += " m_registers[index] = value;\n" 

224 cpp_code += "}\n\n" 

225 return cpp_code 

226 

227 @staticmethod 

228 def _setter_function_signature(register, register_array): 

229 if register_array is None: 

230 return f"set_{register.name}(uint32_t value)" 

231 return f"set_{register_array.name}_{register.name}(size_t array_index, uint32_t value)" 

232 

233 def _getter_function(self, register, register_array): 

234 cpp_code = ( 

235 "uint32_t " 

236 f"{self._class_name}::{self._getter_function_signature(register, register_array)} " 

237 "const\n" 

238 ) 

239 cpp_code += "{\n" 

240 if register_array is None: 

241 cpp_code += f" return m_registers[{register.index}];\n" 

242 else: 

243 cpp_code += ( 

244 f" assert(array_index < {self._array_length_constant_name(register_array)});\n" 

245 ) 

246 cpp_code += ( 

247 f" const size_t index = {register_array.base_index} " 

248 f"+ array_index * {len(register_array.registers)} + {register.index};\n" 

249 ) 

250 cpp_code += " return m_registers[index];\n" 

251 cpp_code += "}\n\n" 

252 return cpp_code 

253 

254 @staticmethod 

255 def _getter_function_signature(register, register_array): 

256 if register_array is None: 

257 return f"get_{register.name}()" 

258 return f"get_{register_array.name}_{register.name}(size_t array_index)"