integration: svd: move svd generation to `export`
authorSean Cross <sean@xobs.io>
Tue, 4 Feb 2020 15:49:08 +0000 (23:49 +0800)
committerSean Cross <sean@xobs.io>
Tue, 4 Feb 2020 15:49:08 +0000 (23:49 +0800)
It was suggested that we should move svd generation into `export`,
alongside the rest of the generators such as csv, json, and h.  This
performs this move, while keeping a compatible `generate_svd()` function
inside `soc/doc/`.

Signed-off-by: Sean Cross <sean@xobs.io>
litex/soc/doc/__init__.py
litex/soc/integration/export.py

index 0a8c15dbb0c8fc301f306ec980d398175ea610f3..38c753c541fedb22eb8998e18c2dc389b0e9cd81 100644 (file)
@@ -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('                <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('                    <size>{}</size>'.format(length), file=svd)
-    print('                    <access>{}</access>'.format(csr.access), file=svd)
-    csr_address = csr_address + 4
-    print('                    <fields>', file=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('                        </field>', 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('                        <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('                            <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()):
-        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('<?xml version="1.0" encoding="utf-8"?>', file=svd)
-        print('', file=svd)
-        print('<device schemaVersion="1.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" >', file=svd)
-        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('', file=svd)
-        print('    <addressUnitBits>8</addressUnitBits>', file=svd)
-        print('    <width>32</width>', file=svd)
-        print('    <size>32</size>', file=svd)
-        print('    <access>read-write</access>', file=svd)
-        print('    <resetValue>0x00000000</resetValue>', file=svd)
-        print('    <resetMask>0xFFFFFFFF</resetMask>', file=svd)
-        print('', file=svd)
-        print('    <peripherals>', file=svd)
-
-        for region in documented_regions:
-            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)
-            if len(region.sections) > 0:
-                print('            <description><![CDATA[{}]]></description>'.format(
-                    reflow(region.sections[0].body())), file=svd)
-            print('            <registers>', 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('            </registers>', file=svd)
-            print('            <addressBlock>', file=svd)
-            print('                <offset>0</offset>', 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('            </interrupt>', file=svd)
-            print('        </peripheral>', file=svd)
-        print('    </peripherals>', file=svd)
-        print('</device>', 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`
index d38c32d65f1f065e0b6d37e7b0668b89ab21a220..c91f535240ebf67cf44263ccd5935fc665fadd7a 100644 (file)
@@ -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('                <register>')
+        svd.append('                    <name>{}</name>'.format(csr.short_numbered_name))
+        if description is not None:
+            svd.append('                    <description><![CDATA[{}]]></description>'.format(description))
+        svd.append('                    <addressOffset>0x{:04x}</addressOffset>'.format(csr_address))
+        svd.append('                    <resetValue>0x{:02x}</resetValue>'.format(csr.reset_value))
+        svd.append('                    <size>{}</size>'.format(length))
+        svd.append('                    <access>{}</access>'.format(csr.access))
+        csr_address = csr_address + 4
+        svd.append('                    <fields>')
+        if hasattr(csr, "fields") and len(csr.fields) > 0:
+            for field in csr.fields:
+                svd.append('                        <field>')
+                svd.append('                            <name>{}</name>'.format(field.name))
+                svd.append('                            <msb>{}</msb>'.format(field.offset +
+                                                                         field.size - 1))
+                svd.append('                            <bitRange>[{}:{}]</bitRange>'.format(
+                    field.offset + field.size - 1, field.offset))
+                svd.append('                            <lsb>{}</lsb>'.format(field.offset))
+                svd.append('                            <description><![CDATA[{}]]></description>'.format(
+                    reflow(field.description)))
+                svd.append('                        </field>')
+        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('                        <field>')
+            svd.append('                            <name>{}</name>'.format(field_name))
+            svd.append('                            <msb>{}</msb>'.format(field_size - 1))
+            svd.append('                            <bitRange>[{}:{}]</bitRange>'.format(field_size - 1, 0))
+            svd.append('                            <lsb>{}</lsb>'.format(0))
+            svd.append('                        </field>')
+        svd.append('                    </fields>')
+        svd.append('                </register>')
+
+    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('<?xml version="1.0" encoding="utf-8"?>')
+    svd.append('')
+    svd.append('<device schemaVersion="1.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" >')
+    svd.append('    <vendor>{}</vendor>'.format(vendor))
+    svd.append('    <name>{}</name>'.format(name.upper()))
+    if description is not None:
+        svd.append('    <description><![CDATA[{}]]></description>'.format(reflow(description)))
+    svd.append('')
+    svd.append('    <addressUnitBits>8</addressUnitBits>')
+    svd.append('    <width>32</width>')
+    svd.append('    <size>32</size>')
+    svd.append('    <access>read-write</access>')
+    svd.append('    <resetValue>0x00000000</resetValue>')
+    svd.append('    <resetMask>0xFFFFFFFF</resetMask>')
+    svd.append('')
+    svd.append('    <peripherals>')
+
+    for region in documented_regions:
+        csr_address = 0
+        svd.append('        <peripheral>')
+        svd.append('            <name>{}</name>'.format(region.name.upper()))
+        svd.append('            <baseAddress>0x{:08X}</baseAddress>'.format(region.origin))
+        svd.append('            <groupName>{}</groupName>'.format(region.name.upper()))
+        if len(region.sections) > 0:
+            svd.append('            <description><![CDATA[{}]]></description>'.format(
+                reflow(region.sections[0].body())))
+        svd.append('            <registers>')
+        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('            </registers>')
+        svd.append('            <addressBlock>')
+        svd.append('                <offset>0</offset>')
+        svd.append('                <size>0x{:x}</size>'.format(csr_address))
+        svd.append('                <usage>registers</usage>')
+        svd.append('            </addressBlock>')
+        if region.name in interrupts:
+            svd.append('            <interrupt>')
+            svd.append('                <name>{}</name>'.format(region.name))
+            svd.append('                <value>{}</value>'.format(interrupts[region.name]))
+            svd.append('            </interrupt>')
+        svd.append('        </peripheral>')
+    svd.append('    </peripherals>')
+    svd.append('</device>')
+    return "\n".join(svd)