lowRISC Contributors | 802543a | 2019-08-31 12:12:56 +0100 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | """ |
| 5 | Generate html documentation from validated register json tree |
| 6 | """ |
| 7 | |
| 8 | import logging as log |
| 9 | import re |
| 10 | import sys |
| 11 | |
| 12 | |
| 13 | def genout(outfile, msg): |
| 14 | outfile.write(msg) |
| 15 | |
| 16 | |
| 17 | # expand !!register references into html links, gen **bold** and *italic* |
| 18 | def desc_expand(s, rnames): |
| 19 | def fieldsub(match): |
| 20 | base = match.group(1).partition('.')[0].lower() |
| 21 | if base in rnames: |
| 22 | if match.group(1)[-1] == ".": |
| 23 | return ('<a href="#Reg_' + base + '"><code class=\"reg\">' + |
| 24 | match.group(1)[:-1] + '</code></a>.') |
| 25 | else: |
| 26 | return ('<a href="#Reg_' + base + '"><code class=\"reg\">' + |
| 27 | match.group(1) + '</code></a>') |
| 28 | log.warn('!!' + match.group(1).partition('.')[0] + |
| 29 | ' not found in register list.') |
| 30 | return match.group(0) |
| 31 | |
| 32 | s = re.sub(r"!!([A-Za-z0-9_.]+)", fieldsub, s) |
| 33 | s = re.sub(r"(?s)\*\*(.+?)\*\*", r'<B>\1</B>', s) |
| 34 | s = re.sub(r"\*([^*]+?)\*", r'<I>\1</I>', s) |
| 35 | return s |
| 36 | |
| 37 | |
| 38 | # Generation of HTML table with register bit-field summary picture |
| 39 | # Max 16-bit wide on one line |
| 40 | |
| 41 | |
| 42 | def gen_tbl_row(outfile, msb, width, close): |
| 43 | if (close): |
| 44 | genout(outfile, "</tr>\n") |
| 45 | genout(outfile, "<tr>") |
| 46 | for x in range(msb, msb - width, -1): |
| 47 | genout(outfile, "<td class=\"bitnum\">" + str(x) + "</td>") |
| 48 | |
| 49 | genout(outfile, "</tr><tr>") |
| 50 | |
| 51 | |
| 52 | def gen_html_reg_pic(outfile, reg, width): |
| 53 | |
| 54 | if (width > 32): |
| 55 | bsize = 3 |
| 56 | nextbit = 63 |
| 57 | hdrbits = 16 |
| 58 | nextline = 48 |
| 59 | elif (width > 16): |
| 60 | bsize = 3 |
| 61 | nextbit = 31 |
| 62 | hdrbits = 16 |
| 63 | nextline = 16 |
| 64 | elif (width > 8): |
| 65 | bsize = 3 |
| 66 | nextbit = 15 |
| 67 | nextline = 0 |
| 68 | hdrbits = 16 |
| 69 | else: |
| 70 | bsize = 12 |
| 71 | nextbit = 7 |
| 72 | nextline = 0 |
| 73 | hdrbits = 8 |
| 74 | |
| 75 | genout(outfile, "<table class=\"regpic\">") |
| 76 | gen_tbl_row(outfile, nextbit, hdrbits, False) |
| 77 | |
| 78 | for field in reversed(reg['fields']): |
| 79 | fieldlsb = field['bitinfo'][2] |
| 80 | fieldwidth = field['bitinfo'][1] |
| 81 | fieldmsb = fieldlsb + fieldwidth - 1 |
| 82 | fname = field['name'] |
| 83 | |
| 84 | while nextbit > fieldmsb: |
| 85 | if (nextbit >= nextline) and (fieldmsb < nextline): |
| 86 | spans = nextbit - (nextline - 1) |
| 87 | else: |
| 88 | spans = nextbit - fieldmsb |
| 89 | genout( |
| 90 | outfile, "<td class=\"unused\" colspan=" + str(spans) + |
| 91 | "> </td>\n") |
| 92 | if (nextbit >= nextline) and (fieldmsb < nextline): |
| 93 | nextbit = nextline - 1 |
| 94 | gen_tbl_row(outfile, nextbit, hdrbits, True) |
| 95 | nextline = nextline - 16 |
| 96 | else: |
| 97 | nextbit = fieldmsb |
| 98 | |
| 99 | while (fieldmsb >= nextline) and (fieldlsb < nextline): |
| 100 | spans = fieldmsb - (nextline - 1) |
| 101 | genout( |
| 102 | outfile, "<td class=\"fname\" colspan=" + str(spans) + ">" + |
| 103 | fname + "...</td>\n") |
| 104 | fname = "..." + field['name'] |
| 105 | fieldwidth = fieldwidth - spans |
| 106 | fieldmsb = nextline - 1 |
| 107 | nextline = nextline - 16 |
| 108 | gen_tbl_row(outfile, fieldmsb, hdrbits, True) |
| 109 | |
| 110 | namelen = len(fname) |
| 111 | if namelen == 0 or fname == ' ': fname = " " |
| 112 | if (namelen > bsize * fieldwidth): |
| 113 | usestyle = (" style=\"font-size:" + str( |
| 114 | (bsize * 100 * fieldwidth) / namelen) + "%\"") |
| 115 | else: |
| 116 | usestyle = "" |
| 117 | |
| 118 | genout( |
| 119 | outfile, "<td class=\"fname\" colspan=" + str(fieldwidth) + |
| 120 | usestyle + ">" + fname + "</td>\n") |
| 121 | |
| 122 | if (fieldlsb == nextline) and nextline > 0: |
| 123 | gen_tbl_row(outfile, nextline - 1, hdrbits, True) |
| 124 | nextline = nextline - 16 |
| 125 | |
| 126 | nextbit = fieldlsb - 1 |
| 127 | while (nextbit > 0): |
| 128 | spans = nextbit - (nextline - 1) |
| 129 | genout(outfile, |
| 130 | "<td class=\"unused\" colspan=" + str(spans) + "> </td>\n") |
| 131 | nextbit = nextline - 1 |
| 132 | if (nextline > 0): |
| 133 | gen_tbl_row(outfile, nextline - 1, hdrbits, True) |
| 134 | nextline = nextline - 16 |
| 135 | |
| 136 | genout(outfile, "</tr></table>") |
| 137 | |
| 138 | |
| 139 | # Generation of HTML table with header, register picture and details |
| 140 | |
| 141 | |
| 142 | def gen_html_register(outfile, reg, comp, width, rnames, toc, toclvl): |
| 143 | def gen_merge(outfile, fieldlsb, mergebase, mergeprev, mergedesc): |
| 144 | genout( |
| 145 | outfile, "<tr><td class=\"regbits\">" + str(fieldlsb - 1) + ':' + |
| 146 | str(mergebase) + "</td>") |
| 147 | genout(outfile, "<td class=\"regperm\"></td>") |
| 148 | genout(outfile, "<td class=\"regrv\"></td>") |
| 149 | genout(outfile, "<td class=\"regfn\"></td>") |
| 150 | if mergeprev != mergedesc: |
| 151 | genout(outfile, |
| 152 | "<td class=\"regde\">" + mergedesc + ".." + mergeprev[4:]) |
| 153 | else: |
| 154 | genout(outfile, "<td class=\"regde\">" + mergedesc) |
| 155 | genout(outfile, "</td></tr>\n") |
| 156 | |
| 157 | rname = reg['name'] |
| 158 | offset = reg['genoffset'] |
| 159 | #in a multireg with multiple regs give anchor with base register name |
| 160 | if 'genbasebits' in reg and rname[-1] == '0': |
| 161 | genout(outfile, "<div id=\"Reg_" + rname[:-1].lower() + "\"></div>\n") |
| 162 | regwen_string = '' |
| 163 | if 'regwen' in reg and (reg['regwen'] != ''): |
| 164 | regwen_string = '<br>Register enable = ' + reg['regwen'] |
| 165 | genout( |
| 166 | outfile, "<table class=\"regdef\" id=\"Reg_" + rname.lower() + "\">\n" |
| 167 | "<tr><th class=\"regdef\" colspan=5>" + comp + "." + rname + " @ + " + |
| 168 | hex(offset) + "<br>" + desc_expand(reg['desc'], rnames) + "<br>" + |
| 169 | "Reset default = " + hex(reg['genresval']) + ", mask " + hex( |
| 170 | reg['genresmask']) + regwen_string + "</th></tr>\n") |
| 171 | if toc != None: |
| 172 | toc.append((toclvl, comp + "." + rname, "Reg_" + rname.lower())) |
| 173 | genout(outfile, "<tr><td colspan=5>") |
| 174 | gen_html_reg_pic(outfile, reg, width) |
| 175 | genout(outfile, "</td></tr>\n") |
| 176 | |
| 177 | genout(outfile, "<tr><th width=5%>Bits</th>") |
| 178 | genout(outfile, "<th width=5%>Type</th>") |
| 179 | genout(outfile, "<th width=5%>Reset</th>") |
| 180 | genout(outfile, "<th>Name</th>") |
| 181 | genout(outfile, "<th>Description</th></tr>") |
| 182 | nextbit = 0 |
| 183 | fcount = 0 |
| 184 | mergebase = -1 |
| 185 | for field in reg['fields']: |
| 186 | fcount += 1 |
| 187 | if not 'name' in field: |
| 188 | fname = "field " + str(fcount) |
| 189 | else: |
| 190 | fname = field['name'] |
| 191 | |
| 192 | fieldlsb = field['bitinfo'][2] |
| 193 | if (fieldlsb > nextbit) and mergebase < 0: |
| 194 | genout(outfile, "<tr><td class=\"regbits\">") |
| 195 | if (nextbit == (fieldlsb - 1)): |
| 196 | genout(outfile, str(nextbit)) |
| 197 | else: |
| 198 | genout(outfile, str(fieldlsb - 1) + ":" + str(nextbit)) |
| 199 | genout(outfile, |
| 200 | "</td><td></td><td></td><td></td><td>Reserved</td></tr>") |
| 201 | if 'genbasebits' in reg: |
| 202 | if (((1 << fieldlsb) & reg['genbasebits']) == 0): |
| 203 | mergeprev = field['desc'] |
| 204 | if (mergebase < 0): |
| 205 | mergebase = fieldlsb |
| 206 | mergedesc = field['desc'] |
| 207 | nextbit = fieldlsb + field['bitinfo'][1] |
| 208 | continue |
| 209 | else: |
| 210 | if (mergebase >= 0): |
| 211 | gen_merge(outfile, fieldlsb, mergebase, mergeprev, |
| 212 | mergedesc) |
| 213 | mergebase = -1 |
| 214 | genout(outfile, "<tr><td class=\"regbits\">" + field['bits'] + "</td>") |
| 215 | genout(outfile, "<td class=\"regperm\">" + field['swaccess'] + "</td>") |
| 216 | genout( |
| 217 | outfile, |
| 218 | "<td class=\"regrv\">" + ('x' if field['genresvalx'] else hex( |
| 219 | field['genresval'])) + "</td>") |
| 220 | genout(outfile, "<td class=\"regfn\">" + fname + "</td>") |
| 221 | if 'desc' in field: |
| 222 | genout( |
| 223 | outfile, "<td class=\"regde\">" + desc_expand( |
| 224 | field['desc'], rnames) + "\n") |
| 225 | else: |
| 226 | genout(outfile, "<td>\n") |
| 227 | |
| 228 | if 'enum' in field: |
| 229 | genout(outfile, " <table>") |
| 230 | for enum in field['enum']: |
| 231 | if (not 'name' in enum): |
| 232 | ename = "enum for " + fname + " in " + rname |
| 233 | else: |
| 234 | ename = enum['name'] |
| 235 | genout(outfile, " <tr><td>" + enum['value'] + "</td>") |
| 236 | genout(outfile, "<td>" + ename + "</td>") |
| 237 | genout( |
| 238 | outfile, "<td>" + desc_expand(enum['desc'], rnames) + |
| 239 | "</td></tr>\n") |
| 240 | |
| 241 | genout(outfile, " </table>") |
| 242 | if 'genrsvdenum' in field: |
| 243 | genout(outfile, "Other values are reserved.") |
| 244 | genout(outfile, "</td></tr>\n") |
| 245 | nextbit = fieldlsb + field['bitinfo'][1] |
| 246 | |
| 247 | # could be in the middle of a merge |
| 248 | if (mergebase >= 0): |
| 249 | gen_merge(outfile, nextbit, mergebase, mergeprev, mergedesc) |
| 250 | |
| 251 | genout(outfile, "</table>\n<br><br>\n") |
| 252 | |
| 253 | return |
| 254 | |
| 255 | |
| 256 | def gen_html_window(outfile, win, comp, regwidth, rnames, toc, toclvl): |
| 257 | wname = win['name'] |
| 258 | offset = win['genoffset'] |
| 259 | genout( |
| 260 | outfile, '<table class="regdef" id="Reg_' + wname.lower() + '">\n' |
| 261 | '<tr><th class="regdef">' + comp + '.' + wname + ' @ + ' + hex(offset) |
| 262 | + '<br>' + win['items'] + ' item ' + win['swaccess'] + |
| 263 | ' window<br>Byte writes are ' + |
| 264 | ('' if win['genbyte-write'] else '<i>not</i> ') + |
| 265 | 'supported</th></tr>\n') |
| 266 | genout(outfile, '<tr><td><table class="regpic">') |
| 267 | genout(outfile, '<tr><td width="10%"></td>') |
| 268 | wid = win['genvalidbits'] |
| 269 | |
| 270 | for x in range(regwidth - 1, -1, -1): |
| 271 | if x == regwidth - 1 or x == wid - 1 or x == 0: |
| 272 | genout(outfile, '<td class="bitnum">' + str(x) + '</td>') |
| 273 | else: |
| 274 | genout(outfile, '<td class="bitnum"></td>') |
| 275 | genout(outfile, '</tr>') |
| 276 | tblmax = int(win['items']) - 1 |
| 277 | for x in [0, 1, 2, tblmax - 1, tblmax]: |
| 278 | if x == 2: |
| 279 | genout( |
| 280 | outfile, '<tr><td> </td><td align=center colspan=' + |
| 281 | str(regwidth) + '>...</td></tr>') |
| 282 | else: |
| 283 | genout( |
| 284 | outfile, '<tr><td class="regbits">+' + |
| 285 | hex(offset + x * (regwidth // 8)) + '</td>') |
| 286 | if wid < regwidth: |
| 287 | genout( |
| 288 | outfile, '<td class="unused" colspan=' + |
| 289 | str(regwidth - wid) + '> </td>\n') |
| 290 | genout( |
| 291 | outfile, |
| 292 | '<td class="fname" colspan=' + str(wid) + '> </td>\n') |
| 293 | else: |
| 294 | genout( |
| 295 | outfile, '<td class="fname" colspan=' + str(regwidth) + |
| 296 | '> </td>\n') |
| 297 | genout(outfile, '</tr>') |
| 298 | genout(outfile, '</td></tr></table>') |
| 299 | genout( |
| 300 | outfile, '<tr><td class="regde">' + desc_expand(win['desc'], rnames) + |
| 301 | '</td></tr>') |
| 302 | genout(outfile, "</table>\n<br><br>\n") |
| 303 | if toc != None: |
| 304 | toc.append((toclvl, comp + "." + wname, "Reg_" + wname.lower())) |
| 305 | |
| 306 | |
| 307 | # Must have called validate, so should have no errors |
| 308 | |
| 309 | |
| 310 | def gen_html(regs, outfile, toclist=None, toclevel=3): |
| 311 | component = regs['name'] |
| 312 | registers = regs['registers'] |
| 313 | rnames = regs['genrnames'] |
| 314 | |
| 315 | if 'regwidth' in regs: |
| 316 | regwidth = int(regs['regwidth'], 0) |
| 317 | else: |
| 318 | regwidth = 32 |
| 319 | |
| 320 | for x in registers: |
| 321 | if 'reserved' in x: |
| 322 | continue |
| 323 | |
| 324 | if 'skipto' in x: |
| 325 | continue |
| 326 | |
| 327 | if 'sameaddr' in x: |
| 328 | for sareg in x['sameaddr']: |
| 329 | gen_html_register(outfile, sareg, component, regwidth, rnames, |
| 330 | toclist, toclevel) |
| 331 | continue |
| 332 | |
| 333 | if 'window' in x: |
| 334 | gen_html_window(outfile, x['window'], component, regwidth, rnames, |
| 335 | toclist, toclevel) |
| 336 | continue |
| 337 | |
| 338 | if 'multireg' in x: |
| 339 | for reg in x['multireg']['genregs']: |
| 340 | gen_html_register(outfile, reg, component, regwidth, rnames, |
| 341 | toclist, toclevel) |
| 342 | continue |
| 343 | |
| 344 | gen_html_register(outfile, x, component, regwidth, rnames, toclist, |
| 345 | toclevel) |
| 346 | return |