mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
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:
parent
73ed7e564c
commit
58598d4fda
2 changed files with 159 additions and 159 deletions
|
@ -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`
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue