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