[doc] Created otbn preprocessor Signed-off-by: Hugo McNally <hugo.mcnally@gmail.com> Co-authored-by: Harry Callahan <hcallahan@lowrisc.org>
diff --git a/book.toml b/book.toml index a22a5bd..ede0c0f 100644 --- a/book.toml +++ b/book.toml
@@ -42,6 +42,9 @@ # Python RegEx identifying testplan paths. testplan-py-regex = '.+_testplan\.hjson' +[preprocessor.otbn] +command = "./util/mdbook_otbn.py" + [preprocessor.doxygen] command = "./util/mdbook_doxygen.py" out-dir = "docs/"
diff --git a/hw/ip/otbn/README.md b/hw/ip/otbn/README.md index 72fe0e5..12d8ade 100644 --- a/hw/ip/otbn/README.md +++ b/hw/ip/otbn/README.md
@@ -84,7 +84,7 @@ OTBN has an in-built call stack which is accessed through the `x1` GPR. This is intended to be used as a return address stack, containing return addresses for the current stack of function calls. -See the documentation for {{< otbnInsnRef "JAL" >}} and {{< otbnInsnRef "JALR" >}} for a description of how to use it for this purpose. +See the documentation for {{#otbn-insn-ref JAL}} and {{#otbn-insn-ref JALR}} for a description of how to use it for this purpose. The call stack has a maximum depth of 8 elements. Each instruction that reads from `x1` pops a single element from the stack. @@ -99,7 +99,7 @@ Control and Status Registers (CSRs) are 32b wide registers used for "special" purposes, as detailed in their description; they are not related to the GPRs. -CSRs can be accessed through dedicated instructions, {{< otbnInsnRef "CSRRS" >}} and {{< otbnInsnRef "CSRRW" >}}. +CSRs can be accessed through dedicated instructions, {{#otbn-insn-ref CSRRS}} and {{#otbn-insn-ref CSRRW}}. Writes to read-only (RO) registers are ignored; they do not signal an error. All read-write (RW) CSRs are set to 0 when OTBN starts an operation (when 1 is written to {{#regref otbn.CMD.start }}). @@ -187,7 +187,7 @@ <td>RW</td> <td>MOD0</td> <td> - Bits [31:0] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [31:0] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -196,7 +196,7 @@ <td>RW</td> <td>MOD1</td> <td> - Bits [63:32] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [63:32] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -205,7 +205,7 @@ <td>RW</td> <td>MOD2</td> <td> - Bits [95:64] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [95:64] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -214,7 +214,7 @@ <td>RW</td> <td>MOD3</td> <td> - Bits [127:96] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [127:96] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -223,7 +223,7 @@ <td>RW</td> <td>MOD4</td> <td> - Bits [159:128] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [159:128] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -232,7 +232,7 @@ <td>RW</td> <td>MOD5</td> <td> - Bits [191:160] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [191:160] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -241,7 +241,7 @@ <td>RW</td> <td>MOD6</td> <td> - Bits [223:192] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [223:192] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -250,7 +250,7 @@ <td>RW</td> <td>MOD7</td> <td> - Bits [255:224] of the modulus operand, used in the {{< otbnInsnRef "BN.ADDM" >}}/{{< otbnInsnRef "BN.SUBM" >}} instructions. + Bits [255:224] of the modulus operand, used in the {{#otbn-insn-ref BN.ADDM}}/{{#otbn-insn-ref BN.SUBM}} instructions. This CSR is mapped to the MOD WSR. </td> </tr> @@ -312,7 +312,7 @@ OTBN has 256b Wide Special purpose Registers (WSRs). These are analogous to the 32b CSRs, but are used by big number instructions. -They can be accessed with the {{< otbnInsnRef "BN.WSRR" >}} and {{< otbnInsnRef "BN.WSRW" >}} instructions. +They can be accessed with the {{#otbn-insn-ref BN.WSRR}} and {{#otbn-insn-ref BN.WSRW}} instructions. Writes to read-only (RO) registers are ignored; they do not signal an error. All read-write (RW) WSRs are set to 0 when OTBN starts an operation (when 1 is written to {{#regref otbn.CMD.start }}). @@ -334,7 +334,7 @@ <td>MOD</td> <td> -The modulus used by the {{< otbnInsnRef "BN.ADDM" >}} and {{< otbnInsnRef "BN.SUBM" >}} instructions. +The modulus used by the {{#otbn-insn-ref BN.ADDM}} and {{#otbn-insn-ref BN.SUBM}} instructions. This WSR is also visible as CSRs `MOD0` through to `MOD7`. </td> @@ -369,7 +369,7 @@ <td>RW</td> <td>ACC</td> <td> - The accumulator register used by the {{< otbnInsnRef "BN.MULQACC" >}} instruction. + The accumulator register used by the {{#otbn-insn-ref BN.MULQACC}} instruction. </td> </tr> <tr> @@ -440,7 +440,7 @@ ### Loop Stack -OTBN has two instructions for hardware-assisted loops: {{< otbnInsnRef "LOOP" >}} and {{< otbnInsnRef "LOOPI" >}}. +OTBN has two instructions for hardware-assisted loops: {{#otbn-insn-ref LOOP}} and {{#otbn-insn-ref LOOPI}}. Both use the same state for tracking control flow. This is a stack of tuples containing a loop count, start address and end address. The stack has a maximum depth of eight and the top of the stack is the current loop. @@ -558,9 +558,9 @@ The data memory (DMEM) is 256b wide and read-write accessible from the base and big number instruction subsets of the OTBN processor core. There are four instructions that can access data memory. -In the base instruction subset, there are {{< otbnInsnRef "LW" >}} (load word) and {{< otbnInsnRef "SW" >}} (store word). +In the base instruction subset, there are {{#otbn-insn-ref LW}} (load word) and {{#otbn-insn-ref SW}} (store word). These access 32b-aligned 32b words. -In the big number instruction subset, there are {{< otbnInsnRef "BN.LID" >}} (load indirect) and {{< otbnInsnRef "BN.SID" >}} (store indirect). +In the big number instruction subset, there are {{#otbn-insn-ref BN.LID}} (load indirect) and {{#otbn-insn-ref BN.SID}} (store indirect). These access 256b-aligned 256b words. Both memories can be accessed through OTBN's register interface ({{#regref otbn.DMEM }} and {{#regref otbn.IMEM }}). @@ -671,7 +671,7 @@ - OTBN transitions into the busy state, and reflects this by setting {{#regref otbn.STATUS }} to `BUSY_EXECUTE`. - The internal randomness source, which provides random numbers to the `URND` CSR and WSR, is re-seeded from the EDN. - The instruction at address zero is fetched and executed. -- From this point on, all subsequent instructions are executed according to their semantics until either an {{< otbnInsnRef "ECALL" >}} instruction is executed, or an error is detected. +- From this point on, all subsequent instructions are executed according to their semantics until either an {{#otbn-insn-ref ECALL}} instruction is executed, or an error is detected. - A [secure wipe of internal state](#design-details-secure-wipe-internal) is performed. - The {{#regref otbn.ERR_BITS }} register is set to indicate either a successful execution (value `0`), or to indicate the error that was observed (a non-zero value). - OTBN transitions into the [idle state](#design-details-operational-states) (in case of a successful execution, or a recoverable error) or the locked state (in case of a fatal error). @@ -701,7 +701,7 @@ The following actions are taken when OTBN detects a recoverable error: -1. The currently running operation is terminated, similar to the way an {{< otbnInsnRef "ECALL" >}} instruction [is executed](#writing-otbn-applications-ecall): +1. The currently running operation is terminated, similar to the way an {{#otbn-insn-ref ECALL}} instruction [is executed](#writing-otbn-applications-ecall): - No more instructions are fetched or executed. - A [secure wipe of internal state](#design-details-secure-wipe-internal) is performed. - The {{#regref otbn.ERR_BITS }} register is set to a non-zero value that describes the error. @@ -719,7 +719,7 @@ The following actions are taken when OTBN detects a fatal error: 1. A [secure wipe of the data memory](#design-details-secure-wipe-dmem) and a [secure wipe of the instruction memory](#design-details-secure-wipe-imem) is initiated. -2. If OTBN [is not idle](#design-details-operational-states), then the currently running operation is terminated, similarly to how an operation ends after an {{< otbnInsnRef "ECALL" >}} instruction [is executed](#writing-otbn-applications-ecall): +2. If OTBN [is not idle](#design-details-operational-states), then the currently running operation is terminated, similarly to how an operation ends after an {{#otbn-insn-ref ECALL}} instruction [is executed](#writing-otbn-applications-ecall): - No more instructions are fetched or executed. - A [secure wipe of internal state](#design-details-secure-wipe-internal) is performed. - The {{#regref otbn.ERR_BITS }} register is set to a non-zero value that describes the error. @@ -928,8 +928,8 @@ Whenever possible the Integrity Protection Code is preserved from its source and written directly to the register files without recalculation, in particular in the following cases: * Data coming from the data memory (DMEM) through the load-store unit to a GPR or WDR. -* Data copied between WDRs using the {{< otbnInsnRef "BN.MOV" >}} or {{< otbnInsnRef "BN.MOVR" >}} instructions. -* Data conditionally copied between WDRs using the {{< otbnInsnRef "BN.SEL" >}} instruction. +* Data copied between WDRs using the {{#otbn-insn-ref BN.MOV}} or {{#otbn-insn-ref BN.MOVR}} instructions. +* Data conditionally copied between WDRs using the {{#otbn-insn-ref BN.SEL}} instruction. * Data copied between the `ACC` and `MOD` WSRs and a WDR. (TODO: Not yet implemented.) * Data copied between any of the `MOD0` to `MOD7` CSRs and a GPR. @@ -1147,9 +1147,9 @@ ## Returning from an application {#writing-otbn-applications-ecall} -The software running on OTBN signals completion by executing the {{< otbnInsnRef "ECALL" >}} instruction. +The software running on OTBN signals completion by executing the {{#otbn-insn-ref ECALL}} instruction. -Once OTBN has executed the {{< otbnInsnRef "ECALL" >}} instruction, the following things happen: +Once OTBN has executed the {{#otbn-insn-ref ECALL}} instruction, the following things happen: - No more instructions are fetched or executed. - A [secure wipe of internal state](#design-details-secure-wipe-internal) is performed. @@ -1161,7 +1161,7 @@ ## Using hardware loops -OTBN provides two hardware loop instructions: {{< otbnInsnRef "LOOP" >}} and {{< otbnInsnRef "LOOPI" >}}. +OTBN provides two hardware loop instructions: {{#otbn-insn-ref LOOP}} and {{#otbn-insn-ref LOOPI}}. ### Loop nesting @@ -1232,8 +1232,8 @@ ## Algorithic Examples: Multiplication with BN.MULQACC The big number instruction subset of OTBN generally operates on WLEN bit numbers. -{{< otbnInsnRef "BN.MULQACC" >}} operates with WLEN/4 bit operands (with a full WLEN accumulator). -This section outlines two techniques to perform larger multiplies by composing multiple {{< otbnInsnRef "BN.MULQACC" >}} instructions. +{{#otbn-insn-ref BN.MULQACC}} operates with WLEN/4 bit operands (with a full WLEN accumulator). +This section outlines two techniques to perform larger multiplies by composing multiple {{#otbn-insn-ref BN.MULQACC}} instructions. ### Multiplying two WLEN/2 numbers with BN.MULQACC
diff --git a/hw/ip/otbn/doc/isa.md b/hw/ip/otbn/doc/isa.md index 8952e6b..440497e 100644 --- a/hw/ip/otbn/doc/isa.md +++ b/hw/ip/otbn/doc/isa.md
@@ -122,8 +122,8 @@ <!-- Documentation for the instructions in the ISA. Generated from ../data/insns.yml. --> # Base Instruction Subset -{{< otbn_isa base >}} +{{#otbn-isa base }} # Big Number Instruction Subset -{{< otbn_isa bignum >}} +{{#otbn-isa bignum }}
diff --git a/util/mdbook_otbn.py b/util/mdbook_otbn.py new file mode 100755 index 0000000..fce1b01 --- /dev/null +++ b/util/mdbook_otbn.py
@@ -0,0 +1,93 @@ +#!/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 +"""mdbook preprocessor that generates ISA tables for the OTBN. + +The preprocessor also handles `{{#otbn-insn-ref ... }}` links to the ISA Table. +""" + +import json +import sys +import re +from pathlib import Path +import tempfile +import subprocess + +from mdbook import utils as md_utils + +REPO_TOP = Path(__file__).resolve().parents[1] + +# We are looking to match on the following example strings +# {{#otbn-isa base }} +OTBN_ISA_BASE_PATTERN = re.compile(r'\{\{#otbn-isa\s+base\s*\}\}') +OTBN_ISA_BIGNUM_PATTERN = re.compile(r'\{\{#otbn-isa\s+bignum\s*\}\}') + +# {{#otbn-insn-ref insn }} +OTBN_INSNREF_PATTERN = re.compile(r'\{\{#otbn-insn-ref\s+?(.+?)\s*?\}\}') + +OTBN_SCRIPT = REPO_TOP / 'hw/ip/otbn/util/yaml_to_doc.py' +OTBN_CFG = REPO_TOP / 'hw/ip/otbn/data/insns.yml' +OTBN_IMPL = REPO_TOP / 'hw/ip/otbn/dv/otbnsim/sim/insn.py' + + +def main() -> None: + md_utils.supports_html_only() + + (base_content, bignum_content) = get_listings() + + # load both the context and the book from stdin + context, book = json.load(sys.stdin) + + isa_book_path = None + for chapter in md_utils.chapters(book["sections"]): + if chapter["source_path"] is None: + continue + + if OTBN_ISA_BASE_PATTERN.search(chapter["content"]) \ + and OTBN_ISA_BIGNUM_PATTERN.search(chapter["content"]): + + chapter["content"] = OTBN_ISA_BASE_PATTERN.sub(base_content, chapter["content"]) + chapter["content"] = OTBN_ISA_BIGNUM_PATTERN.sub(bignum_content, chapter["content"]) + + isa_book_path = chapter["source_path"] + break + + if isa_book_path is None: + sys.exit("No file was found with both {{#otbn-isa base}} and {{#otbn-isa bignum}}") + + def ref_to_link(m: re.Match): + instr = m.group(1) + ref = instr.replace(".", "").lower() + return '<a href="/{}#{}"><code>{}</code></a>)'.format(isa_book_path, ref, instr) + + for chapter in md_utils.chapters(book["sections"]): + chapter["content"] = \ + OTBN_INSNREF_PATTERN.sub( + ref_to_link, + chapter["content"], + ) + + # dump the book into stdout + print(json.dumps(book)) + + +def get_listings() -> (str, str): + """Use the otbn utility scripts to generate the ISA listings.""" + with tempfile.TemporaryDirectory() as tmpdir: + subprocess.run( + [str(OTBN_SCRIPT), str(OTBN_CFG), str(OTBN_IMPL), tmpdir], + check=True, + ) + tmpdir = Path(tmpdir) + with open(tmpdir / "base.md") as f: + base_content = f.read() + + with open(tmpdir / "bignum.md") as f: + bignum_content = f.read() + + return (base_content, bignum_content) + + +if __name__ == "__main__": + main()