307 lines
9.2 KiB
Python
307 lines
9.2 KiB
Python
# 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)
|