integration: svd: move svd generation to export

It was suggested that we should move svd generation into `export`,
alongside the rest of the generators such as csv, json, and h.  This
performs this move, while keeping a compatible `generate_svd()` function
inside `soc/doc/`.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-02-04 23:49:08 +08:00
parent 73ed7e564c
commit 58598d4fda
2 changed files with 159 additions and 159 deletions

View file

@ -6,6 +6,7 @@ import pathlib
import datetime
from litex.soc.interconnect.csr import _CompoundCSR
from litex.soc.integration import export
from .csr import DocumentedCSRRegion
from .module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
from .rst import reflow
@ -26,165 +27,12 @@ html_theme = 'alabaster'
html_static_path = ['_static']
"""
def sub_csr_bit_range(busword, csr, offset):
nwords = (csr.size + busword - 1)//busword
i = nwords - offset - 1
nbits = min(csr.size - i*busword, busword) - 1
name = (csr.name + str(i) if nwords > 1 else csr.name).upper()
origin = i*busword
return (origin, nbits, name)
def print_svd_register(csr, csr_address, description, length, svd):
print(' <register>', file=svd)
print(' <name>{}</name>'.format(csr.short_numbered_name), file=svd)
if description is not None:
print(
' <description><![CDATA[{}]]></description>'.format(description), file=svd)
print(
' <addressOffset>0x{:04x}</addressOffset>'.format(csr_address), file=svd)
print(
' <resetValue>0x{:02x}</resetValue>'.format(csr.reset_value), file=svd)
print(' <size>{}</size>'.format(length), file=svd)
print(' <access>{}</access>'.format(csr.access), file=svd)
csr_address = csr_address + 4
print(' <fields>', file=svd)
if hasattr(csr, "fields") and len(csr.fields) > 0:
for field in csr.fields:
print(' <field>', file=svd)
print(
' <name>{}</name>'.format(field.name), file=svd)
print(' <msb>{}</msb>'.format(field.offset +
field.size - 1), file=svd)
print(' <bitRange>[{}:{}]</bitRange>'.format(
field.offset + field.size - 1, field.offset), file=svd)
print(
' <lsb>{}</lsb>'.format(field.offset), file=svd)
print(' <description><![CDATA[{}]]></description>'.format(
reflow(field.description)), file=svd)
print(' </field>', file=svd)
else:
field_size = csr.size
field_name = csr.short_name.lower()
# Strip off "ev_" from eventmanager fields
if field_name == "ev_enable":
field_name = "enable"
elif field_name == "ev_pending":
field_name = "pending"
elif field_name == "ev_status":
field_name = "status"
print(' <field>', file=svd)
print(' <name>{}</name>'.format(field_name), file=svd)
print(' <msb>{}</msb>'.format(field_size - 1), file=svd)
print(
' <bitRange>[{}:{}]</bitRange>'.format(field_size - 1, 0), file=svd)
print(' <lsb>{}</lsb>'.format(0), file=svd)
print(' </field>', file=svd)
print(' </fields>', file=svd)
print(' </register>', file=svd)
def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, description=None):
interrupts = {}
for csr, irq in sorted(soc.soc_interrupt_map.items()):
interrupts[csr] = irq
documented_regions = []
raw_regions = []
if hasattr(soc, "get_csr_regions"):
raw_regions = soc.get_csr_regions()
else:
for region_name, region in soc.csr_regions.items():
raw_regions.append((region_name, region.origin,
region.busword, region.obj))
for csr_region in raw_regions:
documented_regions.append(DocumentedCSRRegion(
csr_region, csr_data_width=soc.csr_data_width))
def generate_svd(soc, buildpath, filename=None, name="soc", **kwargs):
if filename is None:
filename = name + ".svd"
kwargs["name"] = name
with open(buildpath + "/" + filename, "w", encoding="utf-8") as svd:
print('<?xml version="1.0" encoding="utf-8"?>', file=svd)
print('', file=svd)
print('<device schemaVersion="1.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" >', file=svd)
print(' <vendor>{}</vendor>'.format(vendor), file=svd)
print(' <name>{}</name>'.format(name.upper()), file=svd)
if description is not None:
print(
' <description><![CDATA[{}]]></description>'.format(reflow(description)), file=svd)
print('', file=svd)
print(' <addressUnitBits>8</addressUnitBits>', file=svd)
print(' <width>32</width>', file=svd)
print(' <size>32</size>', file=svd)
print(' <access>read-write</access>', file=svd)
print(' <resetValue>0x00000000</resetValue>', file=svd)
print(' <resetMask>0xFFFFFFFF</resetMask>', file=svd)
print('', file=svd)
print(' <peripherals>', file=svd)
for region in documented_regions:
csr_address = 0
print(' <peripheral>', file=svd)
print(' <name>{}</name>'.format(region.name.upper()), file=svd)
print(
' <baseAddress>0x{:08X}</baseAddress>'.format(region.origin), file=svd)
print(
' <groupName>{}</groupName>'.format(region.name.upper()), file=svd)
if len(region.sections) > 0:
print(' <description><![CDATA[{}]]></description>'.format(
reflow(region.sections[0].body())), file=svd)
print(' <registers>', file=svd)
for csr in region.csrs:
description = None
if hasattr(csr, "description"):
description = csr.description
if isinstance(csr, _CompoundCSR) and len(csr.simple_csrs) > 1:
is_first = True
for i in range(len(csr.simple_csrs)):
(start, length, name) = sub_csr_bit_range(
region.busword, csr, i)
if length > 0:
bits_str = "Bits {}-{} of `{}`.".format(
start, start+length, csr.name)
else:
bits_str = "Bit {} of `{}`.".format(
start, csr.name)
if is_first:
if description is not None:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str + " " + description, length, svd)
else:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
is_first = False
else:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
csr_address = csr_address + 4
else:
length = ((csr.size + region.busword - 1) //
region.busword) * region.busword
print_svd_register(
csr, csr_address, description, length, svd)
csr_address = csr_address + 4
print(' </registers>', file=svd)
print(' <addressBlock>', file=svd)
print(' <offset>0</offset>', file=svd)
print(
' <size>0x{:x}</size>'.format(csr_address), file=svd)
print(' <usage>registers</usage>', file=svd)
print(' </addressBlock>', file=svd)
if region.name in interrupts:
print(' <interrupt>', file=svd)
print(' <name>{}</name>'.format(region.name), file=svd)
print(
' <value>{}</value>'.format(interrupts[region.name]), file=svd)
print(' </interrupt>', file=svd)
print(' </peripheral>', file=svd)
print(' </peripherals>', file=svd)
print('</device>', file=svd)
svd.write(export.get_svd(soc, **kwargs))
def generate_docs(soc, base_dir, project_name="LiteX SoC Project",
@ -282,7 +130,7 @@ Documentation for {}
if len(additional_modules) > 0:
print("""
Modules
-------
=======
.. toctree::
:maxdepth: 1
@ -293,7 +141,7 @@ Modules
if len(documented_regions) > 0:
print("""
Register Groups
---------------
===============
.. toctree::
:maxdepth: 1
@ -303,7 +151,7 @@ Register Groups
print("""
Indices and tables
------------------
==================
* :ref:`genindex`
* :ref:`modindex`

View file

@ -23,6 +23,11 @@ from litex.soc.interconnect.csr import CSRStatus
from litex.build.tools import generated_banner
from litex.soc.doc.rst import reflow
from litex.soc.doc.module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
from litex.soc.doc.csr import DocumentedCSRRegion
from litex.soc.interconnect.csr import _CompoundCSR
# CPU files ----------------------------------------------------------------------------------------
def get_cpu_mak(cpu, compile_software):
@ -273,3 +278,150 @@ def get_csr_csv(csr_regions={}, constants={}, mem_regions={}):
d["memories"][name]["type"],
)
return r
# SVD Export --------------------------------------------------------------------------------------
def get_svd(soc, vendor="litex", name="soc", description=None):
def sub_csr_bit_range(busword, csr, offset):
nwords = (csr.size + busword - 1)//busword
i = nwords - offset - 1
nbits = min(csr.size - i*busword, busword) - 1
name = (csr.name + str(i) if nwords > 1 else csr.name).upper()
origin = i*busword
return (origin, nbits, name)
def print_svd_register(csr, csr_address, description, length, svd):
svd.append(' <register>')
svd.append(' <name>{}</name>'.format(csr.short_numbered_name))
if description is not None:
svd.append(' <description><![CDATA[{}]]></description>'.format(description))
svd.append(' <addressOffset>0x{:04x}</addressOffset>'.format(csr_address))
svd.append(' <resetValue>0x{:02x}</resetValue>'.format(csr.reset_value))
svd.append(' <size>{}</size>'.format(length))
svd.append(' <access>{}</access>'.format(csr.access))
csr_address = csr_address + 4
svd.append(' <fields>')
if hasattr(csr, "fields") and len(csr.fields) > 0:
for field in csr.fields:
svd.append(' <field>')
svd.append(' <name>{}</name>'.format(field.name))
svd.append(' <msb>{}</msb>'.format(field.offset +
field.size - 1))
svd.append(' <bitRange>[{}:{}]</bitRange>'.format(
field.offset + field.size - 1, field.offset))
svd.append(' <lsb>{}</lsb>'.format(field.offset))
svd.append(' <description><![CDATA[{}]]></description>'.format(
reflow(field.description)))
svd.append(' </field>')
else:
field_size = csr.size
field_name = csr.short_name.lower()
# Strip off "ev_" from eventmanager fields
if field_name == "ev_enable":
field_name = "enable"
elif field_name == "ev_pending":
field_name = "pending"
elif field_name == "ev_status":
field_name = "status"
svd.append(' <field>')
svd.append(' <name>{}</name>'.format(field_name))
svd.append(' <msb>{}</msb>'.format(field_size - 1))
svd.append(' <bitRange>[{}:{}]</bitRange>'.format(field_size - 1, 0))
svd.append(' <lsb>{}</lsb>'.format(0))
svd.append(' </field>')
svd.append(' </fields>')
svd.append(' </register>')
interrupts = {}
for csr, irq in sorted(soc.soc_interrupt_map.items()):
interrupts[csr] = irq
documented_regions = []
raw_regions = []
if hasattr(soc, "get_csr_regions"):
raw_regions = soc.get_csr_regions()
else:
for region_name, region in soc.csr_regions.items():
raw_regions.append((region_name, region.origin,
region.busword, region.obj))
for csr_region in raw_regions:
documented_regions.append(DocumentedCSRRegion(
csr_region, csr_data_width=soc.csr_data_width))
svd = []
svd.append('<?xml version="1.0" encoding="utf-8"?>')
svd.append('')
svd.append('<device schemaVersion="1.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="CMSIS-SVD.xsd" >')
svd.append(' <vendor>{}</vendor>'.format(vendor))
svd.append(' <name>{}</name>'.format(name.upper()))
if description is not None:
svd.append(' <description><![CDATA[{}]]></description>'.format(reflow(description)))
svd.append('')
svd.append(' <addressUnitBits>8</addressUnitBits>')
svd.append(' <width>32</width>')
svd.append(' <size>32</size>')
svd.append(' <access>read-write</access>')
svd.append(' <resetValue>0x00000000</resetValue>')
svd.append(' <resetMask>0xFFFFFFFF</resetMask>')
svd.append('')
svd.append(' <peripherals>')
for region in documented_regions:
csr_address = 0
svd.append(' <peripheral>')
svd.append(' <name>{}</name>'.format(region.name.upper()))
svd.append(' <baseAddress>0x{:08X}</baseAddress>'.format(region.origin))
svd.append(' <groupName>{}</groupName>'.format(region.name.upper()))
if len(region.sections) > 0:
svd.append(' <description><![CDATA[{}]]></description>'.format(
reflow(region.sections[0].body())))
svd.append(' <registers>')
for csr in region.csrs:
description = None
if hasattr(csr, "description"):
description = csr.description
if isinstance(csr, _CompoundCSR) and len(csr.simple_csrs) > 1:
is_first = True
for i in range(len(csr.simple_csrs)):
(start, length, name) = sub_csr_bit_range(
region.busword, csr, i)
if length > 0:
bits_str = "Bits {}-{} of `{}`.".format(
start, start+length, csr.name)
else:
bits_str = "Bit {} of `{}`.".format(
start, csr.name)
if is_first:
if description is not None:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str + " " + description, length, svd)
else:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
is_first = False
else:
print_svd_register(
csr.simple_csrs[i], csr_address, bits_str, length, svd)
csr_address = csr_address + 4
else:
length = ((csr.size + region.busword - 1) //
region.busword) * region.busword
print_svd_register(
csr, csr_address, description, length, svd)
csr_address = csr_address + 4
svd.append(' </registers>')
svd.append(' <addressBlock>')
svd.append(' <offset>0</offset>')
svd.append(' <size>0x{:x}</size>'.format(csr_address))
svd.append(' <usage>registers</usage>')
svd.append(' </addressBlock>')
if region.name in interrupts:
svd.append(' <interrupt>')
svd.append(' <name>{}</name>'.format(region.name))
svd.append(' <value>{}</value>'.format(interrupts[region.name]))
svd.append(' </interrupt>')
svd.append(' </peripheral>')
svd.append(' </peripherals>')
svd.append('</device>')
return "\n".join(svd)