Michael Schaffner | 426ed20 | 2021-08-02 17:44:42 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright lowRISC contributors. |
| 3 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 4 | # SPDX-License-Identifier: Apache-2.0 |
| 5 | r"""Top Module Documentation Generator |
| 6 | """ |
| 7 | import os |
| 8 | from tabulate import tabulate |
| 9 | |
| 10 | TABLE_HEADER = '''<!-- |
| 11 | DO NOT EDIT THIS FILE DIRECTLY. |
| 12 | It has been generated with the following command: |
| 13 | ''' |
| 14 | |
| 15 | |
| 16 | def set_md_table_font_size(row, size): |
| 17 | """Wrap each row element with HTML tags and specify font size""" |
| 18 | for k, elem in enumerate(row): |
| 19 | row[k] = '<p style="font-size:' + str(size) + '">' + elem + '</p>' |
| 20 | return row |
| 21 | |
| 22 | |
| 23 | def create_pinout_table(top, c_helper, target): |
| 24 | """Create the pinout table for a specific target and extract some stats""" |
| 25 | header = [ |
| 26 | "Pad Name", "Type", "Bank", "Connection", "Special Function", |
| 27 | "Pinmux Insel Constant / Muxed Output Index", "Description" |
| 28 | ] |
| 29 | table_rows = [set_md_table_font_size(header, "smaller")] |
| 30 | colalign = ("center", ) * len(header) |
| 31 | |
| 32 | # Pad stats |
| 33 | stats = {} |
| 34 | stats['muxed'] = 0 |
| 35 | stats['direct'] = 0 |
| 36 | stats['manual'] = 0 |
| 37 | |
| 38 | # get all pads for this target |
| 39 | pads = top['pinout']['pads'] + target['pinout']['add_pads'] |
Michael Schaffner | 51055db | 2021-11-04 13:07:54 -0700 | [diff] [blame] | 40 | remove_ports = target['pinout']['remove_ports'] |
Michael Schaffner | 426ed20 | 2021-08-02 17:44:42 -0700 | [diff] [blame] | 41 | remove_pads = target['pinout']['remove_pads'] |
| 42 | special_signals = target['pinmux']['special_signals'] |
| 43 | |
| 44 | # map pad names to special function signals |
| 45 | special_pads = {} |
| 46 | for sig in special_signals: |
| 47 | special_pads.update({sig['pad']: {'name': sig['name'], |
| 48 | 'desc': sig['desc']}}) |
| 49 | |
| 50 | i = 2 # insel enum starts at 2 |
| 51 | j = 0 # mio_out enum starts at 0 |
| 52 | for pad in pads: |
| 53 | # get the corresponding insel constant and MIO output index |
| 54 | if pad['connection'] == 'muxed': |
| 55 | name, _, _ = c_helper.pinmux_insel.constants[i] |
| 56 | insel = name.as_c_enum() |
| 57 | name, _, _ = c_helper.pinmux_mio_out.constants[j] |
| 58 | mio_out = name.as_c_enum() |
| 59 | i += 1 |
| 60 | j += 1 |
| 61 | else: |
| 62 | insel = "-" |
| 63 | mio_out = "-" |
Michael Schaffner | 51055db | 2021-11-04 13:07:54 -0700 | [diff] [blame] | 64 | # check whether this pad/port needs to be dropped |
| 65 | if pad['name'] in remove_ports: |
| 66 | continue |
Michael Schaffner | 426ed20 | 2021-08-02 17:44:42 -0700 | [diff] [blame] | 67 | if pad['name'] in remove_pads: |
| 68 | continue |
| 69 | # gather some stats |
| 70 | stats[pad['connection']] += 1 |
| 71 | # annotate special functions |
| 72 | if pad['name'] in special_pads: |
| 73 | special_func = special_pads[pad['name']]['name'] |
| 74 | desc = pad['desc'] + " / " + special_pads[pad['name']]['desc'] |
| 75 | else: |
| 76 | special_func = "-" |
| 77 | desc = pad['desc'] |
| 78 | row = [ |
| 79 | pad['name'], |
| 80 | pad['type'], |
| 81 | pad['bank'], |
| 82 | pad['connection'], |
| 83 | special_func, |
| 84 | insel + ' / ' + mio_out, |
| 85 | desc |
| 86 | ] |
| 87 | table_rows.append(set_md_table_font_size(row, "smaller")) |
| 88 | |
| 89 | ret_str = tabulate(table_rows, |
| 90 | headers="firstrow", |
| 91 | tablefmt="pipe", |
| 92 | colalign=colalign) |
| 93 | |
| 94 | return ret_str, stats |
| 95 | |
| 96 | |
| 97 | def create_pinmux_table(top, c_helper): |
| 98 | """Create the pinmux connectivity table""" |
| 99 | header = [ |
| 100 | "Module / Signal", "Connection", "Pad", |
| 101 | "Pinmux Outsel Constant / Peripheral Input Index", "Description" |
| 102 | ] |
| 103 | table_rows = [set_md_table_font_size(header, "smaller")] |
| 104 | colalign = ("center", ) * len(header) |
| 105 | |
| 106 | i = 3 # outsel enum starts at 3 |
| 107 | j = 0 # periph_input enum starts at 0 |
| 108 | for sig in top['pinmux']['ios']: |
| 109 | port = sig['name'] |
| 110 | if sig['width'] > 1: |
| 111 | port += '[' + str(sig['idx']) + ']' |
| 112 | pad = sig['pad'] if sig['pad'] else '-' |
| 113 | # get the corresponding insel constant |
| 114 | if sig['connection'] == 'muxed' and sig['type'] in ['inout', 'output']: |
| 115 | name, _, _ = c_helper.pinmux_outsel.constants[i] |
| 116 | outsel = name.as_c_enum() |
| 117 | i += 1 |
| 118 | else: |
| 119 | outsel = "-" |
| 120 | # get the corresponding peripheral input index |
| 121 | if sig['connection'] == 'muxed' and sig['type'] in ['inout', 'input']: |
| 122 | name, _, _ = c_helper.pinmux_peripheral_in.constants[j] |
| 123 | periph_in = name.as_c_enum() |
| 124 | j += 1 |
| 125 | else: |
| 126 | periph_in = "-" |
| 127 | row = [ |
| 128 | port, |
| 129 | sig['connection'], |
| 130 | pad, |
| 131 | outsel + " / " + periph_in, |
| 132 | sig['desc'] |
| 133 | ] |
| 134 | table_rows.append(set_md_table_font_size(row, "smaller")) |
| 135 | |
| 136 | return tabulate(table_rows, |
| 137 | headers="firstrow", |
| 138 | tablefmt="pipe", |
| 139 | colalign=colalign) |
| 140 | |
| 141 | |
| 142 | def gen_pinmux_docs(top, c_helper, out_path): |
| 143 | """Generate target summary table and linked subtables""" |
| 144 | |
| 145 | pinmux_path = out_path / '../ip/pinmux/doc' |
| 146 | doc_path = out_path / 'ip/pinmux/doc/autogen' |
| 147 | doc_path.mkdir(parents=True, exist_ok=True) |
| 148 | |
| 149 | # this is used to create relative hyperlinks from the summary table to |
| 150 | # the individual target tables. |
| 151 | relpath_prefix = os.path.relpath(doc_path.resolve(), pinmux_path.resolve()) |
| 152 | |
| 153 | topname = top['name'] |
| 154 | gencmd = ("util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson " |
| 155 | "-o hw/top_{topname}/\n\n".format(topname=topname)) |
| 156 | |
| 157 | header = [ |
| 158 | "Target Name", "#IO Banks", "#Muxed Pads", "#Direct Pads", |
| 159 | "#Manual Pads", "#Total Pads", "Pinout / Pinmux Tables" |
| 160 | ] |
| 161 | table_rows = [header] |
| 162 | colalign = ("center", ) * len(header) |
| 163 | |
| 164 | for target in top['targets']: |
| 165 | |
| 166 | # create pinout/pinmux tables for this target |
| 167 | pinout_table = '---\n' |
| 168 | pinout_table += 'title: ' + target['name'].upper() |
| 169 | pinout_table += ' Target Pinout and Pinmux Connectivity\n' |
| 170 | pinout_table += '---\n' |
| 171 | pinout_table += TABLE_HEADER + gencmd + "-->\n\n" |
| 172 | pinout_table += '## Pinout Table\n\n' |
| 173 | table_str, stats = create_pinout_table(top, c_helper, target) |
| 174 | pinout_table += table_str + '\n' |
| 175 | pinout_table += '## Pinmux Connectivity\n\n' |
| 176 | pinout_table += create_pinmux_table(top, c_helper) |
Michael Schaffner | a35f768 | 2022-05-12 14:18:15 -0700 | [diff] [blame] | 177 | pinout_table += "\n" |
Michael Schaffner | 426ed20 | 2021-08-02 17:44:42 -0700 | [diff] [blame] | 178 | |
| 179 | pinout_table_path = doc_path / ("pinout_" + target['name'] + ".md") |
| 180 | with open(pinout_table_path, 'w') as outfile: |
| 181 | outfile.write(pinout_table) |
| 182 | |
| 183 | # gather some statistics |
| 184 | num_banks = len(top['pinout']['banks']) |
| 185 | # create summary table entry |
| 186 | pinout_table_relpath = relpath_prefix + "/pinout_" + target['name'] + "/index.html" |
| 187 | row = [ |
| 188 | target['name'].upper(), |
| 189 | num_banks, |
| 190 | stats['muxed'], |
| 191 | stats['direct'], |
| 192 | stats['manual'], |
| 193 | stats['muxed'] + stats['direct'] + stats['manual'], |
| 194 | "[Pinout Table](" + str(pinout_table_relpath) + ")" |
| 195 | ] |
| 196 | table_rows.append(row) |
| 197 | |
| 198 | summary_table = tabulate(table_rows, |
| 199 | headers="firstrow", |
| 200 | tablefmt="pipe", |
| 201 | colalign=colalign) |
Michael Schaffner | 1d12d02 | 2022-04-29 12:14:13 -0700 | [diff] [blame] | 202 | summary_table = TABLE_HEADER + gencmd + "-->\n\n" + summary_table + "\n" |
Michael Schaffner | 426ed20 | 2021-08-02 17:44:42 -0700 | [diff] [blame] | 203 | |
| 204 | target_table_path = doc_path / "targets.md" |
| 205 | with open(target_table_path, 'w') as target_outfile: |
| 206 | target_outfile.write(summary_table) |
| 207 | |
| 208 | |
| 209 | def gen_top_docs(top, c_helper, out_path): |
| 210 | # create pinout / pinmux specific tables for all targets |
| 211 | gen_pinmux_docs(top, c_helper, out_path) |