litex-doc: initial merge of lxsocdoc
authorSean Cross <sean@xobs.io>
Tue, 4 Feb 2020 12:14:41 +0000 (20:14 +0800)
committerSean Cross <sean@xobs.io>
Tue, 4 Feb 2020 12:14:41 +0000 (20:14 +0800)
lxsocdoc enables automatic documentation of litex projects, including
automatic generation of SVD files.

This merges the existing lxsocdoc distribution into the `soc/doc` directory.

Signed-off-by: Sean Cross <sean@xobs.io>
litex/soc/doc/__init__.py [new file with mode: 0644]
litex/soc/doc/csr.py [new file with mode: 0644]
litex/soc/doc/module.py [new file with mode: 0644]
litex/soc/doc/rst.py [new file with mode: 0644]
litex/soc/doc/static/WaveDrom.js [new file with mode: 0644]
litex/soc/doc/static/default.js [new file with mode: 0644]

diff --git a/litex/soc/doc/__init__.py b/litex/soc/doc/__init__.py
new file mode 100644 (file)
index 0000000..6262c57
--- /dev/null
@@ -0,0 +1,288 @@
+# This file is Copyright (c) 2020 Sean Cross <sean@xobs.io>
+# License: BSD
+
+import os
+import pathlib
+import datetime
+
+from litex.soc.interconnect.csr import _CompoundCSR
+from .csr import DocumentedCSRRegion
+from .module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
+from .rst import reflow
+
+sphinx_configuration = """
+project = '{}'
+copyright = '{}, {}'
+author = '{}'
+extensions = [
+    'sphinx.ext.autosectionlabel',
+    'sphinxcontrib.wavedrom',{}
+]
+templates_path = ['_templates']
+exclude_patterns = []
+offline_skin_js_path = "https://wavedrom.com/skins/default.js"
+offline_wavedrom_js_path = "https://wavedrom.com/WaveDrom.js"
+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))
+
+    if filename is None:
+        filename = name + ".svd"
+    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)
+                        sub_name = csr.name.upper() + "_" + name
+                        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)
+
+def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
+            author="Anonymous", sphinx_extensions=[], quiet=False, note_pulses=False):
+    """Possible extra extensions:
+        [
+            'm2r',
+            'recommonmark',
+            'sphinx_rtd_theme',
+            'sphinx_autodoc_typehints',
+        ]
+    """
+
+    # Ensure the target directory is a full path
+    if base_dir[-1] != '/':
+        base_dir = base_dir + '/'
+
+    # 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)
+    if not quiet:
+        print("Generate the documentation by running `sphinx-build -M html {} {}_build`".format(base_dir, base_dir))
+
+    # Gather all interrupts so we can easily map IRQ numbers to CSR sections
+    interrupts = {}
+    for csr, irq in sorted(soc.soc_interrupt_map.items()):
+        interrupts[csr] = irq
+
+    # Convert each CSR region into a DocumentedCSRRegion.
+    # This process will also expand each CSR into a DocumentedCSR,
+    # which means that CompoundCSRs (such as CSRStorage and CSRStatus)
+    # that are larger than the buswidth will be turned into multiple
+    # DocumentedCSRs.
+    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))
+    for csr_region in regions:
+        module = None
+        if hasattr(soc, csr_region[0]):
+            module = getattr(soc, csr_region[0])
+            seen_modules.add(module)
+        submodules = gather_submodules(module)
+
+        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_regions.append(documented_region)
+
+    # Document any modules that are not CSRs.
+    # TODO: Add memory maps here.
+    additional_modules = [
+        DocumentedInterrupts(interrupts),
+    ]
+    for (mod_name, mod) in soc._submodules:
+        if mod not in seen_modules:
+            try:
+                additional_modules.append(DocumentedModule(mod_name, mod))
+            except ModuleNotDocumented:
+                pass
+
+    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
+=======
+""", file=index)
+            for module in additional_modules:
+                print("* :doc:`{} <{}>`".format(module.name.upper(), 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("""
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+""", file=index)
+
+    # Create a Region file for each of the documented CSR regions.
+    for region in documented_regions:
+        with open(base_dir + region.name + ".rst", "w", encoding="utf-8") as outfile:
+            region.print_region(outfile, base_dir, note_pulses)
+
+    # Create a Region file for each additional non-CSR module
+    for region in additional_modules:
+        with open(base_dir + region.name + ".rst", "w", encoding="utf-8") as outfile:
+            region.print_region(outfile, base_dir, note_pulses)
+
+    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())
diff --git a/litex/soc/doc/csr.py b/litex/soc/doc/csr.py
new file mode 100644 (file)
index 0000000..7ef6188
--- /dev/null
@@ -0,0 +1,471 @@
+# This file is Copyright (c) 2020 Sean Cross <sean@xobs.io>
+# License: BSD
+
+from migen import *
+from migen.util.misc import xdir
+from migen.fhdl.specials import Memory
+
+from litex.soc.integration.doc import ModuleDoc
+from litex.soc.interconnect.csr_bus import SRAM
+from litex.soc.interconnect.csr import _CompoundCSR, CSRStatus, CSRStorage, CSRField, _CSRBase
+from litex.soc.interconnect.csr_eventmanager import _EventSource, SharedIRQ, EventManager, EventSourceLevel, EventSourceProcess, EventSourcePulse
+
+import textwrap
+
+from .rst import print_table, reflow
+
+class DocumentedCSRField:
+    def __init__(self, field):
+        self.name        = field.name
+        self.size        = field.size
+        self.offset      = field.offset
+        self.reset_value = field.reset.value
+        self.description = field.description
+        self.access      = field.access
+        self.pulse       = field.pulse
+        self.values      = field.values
+
+        # If this is part of a sub-CSR, this value will be different
+        self.start       = None
+
+class DocumentedCSR:
+    def trim(self, docstring):
+        if docstring is not None:
+            return reflow(docstring)
+        return None
+
+    def __init__(self, name, address, short_numbered_name="", short_name="", reset=0, offset=0, size=8, description=None, access="read-write", fields=[]):
+        self.name = name
+        self.short_name = short_name
+        self.short_numbered_name = short_numbered_name
+        self.address = address
+        self.offset = offset
+        self.size = size
+        if size == 0:
+            print("!!! Warning: creating CSR of size 0 {}".format(name))
+        self.description = self.trim(description)
+        self.reset_value = reset
+        self.fields = fields
+        self.access = access
+        for f in self.fields:
+            f.description = self.trim(f.description)
+
+class DocumentedCSRRegion:
+    def __init__(self, csr_region, module=None, submodules=[], csr_data_width=8):
+        (self.name, self.origin, self.busword, self.raw_csrs) = csr_region
+        self.current_address = self.origin
+        self.sections = []
+        self.csrs = []
+        self.csr_data_width = csr_data_width
+
+        # If the section has extra documentation, gather it.
+        if isinstance(module, ModuleDoc):
+            self.sections.append(module)
+        if module is not None and hasattr(module, "get_module_documentation"):
+            docs = module.get_module_documentation()
+            for doc in docs:
+                self.sections.append(doc)
+
+        if isinstance(self.raw_csrs, SRAM):
+            print("{}@{:x}: Found SRAM: {}".format(self.name, self.origin, self.raw_csrs))
+        elif isinstance(self.raw_csrs, list):
+            for csr in self.raw_csrs:
+                if isinstance(csr, _CSRBase):
+                    self.document_csr(csr)
+                elif isinstance(csr, SRAM):
+                    print("{}: Found SRAM in the list: {}".format(self.name, csr))
+                else:
+                    print("{}: Unknown module: {}".format(self.name, csr))
+        elif isinstance(self.raw_csrs, Memory):
+            self.csrs.append(DocumentedCSR(
+                self.name.upper(), self.origin, short_numbered_name=self.name.upper(), short_name=self.name.upper(), reset=0, size=self.raw_csrs.width,
+                description="{} x {}-bit memory".format(self.raw_csrs.width, self.raw_csrs.depth)
+            ))
+            print("{}@{:x}: Found memory that's {} x {} (but memories aren't documented yet)".format(self.name, self.origin, self.raw_csrs.width, self.raw_csrs.depth))
+        else:
+            print("{}@{:x}: Unexpected item on the CSR bus: {}".format(self.name, self.origin, self.raw_csrs))
+
+    def bit_range(self, start, end, empty_if_zero=False):
+        end -= 1
+        if start == end:
+            if empty_if_zero:
+                return ""
+            return "[{}]".format(start)
+        else:
+            return "[{}:{}]".format(end, start)
+
+    def document_interrupt(self, soc, submodules, irq):
+        managers = submodules["event_managers"]
+        for m in managers:
+            sources_u = [y for x, y in xdir(m, True) if isinstance(y, _EventSource)]
+            sources = sorted(sources_u, key=lambda x: x.duid)
+
+            def source_description(src):
+                if hasattr(src, "name") and src.name is not None:
+                    base_text = "`1` if a `{}` event occurred. ".format(src.name)
+                else:
+                    base_text = "`1` if a this particular event occurred. "
+                if hasattr(src, "description") and src.description is not None:
+                    return src.description
+                elif isinstance(src, EventSourceLevel):
+                    return base_text + "This Event is **level triggered** when the signal is **high**."
+                elif isinstance(src, EventSourcePulse):
+                    return base_text + "This Event is triggered on a **rising** edge."
+                elif isinstance(src, EventSourceProcess):
+                    return base_text + "This Event is triggered on a **falling** edge."
+                else:
+                    return base_text + "This Event uses an unknown method of triggering."
+
+            # Patch the DocumentedCSR to add our own Description, if one doesn't exist.
+            for dcsr in self.csrs:
+                short_name = dcsr.short_name.upper()
+                if short_name == m.status.name.upper():
+                    if dcsr.fields is None or len(dcsr.fields) == 0:
+                        fields = []
+                        for i, source in enumerate(sources):
+                            if hasattr(source, "name") and source.name is not None:
+                                fields.append(DocumentedCSRField(CSRField(source.name, offset=i, description="Level of the `{}` event".format(source.name))))
+                            else:
+                                fields.append(DocumentedCSRField(CSRField("event{}".format(i), offset=i, description="Level of the `event{}` event".format(i))))
+                        dcsr.fields = fields
+                    if dcsr.description is None:
+                        dcsr.description = "This register contains the current raw level of the Event trigger.  Writes to this register have no effect."
+                elif short_name == m.pending.name.upper():
+                    if dcsr.fields is None or len(dcsr.fields) == 0:
+                        fields = []
+                        for i, source in enumerate(sources):
+                            if hasattr(source, "name") and source.name is not None:
+                                fields.append(DocumentedCSRField(CSRField(source.name, offset=i, description=source_description(source))))
+                            else:
+                                fields.append(DocumentedCSRField(CSRField("event{}".format(i), offset=i, description=source_description(source))))
+                        dcsr.fields = fields
+                    if dcsr.description is None:
+                        dcsr.description = "When an Event occurs, the corresponding bit will be set in this register.  To clear the Event, set the corresponding bit in this register."
+                elif short_name == m.enable.name.upper():
+                    if dcsr.fields is None or len(dcsr.fields) == 0:
+                        fields = []
+                        for i, source in enumerate(sources):
+                            if hasattr(source, "name") and source.name is not None:
+                                fields.append(DocumentedCSRField(CSRField(source.name, offset=i, description="Write a `1` to enable the `{}` Event".format(source.name))))
+                            else:
+                                fields.append(DocumentedCSRField(CSRField("event{}".format(i), offset=i, description="Write a `1` to enable the `{}` Event".format(i))))
+                        dcsr.fields = fields
+                    if dcsr.description is None:
+                        dcsr.description = "This register enables the corresponding Events.  Write a `0` to this register to disable individual events."
+
+    def sub_csr_bit_range(self, csr, offset):
+        nwords = (csr.size + self.busword - 1)//self.busword
+        i = nwords - offset - 1
+        nbits = min(csr.size - i*self.busword, self.busword) - 1
+        name = (csr.name + str(i) if nwords > 1 else csr.name).upper()
+        origin = i*self.busword
+        return (origin, nbits, name)
+
+    def split_fields(self, fields, start, end):
+        """Split `fields` into a sub-list that only contains the fields
+        between `start` and `end`.
+        This means that sometimes registers will get truncated.  For example,
+        if we're going from [8:15] and we have a register that spans [7:15],
+        the bottom bit will be cut off.  To account for this, we set the `.start`
+        property of the resulting split field to `1`, the `.offset` to `0`, and the
+        `.size` to 7.
+        """
+        split_f = []
+        for field in fields:
+            if field.offset > end:
+                continue
+            if field.offset + field.size < start:
+                continue
+            new_field = DocumentedCSRField(field)
+
+            new_field.offset -= start
+            if new_field.offset < 0:
+                underflow_amount = -new_field.offset
+                new_field.offset = 0
+                new_field.size  -= underflow_amount
+                new_field.start  = underflow_amount
+            # If it extends past the range, clamp the size to the range
+            if new_field.offset + new_field.size > (end - start):
+                new_field.size = (end - start) - new_field.offset + 1
+                if new_field.start is None:
+                    new_field.start = 0
+            split_f.append(new_field)
+        return split_f
+
+    def print_reg(self, reg, stream):
+        print("", file=stream)
+        print("    .. wavedrom::", file=stream)
+        print("        :caption: {}".format(reg.name), file=stream)
+        print("", file=stream)
+        print("        {", file=stream)
+        print("            \"reg\": [", file=stream)
+        if len(reg.fields) > 0:
+            bit_offset = 0
+            for field in reg.fields:
+                field_name = field.name
+                attr_str = ""
+                if field.reset_value != 0:
+                    attr_str = "\"attr\": '" + str(field.reset_value) + "', "
+                type_str = ""
+                if field.pulse:
+                    type_str = "\"type\": 4, "
+                if hasattr(field, "start") and field.start is not None:
+                    field_name = "{}{}".format(field.name, self.bit_range(field.start, field.size + field.start, empty_if_zero=True))
+                term=","
+                if bit_offset != field.offset:
+                    print("                {\"bits\": " + str(field.offset - bit_offset) + "},", file=stream)
+                if field.offset + field.size == self.busword:
+                    term=""
+                print("                {\"name\": \"" + field_name + "\",  " + type_str + attr_str + "\"bits\": " + str(field.size) + "}" + term, file=stream)
+                bit_offset = field.offset + field.size
+            if bit_offset != self.busword:
+                print("                {\"bits\": " + str(self.busword - bit_offset) + "}", file=stream)
+        else:
+            term=""
+            if reg.size != self.csr_data_width:
+                term=","
+            attr_str = ""
+            if reg.reset_value != 0:
+                attr_str = "\"attr\": 'reset: " + str(reg.reset_value) + "', "
+            print("                {\"name\": \"" + reg.short_name.lower() + self.bit_range(reg.offset, reg.offset + reg.size, empty_if_zero=True) + "\", " + attr_str + "\"bits\": " + str(reg.size) + "}" + term, file=stream)
+            if reg.size != self.csr_data_width:
+                print("                {\"bits\": " + str(self.csr_data_width - reg.size) + "},", file=stream)
+        print("            ], \"config\": {\"hspace\": 400, \"bits\": " + str(self.busword) + ", \"lanes\": 1 }, \"options\": {\"hspace\": 400, \"bits\": " + str(self.busword) + ", \"lanes\": 1}", file=stream)
+        print("        }", file=stream)
+        print("", file=stream)
+
+    def get_csr_reset(self, csr):
+        reset = 0
+        if hasattr(csr, "fields"):
+            for f in csr.fields.fields:
+                reset = reset | (f.reset_value << f.offset)
+        elif hasattr(csr, "storage"):
+            reset = int(csr.storage.reset.value)
+        elif hasattr(csr, "status"):
+            reset = int(csr.status.reset.value)
+        return reset
+
+    def get_csr_size(self, csr):
+        nbits = 0
+        if hasattr(csr, "fields"):
+            for f in csr.fields.fields:
+                nbits = max(nbits, f.size + f.offset)
+        elif hasattr(csr, "storage"):
+            nbits = int(csr.storage.nbits)
+        elif hasattr(csr, "status"):
+            nbits = int(csr.status.nbits)
+        elif hasattr(csr ,"r"):
+            nbits = int(csr.r.nbits)
+        elif hasattr(csr, "value"):
+            nbits = int(csr.value.nbits)
+        else:
+            raise ValueError("Internal error: can't determine CSR size of {}".format(csr))
+        return nbits
+
+    def document_csr(self, csr):
+        """Generates one or more DocumentedCSR, which will get appended
+        to self.csrs"""
+        fields = []
+        description = None
+        atomic_write = False
+        full_name = self.name.upper() + "_" + csr.name.upper()
+        reset = 0
+        if isinstance(csr, CSRStatus):
+            access = "read-only"
+        else:
+            access = "read-write"
+
+        if hasattr(csr, "fields"):
+            fields = csr.fields.fields
+        if hasattr(csr, "description"):
+            description = csr.description
+        if hasattr(csr, "atomic_write"):
+            atomic_write = csr.atomic_write
+        size = self.get_csr_size(csr)
+        reset = self.get_csr_reset(csr)
+
+        # If the CSR is composed of multiple sub-CSRs, document each
+        # one individually.
+        if isinstance(csr, _CompoundCSR) and len(csr.simple_csrs) > 1:
+            for i in range(len(csr.simple_csrs)):
+                (start, length, name) = self.sub_csr_bit_range(csr, i)
+                sub_name = self.name.upper() + "_" + name
+                bits_str = "Bits {}-{} of `{}`.".format(start, start+length, full_name)
+                if atomic_write:
+                    if i == (range(len(csr.simple_csrs))-1):
+                        bits_str += "Writing this register triggers an update of " + full_name
+                    else:
+                        bits_str += "The value won't take effect until `" + full_name + "0` is written."
+                if i == 0:
+                    d = description
+                    if description is None:
+                        d = bits_str
+                    else:
+                        d = bits_str + " " + reflow(d)
+                    self.csrs.append(DocumentedCSR(
+                        sub_name, self.current_address, short_numbered_name=name.upper(), short_name=csr.name.upper(), reset=(reset>>start)&((2**length)-1),
+                        offset=start, size=self.csr_data_width,
+                        description=d, fields=self.split_fields(fields, start, start + length), access=access
+                    ))
+                else:
+                    self.csrs.append(DocumentedCSR(
+                        sub_name, self.current_address, short_numbered_name=name.upper(), short_name=csr.name.upper(), reset=(reset>>start)&((2**length)-1),
+                        offset=start, size=self.csr_data_width,
+                        description=bits_str, fields=self.split_fields(fields, start, start + length), access=access
+                    ))
+                self.current_address += 4
+        else:
+            self.csrs.append(DocumentedCSR(
+                full_name, self.current_address, short_numbered_name=csr.name.upper(), short_name=csr.name.upper(), reset=reset, size=size,
+                description=description, fields=fields, access=access
+            ))
+            self.current_address += 4
+
+    def make_value_table(self, values):
+        ret = ""
+        max_value_width=len("Value")
+        max_description_width=len("Description")
+        for v in values:
+            (value, name, description) = (None, None, None)
+            if len(v) == 2:
+                (value, description) = v
+            elif len(v) == 3:
+                (value, name, description) = v
+            else:
+                raise ValueError("Unexpected length of CSRField's value tuple")
+
+            # Ensure the value is a string
+            if not isinstance(value, str):
+                value = "{}".format(value)
+
+            max_value_width = max(max_value_width, len(value))
+            for d in description.splitlines():
+                max_description_width = max(max_description_width, len(d))
+        ret += "\n"
+        ret += "+-" + "-"*max_value_width + "-+-" + "-"*max_description_width + "-+\n"
+        ret += "| " + "Value".ljust(max_value_width) + " | " + "Description".ljust(max_description_width) + " |\n"
+        ret += "+=" + "="*max_value_width + "=+=" +  "="*max_description_width + "=+\n"
+        for v in values:
+            (value, name, description) = (None, None, None)
+            if len(v) == 2:
+                (value, description) = v
+            elif len(v) == 3:
+                (value, name, description) = v
+            else:
+                raise ValueError("Unexpected length of CSRField's value tuple")
+
+            # Ensure the value is a string
+            if not isinstance(value, str):
+                value = "{}".format(value)
+
+            value = value.ljust(max_value_width)
+            first_line = True
+            for d in description.splitlines():
+                if first_line:
+                    ret += "| {} | {} |\n".format(value, d.ljust(max_description_width))
+                    first_line = False
+                else:
+                    ret += "| {} | {} |\n".format(" ".ljust(max_value_width), d.ljust(max_description_width))
+            ret += "+-" + "-"*max_value_width + "-+-" + "-"*max_description_width + "-+\n"
+        return ret
+
+    def print_region(self, stream, base_dir, note_pulses):
+        title = "{}".format(self.name.upper())
+        print(title, file=stream)
+        print("=" * len(title), file=stream)
+        print("", file=stream)
+
+        for section in self.sections:
+            title = textwrap.dedent(section.title())
+            body = textwrap.dedent(section.body())
+            print("{}".format(title), file=stream)
+            print("-" * len(title), file=stream)
+
+            if section.format() == "rst":
+                print(body, file=stream)
+            elif section.format() == "md":
+                filename = section.path()
+                if filename is not None:
+                    print(".. mdinclude:: " + filename, file=stream)
+                else:
+                    temp_filename = self.name + '-' + str(hash(title)) + "." + section.format()
+                    with open(base_dir + "/" + temp_filename, "w") as cache:
+                        print(body, file=cache)
+                    print(".. mdinclude:: " + temp_filename, file=stream)
+            print("", file=stream)
+
+        if len(self.csrs) > 0:
+            title = "Register Listing for {}".format(self.name.upper())
+            print(title, file=stream)
+            print("-" * len(title), file=stream)
+
+            csr_table = [["Register", "Address"]]
+            for csr in self.csrs:
+                csr_table.append([":ref:`{} <{}>`".format(csr.name, csr.name), ":ref:`0x{:08x} <{}>`".format(csr.address, csr.name)])
+            print_table(csr_table, stream)
+
+            for csr in self.csrs:
+                print("{}".format(csr.name), file=stream)
+                print("^" * len(csr.name), file=stream)
+                print("", file=stream)
+                print("`Address: 0x{:08x} + 0x{:x} = 0x{:08x}`".format(self.origin, csr.address - self.origin, csr.address), file=stream)
+                print("", file=stream)
+                if csr.description is not None:
+                    print(textwrap.indent(csr.description, prefix="    "), file=stream)
+                self.print_reg(csr, stream)
+                if len(csr.fields) > 0:
+                    max_field_width=len("Field")
+                    max_name_width=len("Name")
+                    max_description_width=len("Description")
+                    value_tables = {}
+
+                    for f in csr.fields:
+                        field = self.bit_range(f.offset, f.offset + f.size)
+                        max_field_width = max(max_field_width, len(field))
+
+                        name = f.name
+                        if hasattr(f, "start") and f.start is not None:
+                            name = "{}{}".format(f.name, self.bit_range(f.start, f.size + f.start))
+                        max_name_width = max(max_name_width, len(name))
+
+                        description = f.description
+                        if description is None:
+                            description = ""
+                        if note_pulses and f.pulse:
+                            description = description + "\n\nWriting a 1 to this bit triggers the function."
+                        for d in description.splitlines():
+                            max_description_width = max(max_description_width, len(d))
+                        if f.values is not None:
+                            value_tables[f.name] = self.make_value_table(f.values)
+                            for d in value_tables[f.name].splitlines():
+                                max_description_width = max(max_description_width, len(d))
+                    print("", file=stream)
+                    print("+-" + "-"*max_field_width + "-+-" + "-"*max_name_width + "-+-" + "-"*max_description_width + "-+", file=stream)
+                    print("| " + "Field".ljust(max_field_width) + " | " + "Name".ljust(max_name_width) + " | " + "Description".ljust(max_description_width) + " |", file=stream)
+                    print("+=" + "="*max_field_width + "=+=" + "="*max_name_width + "=+=" + "="*max_description_width + "=+", file=stream)
+                    for f in csr.fields:
+                        field = self.bit_range(f.offset, f.offset + f.size).ljust(max_field_width)
+
+                        name = f.name.upper()
+                        if hasattr(f, "start") and f.start is not None:
+                            name = "{}{}".format(f.name.upper(), self.bit_range(f.start, f.size + f.start))
+                        name = name.ljust(max_name_width)
+
+                        description = f.description
+                        if description is None:
+                            description = ""
+                        if note_pulses and f.pulse:
+                            description = description + "\n\nWriting a 1 to this bit triggers the function."
+
+                        if f.name in value_tables:
+                            description += "\n" + value_tables[f.name]
+
+                        first_line = True
+                        for d in description.splitlines():
+                            if first_line:
+                                print("| {} | {} | {} |".format(field, name, d.ljust(max_description_width)), file=stream)
+                                first_line = False
+                            else:
+                                print("| {} | {} | {} |".format(" ".ljust(max_field_width), " ".ljust(max_name_width), d.ljust(max_description_width)), file=stream)
+                        print("+-" + "-"*max_field_width + "-+-" + "-"*max_name_width + "-+-" + "-"*max_description_width + "-+", file=stream)
+                print("", file=stream)
diff --git a/litex/soc/doc/module.py b/litex/soc/doc/module.py
new file mode 100644 (file)
index 0000000..63380b3
--- /dev/null
@@ -0,0 +1,120 @@
+# This file is Copyright (c) 2020 Sean Cross <sean@xobs.io>\r
+# License: BSD\r
+\r
+from migen.fhdl.module import DUID\r
+from migen.util.misc import xdir\r
+\r
+from litex.soc.interconnect.csr_eventmanager import EventManager\r
+from litex.soc.integration.doc import ModuleDoc\r
+\r
+import textwrap\r
+\r
+from .rst import print_table, print_rst\r
+\r
+def gather_submodules_inner(module, depth, seen_modules, submodules):\r
+    if module is None:\r
+        return submodules\r
+    if depth == 0:\r
+        if isinstance(module, ModuleDoc):\r
+            # print("{} is an instance of ModuleDoc".format(module))\r
+            submodules["module_doc"].append(module)\r
+    for k,v in module._submodules:\r
+        # print("{}Submodule {} {}".format(" "*(depth*4), k, v))\r
+        if v not in seen_modules:\r
+            seen_modules.add(v)\r
+            if isinstance(v, EventManager):\r
+                # print("{}{} appears to be an EventManager".format(" "*(depth*4), k))\r
+                submodules["event_managers"].append(v)\r
+\r
+            if isinstance(v, ModuleDoc):\r
+                submodules["module_doc"].append(v)\r
+\r
+            gather_submodules_inner(v, depth + 1, seen_modules, submodules)\r
+    return submodules\r
+\r
+def gather_submodules(module):\r
+    depth = 0\r
+    seen_modules = set()\r
+    submodules = {\r
+        "event_managers": [],\r
+        "module_doc": [],\r
+    }\r
+\r
+    return gather_submodules_inner(module, depth, seen_modules, submodules)\r
+\r
+class ModuleNotDocumented(Exception):\r
+    """Indicates a Module has no documentation or sub-documentation"""\r
+    pass\r
+\r
+class DocumentedModule:\r
+    """Multi-section Documentation of a Module"""\r
+\r
+    def __init__(self, name, module, has_documentation=False):\r
+        self.name = name\r
+        self.sections = []\r
+\r
+        if isinstance(module, ModuleDoc):\r
+            has_documentation = True\r
+            self.sections.append(module)\r
+\r
+        if hasattr(module, "get_module_documentation"):\r
+            for doc in module.get_module_documentation():\r
+                has_documentation = True\r
+                self.sections.append(doc)\r
+\r
+        if not has_documentation:\r
+            raise ModuleNotDocumented()\r
+\r
+    def print_region(self, stream, base_dir, note_pulses=False):\r
+        title = "{}".format(self.name.upper())\r
+        print(title, file=stream)\r
+        print("=" * len(title), file=stream)\r
+        print("", file=stream)\r
+\r
+        for section in self.sections:\r
+            title = textwrap.dedent(section.title())\r
+            body = textwrap.dedent(section.body())\r
+            print("{}".format(title), file=stream)\r
+            print("-" * len(title), file=stream)\r
+            print(textwrap.dedent(body), file=stream)\r
+            print("", file=stream)\r
+\r
+class DocumentedInterrupts(DocumentedModule):\r
+    """A :obj:`DocumentedModule` that automatically documents interrupts in an SoC\r
+\r
+    This creates a :obj:`DocumentedModule` object that prints out the contents\r
+    of the interrupt map of an SoC.\r
+    """\r
+    def __init__(self, interrupts):\r
+        DocumentedModule.__init__(self, "interrupts", None, has_documentation=True)\r
+\r
+        self.irq_table = [["Interrupt", "Module"]]\r
+        for module_name, irq_no in interrupts.items():\r
+            self.irq_table.append([str(irq_no), ":doc:`{} <{}>`".format(module_name.upper(), module_name)])\r
+\r
+    def print_region(self, stream, base_dir, note_pulses=False):\r
+        title = "Interrupt Controller"\r
+        print(title, file=stream)\r
+        print("=" * len(title), file=stream)\r
+        print("", file=stream)\r
+\r
+        print_rst(stream,\r
+        """\r
+        This device has an ``EventManager``-based interrupt\r
+        system.  Individual modules generate `events` which are wired\r
+        into a central interrupt controller.\r
+\r
+        When an interrupt occurs, you should look the interrupt number up\r
+        in the CPU-specific interrupt table and then call the relevant\r
+        module.\r
+        """)\r
+\r
+        section_title = "Assigned Interrupts"\r
+        print("{}".format(section_title), file=stream)\r
+        print("-" * len(section_title), file=stream)\r
+        print("", file=stream)\r
+\r
+        print("The following interrupts are assigned on this system:", file=stream)\r
+        print_table(self.irq_table, stream)\r
+\r
+\r
diff --git a/litex/soc/doc/rst.py b/litex/soc/doc/rst.py
new file mode 100644 (file)
index 0000000..de83e07
--- /dev/null
@@ -0,0 +1,169 @@
+# This file is Copyright (c) 2020 Sean Cross <sean@xobs.io>
+# License: BSD
+
+import textwrap
+
+def make_table(t):
+    """Make a reStructured Text Table
+
+    Returns
+    -------
+
+    A string containing a reStructured Text table.
+    """
+    column_widths = []
+
+    table = "\n"
+    if len(t) <= 0:
+        return table
+
+    # Figure out how wide to make each column
+    for col in t[0]:
+        column_widths.append(0)
+
+    for row in t:
+        for i, column in enumerate(row):
+            column_widths[i] = max(column_widths[i], len(column))
+
+    # Print out header
+    header = t.pop(0)
+    table += "+"
+    for i, column in enumerate(header):
+        table += "-" + "-"*column_widths[i]
+        table += "-+"
+    table += "\n"
+
+    table += "|"
+    for i, column in enumerate(header):
+        table += " " + column.ljust(column_widths[i]) + " |"
+    table += "\n"
+
+    table += "+"
+    for i, column in enumerate(header):
+        table += "=" + "="*column_widths[i]
+        table += "=+"
+    table += "\n"
+
+    for row in t:
+        table += "|"
+        for i, column in enumerate(row):
+            table += " " + column.ljust(column_widths[i]) + " |"
+        table += "\n"
+
+        table += "+"
+        for i, column in enumerate(row):
+            table += "-" + "-"*column_widths[i]
+            table += "-+"
+        table += "\n"
+    table += "\n"
+
+    return table
+
+def print_table(table, stream):
+    """Print a reStructured Text table
+
+    Arguments
+    ---------
+
+    table (:obj:`list` of :obj:`list`s): A list of rows in the table.
+    Each row has several columns.  The first row is the table header.
+
+    stream (:obj:`io`): Destination output file.
+    """
+    column_widths = []
+
+    print("", file=stream)
+    if len(table) <= 0:
+        return
+
+    # Figure out how wide to make each column
+    for col in table[0]:
+        column_widths.append(0)
+
+    for row in table:
+        for i, column in enumerate(row):
+            column_widths[i] = max(column_widths[i], len(column))
+
+    # Print out header
+    header = table.pop(0)
+    print("+", file=stream, end="")
+    for i, column in enumerate(header):
+        print("-" + "-"*column_widths[i], file=stream, end="")
+        print("-+", file=stream, end="")
+    print("", file=stream)
+
+    print("|", file=stream, end="")
+    for i, column in enumerate(header):
+        print(" " + column.ljust(column_widths[i]) + " |", file=stream, end="")
+    print("", file=stream)
+
+    print("+", file=stream, end="")
+    for i, column in enumerate(header):
+        print("=" + "="*column_widths[i], file=stream, end="")
+        print("=+", file=stream, end="")
+    print("", file=stream)
+
+    for row in table:
+        print("|", file=stream, end="")
+        for i, column in enumerate(row):
+            print(" " + column.ljust(column_widths[i]) + " |", file=stream, end="")
+        print("", file=stream)
+
+        print("+", file=stream, end="")
+        for i, column in enumerate(row):
+            print("-" + "-"*column_widths[i], file=stream, end="")
+            print("-+", file=stream, end="")
+        print("", file=stream)
+    print("", file=stream)
+
+def pad_first_line_if_necessary(s):
+    if not isinstance(s, str):
+        return s
+    lines = s.split("\n")
+
+    # If there aren't at least two lines, don't do anything
+    if len(lines) < 2:
+        return s
+
+    # If the first line is blank, don't do anything
+    if lines[0].strip() == "":
+        return s
+
+    # If the pading on line 1 is greater than line 2, pad line 1
+    # and return the result
+    line_0_padding = len(lines[0]) - len(lines[0].lstrip(' '))
+    line_1_padding = len(lines[1]) - len(lines[1].lstrip(' '))
+    if (line_1_padding > 0) and (line_1_padding > line_0_padding):
+        lines[0] = " " * (line_1_padding - line_0_padding) + lines[0]
+        return "\n".join(lines)
+    return s
+
+def reflow(s, width=80):
+    """Reflow the jagged text that gets generated as part
+    of this Python comment.
+
+    In this comment, the first line would be indented relative
+    to the rest.  Additionally, the width of this block would
+    be limited to the original text width.
+
+    To reflow text, break it along \n\n, then dedent and reflow
+    each line individually.
+
+    Finally, append it to a new string to be returned.
+    """
+    if not isinstance(s, str):
+        return s
+    out = []
+    s = pad_first_line_if_necessary(s)
+    for piece in textwrap.dedent(s).split("\n\n"):
+        trimmed_piece = textwrap.fill(textwrap.dedent(piece).strip(), width=width)
+        out.append(trimmed_piece)
+    return "\n\n".join(out)
+
+def _reflow(s, width=80):
+    return reflow(s, width)
+
+def print_rst(stream, s, reflow=True):
+    """Print a given string to the given stream.  Ensure it is reflowed."""
+    print(_reflow(s), file=stream)
+    print("", file=stream)
diff --git a/litex/soc/doc/static/WaveDrom.js b/litex/soc/doc/static/WaveDrom.js
new file mode 100644 (file)
index 0000000..0ef15e9
--- /dev/null
@@ -0,0 +1,3 @@
+/*! wavedrom 2019-05-21 */
+
+!function o(s,i,c){function l(t,e){if(!i[t]){if(!s[t]){var r="function"==typeof require&&require;if(!e&&r)return r(t,!0);if(u)return u(t,!0);var n=new Error("Cannot find module '"+t+"'");throw n.code="MODULE_NOT_FOUND",n}var a=i[t]={exports:{}};s[t][0].call(a.exports,function(e){return l(s[t][1][e]||e)},a,a.exports,o,s,i,c)}return i[t].exports}for(var u="function"==typeof require&&require,e=0;e<c.length;e++)l(c[e]);return l}({1:[function(e,t,r){"use strict";t.exports=function(i,c){var l,u;function f(e){var t=parseInt(u.style.left,10),r=parseInt(u.style.top,10);(e.x<t||e.x>t+u.offsetWidth||e.y<r||e.y>r+u.offsetHeight)&&(u.parentNode.removeChild(u),document.body.removeEventListener("mousedown",f,!1))}(l=document.getElementById(c+i)).childNodes[0].addEventListener("contextmenu",function(e){var t,r,n;(u=document.createElement("div")).className="wavedromMenu",u.style.top=e.y+"px",u.style.left=e.x+"px",t=document.createElement("ul"),(r=document.createElement("li")).innerHTML="Save as PNG",t.appendChild(r),(n=document.createElement("li")).innerHTML="Save as SVG",t.appendChild(n),u.appendChild(t),document.body.appendChild(u),r.addEventListener("click",function(){var e,t,r,n,a,o,s;e="",0!==i&&(e+=(t=document.getElementById(c+0)).innerHTML.substring(166,t.innerHTML.indexOf('<g id="waves_0">'))),e=[l.innerHTML.slice(0,166),e,l.innerHTML.slice(166)].join(""),r="data:image/svg+xml;base64,"+btoa(e),(n=new Image).src=r,(a=document.createElement("canvas")).width=n.width,a.height=n.height,a.getContext("2d").drawImage(n,0,0),o=a.toDataURL("image/png"),(s=document.createElement("a")).href=o,s.download="wavedrom.png",s.click(),u.parentNode.removeChild(u),document.body.removeEventListener("mousedown",f,!1)},!1),n.addEventListener("click",function(){var e,t,r,n;e="",0!==i&&(e+=(t=document.getElementById(c+0)).innerHTML.substring(166,t.innerHTML.indexOf('<g id="waves_0">'))),e=[l.innerHTML.slice(0,166),e,l.innerHTML.slice(166)].join(""),r="data:image/svg+xml;base64,"+btoa(e),(n=document.createElement("a")).href=r,n.download="wavedrom.svg",n.click(),u.parentNode.removeChild(u),document.body.removeEventListener("mousedown",f,!1)},!1),u.addEventListener("contextmenu",function(e){e.preventDefault()},!1),document.body.addEventListener("mousedown",f,!1),e.preventDefault()},!1)}},{}],2:[function(e,t,r){"use strict";t.exports=function(e,t,r){var n,a,o=r.x-t.x,s=r.y-t.y,i=(t.x+r.x)/2,c=(t.y+r.y)/2;switch(e.shape){case"-":break;case"~":n="M "+t.x+","+t.y+" c "+.7*o+", 0 "+.3*o+", "+s+" "+o+", "+s;break;case"-~":n="M "+t.x+","+t.y+" c "+.7*o+", 0 "+o+", "+s+" "+o+", "+s,e.label&&(i=t.x+.75*(r.x-t.x));break;case"~-":n="M "+t.x+","+t.y+" c 0, 0 "+.3*o+", "+s+" "+o+", "+s,e.label&&(i=t.x+.25*(r.x-t.x));break;case"-|":n="m "+t.x+","+t.y+" "+o+",0 0,"+s,e.label&&(i=r.x);break;case"|-":n="m "+t.x+","+t.y+" 0,"+s+" "+o+",0",e.label&&(i=t.x);break;case"-|-":n="m "+t.x+","+t.y+" "+o/2+",0 0,"+s+" "+o/2+",0";break;case"->":a="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none";break;case"~>":a="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none",n="M "+t.x+","+t.y+" c "+.7*o+", 0 "+.3*o+", "+s+" "+o+", "+s;break;case"-~>":a="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none",n="M "+t.x+","+t.y+" c "+.7*o+", 0 "+o+", "+s+" "+o+", "+s,e.label&&(i=t.x+.75*(r.x-t.x));break;case"~->":a="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none",n="M "+t.x+","+t.y+" c 0, 0 "+.3*o+", "+s+" "+o+", "+s,e.label&&(i=t.x+.25*(r.x-t.x));break;case"-|>":a="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none",n="m "+t.x+","+t.y+" "+o+",0 0,"+s,e.label&&(i=r.x);break;case"|->":a="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none",n="m "+t.x+","+t.y+" 0,"+s+" "+o+",0",e.label&&(i=t.x);break;case"-|->":a="marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none",n="m "+t.x+","+t.y+" "+o/2+",0 0,"+s+" "+o/2+",0";break;case"<->":a="marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none";break;case"<~>":a="marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none",n="M "+t.x+","+t.y+" c "+.7*o+", 0 "+.3*o+", "+s+" "+o+", "+s;break;case"<-~>":a="marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none",n="M "+t.x+","+t.y+" c "+.7*o+", 0 "+o+", "+s+" "+o+", "+s,e.label&&(i=t.x+.75*(r.x-t.x));break;case"<-|>":a="marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none",n="m "+t.x+","+t.y+" "+o+",0 0,"+s,e.label&&(i=r.x);break;case"<-|->":a="marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none",n="m "+t.x+","+t.y+" "+o/2+",0 0,"+s+" "+o/2+",0";break;default:a="fill:none;stroke:#F00;stroke-width:1"}return{lx:i,ly:c,d:n,style:a}}},{}],3:[function(e,t,r){t.exports={chars:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,47,74,74,118,89,25,44,44,52,78,37,44,37,37,74,74,74,74,74,74,74,74,74,74,37,37,78,78,78,74,135,89,89,96,96,89,81,103,96,37,67,89,74,109,96,103,89,103,96,89,81,96,89,127,89,87,81,37,37,37,61,74,44,74,74,67,74,74,37,74,74,30,30,67,30,112,74,74,74,74,44,67,37,74,67,95,66,65,67,44,34,44,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,43,74,74,74,74,34,74,44,98,49,74,78,0,98,73,53,73,44,44,44,77,71,37,44,44,49,74,111,111,111,81,89,89,89,89,89,89,133,96,89,89,89,89,37,37,37,37,96,96,103,103,103,103,103,78,103,96,96,96,96,87,89,81,74,74,74,74,74,74,118,67,74,74,74,74,36,36,36,36,74,74,74,74,74,74,74,73,81,74,74,74,74,65,74,65,89,74,89,74,89,74,96,67,96,67,96,67,96,67,96,82,96,74,89,74,89,74,89,74,89,74,89,74,103,74,103,74,103,74,103,74,96,74,96,74,37,36,37,36,37,36,37,30,37,36,98,59,67,30,89,67,67,74,30,74,30,74,39,74,44,74,30,96,74,96,74,96,74,80,96,74,103,74,103,74,103,74,133,126,96,44,96,44,96,44,89,67,89,67,89,67,89,67,81,38,81,50,81,37,96,74,96,74,96,74,96,74,96,74,96,74,127,95,87,65,87,81,67,81,67,81,67,30,84,97,91,84,91,84,94,92,73,104,109,91,84,81,84,100,82,76,74,103,91,131,47,40,99,77,37,79,130,100,84,104,114,87,126,101,87,84,93,84,69,84,46,52,82,52,82,114,89,102,96,100,98,91,70,88,88,77,70,85,89,77,67,84,39,65,61,39,189,173,153,111,105,61,123,123,106,89,74,37,30,103,74,96,74,96,74,96,74,96,74,96,74,81,91,81,91,81,130,131,102,84,103,84,87,78,104,81,104,81,88,76,37,189,173,153,103,84,148,90,100,84,89,74,133,118,103,81],other:114}},{}],4:[function(e,t,r){"use strict";var n=e("onml/lib/stringify.js"),a=e("./w3.js");t.exports=function(e){var t=document.createElementNS(a.svg,"g");return t.innerHTML=n(e),t.childNodes[0]}},{"./w3.js":33,"onml/lib/stringify.js":36}],5:[function(e,t,r){"use strict";var n=e("./eva"),a=e("./render-wave-form");t.exports=function(){a(0,n("InputJSON_0"),"WaveDrom_Display_")}},{"./eva":6,"./render-wave-form":30}],6:[function(require,module,exports){"use strict";function eva(id){var TheTextBox,source;function erra(e){return{signal:[{name:["tspan",["tspan",{class:"error h5"},"Error: "],e.message]}]}}if(TheTextBox=document.getElementById(id),TheTextBox.type&&"textarea"===TheTextBox.type)try{source=eval("("+TheTextBox.value+")")}catch(e){return erra(e)}else try{source=eval("("+TheTextBox.innerHTML+")")}catch(e){return erra(e)}if("[object Object]"!==Object.prototype.toString.call(source))return erra({message:'[Semantic]: The root has to be an Object: "{signal:[...]}"'});if(source.signal){if("[object Array]"!==Object.prototype.toString.call(source.signal))return erra({message:'[Semantic]: "signal" object has to be an Array "signal:[]"'})}else if(source.assign){if("[object Array]"!==Object.prototype.toString.call(source.assign))return erra({message:'[Semantic]: "assign" object hasto be an Array "assign:[]"'})}else if(!source.reg)return erra({message:'[Semantic]: "signal:[...]" or "assign:[...]" property is missing inside the root Object'});return source}module.exports=eva},{}],7:[function(e,t,r){"use strict";t.exports=function(e){var t=0,r=0,n=[];return e.forEach(function(e){"vvv-2"===e||"vvv-3"===e||"vvv-4"===e||"vvv-5"===e?r+=1:0!==r&&(n.push(t-(r+1)/2),r=0),t+=1}),0!==r&&n.push(t-(r+1)/2),n}},{}],8:[function(e,t,r){"use strict";t.exports=function(e,t,r){var n,a,o=[];if(4===e.length){for(a=0;a<r;a+=1){for(o.push(e[0]),n=0;n<t;n+=1)o.push(e[1]);for(o.push(e[2]),n=0;n<t;n+=1)o.push(e[3])}return o}for(1===e.length&&e.push(e[0]),o.push(e[0]),n=0;n<r*(2*(t+1))-1;n+=1)o.push(e[1]);return o}},{}],9:[function(e,t,r){"use strict";var a=e("./gen-brick");t.exports=function(e,t,r){var n;switch(n=[],e){case"p":n=a(["pclk","111","nclk","000"],t,r);break;case"n":n=a(["nclk","000","pclk","111"],t,r);break;case"P":n=a(["Pclk","111","nclk","000"],t,r);break;case"N":n=a(["Nclk","000","pclk","111"],t,r);break;case"l":case"L":case"0":n=a(["000"],t,r);break;case"h":case"H":case"1":n=a(["111"],t,r);break;case"=":case"2":n=a(["vvv-2"],t,r);break;case"3":n=a(["vvv-3"],t,r);break;case"4":n=a(["vvv-4"],t,r);break;case"5":n=a(["vvv-5"],t,r);break;case"d":n=a(["ddd"],t,r);break;case"u":n=a(["uuu"],t,r);break;case"z":n=a(["zzz"],t,r);break;default:n=a(["xxx"],t,r)}return n}},{"./gen-brick":8}],10:[function(e,t,r){"use strict";var v=e("./gen-brick");t.exports=function(e,t,r){var n,a,o,s,i,c,l,u,f,d,h,p,m,x;return n={p:"pclk",n:"nclk",P:"Pclk",N:"Nclk",h:"pclk",l:"nclk",H:"Pclk",L:"Nclk"},a={0:"0",1:"1",x:"x",d:"d",u:"u",z:"z","=":"v",2:"v",3:"v",4:"v",5:"v"},o={0:"",1:"",x:"",d:"",u:"",z:"","=":"-2",2:"-2",3:"-3",4:"-4",5:"-5"},s={p:"0",n:"1",P:"0",N:"1",h:"1",l:"0",H:"1",L:"0",0:"0",1:"1",x:"x",d:"d",u:"u",z:"z","=":"v",2:"v",3:"v",4:"v",5:"v"},i={p:"",n:"",P:"",N:"",h:"",l:"",H:"",L:"",0:"",1:"",x:"",d:"",u:"",z:"","=":"-2",2:"-2",3:"-3",4:"-4",5:"-5"},c={p:"nclk",n:"pclk",P:"nclk",N:"pclk"},l={p:"000",n:"111",P:"000",N:"111"},u={hp:"111",Hp:"111",ln:"000",Ln:"000",nh:"111",Nh:"111",pl:"000",Pl:"000"},d={p:"111",n:"000",P:"111",N:"000",h:"111",l:"000",H:"111",L:"000",0:"000",1:"111",x:"xxx",d:"ddd",u:"uuu",z:"zzz","=":"vvv-2",2:"vvv-2",3:"vvv-3",4:"vvv-4",5:"vvv-5"}[(f=e.split(""))[1]],void 0===(h=n[f[1]])?void 0===(p=a[f[1]])?v(["xxx"],t,r):(m=s[f[0]],v(void 0===m?["xxx"]:[m+"m"+p+i[f[0]]+o[f[1]],d],t,r)):(void 0!==(x=u[e])&&(h=x),p=c[f[1]],v(void 0===p?[h,d]:[h,d,p,l[f[1]]],t,r))}},{"./gen-brick":8}],11:[function(e,t,r){"use strict";t.exports=function(){return["style",".pinname {font-size:12px; font-style:normal; font-variant:normal; font-weight:500; font-stretch:normal; text-align:center; text-anchor:end; font-family:Helvetica} .wirename {font-size:12px; font-style:normal; font-variant:normal; font-weight:500; font-stretch:normal; text-align:center; text-anchor:start; font-family:Helvetica} .wirename:hover {fill:blue} .gate {color:#000; fill:#ffc; fill-opacity: 1;stroke:#000; stroke-width:1; stroke-opacity:1} .gate:hover {fill:red !important; } .wire {fill:none; stroke:#000; stroke-width:1; stroke-opacity:1} .grid {fill:#fff; fill-opacity:1; stroke:none}"]}},{}],12:[function(e,t,r){"use strict";var p=e("./w3");t.exports=function(e,t,r,n,a,o,s){var i,c,l;for(i in n)break;c=n.default||n[i],t&&t.config&&t.config.skin&&n[t.config.skin]&&(c=n[t.config.skin]),l=0===e?c:["svg",{id:"svg",xmlns:p.svg,"xmlns:xlink":p.xlink},["g"]];var u=r.xg+r.xs*(r.xmax+1),f=a.length*r.yo+r.yh0+r.yh1+r.yf0+r.yf1,d=l[l.length-1];d[1]={id:"waves_"+e},d[2]=["g",{id:"lanes_"+e,transform:"translate("+(r.xg+.5)+", "+(r.yh0+r.yh1+.5)+")"}].concat(o),d[3]=["g",{id:"groups_"+e}].concat(s);var h=l[1];return h.id="svgcontent_"+e,h.height=f,h.width=u,h.viewBox="0 0 "+u+" "+f,h.overflow="hidden",l}},{"./w3":33}],13:[function(e,t,r){"use strict";t.exports={xs:20,ys:20,xg:120,yh0:0,yh1:0,yf0:0,yf1:0,y0:5,yo:30,tgo:-10,ym:15,xlabel:6,xmax:1,scale:1,head:{},foot:{}}},{}],14:[function(e,t,r){"use strict";t.exports=function(e,t){var r,n;t.hscale=1,t.hscale0&&(t.hscale=t.hscale0),e&&e.config&&e.config.hscale&&0<(r=Math.round(0<(n=e.config.hscale)?Math.round(n):1))&&(100<r&&(r=100),t.hscale=r),t.yh0=0,t.yh1=0,t.head=e.head,t.xmin_cfg=0,t.xmax_cfg=1e12,e&&e.config&&e.config.hbounds&&2==e.config.hbounds.length&&(e.config.hbounds[0]=Math.floor(e.config.hbounds[0]),e.config.hbounds[1]=Math.ceil(e.config.hbounds[1]),e.config.hbounds[0]<e.config.hbounds[1]&&(t.xmin_cfg=2*Math.floor(e.config.hbounds[0]),t.xmax_cfg=2*Math.floor(e.config.hbounds[1]))),e&&e.head&&((e.head.tick||0===e.head.tick||e.head.tock||0===e.head.tock)&&(t.yh0=20),(e.head.tick||0===e.head.tick)&&(e.head.tick=e.head.tick+t.xmin_cfg/2),(e.head.tock||0===e.head.tock)&&(e.head.tock=e.head.tock+t.xmin_cfg/2),e.head.text&&(t.yh1=46,t.head.text=e.head.text)),t.yf0=0,t.yf1=0,t.foot=e.foot,e&&e.foot&&((e.foot.tick||0===e.foot.tick||e.foot.tock||0===e.foot.tock)&&(t.yf0=20),(e.foot.tick||0===e.foot.tick)&&(e.foot.tick=e.foot.tick+t.xmin_cfg/2),(e.foot.tock||0===e.foot.tock)&&(e.foot.tock=e.foot.tock+t.xmin_cfg/2),e.foot.text&&(t.yf1=46,t.foot.text=e.foot.text))}},{}],15:[function(e,t,r){"use strict";var d=e("./gen-first-wave-brick"),h=e("./gen-wave-brick"),p=e("./find-lane-markers");t.exports=function(e,t,r){var n,a,o,s,i,c,l=[],u=[],f=[];for(o=(l=e.split("")).shift(),i=!1,n=1;"."===l[0]||"|"===l[0];)l.shift(),n+=1;for(u=u.concat(d(o,t,n));l.length;){for(a=o,"<"===(o=l.shift())&&(i=!0,o=l.shift()),">"===o&&(i=!1,o=l.shift()),n=1;"."===l[0]||"|"===l[0];)l.shift(),n+=1;u=i?u.concat(h(a+o,0,n-r.period)):u.concat(h(a+o,t,n))}for(s=0;s<r.phase;s+=1)f.push(u.shift());return 0<f.length?(c=p(f).length,1==p([f[f.length-1]]).length&&1==p([u[0]]).length&&(c-=1)):c=0,[u,c]}},{"./find-lane-markers":7,"./gen-first-wave-brick":9,"./gen-wave-brick":10}],16:[function(e,t,r){"use strict";var f=e("./parse-wave-lane");t.exports=function(e,t){var r,n,a,o,s,i,c,l=[],u=[];for(r in e)n=e[r],t.period=n.period?n.period:1,t.phase=(n.phase?2*n.phase:0)+t.xmin_cfg,l.push([]),u[0]=n.name||" ",u[1]=(n.phase||0)+t.xmin_cfg/2,n.wave?(a=(o=f(n.wave,t.period*t.hscale-1,t))[0],s=o[1]):a=null,l[l.length-1][0]=u.slice(0),l[l.length-1][1]=a,l[l.length-1][2]=(i=s,(c=void 0)===(c=n.data)?null:("string"==typeof c&&(c=c.split(" ")),c=c.slice(i)));return l}},{"./parse-wave-lane":15}],17:[function(e,t,r){"use strict";var a=e("./eva"),o=e("./append-save-as-dialog"),s=e("./render-wave-form");t.exports=function(){var e,t,r,n;for(r=0,e=document.querySelectorAll("*"),t=0;t<e.length;t++)e.item(t).type&&"WaveDrom"===e.item(t).type&&(e.item(t).setAttribute("id","InputJSON_"+r),(n=document.createElement("div")).id="WaveDrom_Display_"+r,e.item(t).parentNode.insertBefore(n,e.item(t)),r+=1);for(t=0;t<r;t+=1)s(t,a("InputJSON_"+t),"WaveDrom_Display_"),o(t,"WaveDrom_Display_");document.head.innerHTML+='<style type="text/css">div.wavedromMenu{position:fixed;border:solid 1pt#CCCCCC;background-color:white;box-shadow:0px 10px 20px #808080;cursor:default;margin:0px;padding:0px;}div.wavedromMenu>ul{margin:0px;padding:0px;}div.wavedromMenu>ul>li{padding:2px 10px;list-style:none;}div.wavedromMenu>ul>li:hover{background-color:#b5d5ff;}</style>'}},{"./append-save-as-dialog":1,"./eva":6,"./render-wave-form":30}],18:[function(e,t,r){"use strict";t.exports=function e(t,r){var n,a,o={},s={x:10};for("string"!=typeof t[0]&&"number"!=typeof t[0]||(a=t[0],s.x=25),r.x+=s.x,n=0;n<t.length;n++)"object"==typeof t[n]&&("[object Array]"===Object.prototype.toString.call(t[n])?(o.y=r.y,(r=e(t[n],r)).groups.push({x:r.xx,y:o.y,height:r.y-o.y,name:r.name})):(r.lanes.push(t[n]),r.width.push(r.x),r.y+=1));return r.xx=r.x,r.x-=s.x,r.name=a,r}},{}],19:[function(e,t,r){"use strict";var a=e("./render-assign.js"),o=e("./render-reg.js"),s=e("./render-signal.js");t.exports=function(e,t,r){var n=t.signal?s(e,t,r):t.assign?a(e,t):t.reg?o(e,t):["div",{}];return n[1].class="WaveDrom",n}},{"./render-assign.js":21,"./render-reg.js":27,"./render-signal.js":28}],20:[function(e,t,r){"use strict";var w=e("./arc-shape.js"),j=e("./render-label.js");t.exports=function(e,t,r,n){var a,o,s,i,c,l,u,f,d,h,p,m,x=[],v={words:[],from:0,shape:"",to:0,label:""},g={},y=["g",{id:"wavearcs_"+t}];if(e){for(a in e)if(n.period=e[a].period?e[a].period:1,n.phase=(e[a].phase?2*e[a].phase:0)+n.xmin_cfg,s=e[a].node)for(x=s.split(""),i=0;x.length;)"."!==(c=x.shift())&&(g[c]={x:n.xs*(2*i*n.period*n.hscale-n.phase)+n.xlabel,y:a*n.yo+n.y0+.5*n.ys}),i+=1;if(r.edge)for(a in r.edge)if(v.words=r.edge[a].split(" "),v.label=r.edge[a].substring(v.words[0].length),v.label=v.label.substring(1),v.from=v.words[0].substr(0,1),v.to=v.words[0].substr(-1,1),v.shape=v.words[0].slice(1,-1),u=g[v.from],f=g[v.to],u&&f){var b=(l=w(v,u,f)).lx,k=l.ly;y=y.concat([(d=v,h=u,p=f,m=l,["path",{id:"gmark_"+d.from+"_"+d.to,d:m.d||"M "+h.x+","+h.y+" "+p.x+","+p.y,style:m.style||"fill:none;stroke:#00F;stroke-width:1"}])]),v.label&&(y=y.concat([j({x:b,y:k},v.label)]))}for(o in g)o===o.toLowerCase()&&0<g[o].x&&(y=y.concat([j({x:g[o].x,y:g[o].y},o+"")]))}return y}},{"./arc-shape.js":2,"./render-label.js":24}],21:[function(e,t,r){"use strict";var h=e("./insert-svg-template-assign");function p(e,t){var r,n,a;for(t.xmax=Math.max(t.xmax,t.x),r=t.y,a=e.length,n=1;n<a;n++)"[object Array]"===Object.prototype.toString.call(e[n])?t=p(e[n],{x:t.x+1,y:t.y,xmax:t.xmax}):(e[n]={name:e[n],x:t.x+1,y:t.y},t.y+=2);return e[0]={name:e[0],x:t.x,y:Math.round((r+(t.y-2))/2)},t.x--,t}function m(e,t){var r,n,a,o,s,i=["g"],c=[];if("[object Array]"===Object.prototype.toString.call(e)){for(n=e.length,c.push(e[0].name),c.push([32*(t-e[0].x),8*e[0].y]),r=1;r<n;r++)"[object Array]"===Object.prototype.toString.call(e[r])?c.push([32*(t-e[r][0].x),8*e[r][0].y]):c.push([32*(t-e[r].x),8*e[r].y]);for(i.push(function(e){var t,r,n,a=["g"],o=[],s=e.length;for(t=2;t<s;t++)o.push(e[t][1]);for(r=Math.min.apply(null,o),n=Math.max.apply(null,o),a.push(["g",{transform:"translate(16,0)"},["path",{d:"M  "+e[2][0]+","+r+" "+e[2][0]+","+n,class:"wire"}]]),t=2;t<s;t++)a.push(["g",["path",{d:"m  "+e[t][0]+","+e[t][1]+" 16,0",class:"wire"}]]);return a.push(["g",{transform:"translate("+e[1][0]+","+e[1][1]+")"},["title",e[0]],function(e,t,r){var n,a,o=" M 4,0 C 4,1.1 3.1,2 2,2 0.9,2 0,1.1 0,0 c 0,-1.1 0.9,-2 2,-2 1.1,0 2,0.9 2,2 z";return r===t&&(t=-(r=4)),a={BUF:1,INV:1,AND:"&",NAND:"&",OR:"≥1",NOR:"≥1",XOR:"=1",XNOR:"=1",box:""}[e],(n={"~":"M -11,-6 -11,6 0,0 z m -5,6 5,0"+o,"=":"M -11,-6 -11,6 0,0 z m -5,6 5,0","&":"m -16,-10 5,0 c 6,0 11,4 11,10 0,6 -5,10 -11,10 l -5,0 z","~&":"m -16,-10 5,0 c 6,0 11,4 11,10 0,6 -5,10 -11,10 l -5,0 z"+o,"|":"m -18,-10 4,0 c 6,0 12,5 14,10 -2,5 -8,10 -14,10 l -4,0 c 2.5,-5 2.5,-15 0,-20 z","~|":"m -18,-10 4,0 c 6,0 12,5 14,10 -2,5 -8,10 -14,10 l -4,0 c 2.5,-5 2.5,-15 0,-20 z"+o,"^":"m -21,-10 c 1,3 2,6 2,10 m 0,0 c 0,4 -1,7 -2,10 m 3,-20 4,0 c 6,0 12,5 14,10 -2,5 -8,10 -14,10 l -4,0 c 1,-3 2,-6 2,-10 0,-4 -1,-7 -2,-10 z","~^":"m -21,-10 c 1,3 2,6 2,10 m 0,0 c 0,4 -1,7 -2,10 m 3,-20 4,0 c 6,0 12,5 14,10 -2,5 -8,10 -14,10 l -4,0 c 1,-3 2,-6 2,-10 0,-4 -1,-7 -2,-10 z"+o,"+":"m -8,5 0,-10 m -5,5 10,0 m 3,0 c 0,4.418278 -3.581722,8 -8,8 -4.418278,0 -8,-3.581722 -8,-8 0,-4.418278 3.581722,-8 8,-8 4.418278,0 8,3.581722 8,8 z","*":"m -4,4 -8,-8 m 0,8 8,-8 m 4,4 c 0,4.418278 -3.581722,8 -8,8 -4.418278,0 -8,-3.581722 -8,-8 0,-4.418278 3.581722,-8 8,-8 4.418278,0 8,3.581722 8,8 z"}[e])?["path",{class:"gate",d:n}]:a?["g",["path",{class:"gate",d:"m -16,"+(t-3)+" 16,0 0,"+(r-t+6)+" -16,0 z"+({INV:1,NAND:1,NOR:1,XNOR:1}[e]?o:"")}],["text",["tspan",{x:"-14",y:"4",class:"wirename"},a+""]]]:["text",["tspan",{x:"-14",y:"4",class:"wirename"},e+""]]}(e[0],r-e[1][1],n-e[1][1])]),a}(c)),r=1;r<n;r++)i.push(m(e[r],t))}else s=e.name,a=32*(t-e.x),o=8*e.y,i.push(["g",{transform:"translate("+a+","+o+")"},["title",s],["path",{d:"M 2,0 a 2,2 0 1 1 -4,0 2,2 0 1 1 4,0 z"}],["text",["tspan",{x:"-4",y:"4",class:"pinname"},s]]]);return i}t.exports=function(e,t){var r,n,a,o,s,i,c,l,u,f=["g"],d=["g"];for(c=t.assign.length,n={x:0,y:2,xmax:0},r=t.assign,i=0;i<c;i++)(n=p(r[i],n)).x++;for(a=n.xmax+3,i=0;i<c;i++)f.push(m(r[i],a));for(o=32*(a+1)+1,s=8*(n.y+1)-7,c=4*(a+1),u=n.y+1,i=0;i<=c;i++)for(l=0;l<=u;l++)d.push(["rect",{height:1,width:1,x:8*i-.5,y:8*l-.5,class:"grid"}]);return["svg",{id:"svgcontent_"+e,viewBox:"0 0 "+o+" "+s,width:o,height:s},h(),["g",{transform:"translate(0.5, 0.5)"},d,f]]}},{"./insert-svg-template-assign":11}],22:[function(e,t,r){"use strict";function s(e,t){for(var r,n=[],a=(e||"").split(""),o=0,s=!1;a.length;)"<"===(r=a.shift())&&(s=!0,r=a.shift()),">"===r&&(s=!1,r=a.shift()),o+=s?1:2*t.period,"|"===r&&n.push(["use",{"xlink:href":"#gap",transform:"translate("+t.xs*((o-(s?0:t.period))*t.hscale-t.phase)+")"}]);return n}t.exports=function(e,t,r){var n,a,o=[];if(e)for(n in e)r.period=e[n].period?e[n].period:1,r.phase=(e[n].phase?2*e[n].phase:0)+r.xmin_cfg,a=s(e[n].wave,r),o=o.concat([["g",{id:"wavegap_"+n+"_"+t,transform:"translate(0,"+(r.y0+n*r.yo)+")"}].concat(a)]);return["g",{id:"wavegaps_"+t}].concat(o)}},{}],23:[function(e,t,r){"use strict";var c=e("tspan");t.exports=function(e,r,n){var a,o,s,i=["g"];return e.forEach(function(e,t){i.push(["path",{id:"group_"+t+"_"+r,d:"m "+(e.x+.5)+","+(e.y*n.yo+3.5+n.yh0+n.yh1)+" c -3,0 -5,2 -5,5 l 0,"+(e.height*n.yo-16)+" c 0,3 2,5 5,5",style:"stroke:#0041c4;stroke-width:1;fill:none"}]),void 0!==e.name&&(a=e.x-10,o=n.yo*(e.y+e.height/2)+n.yh0+n.yh1,(s=c.parse(e.name)).unshift("text",{"text-anchor":"middle",class:"info","xml:space":"preserve"}),i.push(["g",{transform:"translate("+a+","+o+")"},["g",{transform:"rotate(270)"},s]]))}),i}},{tspan:37}],24:[function(e,t,r){"use strict";var n=e("tspan"),a=e("./text-width.js");t.exports=function(e,t){var r=a(t,8)+2;return["g",{transform:"translate("+e.x+","+e.y+")"},["rect",{x:-(r>>1),y:-5,width:r,height:10,style:"fill:#FFF;"}],["text",{"text-anchor":"middle",y:3,style:"font-size:8px;"}].concat(n.parse(t))]}},{"./text-width.js":32,tspan:37}],25:[function(e,t,r){"use strict";var s=e("./render-marks"),i=e("./render-arcs"),c=e("./render-gaps");t.exports=function(e,t,r,n,a,o){return[s(t,e,o,a)].concat(r.res).concat([i(n.lanes,e,a,o)]).concat([c(n.lanes,e,o)])}},{"./render-arcs":20,"./render-gaps":22,"./render-marks":26}],26:[function(e,t,r){"use strict";var m=e("tspan");function u(e,t,r){return e[t]&&e[t].text?[["text",{x:e.xmax*e.xs/2,y:r,fill:"#000","text-anchor":"middle","xml:space":"preserve"}].concat(m.parse(e[t].text))]:[]}function f(e,t,r,n,a,o,s){var i,c,l,u,f=1,d=0,h=[];if(void 0===e[t]||void 0===e[t][r])return[];if("string"==typeof(c=e[t][r]))c=c.split(" ");else if("number"==typeof c||"boolean"==typeof c)for(i=Number(c),c=[],u=0;u<s;u+=1)c.push(u+i);if("[object Array]"!==Object.prototype.toString.call(c))return[];if(0===c.length)return[];if(1===c.length)if(i=Number(c[0]),isNaN(i))h=c;else for(u=0;u<s;u+=1)h[u]=u+i;else if(2===c.length)if(i=Number(c[0]),f=Number(c[1]),2===(l=c[1].split(".")).length&&(d=l[1].length),isNaN(i)||isNaN(f))h=c;else for(i*=f,u=0;u<s;u+=1)h[u]=(f*u+i).toFixed(d);else h=c;var p=[];for(u=0;u<s;u+=1)p=p.concat([["text",{x:u*a+n,y:o,class:"muted","text-anchor":"middle","xml:space":"preserve"}].concat(m.parse(h[u]))]);return p}t.exports=function(e,t,r,n){var a,o=2*r.hscale,s=o*r.xs,i=r.xmax/o,c=e.length*r.yo,l=["g",{id:"gmarks_"+t}];if(!n||!n.config||!1!==n.config.marks)for(a=0;a<1+i;a+=1)l=l.concat([["path",{id:"gmark_"+a+"_"+t,d:"m "+a*s+",0 0,"+c,style:"stroke:#888;stroke-width:0.5;stroke-dasharray:1,3"}]]);return l.concat(u(r,"head",r.yh0?-33:-13)).concat(u(r,"foot",c+(r.yf0?45:25))).concat(f(r,"head","tick",0,s,-5,1+i)).concat(f(r,"head","tock",s/2,s,-5,i)).concat(f(r,"foot","tick",0,s,15+c,1+i)).concat(f(r,"foot","tock",s/2,s,15+c,i))}},{tspan:37}],27:[function(e,t,r){"use strict";var n=e("bit-field/lib/render");t.exports=function(e,t){return n(t.reg,t.config)}},{"bit-field/lib/render":35}],28:[function(e,t,r){"use strict";var c=e("./rec"),l=e("./lane"),u=e("./parse-config"),f=e("./parse-wave-lanes"),d=e("./render-groups"),h=e("./render-lanes"),p=e("./render-wave-lane"),m=e("./insert-svg-template");t.exports=function(e,t,r){!function(e,t,r,n){if(0===e){var a,o,s;for(a in n)break;o=n.default||n[a],t&&t.config&&t.config.skin&&n[t.config.skin]&&(o=n[t.config.skin]),s=o[3][1][2][1],r.xs=Number(s.width),r.ys=Number(s.height),r.xlabel=Number(s.x),r.ym=Number(s.y)}}(e,t,l,r),u(t,l);var n=c(t.signal,{x:0,y:0,xmax:0,width:[],lanes:[],groups:[]}),a=f(n.lanes,l),o=p(a,e,l),s=d(n.groups,e,l),i=o.glengths.reduce(function(e,t,r){return Math.max(e,t+n.width[r])},0);return l.xg=Math.ceil((i-l.tgo)/l.xs)*l.xs,m(e,t,l,r,a,h(e,a,o,n,t,l),s)}},{"./insert-svg-template":12,"./lane":13,"./parse-config":14,"./parse-wave-lanes":16,"./rec":18,"./render-groups":23,"./render-lanes":25,"./render-wave-lane":31}],29:[function(e,t,r){"use strict";var a=e("./render-any.js"),o=e("./create-element");t.exports=function(e,t,r,n){for(;r.childNodes.length;)r.removeChild(r.childNodes[0]);r.insertBefore(o(a(e,t,n)),null)}},{"./create-element":4,"./render-any.js":19}],30:[function(e,t,r){"use strict";var n=e("./render-wave-element");t.exports=function(e,t,r){n(e,t,document.getElementById(r+e),window.WaveSkin)}},{"./render-wave-element":29}],31:[function(e,t,r){"use strict";var l=e("tspan"),u=e("./text-width.js"),s=e("./find-lane-markers");function f(e,t){var r,n,a=[],o=[];if(e[1]){for(r=0;r<e[1].length;r+=1)a.push(["use",{"xlink:href":"#"+e[1][r],transform:"translate("+r*t.xs+")"}]);if(e[2]&&e[2].length&&(o=s(e[1])).length)for(n in o)e[2]&&void 0!==e[2][n]&&a.push(["text",{x:o[n]*t.xs+t.xlabel,y:t.ym,"text-anchor":"middle","xml:space":"preserve"}].concat(l.parse(e[2][n])))}return a}t.exports=function(e,t,r){var n,a,o,s=0,i=[],c=[];for(n=0;n<e.length;n+=1)(a=e[n][0][0])&&(o=0<(o=e[n][0][1])?Math.ceil(2*o)-2*o:-2*o,c.push(["g",{id:"wavelane_"+n+"_"+t,transform:"translate(0,"+(r.y0+n*r.yo)+")"},["text",{x:r.tgo,y:r.ym,class:"info","text-anchor":"end","xml:space":"preserve"}].concat(l.parse(a)),["g",{id:"wavelane_draw_"+n+"_"+t,transform:"translate("+o*r.xs+", 0)"}].concat(f(e[n],r))]),s=Math.max(s,(e[n][1]||[]).length),i.push(u(a,11)));return r.xmax=Math.min(s,r.xmax_cfg-r.xmin_cfg),r.xg=20,{glengths:i,res:c}}},{"./find-lane-markers":7,"./text-width.js":32,tspan:37}],32:[function(e,t,r){"use strict";var i=e("./char-width");t.exports=function(e,t){var r,n,a,o,s;for(t=t||11,n=e.length,r=s=0;r<n;r++)a=e.charCodeAt(r),void 0===(o=i.chars[a])&&(o=i.other),s+=o;return s*t/100}},{"./char-width":3}],33:[function(e,t,r){"use strict";t.exports={svg:"http://www.w3.org/2000/svg",xlink:"http://www.w3.org/1999/xlink",xmlns:"http://www.w3.org/XML/1998/namespace"}},{}],34:[function(e,t,r){"use strict";window.WaveDrom=window.WaveDrom||{};var n=e("./process-all"),a=e("./eva"),o=e("./render-wave-form"),s=e("./editor-refresh");window.WaveDrom.ProcessAll=n,window.WaveDrom.RenderWaveForm=o,window.WaveDrom.EditorRefresh=s,window.WaveDrom.eva=a},{"./editor-refresh":5,"./eva":6,"./process-all":17,"./render-wave-form":30}],35:[function(e,t,r){"use strict";var a=e("tspan"),f={2:0,3:80,4:170,5:45,6:126,7:215};function d(e,t){return"translate("+e+","+t+")"}function h(e,t,r){var n={};return t&&(n.x=t),r&&(n.y=r),["text",n].concat(a.parse(e))}function i(e,t,r){return"number"==typeof e&&t<e?0|e:r}function c(e,t,r){var n={};return t?(n.x1=t,n.x2=t+e):n.x2=e,r&&(n.y1=r,n.y2=r),["line",n]}function l(e,t,r){var n={};return t&&(n.x1=t,n.x2=t),r?(n.y1=r,n.y2=r+e):n.y2=e,["line",n]}function p(e,t,r,n,a){var o,s=["g",{}];if("number"!=typeof e)return h(e,t,r);for(o=0;o<a;o++)s.push(h(e>>o&1,t+n*(a/2-o-.5),r));return s}function n(e,o){var s=o.hspace/o.mod,i=["g",{transform:d(s/2,o.vspace/5)}],c=["g",{transform:d(s/2,o.vspace/2+4)}],l=["g",{transform:d(s/2,o.vspace)}],u=["g",{transform:d(0,o.vspace/4)}];return e.forEach(function(e){var t,r,n,a;if(t=0,r=o.mod-1,n=o.index*o.mod,a=(o.index+1)*o.mod-1,e.lsb/o.mod>>0===o.index)t=e.lsbm,n=e.lsb,e.msb/o.mod>>0===o.index&&(a=e.msb,r=e.msbm);else{if(e.msb/o.mod>>0!==o.index)return;a=e.msb,r=e.msbm}i.push(h(n,s*(o.mod-t-1))),t!==r&&i.push(h(a,s*(o.mod-r-1))),e.name&&c.push(p(e.name,s*(o.mod-(r+t)/2-1),0,s,e.bits)),void 0!==e.name&&void 0===e.type||u.push(["rect",{style:"fill-opacity:0.1"+function(e){var t=f[e];return void 0!==t?";fill:hsl("+t+",100%,50%)":""}(e.type),x:s*(o.mod-r-1),y:0,width:s*(r-t+1),height:o.vspace/2}]),void 0!==e.attr&&l.push(function(n,e,a,t,r){var o=a*(e.mod-(r+t)/2-1);return Array.isArray(n.attr)?n.attr.reduce(function(e,t,r){return null==t?e:e.concat([p(t,o,16*r,a,n.bits)])},["g",{}]):p(n.attr,o,0,a,n.bits)}(e,o,s,t,r))}),["g",u,i,c,l]}function u(e,t){return["g",{transform:d(4.5,(t.lanes-t.index-1)*t.vspace+.5),"text-anchor":"middle","font-size":t.fontsize,"font-family":t.fontfamily||"sans-serif","font-weight":t.fontweight||"normal"},function(e,t){var r=t.hspace,n=t.vspace,a=t.mod,o=["g",{transform:d(0,n/4),stroke:"black","stroke-width":1,"stroke-linecap":"round"}];o.push(c(r)),o.push(l(n/2)),o.push(c(r,0,n/2));for(var s=t.index*t.mod,i=t.mod;i===t.mod||e.some(function(e){return e.lsb===s})?o.push(l(n/2,i*(r/a))):(o.push(l(n/16,i*(r/a))),o.push(l(n/16,i*(r/a),7*n/16))),s++,--i;);return o}(e,t),n(e,t)]}t.exports=function(e,t){(t="object"==typeof t?t:{}).vspace=i(t.vspace,19,80),t.hspace=i(t.hspace,39,800),t.lanes=i(t.lanes,0,1),t.bits=i(t.bits,4,32),t.fontsize=i(t.fontsize,5,14),t.bigendian=t.bigendian||!1;var r,n=16*e.reduce(function(e,t){return Math.max(e,Array.isArray(t.attr)?t.attr.length:0)},0),a=function(e,t){return["svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:t,viewBox:[0,0,e,t].join(" ")}]}(t.hspace+9,(t.vspace+n)*t.lanes+5),o=0,s=t.bits/t.lanes;for(t.mod=0|s,e.forEach(function(e){e.lsb=o,e.lsbm=o%s,o+=e.bits,e.msb=o-1,e.msbm=e.msb%s}),r=0;r<t.lanes;r++)t.index=r,a.push(u(e,t));return a}},{tspan:37}],36:[function(e,t,r){"use strict";t.exports=function(e,t){var i="",r=function(e){return e};return 0<t&&(i="\n",r=function(e){var n=" ".repeat(e);return function(e){var t,r=[];return"string"!=typeof e?e:1===(t=e.split("\n")).length?n+e:(t.forEach(function(e){""!==e.trim()?r.push(n+e):r.push(e)}),r.join("\n"))}}(t)),function n(e){var a,o,s;return s=!(o=""),e.some(function(t,e,r){if(0===e)return a="<"+t,1===r.length||void 0;if(1===e){if(function(e){return e&&"[object Object]"===Object.prototype.toString.call(e)}(t))return Object.keys(t).forEach(function(e){a+=" "+e+'="'+t[e]+'"'}),2===r.length||void(a+=">");a+=">"}switch(typeof t){case"string":case"number":case"boolean":return void(o+=t+i)}s=!1,o+=n(t)})?a+"/>"+i:s?a+function(e){var t=e.split("\n"),r=[];return t.forEach(function(e){""!==e.trim()&&r.push(e)}),r.join("\n")}(o)+"</"+e[0]+">"+i:a+i+r(o)+"</"+e[0]+">"+i}(e)}},{}],37:[function(e,t,r){"use strict";var n=e("./parse"),a=e("./reparse");r.parse=n,r.reparse=a},{"./parse":38,"./reparse":39}],38:[function(e,t,r){"use strict";var s=/<o>|<ins>|<s>|<sub>|<sup>|<b>|<i>|<tt>|<\/o>|<\/ins>|<\/s>|<\/sub>|<\/sup>|<\/b>|<\/i>|<\/tt>/;function i(r,e){e.add&&e.add.split(";").forEach(function(e){var t=e.split(" ");r[t[0]][t[1]]=!0}),e.del&&e.del.split(";").forEach(function(e){var t=e.split(" ");delete r[t[0]][t[1]]})}var c={"<o>":{add:"text-decoration overline"},"</o>":{del:"text-decoration overline"},"<ins>":{add:"text-decoration underline"},"</ins>":{del:"text-decoration underline"},"<s>":{add:"text-decoration line-through"},"</s>":{del:"text-decoration line-through"},"<b>":{add:"font-weight bold"},"</b>":{del:"font-weight bold"},"<i>":{add:"font-style italic"},"</i>":{del:"font-style italic"},"<sub>":{add:"baseline-shift sub;font-size .7em"},"</sub>":{del:"baseline-shift sub;font-size .7em"},"<sup>":{add:"baseline-shift super;font-size .7em"},"</sup>":{del:"baseline-shift super;font-size .7em"},"<tt>":{add:"font-family monospace"},"</tt>":{del:"font-family monospace"}};function l(n){return Object.keys(n).reduce(function(e,t){var r=Object.keys(n[t]);return 0<r.length&&(e[t]=r.join(" ")),e},{})}t.exports=function(e){var t,r,n,a,o;if(void 0===e)return[];if("number"==typeof e)return[e+""];if("string"!=typeof e)return[e];for(r=[],t={"text-decoration":{},"font-weight":{},"font-style":{},"baseline-shift":{},"font-size":{},"font-family":{}};;){if(-1===(n=e.search(s)))return r.push(["tspan",l(t),e]),r;if(0<n&&(o=e.slice(0,n),r.push(["tspan",l(t),o])),a=e.match(s)[0],i(t,c[a]),0===(e=e.slice(n+a.length)).length)return r}}},{}],39:[function(e,t,r){"use strict";var n=e("./parse");t.exports=function(e){var s=e.createElement;function t(e,t){var r=e[0],n=e[1],a=Object.keys(n).reduce(function(e,t){return e[function(e){var t=e.match(/(\w+)-(\w)(\w+)/);return null===t?e:t[1]+t[2].toUpperCase()+t[3]}(t)]=n[t],e},{}),o=e[2];return a.key=t,s(r,a,o)}return function(e){return n(e).map(t)}}},{"./parse":38}]},{},[34]);
\ No newline at end of file
diff --git a/litex/soc/doc/static/default.js b/litex/soc/doc/static/default.js
new file mode 100644 (file)
index 0000000..a310df8
--- /dev/null
@@ -0,0 +1,3 @@
+var WaveSkin=WaveSkin||{};WaveSkin.default=["svg",{"id":"svg","xmlns":"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink","height":"0"},["style",{"type":"text/css"},"text{font-size:11pt;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;fill-opacity:1;font-family:Helvetica}.muted{fill:#aaa}.warning{fill:#f6b900}.error{fill:#f60000}.info{fill:#0041c4}.success{fill:#00ab00}.h1{font-size:33pt;font-weight:bold}.h2{font-size:27pt;font-weight:bold}.h3{font-size:20pt;font-weight:bold}.h4{font-size:14pt;font-weight:bold}.h5{font-size:11pt;font-weight:bold}.h6{font-size:8pt;font-weight:bold}.s1{fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}.s2{fill:none;stroke:#000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}.s3{color:#000;fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 3;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate}.s4{color:#000;fill:none;stroke:#000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible}.s5{fill:#fff;stroke:none}.s6{color:#000;fill:#ffffb4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate}.s7{color:#000;fill:#ffe0b9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate}.s8{color:#000;fill:#b9e0ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate}.s9{fill:#000;fill-opacity:1;stroke:none}.s10{color:#000;fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate}.s11{fill:#0041c4;fill-opacity:1;stroke:none}.s12{fill:none;stroke:#0041c4;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none}"],["defs",["g",{"id":"socket"},["rect",{"y":"15","x":"6","height":"20","width":"20"}]],["g",{"id":"pclk"},["path",{"d":"M0,20 0,0 20,0","class":"s1"}]],["g",{"id":"nclk"},["path",{"d":"m0,0 0,20 20,0","class":"s1"}]],["g",{"id":"000"},["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"0m0"},["path",{"d":"m0,20 3,0 3,-10 3,10 11,0","class":"s1"}]],["g",{"id":"0m1"},["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"0mx"},["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 5,20","class":"s2"}],["path",{"d":"M20,0 4,16","class":"s2"}],["path",{"d":"M15,0 6,9","class":"s2"}],["path",{"d":"M10,0 9,1","class":"s2"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"0md"},["path",{"d":"m8,20 10,0","class":"s3"}],["path",{"d":"m0,20 5,0","class":"s1"}]],["g",{"id":"0mu"},["path",{"d":"m0,20 3,0 C 7,10 10.107603,0 20,0","class":"s1"}]],["g",{"id":"0mz"},["path",{"d":"m0,20 3,0 C 10,10 15,10 20,10","class":"s1"}]],["g",{"id":"111"},["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"1m0"},["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}]],["g",{"id":"1m1"},["path",{"d":"M0,0 3,0 6,10 9,0 20,0","class":"s1"}]],["g",{"id":"1mx"},["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 8,17","class":"s2"}],["path",{"d":"M20,0 7,13","class":"s2"}],["path",{"d":"M15,0 6,9","class":"s2"}],["path",{"d":"M10,0 5,5","class":"s2"}],["path",{"d":"M3.5,1.5 5,0","class":"s2"}]],["g",{"id":"1md"},["path",{"d":"m0,0 3,0 c 4,10 7,20 17,20","class":"s1"}]],["g",{"id":"1mu"},["path",{"d":"M0,0 5,0","class":"s1"}],["path",{"d":"M8,0 18,0","class":"s3"}]],["g",{"id":"1mz"},["path",{"d":"m0,0 3,0 c 7,10 12,10 17,10","class":"s1"}]],["g",{"id":"xxx"},["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"M0,5 5,0","class":"s2"}],["path",{"d":"M0,10 10,0","class":"s2"}],["path",{"d":"M0,15 15,0","class":"s2"}],["path",{"d":"M0,20 20,0","class":"s2"}],["path",{"d":"M5,20 20,5","class":"s2"}],["path",{"d":"M10,20 20,10","class":"s2"}],["path",{"d":"m15,20 5,-5","class":"s2"}]],["g",{"id":"xm0"},["path",{"d":"M0,0 4,0 9,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,5 4,1","class":"s2"}],["path",{"d":"M0,10 5,5","class":"s2"}],["path",{"d":"M0,15 6,9","class":"s2"}],["path",{"d":"M0,20 7,13","class":"s2"}],["path",{"d":"M5,20 8,17","class":"s2"}]],["g",{"id":"xm1"},["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"M0,20 4,20 9,0","class":"s1"}],["path",{"d":"M0,5 5,0","class":"s2"}],["path",{"d":"M0,10 9,1","class":"s2"}],["path",{"d":"M0,15 7,8","class":"s2"}],["path",{"d":"M0,20 5,15","class":"s2"}]],["g",{"id":"xmx"},["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"M0,5 5,0","class":"s2"}],["path",{"d":"M0,10 10,0","class":"s2"}],["path",{"d":"M0,15 15,0","class":"s2"}],["path",{"d":"M0,20 20,0","class":"s2"}],["path",{"d":"M5,20 20,5","class":"s2"}],["path",{"d":"M10,20 20,10","class":"s2"}],["path",{"d":"m15,20 5,-5","class":"s2"}]],["g",{"id":"xmd"},["path",{"d":"m0,0 4,0 c 3,10 6,20 16,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,5 4,1","class":"s2"}],["path",{"d":"M0,10 5.5,4.5","class":"s2"}],["path",{"d":"M0,15 6.5,8.5","class":"s2"}],["path",{"d":"M0,20 8,12","class":"s2"}],["path",{"d":"m5,20 5,-5","class":"s2"}],["path",{"d":"m10,20 2.5,-2.5","class":"s2"}]],["g",{"id":"xmu"},["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"m0,20 4,0 C 7,10 10,0 20,0","class":"s1"}],["path",{"d":"M0,5 5,0","class":"s2"}],["path",{"d":"M0,10 10,0","class":"s2"}],["path",{"d":"M0,15 10,5","class":"s2"}],["path",{"d":"M0,20 6,14","class":"s2"}]],["g",{"id":"xmz"},["path",{"d":"m0,0 4,0 c 6,10 11,10 16,10","class":"s1"}],["path",{"d":"m0,20 4,0 C 10,10 15,10 20,10","class":"s1"}],["path",{"d":"M0,5 4.5,0.5","class":"s2"}],["path",{"d":"M0,10 6.5,3.5","class":"s2"}],["path",{"d":"M0,15 8.5,6.5","class":"s2"}],["path",{"d":"M0,20 11.5,8.5","class":"s2"}]],["g",{"id":"ddd"},["path",{"d":"m0,20 20,0","class":"s3"}]],["g",{"id":"dm0"},["path",{"d":"m0,20 10,0","class":"s3"}],["path",{"d":"m12,20 8,0","class":"s1"}]],["g",{"id":"dm1"},["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"dmx"},["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 5,20","class":"s2"}],["path",{"d":"M20,0 4,16","class":"s2"}],["path",{"d":"M15,0 6,9","class":"s2"}],["path",{"d":"M10,0 9,1","class":"s2"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"dmd"},["path",{"d":"m0,20 20,0","class":"s3"}]],["g",{"id":"dmu"},["path",{"d":"m0,20 3,0 C 7,10 10.107603,0 20,0","class":"s1"}]],["g",{"id":"dmz"},["path",{"d":"m0,20 3,0 C 10,10 15,10 20,10","class":"s1"}]],["g",{"id":"uuu"},["path",{"d":"M0,0 20,0","class":"s3"}]],["g",{"id":"um0"},["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}]],["g",{"id":"um1"},["path",{"d":"M0,0 10,0","class":"s3"}],["path",{"d":"m12,0 8,0","class":"s1"}]],["g",{"id":"umx"},["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 8,17","class":"s2"}],["path",{"d":"M20,0 7,13","class":"s2"}],["path",{"d":"M15,0 6,9","class":"s2"}],["path",{"d":"M10,0 5,5","class":"s2"}],["path",{"d":"M3.5,1.5 5,0","class":"s2"}]],["g",{"id":"umd"},["path",{"d":"m0,0 3,0 c 4,10 7,20 17,20","class":"s1"}]],["g",{"id":"umu"},["path",{"d":"M0,0 20,0","class":"s3"}]],["g",{"id":"umz"},["path",{"d":"m0,0 3,0 c 7,10 12,10 17,10","class":"s4"}]],["g",{"id":"zzz"},["path",{"d":"m0,10 20,0","class":"s1"}]],["g",{"id":"zm0"},["path",{"d":"m0,10 6,0 3,10 11,0","class":"s1"}]],["g",{"id":"zm1"},["path",{"d":"M0,10 6,10 9,0 20,0","class":"s1"}]],["g",{"id":"zmx"},["path",{"d":"m6,10 3,10 11,0","class":"s1"}],["path",{"d":"M0,10 6,10 9,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 8,17","class":"s2"}],["path",{"d":"M20,0 7,13","class":"s2"}],["path",{"d":"M15,0 6.5,8.5","class":"s2"}],["path",{"d":"M10,0 9,1","class":"s2"}]],["g",{"id":"zmd"},["path",{"d":"m0,10 7,0 c 3,5 8,10 13,10","class":"s1"}]],["g",{"id":"zmu"},["path",{"d":"m0,10 7,0 C 10,5 15,0 20,0","class":"s1"}]],["g",{"id":"zmz"},["path",{"d":"m0,10 20,0","class":"s1"}]],["g",{"id":"gap"},["path",{"d":"m7,-2 -4,0 c -5,0 -5,24 -10,24 l 4,0 C 2,22 2,-2 7,-2 z","class":"s5"}],["path",{"d":"M-7,22 C -2,22 -2,-2 3,-2","class":"s1"}],["path",{"d":"M-3,22 C 2,22 2,-2 7,-2","class":"s1"}]],["g",{"id":"0mv-3"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s6"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"1mv-3"},["path",{"d":"M2.875,0 20,0 20,20 9,20 z","class":"s6"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"xmv-3"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s6"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,5 3.5,1.5","class":"s2"}],["path",{"d":"M0,10 4.5,5.5","class":"s2"}],["path",{"d":"M0,15 6,9","class":"s2"}],["path",{"d":"M0,20 4,16","class":"s2"}]],["g",{"id":"dmv-3"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s6"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"umv-3"},["path",{"d":"M3,0 20,0 20,20 9,20 z","class":"s6"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"zmv-3"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s6"}],["path",{"d":"m6,10 3,10 11,0","class":"s1"}],["path",{"d":"M0,10 6,10 9,0 20,0","class":"s1"}]],["g",{"id":"vvv-3"},["path",{"d":"M20,20 0,20 0,0 20,0","class":"s6"}],["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vm0-3"},["path",{"d":"M0,20 0,0 3,0 9,20","class":"s6"}],["path",{"d":"M0,0 3,0 9,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vm1-3"},["path",{"d":"M0,0 0,20 3,20 9,0","class":"s6"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0","class":"s1"}]],["g",{"id":"vmx-3"},["path",{"d":"M0,0 0,20 3,20 6,10 3,0","class":"s6"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 8,17","class":"s2"}],["path",{"d":"M20,0 7,13","class":"s2"}],["path",{"d":"M15,0 7,8","class":"s2"}],["path",{"d":"M10,0 9,1","class":"s2"}]],["g",{"id":"vmd-3"},["path",{"d":"m0,0 0,20 20,0 C 10,20 7,10 3,0","class":"s6"}],["path",{"d":"m0,0 3,0 c 4,10 7,20 17,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vmu-3"},["path",{"d":"m0,0 0,20 3,0 C 7,10 10,0 20,0","class":"s6"}],["path",{"d":"m0,20 3,0 C 7,10 10,0 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vmz-3"},["path",{"d":"M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20","class":"s6"}],["path",{"d":"m0,0 3,0 c 7,10 12,10 17,10","class":"s1"}],["path",{"d":"m0,20 3,0 C 10,10 15,10 20,10","class":"s1"}]],["g",{"id":"vmv-3-3"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s6"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s6"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-3-4"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s7"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s6"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-3-5"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s8"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s6"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-4-3"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s6"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s7"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-4-4"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s7"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s7"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-4-5"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s8"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s7"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-5-3"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s6"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s8"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-5-4"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s7"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s8"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-5-5"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s8"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s8"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"0mv-4"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s7"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"1mv-4"},["path",{"d":"M2.875,0 20,0 20,20 9,20 z","class":"s7"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"xmv-4"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s7"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,5 3.5,1.5","class":"s2"}],["path",{"d":"M0,10 4.5,5.5","class":"s2"}],["path",{"d":"M0,15 6,9","class":"s2"}],["path",{"d":"M0,20 4,16","class":"s2"}]],["g",{"id":"dmv-4"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s7"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"umv-4"},["path",{"d":"M3,0 20,0 20,20 9,20 z","class":"s7"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"zmv-4"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s7"}],["path",{"d":"m6,10 3,10 11,0","class":"s1"}],["path",{"d":"M0,10 6,10 9,0 20,0","class":"s1"}]],["g",{"id":"0mv-5"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s8"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"1mv-5"},["path",{"d":"M2.875,0 20,0 20,20 9,20 z","class":"s8"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"xmv-5"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s8"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,5 3.5,1.5","class":"s2"}],["path",{"d":"M0,10 4.5,5.5","class":"s2"}],["path",{"d":"M0,15 6,9","class":"s2"}],["path",{"d":"M0,20 4,16","class":"s2"}]],["g",{"id":"dmv-5"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s8"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"umv-5"},["path",{"d":"M3,0 20,0 20,20 9,20 z","class":"s8"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"zmv-5"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s8"}],["path",{"d":"m6,10 3,10 11,0","class":"s1"}],["path",{"d":"M0,10 6,10 9,0 20,0","class":"s1"}]],["g",{"id":"vvv-4"},["path",{"d":"M20,20 0,20 0,0 20,0","class":"s7"}],["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vm0-4"},["path",{"d":"M0,20 0,0 3,0 9,20","class":"s7"}],["path",{"d":"M0,0 3,0 9,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vm1-4"},["path",{"d":"M0,0 0,20 3,20 9,0","class":"s7"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0","class":"s1"}]],["g",{"id":"vmx-4"},["path",{"d":"M0,0 0,20 3,20 6,10 3,0","class":"s7"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 8,17","class":"s2"}],["path",{"d":"M20,0 7,13","class":"s2"}],["path",{"d":"M15,0 7,8","class":"s2"}],["path",{"d":"M10,0 9,1","class":"s2"}]],["g",{"id":"vmd-4"},["path",{"d":"m0,0 0,20 20,0 C 10,20 7,10 3,0","class":"s7"}],["path",{"d":"m0,0 3,0 c 4,10 7,20 17,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vmu-4"},["path",{"d":"m0,0 0,20 3,0 C 7,10 10,0 20,0","class":"s7"}],["path",{"d":"m0,20 3,0 C 7,10 10,0 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vmz-4"},["path",{"d":"M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20","class":"s7"}],["path",{"d":"m0,0 3,0 c 7,10 12,10 17,10","class":"s1"}],["path",{"d":"m0,20 3,0 C 10,10 15,10 20,10","class":"s1"}]],["g",{"id":"vvv-5"},["path",{"d":"M20,20 0,20 0,0 20,0","class":"s8"}],["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vm0-5"},["path",{"d":"M0,20 0,0 3,0 9,20","class":"s8"}],["path",{"d":"M0,0 3,0 9,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vm1-5"},["path",{"d":"M0,0 0,20 3,20 9,0","class":"s8"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0","class":"s1"}]],["g",{"id":"vmx-5"},["path",{"d":"M0,0 0,20 3,20 6,10 3,0","class":"s8"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 8,17","class":"s2"}],["path",{"d":"M20,0 7,13","class":"s2"}],["path",{"d":"M15,0 7,8","class":"s2"}],["path",{"d":"M10,0 9,1","class":"s2"}]],["g",{"id":"vmd-5"},["path",{"d":"m0,0 0,20 20,0 C 10,20 7,10 3,0","class":"s8"}],["path",{"d":"m0,0 3,0 c 4,10 7,20 17,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vmu-5"},["path",{"d":"m0,0 0,20 3,0 C 7,10 10,0 20,0","class":"s8"}],["path",{"d":"m0,20 3,0 C 7,10 10,0 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vmz-5"},["path",{"d":"M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20","class":"s8"}],["path",{"d":"m0,0 3,0 c 7,10 12,10 17,10","class":"s1"}],["path",{"d":"m0,20 3,0 C 10,10 15,10 20,10","class":"s1"}]],["g",{"id":"Pclk"},["path",{"d":"M-3,12 0,3 3,12 C 1,11 -1,11 -3,12 z","class":"s9"}],["path",{"d":"M0,20 0,0 20,0","class":"s1"}]],["g",{"id":"Nclk"},["path",{"d":"M-3,8 0,17 3,8 C 1,9 -1,9 -3,8 z","class":"s9"}],["path",{"d":"m0,0 0,20 20,0","class":"s1"}]],["g",{"id":"vvv-2"},["path",{"d":"M20,20 0,20 0,0 20,0","class":"s10"}],["path",{"d":"m0,20 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vm0-2"},["path",{"d":"M0,20 0,0 3,0 9,20","class":"s10"}],["path",{"d":"M0,0 3,0 9,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vm1-2"},["path",{"d":"M0,0 0,20 3,20 9,0","class":"s10"}],["path",{"d":"M0,0 20,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0","class":"s1"}]],["g",{"id":"vmx-2"},["path",{"d":"M0,0 0,20 3,20 6,10 3,0","class":"s10"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m20,15 -5,5","class":"s2"}],["path",{"d":"M20,10 10,20","class":"s2"}],["path",{"d":"M20,5 8,17","class":"s2"}],["path",{"d":"M20,0 7,13","class":"s2"}],["path",{"d":"M15,0 7,8","class":"s2"}],["path",{"d":"M10,0 9,1","class":"s2"}]],["g",{"id":"vmd-2"},["path",{"d":"m0,0 0,20 20,0 C 10,20 7,10 3,0","class":"s10"}],["path",{"d":"m0,0 3,0 c 4,10 7,20 17,20","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"vmu-2"},["path",{"d":"m0,0 0,20 3,0 C 7,10 10,0 20,0","class":"s10"}],["path",{"d":"m0,20 3,0 C 7,10 10,0 20,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"vmz-2"},["path",{"d":"M0,0 3,0 C 10,10 15,10 20,10 15,10 10,10 3,20 L 0,20","class":"s10"}],["path",{"d":"m0,0 3,0 c 7,10 12,10 17,10","class":"s1"}],["path",{"d":"m0,20 3,0 C 10,10 15,10 20,10","class":"s1"}]],["g",{"id":"0mv-2"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s10"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"1mv-2"},["path",{"d":"M2.875,0 20,0 20,20 9,20 z","class":"s10"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"xmv-2"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s10"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,5 3.5,1.5","class":"s2"}],["path",{"d":"M0,10 4.5,5.5","class":"s2"}],["path",{"d":"M0,15 6,9","class":"s2"}],["path",{"d":"M0,20 4,16","class":"s2"}]],["g",{"id":"dmv-2"},["path",{"d":"M9,0 20,0 20,20 3,20 z","class":"s10"}],["path",{"d":"M3,20 9,0 20,0","class":"s1"}],["path",{"d":"m0,20 20,0","class":"s1"}]],["g",{"id":"umv-2"},["path",{"d":"M3,0 20,0 20,20 9,20 z","class":"s10"}],["path",{"d":"m3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,0 20,0","class":"s1"}]],["g",{"id":"zmv-2"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s10"}],["path",{"d":"m6,10 3,10 11,0","class":"s1"}],["path",{"d":"M0,10 6,10 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-3-2"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s10"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s6"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-4-2"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s10"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s7"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-5-2"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s10"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s8"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-2-3"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s6"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s10"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-2-4"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s7"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s10"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-2-5"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s8"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s10"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"vmv-2-2"},["path",{"d":"M9,0 20,0 20,20 9,20 6,10 z","class":"s10"}],["path",{"d":"M3,0 0,0 0,20 3,20 6,10 z","class":"s10"}],["path",{"d":"m0,0 3,0 6,20 11,0","class":"s1"}],["path",{"d":"M0,20 3,20 9,0 20,0","class":"s1"}]],["g",{"id":"arrow0"},["path",{"d":"m-12,-3 9,3 -9,3 c 1,-2 1,-4 0,-6 z","class":"s11"}],["path",{"d":"M0,0 -15,0","class":"s12"}]],["marker",{"id":"arrowhead","style":"fill:#0041c4","markerHeight":"7","markerWidth":"10","markerUnits":"strokeWidth","viewBox":"0 -4 11 8","refX":"15","refY":"0","orient":"auto"},["path",{"d":"M0 -4 11 0 0 4z"}]],["marker",{"id":"arrowtail","style":"fill:#0041c4","markerHeight":"7","markerWidth":"10","markerUnits":"strokeWidth","viewBox":"-11 -4 11 8","refX":"-15","refY":"0","orient":"auto"},["path",{"d":"M0 -4 -11 0 0 4z"}]]],["g",{"id":"waves"},["g",{"id":"lanes"}],["g",{"id":"groups"}]]];
+try { module.exports = WaveSkin; } catch(err) {}
+