|  | # 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) |