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, REGISTER_MODES 

10from .html_translator import HtmlTranslator 

11 

12 

13class RegisterHtmlGenerator: 

14 """ 

15 Generate a HTML page with register information. 

16 """ 

17 

18 def __init__(self, module_name, generated_info): 

19 self.module_name = module_name 

20 self.generated_info = generated_info 

21 self._html_translator = HtmlTranslator() 

22 

23 def get_register_table(self, register_objects): 

24 """ 

25 Get a HTML table with register infomation. Can be included in other documents. 

26 

27 Returns: 

28 str: HTML code. 

29 """ 

30 if not register_objects: 

31 return "" 

32 

33 html = self._file_header() 

34 html += self._get_register_table(register_objects) 

35 return html 

36 

37 def get_constant_table(self, constants): 

38 """ 

39 Get a HTML table with constant infomation. Can be included in other documents. 

40 

41 Returns: 

42 str: HTML code. 

43 """ 

44 if not constants: 

45 return "" 

46 

47 html = self._file_header() 

48 html += self._get_constant_table(constants) 

49 return html 

50 

51 def get_page(self, register_objects, constants): 

52 """ 

53 Get a complete HTML page with register and constant infomation. 

54 

55 Returns: 

56 str: HTML code. 

57 """ 

58 title = f"Documentation of {self.module_name} registers" 

59 html = f"""\ 

60{self._file_header()} 

61 

62<!DOCTYPE html> 

63<html> 

64<head> 

65 <title>{title}</title> 

66 <!-- Include the style both inline and as a link to a separate CSS file. --> 

67 <!-- Some tools, e.g. Jenkins, will not render with an inline styleesheet. --> 

68 <!-- For other tools, e.g. page inclusion in sphinx, the style must be in the file. --> 

69 <link rel="stylesheet" href="regs_style.css"> 

70 <style> 

71 {self.get_page_style()} 

72 </style> 

73</head> 

74<body> 

75 <h1>{title}</h1> 

76 <p>This document is a specification for the register interface of the FPGA module \ 

77<b>{self.module_name}</b>.</p> 

78 <p>{' '.join(self.generated_info)}</p> 

79 <h2>Register modes</h2> 

80 <p>The following register modes are available.</p> 

81{self._get_mode_descriptions()} 

82""" 

83 

84 html += " <h2>Registers</h2>\n" 

85 if register_objects: 

86 html += f""" 

87 <p>The following registers make up the register map.</p> 

88{self._get_register_table(register_objects)} 

89""" 

90 else: 

91 html += " <p>This module does not have any registers.</p>" 

92 

93 html += " <h2>Constants</h2>\n" 

94 if constants: 

95 html += f""" 

96 <p>The following constants are part of the register interface.</p> 

97{self._get_constant_table(constants)}""" 

98 else: 

99 html += " <p>This module does not have any constants.</p>" 

100 

101 html += """ 

102</body> 

103</html>""" 

104 

105 return html 

106 

107 @staticmethod 

108 def get_page_style(table_style=None, font_style=None, extra_style=""): 

109 """ 

110 Get a CSS style for the register pages. Shall be saved to a file called ``regs_style.css``. 

111 

112 Returns: 

113 str: CSS code. 

114 """ 

115 if font_style is None: 

116 font_style = """ 

117html * { 

118 font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; 

119}""" 

120 

121 if table_style is None: 

122 table_style = """ 

123table { 

124 border-collapse: collapse; 

125} 

126td, th { 

127 border-width: 1px; 

128 border-style: solid; 

129 border-color: #ddd; 

130 padding: 8px; 

131} 

132td.array_header { 

133 border-top-width: 10px; 

134 border-top-color: #4cacaf; 

135} 

136td.array_footer { 

137 border-bottom-width: 10px; 

138 border-bottom-color: #4cacaf; 

139} 

140tr:nth-child(even) { 

141 background-color: #f2f2f2; 

142} 

143th { 

144 padding-top: 12px; 

145 padding-bottom: 12px; 

146 text-align: left; 

147 background-color: #4CAF50; 

148 color: white; 

149}""" 

150 

151 style = f""" 

152{font_style} 

153{table_style} 

154{extra_style}""" 

155 return style 

156 

157 @staticmethod 

158 def _comment(comment): 

159 return f"<!-- {comment} -->\n" 

160 

161 def _file_header(self): 

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

163 

164 @staticmethod 

165 def _to_hex_string(value, num_nibbles=4): 

166 if value < 0: 

167 return "N/A" 

168 

