diff --git a/litex/soc/doc/__init__.py b/litex/soc/doc/__init__.py
index 6262c575e..0a8c15dbb 100644
--- a/litex/soc/doc/__init__.py
+++ b/litex/soc/doc/__init__.py
@@ -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(' ', file=svd)
print(' {}'.format(csr.short_numbered_name), file=svd)
if description is not None:
- print(' '.format(description), file=svd)
- print(' 0x{:04x}'.format(csr_address), file=svd)
- print(' 0x{:02x}'.format(csr.reset_value), file=svd)
+ print(
+ ' '.format(description), file=svd)
+ print(
+ ' 0x{:04x}'.format(csr_address), file=svd)
+ print(
+ ' 0x{:02x}'.format(csr.reset_value), file=svd)
print(' {}'.format(length), file=svd)
print(' {}'.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(' ', file=svd)
- print(' {}'.format(field.name), file=svd)
- print(' {}'.format(field.offset + field.size - 1), file=svd)
- print(' [{}:{}]'.format(field.offset + field.size - 1, field.offset), file=svd)
- print(' {}'.format(field.offset), file=svd)
- print(' '.format(reflow(field.description)), file=svd)
+ print(
+ ' {}'.format(field.name), file=svd)
+ print(' {}'.format(field.offset +
+ field.size - 1), file=svd)
+ print(' [{}:{}]'.format(
+ field.offset + field.size - 1, field.offset), file=svd)
+ print(
+ ' {}'.format(field.offset), file=svd)
+ print(' '.format(
+ reflow(field.description)), file=svd)
print(' ', file=svd)
else:
field_size = csr.size
@@ -67,12 +77,14 @@ def print_svd_register(csr, csr_address, description, length, svd):
print(' ', file=svd)
print(' {}'.format(field_name), file=svd)
print(' {}'.format(field_size - 1), file=svd)
- print(' [{}:{}]'.format(field_size - 1, 0), file=svd)
+ print(
+ ' [{}:{}]'.format(field_size - 1, 0), file=svd)
print(' {}'.format(0), file=svd)
print(' ', file=svd)
print(' ', file=svd)
print(' ', 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(' {}'.format(vendor), file=svd)
print(' {}'.format(name.upper()), file=svd)
if description is not None:
- print(' '.format(reflow(description)), file=svd)
+ print(
+ ' '.format(reflow(description)), file=svd)
print('', file=svd)
print(' 8', file=svd)
print(' 32', file=svd)
@@ -113,10 +128,13 @@ def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, desc
csr_address = 0
print(' ', file=svd)
print(' {}'.format(region.name.upper()), file=svd)
- print(' 0x{:08X}'.format(region.origin), file=svd)
- print(' {}'.format(region.name.upper()), file=svd)
+ print(
+ ' 0x{:08X}'.format(region.origin), file=svd)
+ print(
+ ' {}'.format(region.name.upper()), file=svd)
if len(region.sections) > 0:
- print(' '.format(reflow(region.sections[0].body())), file=svd)
+ print(' '.format(
+ reflow(region.sections[0].body())), file=svd)
print(' ', 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(' ', file=svd)
print(' ', file=svd)
print(' 0', file=svd)
- print(' 0x{:x}'.format(csr_address), file=svd)
+ print(
+ ' 0x{:x}'.format(csr_address), file=svd)
print(' registers', file=svd)
print(' ', file=svd)
if region.name in interrupts:
print(' ', file=svd)
print(' {}'.format(region.name), file=svd)
- print(' {}'.format(interrupts[region.name]), file=svd)
+ print(
+ ' {}'.format(interrupts[region.name]), file=svd)
print(' ', file=svd)
print(' ', file=svd)
print(' ', file=svd)
print('', 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
- 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)
+ # 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(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
- with open(base_dir + "index.rst", "w", encoding="utf-8") as index:
- print("""
+ # 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("""
+ if len(additional_modules) > 0:
+ print("""
Modules
-=======
-""", file=index)
- for module in additional_modules:
- print("* :doc:`{} <{}>`".format(module.name.upper(), module.name), file=index)
+-------
- if len(documented_regions) > 0:
- print("""
+.. toctree::
+ :maxdepth: 1
+""", file=index)
+ for module in additional_modules:
+ print(" {}".format(module.name), file=index)
+
+ if len(documented_regions) > 0:
+ print("""
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
-==================
+------------------
* :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())