soc: doc: use sphinx toctree as it was intended

The sphinx toctree was behaving oddly, and so previously we were
ignoring it completely.  This patch causes it to be used correctly,
which removes the need for double-including various sections.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-02-04 20:34:10 +08:00
parent 7c3bc0b09f
commit 73ed7e564c
1 changed files with 102 additions and 61 deletions

View File

@ -10,7 +10,7 @@ from .csr import DocumentedCSRRegion
from .module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
from .rst import reflow
sphinx_configuration = """
default_sphinx_configuration = """
project = '{}'
copyright = '{}, {}'
author = '{}'
@ -26,6 +26,7 @@ html_theme = 'alabaster'
html_static_path = ['_static']
"""
def sub_csr_bit_range(busword, csr, offset):
nwords = (csr.size + busword - 1)//busword
i = nwords - offset - 1
@ -34,13 +35,17 @@ def sub_csr_bit_range(busword, csr, offset):
origin = i*busword
return (origin, nbits, name)
def print_svd_register(csr, csr_address, description, length, svd):
print(' <register>', file=svd)
print(' <name>{}</name>'.format(csr.short_numbered_name), file=svd)
if description is not None:
print(' <description><![CDATA[{}]]></description>'.format(description), file=svd)
print(' <addressOffset>0x{:04x}</addressOffset>'.format(csr_address), file=svd)
print(' <resetValue>0x{:02x}</resetValue>'.format(csr.reset_value), file=svd)
print(
' <description><![CDATA[{}]]></description>'.format(description), file=svd)
print(
' <addressOffset>0x{:04x}</addressOffset>'.format(csr_address), file=svd)
print(
' <resetValue>0x{:02x}</resetValue>'.format(csr.reset_value), file=svd)
print(' <size>{}</size>'.format(length), file=svd)
print(' <access>{}</access>'.format(csr.access), file=svd)
csr_address = csr_address + 4
@ -48,11 +53,16 @@ def print_svd_register(csr, csr_address, description, length, svd):
if hasattr(csr, "fields") and len(csr.fields) > 0:
for field in csr.fields:
print(' <field>', file=svd)
print(' <name>{}</name>'.format(field.name), file=svd)
print(' <msb>{}</msb>'.format(field.offset + field.size - 1), file=svd)
print(' <bitRange>[{}:{}]</bitRange>'.format(field.offset + field.size - 1, field.offset), file=svd)
print(' <lsb>{}</lsb>'.format(field.offset), file=svd)
print(' <description><![CDATA[{}]]></description>'.format(reflow(field.description)), file=svd)
print(
' <name>{}</name>'.format(field.name), file=svd)
print(' <msb>{}</msb>'.format(field.offset +
field.size - 1), file=svd)
print(' <bitRange>[{}:{}]</bitRange>'.format(
field.offset + field.size - 1, field.offset), file=svd)
print(
' <lsb>{}</lsb>'.format(field.offset), file=svd)
print(' <description><![CDATA[{}]]></description>'.format(
reflow(field.description)), file=svd)
print(' </field>', file=svd)
else:
field_size = csr.size
@ -67,12 +77,14 @@ def print_svd_register(csr, csr_address, description, length, svd):
print(' <field>', file=svd)
print(' <name>{}</name>'.format(field_name), file=svd)
print(' <msb>{}</msb>'.format(field_size - 1), file=svd)
print(' <bitRange>[{}:{}]</bitRange>'.format(field_size - 1, 0), file=svd)
print(
' <bitRange>[{}:{}]</bitRange>'.format(field_size - 1, 0), file=svd)
print(' <lsb>{}</lsb>'.format(0), file=svd)
print(' </field>', file=svd)
print(' </fields>', file=svd)
print(' </register>', file=svd)
def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, description=None):
interrupts = {}
for csr, irq in sorted(soc.soc_interrupt_map.items()):
@ -85,9 +97,11 @@ def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, desc
raw_regions = soc.get_csr_regions()
else:
for region_name, region in soc.csr_regions.items():
raw_regions.append((region_name, region.origin, region.busword, region.obj))
raw_regions.append((region_name, region.origin,
region.busword, region.obj))
for csr_region in raw_regions:
documented_regions.append(DocumentedCSRRegion(csr_region, csr_data_width=soc.csr_data_width))
documented_regions.append(DocumentedCSRRegion(
csr_region, csr_data_width=soc.csr_data_width))
if filename is None:
filename = name + ".svd"
@ -98,7 +112,8 @@ def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, desc
print(' <vendor>{}</vendor>'.format(vendor), file=svd)
print(' <name>{}</name>'.format(name.upper()), file=svd)
if description is not None:
print(' <description><![CDATA[{}]]></description>'.format(reflow(description)), file=svd)
print(
' <description><![CDATA[{}]]></description>'.format(reflow(description)), file=svd)
print('', file=svd)
print(' <addressUnitBits>8</addressUnitBits>', file=svd)
print(' <width>32</width>', file=svd)
@ -113,10 +128,13 @@ def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, desc
csr_address = 0
print(' <peripheral>', file=svd)
print(' <name>{}</name>'.format(region.name.upper()), file=svd)
print(' <baseAddress>0x{:08X}</baseAddress>'.format(region.origin), file=svd)
print(' <groupName>{}</groupName>'.format(region.name.upper()), file=svd)
print(
' <baseAddress>0x{:08X}</baseAddress>'.format(region.origin), file=svd)
print(
' <groupName>{}</groupName>'.format(region.name.upper()), file=svd)
if len(region.sections) > 0:
print(' <description><![CDATA[{}]]></description>'.format(reflow(region.sections[0].body())), file=svd)
print(' <description><![CDATA[{}]]></description>'.format(
reflow(region.sections[0].body())), file=svd)
print(' <registers>', file=svd)
for csr in region.csrs:
description = None
@ -125,42 +143,53 @@ def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, desc
if isinstance(csr, _CompoundCSR) and len(csr.simple_csrs) > 1:
is_first = True
for i in range(len(csr.simple_csrs)):
(start, length, name) = sub_csr_bit_range(region.busword, csr, i)
sub_name = csr.name.upper() + "_" + name
(start, length, name) = sub_csr_bit_range(
region.busword, csr, i)
if length > 0:
bits_str = "Bits {}-{} of `{}`.".format(start, start+length, csr.name)
bits_str = "Bits {}-{} of `{}`.".format(
start, start+length, csr.name)
else:
bits_str = "Bit {} of `{}`.".format(start, csr.name)
bits_str = "Bit {} of `{}`.".format(
start, csr.name)
if is_first:
if description is not None:
print_svd_register(csr.simple_csrs[i], csr_address, bits_str + " " + description, length, svd)
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str + " " + description, length, svd)
else:
print_svd_register(csr.simple_csrs[i], csr_address, bits_str, length, svd)
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
is_first = False
else:
print_svd_register(csr.simple_csrs[i], csr_address, bits_str, length, svd)
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
csr_address = csr_address + 4
else:
length = ((csr.size + region.busword - 1)//region.busword) * region.busword
print_svd_register(csr, csr_address, description, length, svd)
length = ((csr.size + region.busword - 1) //
region.busword) * region.busword
print_svd_register(
csr, csr_address, description, length, svd)
csr_address = csr_address + 4
print(' </registers>', file=svd)
print(' <addressBlock>', file=svd)
print(' <offset>0</offset>', file=svd)
print(' <size>0x{:x}</size>'.format(csr_address), file=svd)
print(
' <size>0x{:x}</size>'.format(csr_address), file=svd)
print(' <usage>registers</usage>', file=svd)
print(' </addressBlock>', file=svd)
if region.name in interrupts:
print(' <interrupt>', file=svd)
print(' <name>{}</name>'.format(region.name), file=svd)
print(' <value>{}</value>'.format(interrupts[region.name]), file=svd)
print(
' <value>{}</value>'.format(interrupts[region.name]), file=svd)
print(' </interrupt>', file=svd)
print(' </peripheral>', file=svd)
print(' </peripherals>', file=svd)
print('</device>', file=svd)
def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
author="Anonymous", sphinx_extensions=[], quiet=False, note_pulses=False):
author="Anonymous", sphinx_extensions=[], quiet=False, note_pulses=False,
from_scratch=True):
"""Possible extra extensions:
[
'm2r',
@ -177,13 +206,17 @@ def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
# Ensure the output directory exists
pathlib.Path(base_dir + "/_static").mkdir(parents=True, exist_ok=True)
# Create various Sphinx plumbing
# Create the sphinx configuration file if the user has requested,
# or if it doesn't exist already.
if from_scratch or not os.path.isfile(base_dir + "conf.py"):
with open(base_dir + "conf.py", "w", encoding="utf-8") as conf:
year = datetime.datetime.now().year
sphinx_ext_str = ""
for ext in sphinx_extensions:
sphinx_ext_str += "\n \"{}\",".format(ext)
print(sphinx_configuration.format(project_name, year, author, author, sphinx_ext_str), file=conf)
print(default_sphinx_configuration.format(project_name, year,
author, author, sphinx_ext_str), file=conf)
if not quiet:
print("Generate the documentation by running `sphinx-build -M html {} {}_build`".format(base_dir, base_dir))
@ -200,13 +233,16 @@ def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
documented_regions = []
seen_modules = set()
regions = []
# Previously, litex contained a function to gather csr regions.
if hasattr(soc, "get_csr_regions"):
regions = soc.get_csr_regions()
else:
# Now we just access the regions directly.
for region_name, region in soc.csr_regions.items():
regions.append((region_name, region.origin, region.busword, region.obj))
regions.append((region_name, region.origin,
region.busword, region.obj))
for csr_region in regions:
module = None
if hasattr(soc, csr_region[0]):
@ -214,9 +250,11 @@ def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
seen_modules.add(module)
submodules = gather_submodules(module)
documented_region = DocumentedCSRRegion(csr_region, module, submodules, csr_data_width=soc.csr_data_width)
documented_region = DocumentedCSRRegion(
csr_region, module, submodules, csr_data_width=soc.csr_data_width)
if documented_region.name in interrupts:
documented_region.document_interrupt(soc, submodules, interrupts[documented_region.name])
documented_region.document_interrupt(
soc, submodules, interrupts[documented_region.name])
documented_regions.append(documented_region)
# Document any modules that are not CSRs.
@ -231,38 +269,41 @@ def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
except ModuleNotDocumented:
pass
# Create index.rst containing links to all of the generated files.
# If the user has set `from_scratch=False`, then skip this step.
if from_scratch or not os.path.isfile(base_dir + "index.rst"):
with open(base_dir + "index.rst", "w", encoding="utf-8") as index:
print("""
Documentation for {}
{}
.. toctree::
:hidden:
""".format(project_name, "="*len("Documentation for " + project_name)), file=index)
for module in additional_modules:
print(" {}".format(module.name), file=index)
for region in documented_regions:
print(" {}".format(region.name), file=index)
if len(additional_modules) > 0:
print("""
Modules
=======
-------
.. toctree::
:maxdepth: 1
""", file=index)
for module in additional_modules:
print("* :doc:`{} <{}>`".format(module.name.upper(), module.name), file=index)
print(" {}".format(module.name), file=index)
if len(documented_regions) > 0:
print("""
Register Groups
===============
---------------
.. toctree::
:maxdepth: 1
""", file=index)
for region in documented_regions:
print("* :doc:`{} <{}>`".format(region.name.upper(), region.name), file=index)
print(" {}".format(region.name), file=index)
print("""
Indices and tables
==================
------------------
* :ref:`genindex`
* :ref:`modindex`
@ -279,10 +320,10 @@ Indices and tables
with open(base_dir + region.name + ".rst", "w", encoding="utf-8") as outfile:
region.print_region(outfile, base_dir, note_pulses)
# Copy over wavedrom javascript and configuration files
with open(os.path.dirname(__file__) + "/static/WaveDrom.js", "r") as wd_in:
with open(base_dir + "/_static/WaveDrom.js", "w") as wd_out:
wd_out.write(wd_in.read())
with open(os.path.dirname(__file__) + "/static/default.js", "r") as wd_in:
with open(base_dir + "/_static/default.js", "w") as wd_out:
wd_out.write(wd_in.read())