| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| """ |
| Generates the documentation for the register tool |
| |
| """ |
| from .access import SWACCESS_PERMITTED, HWACCESS_PERMITTED |
| from reggen import (validate, |
| ip_block, enum_entry, field, |
| register, multi_register, window) |
| |
| |
| def genout(outfile, msg): |
| outfile.write(msg) |
| |
| |
| doc_intro = """ |
| |
| <!-- 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 |
| ---- | ----------- |
| """ |
| |
| swaccess_intro = """ |
| |
| Register fields are tagged using the swaccess key to describe the |
| permitted access and side-effects. This key must have one of these |
| values: |
| |
| """ |
| |
| hwaccess_intro = """ |
| |
| 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: |
| |
| """ |
| |
| top_example = """ |
| The basic structure of a register definition file is thus: |
| |
| ```hjson |
| { |
| name: "GP", |
| regwidth: "32", |
| registers: [ |
| // register definitions... |
| ] |
| } |
| |
| ``` |
| |
| """ |
| |
| register_example = """ |
| |
| 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. |
| |
| """ |
| |
| field_example = """ |
| |
| 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.) |
| |
| """ |
| |
| offset_intro = """ |
| |
| """ |
| |
| multi_intro = """ |
| |
| 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). |
| |
| """ |
| |
| window_intro = """ |
| |
| 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). |
| |
| """ |
| |
| regwen_intro = """ |
| |
| 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", |
| ... |
| } |
| ``` |
| """ |
| |
| doc_tail = """ |
| |
| (end of output generated by `regtool.py --doc`) |
| |
| """ |
| |
| |
| def doc_tbl_head(outfile, use): |
| if use is not None: |
| genout(outfile, "\nKey | Kind | Type | Description of Value\n") |
| genout(outfile, "--- | ---- | ---- | --------------------\n") |
| else: |
| genout(outfile, "\nKey | Description\n") |
| genout(outfile, "--- | -----------\n") |
| |
| |
| def doc_tbl_line(outfile, key, use, desc): |
| if use is not None: |
| desc_key, desc_txt = desc |
| val_type = (validate.val_types[desc_key][0] |
| if desc_key is not None else None) |
| else: |
| assert isinstance(desc, str) |
| val_type = None |
| desc_txt = desc |
| |
| if val_type is not None: |
| genout( |
| outfile, '{} | {} | {} | {}\n'.format(key, validate.key_use[use], |
| val_type, desc_txt)) |
| else: |
| genout(outfile, key + " | " + desc_txt + "\n") |
| |
| |
| def document(outfile): |
| genout(outfile, doc_intro) |
| for x in validate.val_types: |
| genout( |
| outfile, |
| validate.val_types[x][0] + " | " + validate.val_types[x][1] + "\n") |
| |
| genout(outfile, swaccess_intro) |
| doc_tbl_head(outfile, None) |
| for key, value in SWACCESS_PERMITTED.items(): |
| doc_tbl_line(outfile, key, None, value[0]) |
| |
| genout(outfile, hwaccess_intro) |
| doc_tbl_head(outfile, None) |
| for key, value in HWACCESS_PERMITTED.items(): |
| doc_tbl_line(outfile, key, None, value[0]) |
| |
| genout( |
| outfile, "\n\nThe top level of the JSON is a group containing " |
| "the following keys:\n") |
| doc_tbl_head(outfile, 1) |
| for k, v in ip_block.REQUIRED_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'r', v) |
| for k, v in ip_block.OPTIONAL_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'o', v) |
| genout(outfile, top_example) |
| |
| genout( |
| outfile, "\n\nThe list of registers includes register definition " |
| "groups containing the following keys:\n") |
| doc_tbl_head(outfile, 1) |
| for k, v in register.REQUIRED_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'r', v) |
| for k, v in register.OPTIONAL_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'o', v) |
| genout(outfile, register_example) |
| |
| genout( |
| outfile, "\n\nIn the fields list each field definition is a group " |
| "itself containing the following keys:\n") |
| doc_tbl_head(outfile, 1) |
| for k, v in field.REQUIRED_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'r', v) |
| for k, v in field.OPTIONAL_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'o', v) |
| genout(outfile, field_example) |
| |
| genout(outfile, "\n\nDefinitions in an enumeration group contain:\n") |
| doc_tbl_head(outfile, 1) |
| for k, v in enum_entry.REQUIRED_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'r', v) |
| |
| genout( |
| outfile, "\n\nThe list of registers may include single entry groups " |
| "to control the offset, open a window or generate registers:\n") |
| doc_tbl_head(outfile, 1) |
| for x in validate.list_optone: |
| doc_tbl_line(outfile, x, 'o', validate.list_optone[x]) |
| |
| genout(outfile, offset_intro) |
| genout(outfile, regwen_intro) |
| |
| genout(outfile, window_intro) |
| doc_tbl_head(outfile, 1) |
| for k, v in window.REQUIRED_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'r', v) |
| for k, v in window.OPTIONAL_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'o', v) |
| |
| genout(outfile, multi_intro) |
| doc_tbl_head(outfile, 1) |
| for k, v in multi_register.REQUIRED_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'r', v) |
| for k, v in multi_register.OPTIONAL_FIELDS.items(): |
| doc_tbl_line(outfile, k, 'o', v) |
| |
| genout(outfile, doc_tail) |