diff --git a/litex/soc/doc/__init__.py b/litex/soc/doc/__init__.py
index 0a8c15dbb..38c753c54 100644
--- a/litex/soc/doc/__init__.py
+++ b/litex/soc/doc/__init__.py
@@ -6,6 +6,7 @@ import pathlib
import datetime
from litex.soc.interconnect.csr import _CompoundCSR
+from litex.soc.integration import export
from .csr import DocumentedCSRRegion
from .module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
from .rst import reflow
@@ -26,165 +27,12 @@ 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
- nbits = min(csr.size - i*busword, busword) - 1
- name = (csr.name + str(i) if nwords > 1 else csr.name).upper()
- 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(length), file=svd)
- print(' {}'.format(csr.access), file=svd)
- csr_address = csr_address + 4
- print(' ', file=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(' ', file=svd)
- else:
- field_size = csr.size
- field_name = csr.short_name.lower()
- # Strip off "ev_" from eventmanager fields
- if field_name == "ev_enable":
- field_name = "enable"
- elif field_name == "ev_pending":
- field_name = "pending"
- elif field_name == "ev_status":
- field_name = "status"
- 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(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()):
- interrupts[csr] = irq
-
- documented_regions = []
-
- raw_regions = []
- if hasattr(soc, "get_csr_regions"):
- 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))
- for csr_region in raw_regions:
- documented_regions.append(DocumentedCSRRegion(
- csr_region, csr_data_width=soc.csr_data_width))
-
+def generate_svd(soc, buildpath, filename=None, name="soc", **kwargs):
if filename is None:
filename = name + ".svd"
+ kwargs["name"] = name
with open(buildpath + "/" + filename, "w", encoding="utf-8") as svd:
- print('', file=svd)
- print('', file=svd)
- print('', file=svd)
- print(' {}'.format(vendor), file=svd)
- print(' {}'.format(name.upper()), file=svd)
- if description is not None:
- print(
- ' '.format(reflow(description)), file=svd)
- print('', file=svd)
- print(' 8', file=svd)
- print(' 32', file=svd)
- print(' 32', file=svd)
- print(' read-write', file=svd)
- print(' 0x00000000', file=svd)
- print(' 0xFFFFFFFF', file=svd)
- print('', file=svd)
- print(' ', file=svd)
-
- for region in documented_regions:
- 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)
- if len(region.sections) > 0:
- print(' '.format(
- reflow(region.sections[0].body())), file=svd)
- print(' ', file=svd)
- for csr in region.csrs:
- description = None
- if hasattr(csr, "description"):
- description = csr.description
- 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)
- if length > 0:
- bits_str = "Bits {}-{} of `{}`.".format(
- start, start+length, csr.name)
- else:
- 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)
- else:
- 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)
- 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)
- csr_address = csr_address + 4
- print(' ', file=svd)
- print(' ', file=svd)
- print(' 0', 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(' ', file=svd)
- print(' ', file=svd)
- print(' ', file=svd)
- print('', file=svd)
+ svd.write(export.get_svd(soc, **kwargs))
def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
@@ -282,7 +130,7 @@ Documentation for {}
if len(additional_modules) > 0:
print("""
Modules
--------
+=======
.. toctree::
:maxdepth: 1
@@ -293,7 +141,7 @@ Modules
if len(documented_regions) > 0:
print("""
Register Groups
----------------
+===============
.. toctree::
:maxdepth: 1
@@ -303,7 +151,7 @@ Register Groups
print("""
Indices and tables
-------------------
+==================
* :ref:`genindex`
* :ref:`modindex`
diff --git a/litex/soc/integration/export.py b/litex/soc/integration/export.py
index d38c32d65..c91f53524 100644
--- a/litex/soc/integration/export.py
+++ b/litex/soc/integration/export.py
@@ -23,6 +23,11 @@ from litex.soc.interconnect.csr import CSRStatus
from litex.build.tools import generated_banner
+from litex.soc.doc.rst import reflow
+from litex.soc.doc.module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
+from litex.soc.doc.csr import DocumentedCSRRegion
+from litex.soc.interconnect.csr import _CompoundCSR
+
# CPU files ----------------------------------------------------------------------------------------
def get_cpu_mak(cpu, compile_software):
@@ -273,3 +278,150 @@ def get_csr_csv(csr_regions={}, constants={}, mem_regions={}):
d["memories"][name]["type"],
)
return r
+
+# SVD Export --------------------------------------------------------------------------------------
+
+def get_svd(soc, vendor="litex", name="soc", description=None):
+ def sub_csr_bit_range(busword, csr, offset):
+ nwords = (csr.size + busword - 1)//busword
+ i = nwords - offset - 1
+ nbits = min(csr.size - i*busword, busword) - 1
+ name = (csr.name + str(i) if nwords > 1 else csr.name).upper()
+ origin = i*busword
+ return (origin, nbits, name)
+
+ def print_svd_register(csr, csr_address, description, length, svd):
+ svd.append(' ')
+ svd.append(' {}'.format(csr.short_numbered_name))
+ if description is not None:
+ svd.append(' '.format(description))
+ svd.append(' 0x{:04x}'.format(csr_address))
+ svd.append(' 0x{:02x}'.format(csr.reset_value))
+ svd.append(' {}'.format(length))
+ svd.append(' {}'.format(csr.access))
+ csr_address = csr_address + 4
+ svd.append(' ')
+ if hasattr(csr, "fields") and len(csr.fields) > 0:
+ for field in csr.fields:
+ svd.append(' ')
+ svd.append(' {}'.format(field.name))
+ svd.append(' {}'.format(field.offset +
+ field.size - 1))
+ svd.append(' [{}:{}]'.format(
+ field.offset + field.size - 1, field.offset))
+ svd.append(' {}'.format(field.offset))
+ svd.append(' '.format(
+ reflow(field.description)))
+ svd.append(' ')
+ else:
+ field_size = csr.size
+ field_name = csr.short_name.lower()
+ # Strip off "ev_" from eventmanager fields
+ if field_name == "ev_enable":
+ field_name = "enable"
+ elif field_name == "ev_pending":
+ field_name = "pending"
+ elif field_name == "ev_status":
+ field_name = "status"
+ svd.append(' ')
+ svd.append(' {}'.format(field_name))
+ svd.append(' {}'.format(field_size - 1))
+ svd.append(' [{}:{}]'.format(field_size - 1, 0))
+ svd.append(' {}'.format(0))
+ svd.append(' ')
+ svd.append(' ')
+ svd.append(' ')
+
+ interrupts = {}
+ for csr, irq in sorted(soc.soc_interrupt_map.items()):
+ interrupts[csr] = irq
+
+ documented_regions = []
+
+ raw_regions = []
+ if hasattr(soc, "get_csr_regions"):
+ 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))
+ for csr_region in raw_regions:
+ documented_regions.append(DocumentedCSRRegion(
+ csr_region, csr_data_width=soc.csr_data_width))
+
+ svd = []
+ svd.append('')
+ svd.append('')
+ svd.append('')
+ svd.append(' {}'.format(vendor))
+ svd.append(' {}'.format(name.upper()))
+ if description is not None:
+ svd.append(' '.format(reflow(description)))
+ svd.append('')
+ svd.append(' 8')
+ svd.append(' 32')
+ svd.append(' 32')
+ svd.append(' read-write')
+ svd.append(' 0x00000000')
+ svd.append(' 0xFFFFFFFF')
+ svd.append('')
+ svd.append(' ')
+
+ for region in documented_regions:
+ csr_address = 0
+ svd.append(' ')
+ svd.append(' {}'.format(region.name.upper()))
+ svd.append(' 0x{:08X}'.format(region.origin))
+ svd.append(' {}'.format(region.name.upper()))
+ if len(region.sections) > 0:
+ svd.append(' '.format(
+ reflow(region.sections[0].body())))
+ svd.append(' ')
+ for csr in region.csrs:
+ description = None
+ if hasattr(csr, "description"):
+ description = csr.description
+ 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)
+ if length > 0:
+ bits_str = "Bits {}-{} of `{}`.".format(
+ start, start+length, csr.name)
+ else:
+ 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)
+ else:
+ 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)
+ 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)
+ csr_address = csr_address + 4
+ svd.append(' ')
+ svd.append(' ')
+ svd.append(' 0')
+ svd.append(' 0x{:x}'.format(csr_address))
+ svd.append(' registers')
+ svd.append(' ')
+ if region.name in interrupts:
+ svd.append(' ')
+ svd.append(' {}'.format(region.name))
+ svd.append(' {}'.format(interrupts[region.name]))
+ svd.append(' ')
+ svd.append(' ')
+ svd.append(' ')
+ svd.append('')
+ return "\n".join(svd)