[doc] Add autogen generator that can run commands
The tool looks for:
<!-- BEGIN AUTOGEN cmd -->
...
<!-- END AUTOGEN -->
and replaces ... with the output of `cmd`. Note that the command is executed
in a shell.
Signed-off-by: Amaury Pouly <amaury.pouly@lowrisc.org>
Co-authored-by: Hugo McNally <hugo.mcnally@gmail.com>
diff --git a/hw/top_earlgrey/doc/design/README.md b/hw/top_earlgrey/doc/design/README.md
index 8cb6fd2..fd03e15 100644
--- a/hw/top_earlgrey/doc/design/README.md
+++ b/hw/top_earlgrey/doc/design/README.md
@@ -379,13 +379,141 @@
For the purpose of `top_earlgrey`, the first option has been chosen to benefit software development and testing
-{{< topLevelDoc "earlgrey" "mmap" >}}
+<!-- BEGIN AUTOGEN util/design/gen-top-docs.py -t hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson -g mmap -->
+| Name | Type | Byte Address |
+|:------------------|:--------------|:------------------|
+| uart0 | uart | 0x40000000 (regs) |
+| uart1 | uart | 0x40010000 (regs) |
+| uart2 | uart | 0x40020000 (regs) |
+| uart3 | uart | 0x40030000 (regs) |
+| gpio | gpio | 0x40040000 (regs) |
+| spi_device | spi_device | 0x40050000 (regs) |
+| i2c0 | i2c | 0x40080000 (regs) |
+| i2c1 | i2c | 0x40090000 (regs) |
+| i2c2 | i2c | 0x400A0000 (regs) |
+| pattgen | pattgen | 0x400E0000 (regs) |
+| rv_timer | rv_timer | 0x40100000 (regs) |
+| otp_ctrl | otp_ctrl | 0x40130000 (core) |
+| | | 0x40132000 (prim) |
+| lc_ctrl | lc_ctrl | 0x40140000 (regs) |
+| alert_handler | alert_handler | 0x40150000 (regs) |
+| spi_host0 | spi_host | 0x40300000 (regs) |
+| spi_host1 | spi_host | 0x40310000 (regs) |
+| usbdev | usbdev | 0x40320000 (regs) |
+| pwrmgr_aon | pwrmgr | 0x40400000 (regs) |
+| rstmgr_aon | rstmgr | 0x40410000 (regs) |
+| clkmgr_aon | clkmgr | 0x40420000 (regs) |
+| sysrst_ctrl_aon | sysrst_ctrl | 0x40430000 (regs) |
+| adc_ctrl_aon | adc_ctrl | 0x40440000 (regs) |
+| pwm_aon | pwm | 0x40450000 (regs) |
+| pinmux_aon | pinmux | 0x40460000 (regs) |
+| aon_timer_aon | aon_timer | 0x40470000 (regs) |
+| ast | ast | 0x40480000 (regs) |
+| sensor_ctrl | sensor_ctrl | 0x40490000 (regs) |
+| sram_ctrl_ret_aon | sram_ctrl | 0x40500000 (regs) |
+| | | 0x40600000 (ram) |
+| flash_ctrl | flash_ctrl | 0x41000000 (core) |
+| | | 0x41008000 (prim) |
+| | | 0x20000000 (mem) |
+| rv_dm | rv_dm | 0x00010000 (mem) |
+| | | 0x41200000 (regs) |
+| rv_plic | rv_plic | 0x48000000 (regs) |
+| aes | aes | 0x41100000 (regs) |
+| hmac | hmac | 0x41110000 (regs) |
+| kmac | kmac | 0x41120000 (regs) |
+| otbn | otbn | 0x41130000 (regs) |
+| keymgr | keymgr | 0x41140000 (regs) |
+| csrng | csrng | 0x41150000 (regs) |
+| entropy_src | entropy_src | 0x41160000 (regs) |
+| edn0 | edn | 0x41170000 (regs) |
+| edn1 | edn | 0x41180000 (regs) |
+| sram_ctrl_main | sram_ctrl | 0x411C0000 (regs) |
+| | | 0x10000000 (ram) |
+| rom_ctrl | rom_ctrl | 0x00008000 (rom) |
+| | | 0x411e0000 (regs) |
+| rv_core_ibex | rv_core_ibex | 0x411F0000 (cfg) |
+
+<!-- END AUTOGEN -->
## Hardware Interfaces
### Pinout
-{{< topLevelDoc "earlgrey" "pinout" >}}
+<!-- BEGIN AUTOGEN util/design/gen-top-docs.py -t hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson -g pinout -->
+| ID | Name | Bank | Type | Connection Type | Description |
+|:-----|:-----------------|:-------|:-------------|:------------------|:-------------------------------------------|
+| 0 | POR_N | VCC | InputStd | manual | System reset |
+| 1 | USB_P | VCC | DualBidirTol | manual | USB P signal |
+| 2 | USB_N | VCC | DualBidirTol | manual | USB N signal |
+| 3 | CC1 | AVCC | InputStd | manual | ADC input 1 |
+| 4 | CC2 | AVCC | InputStd | manual | ADC input 2 |
+| 5 | FLASH_TEST_VOLT | VCC | AnalogIn0 | manual | Flash test voltage input |
+| 6 | FLASH_TEST_MODE0 | VCC | InputStd | manual | Flash test mode signal |
+| 7 | FLASH_TEST_MODE1 | VCC | InputStd | manual | Flash test mode signal |
+| 8 | OTP_EXT_VOLT | VCC | AnalogIn1 | manual | OTP external voltage input |
+| 9 | SPI_HOST_D0 | VIOA | BidirStd | direct | SPI host data |
+| 10 | SPI_HOST_D1 | VIOA | BidirStd | direct | SPI host data |
+| 11 | SPI_HOST_D2 | VIOA | BidirStd | direct | SPI host data |
+| 12 | SPI_HOST_D3 | VIOA | BidirStd | direct | SPI host data |
+| 13 | SPI_HOST_CLK | VIOA | BidirStd | direct | SPI host clock |
+| 14 | SPI_HOST_CS_L | VIOA | BidirStd | direct | SPI host chip select |
+| 15 | SPI_DEV_D0 | VIOA | BidirStd | direct | SPI device data |
+| 16 | SPI_DEV_D1 | VIOA | BidirStd | direct | SPI device data |
+| 17 | SPI_DEV_D2 | VIOA | BidirStd | direct | SPI device data |
+| 18 | SPI_DEV_D3 | VIOA | BidirStd | direct | SPI device data |
+| 19 | SPI_DEV_CLK | VIOA | InputStd | direct | SPI device clock |
+| 20 | SPI_DEV_CS_L | VIOA | InputStd | direct | SPI device chip select |
+| 0 | IOA0 | VIOA | BidirStd | muxed | Muxed IO pad |
+| 1 | IOA1 | VIOA | BidirStd | muxed | Muxed IO pad |
+| 2 | IOA2 | VIOA | BidirStd | muxed | Muxed IO pad |
+| 3 | IOA3 | VIOA | BidirStd | muxed | Muxed IO pad |
+| 4 | IOA4 | VIOA | BidirStd | muxed | Muxed IO pad |
+| 5 | IOA5 | VIOA | BidirStd | muxed | Muxed IO pad |
+| 6 | IOA6 | VIOA | BidirOd | muxed | Muxed IO pad |
+| 7 | IOA7 | VIOA | BidirOd | muxed | Muxed IO pad |
+| 8 | IOA8 | VIOA | BidirOd | muxed | Muxed IO pad |
+| 9 | IOB0 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 10 | IOB1 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 11 | IOB2 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 12 | IOB3 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 13 | IOB4 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 14 | IOB5 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 15 | IOB6 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 16 | IOB7 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 17 | IOB8 | VIOB | BidirStd | muxed | Muxed IO pad |
+| 18 | IOB9 | VIOB | BidirOd | muxed | Muxed IO pad |
+| 19 | IOB10 | VIOB | BidirOd | muxed | Muxed IO pad |
+| 20 | IOB11 | VIOB | BidirOd | muxed | Muxed IO pad |
+| 21 | IOB12 | VIOB | BidirOd | muxed | Muxed IO pad |
+| 22 | IOC0 | VCC | BidirStd | muxed | Muxed IO pad |
+| 23 | IOC1 | VCC | BidirStd | muxed | Muxed IO pad |
+| 24 | IOC2 | VCC | BidirStd | muxed | Muxed IO pad |
+| 25 | IOC3 | VCC | BidirStd | muxed | Muxed IO pad |
+| 26 | IOC4 | VCC | BidirStd | muxed | Muxed IO pad |
+| 27 | IOC5 | VCC | BidirStd | muxed | Muxed IO pad |
+| 28 | IOC6 | VCC | BidirStd | muxed | Muxed IO pad |
+| 29 | IOC7 | VCC | BidirStd | muxed | Muxed IO pad |
+| 30 | IOC8 | VCC | BidirStd | muxed | Muxed IO pad |
+| 31 | IOC9 | VCC | BidirStd | muxed | Muxed IO pad |
+| 32 | IOC10 | VCC | BidirOd | muxed | Muxed IO pad |
+| 33 | IOC11 | VCC | BidirOd | muxed | Muxed IO pad |
+| 34 | IOC12 | VCC | BidirOd | muxed | Muxed IO pad |
+| 35 | IOR0 | VCC | BidirStd | muxed | Muxed IO pad |
+| 36 | IOR1 | VCC | BidirStd | muxed | Muxed IO pad |
+| 37 | IOR2 | VCC | BidirStd | muxed | Muxed IO pad |
+| 38 | IOR3 | VCC | BidirStd | muxed | Muxed IO pad |
+| 39 | IOR4 | VCC | BidirStd | muxed | Muxed IO pad |
+| 40 | IOR5 | VCC | BidirStd | muxed | Muxed IO pad |
+| 41 | IOR6 | VCC | BidirStd | muxed | Muxed IO pad |
+| 42 | IOR7 | VCC | BidirStd | muxed | Muxed IO pad |
+| 21 | IOR8 | VCC | BidirOd | direct | Dedicated sysrst_ctrl output (ec_rst_l) |
+| 22 | IOR9 | VCC | BidirOd | direct | Dedicated sysrst_ctrl output (flash_wp_l)) |
+| 43 | IOR10 | VCC | BidirOd | muxed | Muxed IO pad |
+| 44 | IOR11 | VCC | BidirOd | muxed | Muxed IO pad |
+| 45 | IOR12 | VCC | BidirOd | muxed | Muxed IO pad |
+| 46 | IOR13 | VCC | BidirOd | muxed | Muxed IO pad |
+
+<!-- END AUTOGEN -->
# RTL Implementation Notes
diff --git a/util/autogen_md.py b/util/autogen_md.py
new file mode 100755
index 0000000..e68bf30
--- /dev/null
+++ b/util/autogen_md.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""Go through all markdown files and autogenerate content based on commands
+recorded in the file.
+"""
+
+import sys
+from pathlib import Path
+import re
+import subprocess
+import argparse
+import os
+from typing import Callable
+
+START_MARKER_PATTERN = re.compile(r"\n<!--\s*BEGIN\s*AUTOGEN\s*(?P<cmd>.+)\s*-->\n")
+END_MARKER_PATTERN = re.compile(r"\n<!--\s*END\s*AUTOGEN\s*-->\n")
+
+
+def apply_to_all_files(path: Path, fn: Callable[[Path], None]):
+ for child in path.iterdir():
+ if child.is_file():
+ fn(child)
+ if child.is_dir():
+ apply_to_all_files(child, fn)
+
+
+def autogen_rewrite_md(filepath: Path, dry_run: bool):
+ # only consider .md files
+ if not filepath.name.endswith(".md"):
+ return
+ content = filepath.read_text()
+ modified = False
+ pos = 0
+ while True:
+ # search next start marker
+ match_start = START_MARKER_PATTERN.search(content, pos)
+ if match_start is None:
+ # no more replacements to do
+ break
+ cmd = match_start.group('cmd').strip()
+ # search end marker after the start marker
+ match_end = END_MARKER_PATTERN.search(content, pos)
+ if match_end is None:
+ sys.exit(f"Error in {filepath}: start marker with command '{cmd}' without end marker")
+
+ print(f"In {filepath}: running '{cmd}'")
+ if dry_run:
+ # don't run
+ pos = match_end.end(0)
+ continue
+ res = subprocess.run(cmd, shell=True, capture_output=True)
+ if res.stderr:
+ print(
+ f"The command '{cmd}' output "
+ f"the following error messages:\n{res.stderr.decode()}"
+ )
+ if res.returncode != 0:
+ sys.exit(f"The command '{cmd}' had a non-zero return code.")
+
+ new_content = res.stdout.decode()
+ # replace content
+ content = content[0:match_start.end(0)] + new_content + content[match_end.start(0):]
+ modified = True
+ # resume after end line, adjust for new content size
+ pos = match_end.end(0) + len(new_content) - (match_end.start(0) - match_start.end(0))
+ # write back
+ if modified:
+ filepath.write_text(content)
+
+
+def main(args: argparse.Namespace):
+ this_file_path = Path(__file__)
+ repo_root = this_file_path.parent.parent.resolve()
+ os.chdir(repo_root)
+ apply_to_all_files(repo_root, lambda x: autogen_rewrite_md(x, args.dry_run))
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--dry-run",
+ help = "print commands to execute but do not execute them",
+ action="store_true")
+ args = parser.parse_args()
+ main(args)
diff --git a/util/design/gen-top-docs.py b/util/design/gen-top-docs.py
index 784a712..5cceed5 100755
--- a/util/design/gen-top-docs.py
+++ b/util/design/gen-top-docs.py
@@ -4,9 +4,9 @@
# SPDX-License-Identifier: Apache-2.0
r"""Generates top level documentation from an hjson configuration file."""
+import sys
import argparse
import logging as log
-from pathlib import Path
import hjson
from lib import common
@@ -72,40 +72,30 @@
description=common.wrapped_docstring(),
formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument("--topcfg",
- "-t",
- required=True,
- help="Topgen generated config file `top_{name}.hjson`.")
parser.add_argument(
- "--outdir",
- "-o",
- help="Target TOP documentation directory.")
-
+ "--topcfg",
+ "-t",
+ required=True,
+ help="Topgen generated config file `top_{name}.hjson`.",
+ )
+ parser.add_argument(
+ "--generator",
+ "-g",
+ help="Select generator",
+ )
args = parser.parse_args()
+ gen = args.generator
- outdir = Path(args.outdir)
-
- doc_generators = [
- {
- "filename": "mmap.md",
- "generator": generate_mmap_table,
- },
- {
- "filename": "pinout.md",
- "generator": generate_pinout_table,
- },
- ]
-
+ doc_generators = {
+ "mmap": generate_mmap_table,
+ "pinout": generate_pinout_table,
+ }
with open(args.topcfg, 'r') as infile:
top_level = hjson.load(infile)
- top_outdir = outdir / top_level["name"]
- top_outdir.mkdir(parents=True, exist_ok=True)
+ if gen not in doc_generators:
+ sys.exit(f"Unknown generator {gen}")
- for doc in doc_generators:
- outfile = top_outdir / doc["filename"]
- table = doc["generator"](top_level)
- with open(outfile, 'w') as f:
- f.write(to_markdown(table))
+ print(doc_generators[gen](top_level))
if __name__ == "__main__":
diff --git a/util/reggen/README.md b/util/reggen/README.md
index 1c84eab..b13a1a6 100644
--- a/util/reggen/README.md
+++ b/util/reggen/README.md
@@ -29,7 +29,344 @@
For more detail on the non-register entries of the Hjson configuration file, see [this section](../../doc/contributing/hw/comportability/README.md#configuration-description-hjson) of the Comportability Specification.
-{{% selfdoc "reggen" %}}
+<!-- BEGIN AUTOGEN python3 util/selfdoc.py reggen -->
+
+
+<!-- Start of output generated by `regtool.py --doc` -->
+
+The tables describe each key and the type of the value. The following
+types are used:
+
+Type | Description
+---- | -----------
+int | integer (binary 0b, octal 0o, decimal, hex 0x)
+xint | x for undefined otherwise int
+bitrange | bit number as decimal integer, or bit-range as decimal integers msb:lsb
+list | comma separated list enclosed in `[]`
+name list | comma separated list enclosed in `[]` of one or more groups that have just name and dscr keys. e.g. `{ name: "name", desc: "description"}`
+name list+ | name list that optionally contains a width
+parameter list | parameter list having default value optionally
+group | comma separated group of key:value enclosed in `{}`
+list of group | comma separated group of key:value enclosed in `{}` the second entry of the list is the sub group format
+string | string, typically short
+text | string, may be multi-line enclosed in `'''` may use `**bold**`, `*italic*` or `!!Reg` markup
+tuple | tuple enclosed in ()
+python int | Native Python type int (generated)
+python Bool | Native Python type Bool (generated)
+python list | Native Python type list (generated)
+python enum | Native Python type enum (generated)
+
+
+Register fields are tagged using the swaccess key to describe the
+permitted access and side-effects. This key must have one of these
+values:
+
+
+Key | Description
+--- | -----------
+none | No access
+ro | Read Only
+rc | Read Only, reading clears
+rw | Read/Write
+r0w1c | Read zero, Write with 1 clears
+rw1s | Read, Write with 1 sets
+rw1c | Read, Write with 1 clears
+rw0c | Read, Write with 0 clears
+wo | Write Only
+
+
+Register fields are tagged using the hwaccess key to describe the
+permitted access from hardware logic and side-effects. This key must
+have one of these values:
+
+
+Key | Description
+--- | -----------
+hro | Read Only
+hrw | Read/Write
+hwo | Write Only
+none | No Access Needed
+
+
+The top level of the JSON is a group containing the following keys:
+
+Key | Kind | Type | Description of Value
+--- | ---- | ---- | --------------------
+name | required | string | name of the component
+clocking | required | list | clocking for the device
+bus_interfaces | required | list | bus interfaces for the device
+registers | required | list | list of register definition groups and offset control groups
+one_line_desc | optional | string | one-line description of the component
+one_paragraph_desc | optional | string | one-paragraph description of the component
+revisions | optional | list | list with revision records
+design_spec | optional | string | path to the design specification, relative to repo root
+dv_doc | optional | string | path to the DV document, relative to repo root
+hw_checklist | optional | string | path to the hw_checklist, relative to repo root
+sw_checklist | optional | string | path to the sw_checklist, relative to repo root
+design_stage | optional | string | design stage of module
+dif_stage | optional | string | DIF stage of module
+verification_stage | optional | string | verification stage of module
+notes | optional | string | random notes
+version | optional | string | module version
+life_stage | optional | string | life stage of module
+commit_id | optional | string | commit ID of last stage sign-off
+alert_list | optional | name list+ | list of peripheral alerts
+available_inout_list | optional | name list+ | list of available peripheral inouts
+available_input_list | optional | name list+ | list of available peripheral inputs
+available_output_list | optional | name list+ | list of available peripheral outputs
+expose_reg_if | optional | python Bool | if set, expose reg interface in reg2hw signal
+interrupt_list | optional | name list+ | list of peripheral interrupts
+inter_signal_list | optional | list | list of inter-module signals
+no_auto_alert_regs | optional | string | Set to true to suppress automatic generation of alert test registers. Defaults to true if no alert_list is present. Otherwise this defaults to false.
+no_auto_intr_regs | optional | string | Set to true to suppress automatic generation of interrupt registers. Defaults to true if no interrupt_list is present. Otherwise this defaults to false.
+param_list | optional | parameter list | list of parameters of the IP
+regwidth | optional | int | width of registers in bits (default 32)
+reset_request_list | optional | list | list of signals requesting reset
+scan | optional | python Bool | Indicates the module have `scanmode_i`
+scan_reset | optional | python Bool | Indicates the module have `scan_rst_ni`
+scan_en | optional | python Bool | Indicates the module has `scan_en_i`
+SPDX-License-Identifier | optional | string | License ientifier (if using pure json) Only use this if unable to put this information in a comment at the top of the file.
+wakeup_list | optional | name list+ | list of peripheral wakeups
+countermeasures | optional | name list | list of countermeasures in this block
+
+The basic structure of a register definition file is thus:
+
+```hjson
+{
+ name: "GP",
+ regwidth: "32",
+ registers: [
+ // register definitions...
+ ]
+}
+
+```
+
+
+
+The list of registers includes register definition groups containing the following keys:
+
+Key | Kind | Type | Description of Value
+--- | ---- | ---- | --------------------
+name | required | string | name of the register
+desc | required | text | description of the register. This field supports the markdown syntax.
+fields | required | list | list of register field description groups
+alias_target | optional | string | name of the register to apply the alias definition to.
+async | optional | string | indicates the register must cross to a different clock domain before use. The value shown here should correspond to one of the module's clocks.
+sync | optional | string | indicates the register needs to be on another clock/reset domain.The value shown here should correspond to one of the module's clocks.
+swaccess | optional | string | software access permission to use for fields that don't specify swaccess
+hwaccess | optional | string | hardware access permission to use for fields that don't specify hwaccess
+hwext | optional | string | 'true' if the register is stored outside of the register module
+hwqe | optional | string | 'true' if hardware uses 'q' enable signal, which is latched signal of software write pulse.
+hwre | optional | string | 'true' if hardware uses 're' signal, which is latched signal of software read pulse.
+regwen | optional | string | if register is write-protected by another register, that register name should be given here. empty-string for no register write protection
+resval | optional | int | reset value of full register (default 0)
+tags | optional | string | tags for the register, following the format 'tag_name:item1:item2...'
+shadowed | optional | string | 'true' if the register is shadowed
+update_err_alert | optional | string | alert that will be triggered if this shadowed register has update error
+storage_err_alert | optional | string | alert that will be triggered if this shadowed register has storage error
+
+
+The basic register definition group will follow this pattern:
+
+```hjson
+ { name: "REGA",
+ desc: "Description of register",
+ swaccess: "rw",
+ resval: "42",
+ fields: [
+ // bit field definitions...
+ ]
+ }
+```
+
+The name and brief description are required. If the swaccess key is
+provided it describes the access pattern that will be used by all
+bitfields in the register that do not override with their own swaccess
+key. This is a useful shortcut because in most cases a register will
+have the same access restrictions for all fields. The reset value of
+the register may also be provided here or in the individual fields. If
+it is provided in both places then they must match, if it is provided
+in neither place then the reset value defaults to zero for all except
+write-only fields when it defaults to x.
+
+
+
+In the fields list each field definition is a group itself containing the following keys:
+
+Key | Kind | Type | Description of Value
+--- | ---- | ---- | --------------------
+bits | required | bitrange | bit or bit range (msb:lsb)
+name | optional | string | name of the field
+desc | optional | text | description of field (required if the field has a name). This field supports the markdown syntax.
+alias_target | optional | string | name of the field to apply the alias definition to.
+swaccess | optional | string | software access permission, copied from register if not provided in field. (Tool adds if not provided.)
+hwaccess | optional | string | hardware access permission, copied from register if not provided in field. (Tool adds if not provided.)
+hwqe | optional | bitrange | 'true' if hardware uses 'q' enable signal, which is latched signal of software write pulse. Copied from register if not provided in field. (Tool adds if not provided.)
+resval | optional | xint | reset value, comes from register resval if not provided in field. Zero if neither are provided and the field is readable, x if neither are provided and the field is wo. Must match if both are provided.
+enum | optional | list | list of permitted enumeration groups
+tags | optional | string | tags for the field, followed by the format 'tag_name:item1:item2...'
+mubi | optional | bitrange | boolean flag for whether the field is a multi-bit type
+auto_split | optional | bitrange | boolean flag which determines whether the field should be automatically separated into 1-bit sub-fields.This flag is used as a hint for automatically generated software headers with register description.
+
+
+Field names should be relatively short because they will be used
+frequently (and need to fit in the register layout picture!) The field
+description is expected to be longer and will most likely make use of
+the Hjson ability to include multi-line strings. An example with three
+fields:
+
+```hjson
+ fields: [
+ { bits: "15:0",
+ name: "RXS",
+ desc: '''
+ Last 16 oversampled values of RX. These are captured at 16x the baud
+ rate clock. This is a shift register with the most recent bit in
+ bit 0 and the oldest in bit 15. Only valid when ENRXS is set.
+ '''
+ }
+ { bits: "16",
+ name: "ENRXS",
+ desc: '''
+ If this bit is set the receive oversampled data is collected
+ in the RXS field.
+ '''
+ }
+ {bits: "20:19", name: "TXILVL",
+ desc: "Trigger level for TX interrupts",
+ resval: "2",
+ enum: [
+ { value: "0", name: "txlvl1", desc: "1 character" },
+ { value: "1", name: "txlvl4", desc: "4 characters" },
+ { value: "2", name: "txlvl8", desc: "8 characters" },
+ { value: "3", name: "txlvl16", desc: "16 characters" }
+ ]
+ }
+ ]
+```
+
+In all of these the swaccess parameter is inherited from the register
+level, and will be added so this key is always available to the
+backend. The RXS and ENRXS will default to zero reset value (unless
+something different is provided for the register) and will have the
+key added, but TXILVL expicitly sets its reset value as 2.
+
+The missing bits 17 and 18 will be treated as reserved by the tool, as
+will any bits between 21 and the maximum in the register.
+
+The TXILVL is an example using an enumeration to specify all valid
+values for the field. In this case all possible values are described,
+if the list is incomplete then the field is marked with the rsvdenum
+key so the backend can take appropriate action. (If the enum field is
+more than 7 bits then the checking is not done.)
+
+
+
+Definitions in an enumeration group contain:
+
+Key | Kind | Type | Description of Value
+--- | ---- | ---- | --------------------
+name | required | string | name of the member of the enum
+desc | required | text | description when field has this value
+value | required | int | value of this member of the enum
+
+
+The list of registers may include single entry groups to control the offset, open a window or generate registers:
+
+Key | Kind | Type | Description of Value
+--- | ---- | ---- | --------------------
+reserved | optional | int | number of registers to reserve space for
+skipto | optional | int | set next register offset to value
+window | optional | group | group defining an address range for something other than standard registers
+multireg | optional | group | group defining registers generated from a base instance.
+
+
+
+
+Registers can protect themselves from software writes by using the
+register attribute regwen. When not an emptry string (the default
+value), regwen indicates that another register must be true in order
+to allow writes to this register. This is useful for the prevention
+of software modification. The register-enable register (call it
+REGWEN) must be one bit in width, and should default to 1 and be rw1c
+for preferred security control. This allows all writes to proceed
+until at some point software disables future modifications by clearing
+REGWEN. An error is reported if REGWEN does not exist, contains more
+than one bit, is not `rw1c` or does not default to 1. One REGWEN can
+protect multiple registers. The REGWEN register must precede those
+registers that refer to it in the .hjson register list. An example:
+
+```hjson
+ { name: "REGWEN",
+ desc: "Register write enable for a bank of registers",
+ swaccess: "rw1c",
+ fields: [ { bits: "0", resval: "1" } ]
+ }
+ { name: "REGA",
+ swaccess: "rw",
+ regwen: "REGWEN",
+ ...
+ }
+ { name: "REGB",
+ swaccess: "rw",
+ regwen: "REGWEN",
+ ...
+ }
+```
+
+
+A window defines an open region of the register space that can be used
+for things that are not registers (for example access to a buffer ram).
+
+
+Key | Kind | Type | Description of Value
+--- | ---- | ---- | --------------------
+name | required | string | name of the window
+desc | required | text | description of the window
+items | required | int | size in fieldaccess width words of the window
+swaccess | required | string | software access permitted
+data-intg-passthru | optional | string | True if the window has data integrity pass through. Defaults to false if not present.
+byte-write | optional | string | True if byte writes are supported. Defaults to false if not present.
+validbits | optional | int | Number of valid data bits within regwidth sized word. Defaults to regwidth. If smaller than the regwidth then in each word of the window bits [regwidth-1:validbits] are unused and bits [validbits-1:0] are valid.
+unusual | optional | string | True if window has unusual parameters (set to prevent Unusual: errors).Defaults to false if not present.
+
+
+The multireg expands on the register required fields and will generate
+a list of the generated registers (that contain all required and
+generated keys for an actual register).
+
+
+Key | Kind | Type | Description of Value
+--- | ---- | ---- | --------------------
+name | required | string | base name of the registers
+desc | required | text | description of the registers
+count | required | string | number of instances to generate. This field can be integer or string matching from param_list.
+cname | required | string | base name for each instance, mostly useful for referring to instance in messages.
+fields | required | list | list of register field description groups. Describes bit positions used for base instance.
+alias_target | optional | string | name of the register to apply the alias definition to.
+async | optional | string | indicates the register must cross to a different clock domain before use. The value shown here should correspond to one of the module's clocks.
+sync | optional | string | indicates the register needs to be on another clock/reset domain.The value shown here should correspond to one of the module's clocks.
+swaccess | optional | string | software access permission to use for fields that don't specify swaccess
+hwaccess | optional | string | hardware access permission to use for fields that don't specify hwaccess
+hwext | optional | string | 'true' if the register is stored outside of the register module
+hwqe | optional | string | 'true' if hardware uses 'q' enable signal, which is latched signal of software write pulse.
+hwre | optional | string | 'true' if hardware uses 're' signal, which is latched signal of software read pulse.
+regwen | optional | string | if register is write-protected by another register, that register name should be given here. empty-string for no register write protection
+resval | optional | int | reset value of full register (default 0)
+tags | optional | string | tags for the register, following the format 'tag_name:item1:item2...'
+shadowed | optional | string | 'true' if the register is shadowed
+update_err_alert | optional | string | alert that will be triggered if this shadowed register has update error
+storage_err_alert | optional | string | alert that will be triggered if this shadowed register has storage error
+regwen_multi | optional | python Bool | If true, regwen term increments along with current multireg count.
+compact | optional | python Bool | If true, allow multireg compacting.If false, do not compact.
+cdc | optional | string | indicates the register must cross to a different clock domain before use. The value shown here should correspond to one of the module's clocks.
+
+
+(end of output generated by `regtool.py --doc`)
+
+
+<!-- END AUTOGEN -->
The tool will normally generate the register address offset by starting from 0 and allocating the registers in the order they are in the input file.
Between each register the offset is incremented by the number of bytes in the `regwidth` (4 bytes for the default 32-bit `regwidth`), so the registers end up packed into the smallest space.
diff --git a/util/selfdoc.py b/util/selfdoc.py
new file mode 100755
index 0000000..d67ec3a
--- /dev/null
+++ b/util/selfdoc.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+import sys
+import reggen.gen_selfdoc as reggen_selfdoc
+import tlgen
+from typing import TextIO
+
+
+def generate_selfdocs(tool: str, fout: TextIO):
+ """Generate documents for the tools in `util/`
+
+ Each tool creates selfdoc differently. Manually invoked.
+ """
+ if tool == "reggen":
+ reggen_selfdoc.document(fout)
+ elif tool == "tlgen":
+ fout.write(tlgen.selfdoc(heading=3, cmd='tlgen.py --doc'))
+ else:
+ sys.exit(f"unknown tool \"{tool}\"")
+
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ sys.exit("usage: selfdoc <tool>")
+ # running this file as standalone prints the documentation
+ generate_selfdocs(sys.argv[1], sys.stdout)
diff --git a/util/tlgen/README.md b/util/tlgen/README.md
index c050275..e52cbe3 100644
--- a/util/tlgen/README.md
+++ b/util/tlgen/README.md
@@ -53,7 +53,88 @@
*Optional* keys may be provided in the input files.
The tool also may insert the optional keys with default value.
-{{% selfdoc "tlgen" %}}
+<!-- BEGIN AUTOGEN python3 util/selfdoc.py tlgen -->
+
+
+The tables describe each key and the type of the value. The following
+types are used:
+
+Type | Description
+---- | -----------
+int | integer (binary 0b, octal 0o, decimal, hex 0x)
+xint | x for undefined otherwise int
+bitrange | bit number as decimal integer, or bit-range as decimal integers msb:lsb
+list | comma separated list enclosed in `[]`
+name list | comma separated list enclosed in `[]` of one or more groups that have just name and dscr keys. e.g. `{ name: "name", desc: "description"}`
+name list+ | name list that optionally contains a width
+parameter list | parameter list having default value optionally
+group | comma separated group of key:value enclosed in `{}`
+list of group | comma separated group of key:value enclosed in `{}` the second entry of the list is the sub group format
+string | string, typically short
+text | string, may be multi-line enclosed in `'''` may use `**bold**`, `*italic*` or `!!Reg` markup
+tuple | tuple enclosed in ()
+python int | Native Python type int (generated)
+python Bool | Native Python type Bool (generated)
+python list | Native Python type list (generated)
+python enum | Native Python type enum (generated)
+### Top configuration
+
+
+Crossbar configuration format.
+
+
+
+Field | Kind | Type | Description
+----- | ---- | ---- | ------------
+name | required | string | Name of the crossbar
+clock | required | string | Main clock. Internal components use this clock. If not specified, it is assumed to be in main clock domain
+reset | required | string | Main reset
+connections | required | group | List of edge. Key is host, entry in value list is device
+clock_connections | required | group | list of clocks
+nodes | required | list of group | List of nodes group
+type | optional | string | Indicate Hjson type. "xbar" always if exist
+clock_group | optional | string | Remnant from auto-generation scripts. Ignore.
+clock_srcs | optional | group | Remnant from auto-generation scripts. Ignore.
+domain | optional | string | Power domain for the crossbar
+reset_connections | added by tool | group | Generated by topgen. Key is the reset signal inside IP and value is the top reset signal
+
+
+### Node configuration
+
+
+Crossbar node description. It can be host, device, or internal nodes.
+
+
+
+Field | Kind | Type | Description
+----- | ---- | ---- | ------------
+name | required | string | Module instance name
+stub | required | python Bool | Real node or stub. Stubs only occupy address ranges
+type | required | string | Module type: {"host", "device", "async", "socket_1n", "socket_m1"}
+clock | optional | string | main clock of the port
+reset | optional | string | main reset of the port
+pipeline | optional | python Bool | If true, pipeline is added in front of the port
+req_fifo_pass | optional | python Bool | If true, pipeline fifo has passthrough behavior on req
+rsp_fifo_pass | optional | python Bool | If true, pipeline fifo has passthrough behavior on rsp
+inst_type | optional | string | Instance type
+xbar | optional | python Bool | If true, the node is connected to another Xbar
+addr_range | optional | list of group | List of addr_range group
+
+
+### Address configuration
+
+Device Node address configuration. It contains the base address and the size in bytes.
+
+
+
+Field | Kind | Type | Description
+----- | ---- | ---- | ------------
+base_addr | required | int | Base address of the device. It is required for the device
+size_byte | required | int | Memory space of the device. It is required for the device
+
+
+
+<!-- END AUTOGEN -->
## Fabrication process