|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  | """ | 
|  | Generate HTML documentation from validated register Hjson tree | 
|  | """ | 
|  |  | 
|  | import logging as log | 
|  | import re | 
|  |  | 
|  |  | 
|  | def genout(outfile, msg): | 
|  | outfile.write(msg) | 
|  |  | 
|  |  | 
|  | # expand !!register references into HTML links, gen **bold** and *italic* | 
|  | def desc_expand(s, rnames): | 
|  | def fieldsub(match): | 
|  | base = match.group(1).partition('.')[0].lower() | 
|  | if base in rnames: | 
|  | if match.group(1)[-1] == ".": | 
|  | return ('<a href="#Reg_' + base + '"><code class=\"reg\">' + | 
|  | match.group(1)[:-1] + '</code></a>.') | 
|  | else: | 
|  | return ('<a href="#Reg_' + base + '"><code class=\"reg\">' + | 
|  | match.group(1) + '</code></a>') | 
|  | log.warn('!!' + match.group(1).partition('.')[0] + | 
|  | ' not found in register list.') | 
|  | return match.group(0) | 
|  |  | 
|  | s = re.sub(r"!!([A-Za-z0-9_.]+)", fieldsub, s) | 
|  | s = re.sub(r"(?s)\*\*(.+?)\*\*", r'<B>\1</B>', s) | 
|  | s = re.sub(r"\*([^*]+?)\*", r'<I>\1</I>', s) | 
|  | return s | 
|  |  | 
|  |  | 
|  | # Generation of HTML table with register bit-field summary picture | 
|  | # Max 16-bit wide on one line | 
|  |  | 
|  |  | 
|  | def gen_tbl_row(outfile, msb, width, close): | 
|  | if (close): | 
|  | genout(outfile, "</tr>\n") | 
|  | genout(outfile, "<tr>") | 
|  | for x in range(msb, msb - width, -1): | 
|  | genout(outfile, "<td class=\"bitnum\">" + str(x) + "</td>") | 
|  |  | 
|  | genout(outfile, "</tr><tr>") | 
|  |  | 
|  |  | 
|  | def gen_html_reg_pic(outfile, reg, width): | 
|  |  | 
|  | if (width > 32): | 
|  | bsize = 3 | 
|  | nextbit = 63 | 
|  | hdrbits = 16 | 
|  | nextline = 48 | 
|  | elif (width > 16): | 
|  | bsize = 3 | 
|  | nextbit = 31 | 
|  | hdrbits = 16 | 
|  | nextline = 16 | 
|  | elif (width > 8): | 
|  | bsize = 3 | 
|  | nextbit = 15 | 
|  | nextline = 0 | 
|  | hdrbits = 16 | 
|  | else: | 
|  | bsize = 12 | 
|  | nextbit = 7 | 
|  | nextline = 0 | 
|  | hdrbits = 8 | 
|  |  | 
|  | genout(outfile, "<table class=\"regpic\">") | 
|  | gen_tbl_row(outfile, nextbit, hdrbits, False) | 
|  |  | 
|  | for field in reversed(reg['fields']): | 
|  | fieldlsb = field['bitinfo'][2] | 
|  | fieldwidth = field['bitinfo'][1] | 
|  | fieldmsb = fieldlsb + fieldwidth - 1 | 
|  | fname = field['name'] | 
|  |  | 
|  | while nextbit > fieldmsb: | 
|  | if (nextbit >= nextline) and (fieldmsb < nextline): | 
|  | spans = nextbit - (nextline - 1) | 
|  | else: | 
|  | spans = nextbit - fieldmsb | 
|  | genout( | 
|  | outfile, "<td class=\"unused\" colspan=" + str(spans) + | 
|  | "> </td>\n") | 
|  | if (nextbit >= nextline) and (fieldmsb < nextline): | 
|  | nextbit = nextline - 1 | 
|  | gen_tbl_row(outfile, nextbit, hdrbits, True) | 
|  | nextline = nextline - 16 | 
|  | else: | 
|  | nextbit = fieldmsb | 
|  |  | 
|  | while (fieldmsb >= nextline) and (fieldlsb < nextline): | 
|  | spans = fieldmsb - (nextline - 1) | 
|  | genout( | 
|  | outfile, "<td class=\"fname\" colspan=" + str(spans) + ">" + | 
|  | fname + "...</td>\n") | 
|  | fname = "..." + field['name'] | 
|  | fieldwidth = fieldwidth - spans | 
|  | fieldmsb = nextline - 1 | 
|  | nextline = nextline - 16 | 
|  | gen_tbl_row(outfile, fieldmsb, hdrbits, True) | 
|  |  | 
|  | namelen = len(fname) | 
|  | if namelen == 0 or fname == ' ': | 
|  | fname = " " | 
|  | if (namelen > bsize * fieldwidth): | 
|  | usestyle = (" style=\"font-size:" + str( | 
|  | (bsize * 100 * fieldwidth) / namelen) + "%\"") | 
|  | else: | 
|  | usestyle = "" | 
|  |  | 
|  | genout( | 
|  | outfile, "<td class=\"fname\" colspan=" + str(fieldwidth) + | 
|  | usestyle + ">" + fname + "</td>\n") | 
|  |  | 
|  | if (fieldlsb == nextline) and nextline > 0: | 
|  | gen_tbl_row(outfile, nextline - 1, hdrbits, True) | 
|  | nextline = nextline - 16 | 
|  |  | 
|  | nextbit = fieldlsb - 1 | 
|  | while (nextbit > 0): | 
|  | spans = nextbit - (nextline - 1) | 
|  | genout(outfile, | 
|  | "<td class=\"unused\" colspan=" + str(spans) + "> </td>\n") | 
|  | nextbit = nextline - 1 | 
|  | if (nextline > 0): | 
|  | gen_tbl_row(outfile, nextline - 1, hdrbits, True) | 
|  | nextline = nextline - 16 | 
|  |  | 
|  | genout(outfile, "</tr></table>") | 
|  |  | 
|  |  | 
|  | # Generation of HTML table with header, register picture and details | 
|  |  | 
|  |  | 
|  | def gen_html_register(outfile, reg, comp, width, rnames, toc, toclvl): | 
|  | def gen_merge(outfile, fieldlsb, mergebase, mergeprev, mergedesc): | 
|  | genout( | 
|  | outfile, "<tr><td class=\"regbits\">" + str(fieldlsb - 1) + ':' + | 
|  | str(mergebase) + "</td>") | 
|  | genout(outfile, "<td class=\"regperm\"></td>") | 
|  | genout(outfile, "<td class=\"regrv\"></td>") | 
|  | genout(outfile, "<td class=\"regfn\"></td>") | 
|  | if mergeprev != mergedesc: | 
|  | genout(outfile, | 
|  | "<td class=\"regde\">" + mergedesc + ".." + mergeprev[4:]) | 
|  | else: | 
|  | genout(outfile, "<td class=\"regde\">" + mergedesc) | 
|  | genout(outfile, "</td></tr>\n") | 
|  |  | 
|  | rname = reg['name'] | 
|  | offset = reg['genoffset'] | 
|  | # in a multireg with multiple regs give anchor with base register name | 
|  | if 'genbasebits' in reg and rname[-1] == '0': | 
|  | genout(outfile, "<div id=\"Reg_" + rname[:-1].lower() + "\"></div>\n") | 
|  | regwen_string = '' | 
|  | if 'regwen' in reg and (reg['regwen'] != ''): | 
|  | regwen_string = '<br>Register enable = ' + reg['regwen'] | 
|  | genout( | 
|  | outfile, "<table class=\"regdef\" id=\"Reg_" + rname.lower() + "\">\n" | 
|  | "<tr><th class=\"regdef\" colspan=5><div>" + comp + "." + rname + | 
|  | " @ + " + hex(offset) + "</div><div>" + | 
|  | desc_expand(reg['desc'], rnames) + "</div>" + "<div>Reset default = " + | 
|  | hex(reg['genresval']) + ", mask " + hex(reg['genresmask']) + | 
|  | regwen_string + "</div></th></tr>\n") | 
|  | if toc is not None: | 
|  | toc.append((toclvl, comp + "." + rname, "Reg_" + rname.lower())) | 
|  | genout(outfile, "<tr><td colspan=5>") | 
|  | gen_html_reg_pic(outfile, reg, width) | 
|  | genout(outfile, "</td></tr>\n") | 
|  |  | 
|  | genout(outfile, "<tr><th width=5%>Bits</th>") | 
|  | genout(outfile, "<th width=5%>Type</th>") | 
|  | genout(outfile, "<th width=5%>Reset</th>") | 
|  | genout(outfile, "<th>Name</th>") | 
|  | genout(outfile, "<th>Description</th></tr>") | 
|  | nextbit = 0 | 
|  | fcount = 0 | 
|  | mergebase = -1 | 
|  | for field in reg['fields']: | 
|  | fcount += 1 | 
|  | if 'name' not in field: | 
|  | fname = "field " + str(fcount) | 
|  | else: | 
|  | fname = field['name'] | 
|  |  | 
|  | fieldlsb = field['bitinfo'][2] | 
|  | if (fieldlsb > nextbit) and mergebase < 0: | 
|  | genout(outfile, "<tr><td class=\"regbits\">") | 
|  | if (nextbit == (fieldlsb - 1)): | 
|  | genout(outfile, str(nextbit)) | 
|  | else: | 
|  | genout(outfile, str(fieldlsb - 1) + ":" + str(nextbit)) | 
|  | genout(outfile, | 
|  | "</td><td></td><td></td><td></td><td>Reserved</td></tr>") | 
|  | if 'genbasebits' in reg: | 
|  | if (((1 << fieldlsb) & reg['genbasebits']) == 0): | 
|  | mergeprev = field['desc'] | 
|  | if (mergebase < 0): | 
|  | mergebase = fieldlsb | 
|  | mergedesc = field['desc'] | 
|  | nextbit = fieldlsb + field['bitinfo'][1] | 
|  | continue | 
|  | else: | 
|  | if (mergebase >= 0): | 
|  | gen_merge(outfile, fieldlsb, mergebase, mergeprev, | 
|  | mergedesc) | 
|  | mergebase = -1 | 
|  | genout(outfile, "<tr><td class=\"regbits\">" + field['bits'] + "</td>") | 
|  | genout(outfile, "<td class=\"regperm\">" + field['swaccess'] + "</td>") | 
|  | genout( | 
|  | outfile, "<td class=\"regrv\">" + | 
|  | ('x' if field['genresvalx'] else hex(field['genresval'])) + | 
|  | "</td>") | 
|  | genout(outfile, "<td class=\"regfn\">" + fname + "</td>") | 
|  | if 'desc' in field: | 
|  | genout( | 
|  | outfile, "<td class=\"regde\">" + | 
|  | desc_expand(field['desc'], rnames) + "\n") | 
|  | else: | 
|  | genout(outfile, "<td>\n") | 
|  |  | 
|  | if 'enum' in field: | 
|  | genout(outfile, "    <table>") | 
|  | for enum in field['enum']: | 
|  | if 'name' not in enum: | 
|  | ename = "enum for " + fname + " in " + rname | 
|  | else: | 
|  | ename = enum['name'] | 
|  | genout(outfile, "    <tr><td>" + enum['value'] + "</td>") | 
|  | genout(outfile, "<td>" + ename + "</td>") | 
|  | genout( | 
|  | outfile, "<td>" + desc_expand(enum['desc'], rnames) + | 
|  | "</td></tr>\n") | 
|  |  | 
|  | genout(outfile, "    </table>") | 
|  | if 'genrsvdenum' in field: | 
|  | genout(outfile, "Other values are reserved.") | 
|  | genout(outfile, "</td></tr>\n") | 
|  | nextbit = fieldlsb + field['bitinfo'][1] | 
|  |  | 
|  | # could be in the middle of a merge | 
|  | if (mergebase >= 0): | 
|  | gen_merge(outfile, nextbit, mergebase, mergeprev, mergedesc) | 
|  |  | 
|  | genout(outfile, "</table>\n<br><br>\n") | 
|  |  | 
|  | return | 
|  |  | 
|  |  | 
|  | def gen_html_window(outfile, win, comp, regwidth, rnames, toc, toclvl): | 
|  | wname = win['name'] | 
|  | offset = win['genoffset'] | 
|  | genout( | 
|  | outfile, '<table class="regdef" id="Reg_' + wname.lower() + '">\n' | 
|  | '<tr><th class="regdef"><div>' + comp + '.' + wname + ' @ + ' + | 
|  | hex(offset) + '</div><div>' + win['items'] + ' item ' + | 
|  | win['swaccess'] + ' window</div><div>Byte writes are ' + | 
|  | ('' if win['genbyte-write'] else '<i>not</i> ') + | 
|  | 'supported</div></th></tr>\n') | 
|  | genout(outfile, '<tr><td><table class="regpic">') | 
|  | genout(outfile, '<tr><td width="10%"></td>') | 
|  | wid = win['genvalidbits'] | 
|  |  | 
|  | for x in range(regwidth - 1, -1, -1): | 
|  | if x == regwidth - 1 or x == wid - 1 or x == 0: | 
|  | genout(outfile, '<td class="bitnum">' + str(x) + '</td>') | 
|  | else: | 
|  | genout(outfile, '<td class="bitnum"></td>') | 
|  | genout(outfile, '</tr>') | 
|  | tblmax = int(win['items']) - 1 | 
|  | for x in [0, 1, 2, tblmax - 1, tblmax]: | 
|  | if x == 2: | 
|  | genout( | 
|  | outfile, '<tr><td> </td><td align=center colspan=' + | 
|  | str(regwidth) + '>...</td></tr>') | 
|  | else: | 
|  | genout( | 
|  | outfile, '<tr><td class="regbits">+' + | 
|  | hex(offset + x * (regwidth // 8)) + '</td>') | 
|  | if wid < regwidth: | 
|  | genout( | 
|  | outfile, '<td class="unused" colspan=' + | 
|  | str(regwidth - wid) + '> </td>\n') | 
|  | genout( | 
|  | outfile, | 
|  | '<td class="fname" colspan=' + str(wid) + '> </td>\n') | 
|  | else: | 
|  | genout( | 
|  | outfile, '<td class="fname" colspan=' + str(regwidth) + | 
|  | '> </td>\n') | 
|  | genout(outfile, '</tr>') | 
|  | genout(outfile, '</td></tr></table>') | 
|  | genout( | 
|  | outfile, '<tr><td class="regde">' + desc_expand(win['desc'], rnames) + | 
|  | '</td></tr>') | 
|  | genout(outfile, "</table>\n<br><br>\n") | 
|  | if toc is not None: | 
|  | toc.append((toclvl, comp + "." + wname, "Reg_" + wname.lower())) | 
|  |  | 
|  |  | 
|  | # Must have called validate, so should have no errors | 
|  |  | 
|  |  | 
|  | def gen_html(regs, outfile, toclist=None, toclevel=3): | 
|  | component = regs['name'] | 
|  | registers = regs['registers'] | 
|  | rnames = regs['genrnames'] | 
|  |  | 
|  | if 'regwidth' in regs: | 
|  | regwidth = int(regs['regwidth'], 0) | 
|  | else: | 
|  | regwidth = 32 | 
|  |  | 
|  | for x in registers: | 
|  | if 'reserved' in x: | 
|  | continue | 
|  |  | 
|  | if 'skipto' in x: | 
|  | continue | 
|  |  | 
|  | if 'sameaddr' in x: | 
|  | for sareg in x['sameaddr']: | 
|  | gen_html_register(outfile, sareg, component, regwidth, rnames, | 
|  | toclist, toclevel) | 
|  | continue | 
|  |  | 
|  | if 'window' in x: | 
|  | gen_html_window(outfile, x['window'], component, regwidth, rnames, | 
|  | toclist, toclevel) | 
|  | continue | 
|  |  | 
|  | if 'multireg' in x: | 
|  | for reg in x['multireg']['genregs']: | 
|  | gen_html_register(outfile, reg, component, regwidth, rnames, | 
|  | toclist, toclevel) | 
|  | continue | 
|  |  | 
|  | gen_html_register(outfile, x, component, regwidth, rnames, toclist, | 
|  | toclevel) | 
|  | return |