169 formatting_string = "0x{:0%iX}" % num_nibbles 

170 return formatting_string.format(value) 

171 

172 def _annotate_register_array(self, register_object): 

173 description = self._html_translator.translate(register_object.description) 

174 html = f""" 

175 <tr> 

176 <td class="array_header" colspan=5> 

177 Register array <strong>{register_object.name}</strong>, \ 

178repeated {register_object.length} times. 

179 Iterator <i>i &isin; [0, {register_object.length - 1}].</i> 

180 </td> 

181 <td class="array_header">{description}</td> 

182 </tr>""" 

183 array_index_increment = len(register_object.registers) 

184 for register in register_object.registers: 

185 register_index = register_object.base_index + register.index 

186 html += self._annotate_register(register, register_index, array_index_increment) 

187 

188 html += f""" 

189 <tr> 

190 <td colspan="6" class="array_footer"> 

191 End register array <strong>{register_object.name}.</strong> 

192 </td> 

193 </tr>""" 

194 return html 

195 

196 def _annotate_register(self, register, register_array_index=None, array_index_increment=None): 

197 if register_array_index is None: 

198 address_readable = self._to_hex_string(register.address) 

199 index = register.address // 4 

200 else: 

201 register_address = self._to_hex_string(4 * register_array_index) 

202 address_increment = self._to_hex_string(4 * array_index_increment) 

203 address_readable = f"{register_address} + i &times; {address_increment}" 

204 

205 index = f"{register_array_index} + i &times; {array_index_increment}" 

206 

207 description = self._html_translator.translate(register.description) 

208 html = f""" 

209 <tr> 

210 <td><strong>{register.name}</strong></td> 

211 <td>{index}</td> 

212 <td>{address_readable}</td> 

213 <td>{REGISTER_MODES[register.mode].mode_readable}</td> 

214 <td>{self._to_hex_string(register.default_value, num_nibbles=1)}</td> 

215 <td>{description}</td> 

216 </tr>""" 

217 

218 for field in register.fields: 

219 html += self._annotate_field(field) 

220 

221 return html 

222 

223 def _annotate_field(self, field): 

224 description = self._html_translator.translate(field.description) 

225 html = f""" 

226 <tr> 

227 <td>&nbsp;&nbsp;<em>{field.name}</em></td> 

228 <td>&nbsp;&nbsp;{field.range}</td> 

229 <td></td> 

230 <td></td> 

231 <td>{field.default_value_str}</td> 

232 <td>{description}</td> 

233 </tr>""" 

234 

235 return html 

236 

237 def _get_register_table(self, register_objects): 

238 html = """ 

239<table> 

240<thead> 

241 <tr> 

242 <th>Name</th> 

243 <th>Index</th> 

244 <th>Address</th> 

245 <th>Mode</th> 

246 <th>Default value</th> 

247 <th>Description</th> 

248 </tr> 

249</thead> 

250<tbody>""" 

251 

252 for register_object in register_objects: 

253 if isinstance(register_object, Register): 

254 html += self._annotate_register(register_object) 

255 else: 

256 html += self._annotate_register_array(register_object) 

257 

258 html += """ 

259</tbody> 

260</table>""" 

261 

262 return html 

263 

264 def _get_constant_table(self, constants): 

265 html = """ 

266<table> 

267<thead> 

268 <tr> 

269 <th>Name</th> 

270 <th>Value (decimal)</th> 

271 <th>Value (hexadecimal)</th> 

272 <th>Description</th> 

273 </tr> 

274</thead> 

275<tbody>""" 

276 

277 for constant in constants: 

278 description = self._html_translator.translate(constant.description) 

279 html += f""" 

280 <tr> 

281 <td><strong>{constant.name}</strong></td> 

282 <td>{constant.value}</td> 

283 <td>{self._to_hex_string(constant.value, num_nibbles=8)}</td> 

284 <td>{description}</td> 

285 </tr>""" 

286 

287 html += """ 

288</tbody> 

289</table>""" 

290 return html 

291 

292 @staticmethod 

293 def _get_mode_descriptions(): 

294 html = """ 

295<table> 

296<thead> 

297 <tr> 

298 <th>Mode</th> 

299 <th>Description</th> 

300 </tr> 

301</thead> 

302<tbody>""" 

303 

304 for mode in REGISTER_MODES.values(): 

305 html += f""" 

306<tr> 

307 <td>{mode.mode_readable}</td> 

308 <td>{mode.description}</td> 

309</tr> 

310""" 

311 html += """ 

312</tbody> 

313</table>""" 

314 return html