From: Sean Cross Date: Tue, 4 Feb 2020 15:49:08 +0000 (+0800) Subject: integration: svd: move svd generation to `export` X-Git-Tag: 24jan2021_ls180~691^2 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=58598d4fda74d038a351271768fa7f34cfa6bca2;p=litex.git 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 --- diff --git a/litex/soc/doc/__init__.py b/litex/soc/doc/__init__.py index 0a8c15db..38c753c5 100644 --- a/litex/soc/doc/__init__.py +++ b/litex/soc/doc/__init__.py @@ -6,6 +6,7 @@ import pathlib import datetime from litex.soc.interconnect.csr import _CompoundCSR +from litex.soc.integration import export from .csr import DocumentedCSRRegion from .module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts from .rst import reflow @@ -26,165 +27,12 @@ html_theme = 'alabaster' html_static_path = ['_static'] """ - -def sub_csr_bit_range(busword, csr, offset): - nwords = (csr.size + busword - 1)//busword - i = nwords - offset - 1 - nbits = min(csr.size - i*busword, busword) - 1 - name = (csr.name + str(i) if nwords > 1 else csr.name).upper() - origin = i*busword - return (origin, nbits, name) - - -def print_svd_register(csr, csr_address, description, length, svd): - print(' ', file=svd) - print(' {}'.format(csr.short_numbered_name), file=svd) - if description is not None: - print( - ' '.format(description), file=svd) - print( - ' 0x{:04x}'.format(csr_address), file=svd) - print( - ' 0x{:02x}'.format(csr.reset_value), file=svd) - print(' {}'.format(length), file=svd) - print(' {}'.format(csr.access), file=svd) - csr_address = csr_address + 4 - print(' ', file=svd) - if hasattr(csr, "fields") and len(csr.fields) > 0: - for field in csr.fields: - print(' ', file=svd) - print( - ' {}'.format(field.name), file=svd) - print(' {}'.format(field.offset + - field.size - 1), file=svd) - print(' [{}:{}]'.format( - field.offset + field.size - 1, field.offset), file=svd) - print( - ' {}'.format(field.offset), file=svd) - print(' '.format( - reflow(field.description)), file=svd) - print(' ', file=svd) - else: - field_size = csr.size - field_name = csr.short_name.lower() - # Strip off "ev_" from eventmanager fields - if field_name == "ev_enable": - field_name = "enable" - elif field_name == "ev_pending": - field_name = "pending" - elif field_name == "ev_status": - field_name = "status" - print(' ', file=svd) - print(' {}'.format(field_name), file=svd) - print(' {}'.format(field_size - 1), file=svd) - print( - ' [{}:{}]'.format(field_size - 1, 0), file=svd) - print(' {}'.format(0), file=svd) - print(' ', file=svd) - print(' ', file=svd) - print(' ', file=svd) - - -def generate_svd(soc, buildpath, vendor="litex", name="soc", filename=None, description=None): - interrupts = {} - for csr, irq in sorted(soc.soc_interrupt_map.items()): - interrupts[csr] = irq - - documented_regions = [] - - raw_regions = [] - if hasattr(soc, "get_csr_regions"): - raw_regions = soc.get_csr_regions() - else: - for region_name, region in soc.csr_regions.items(): - raw_regions.append((region_name, region.origin, - region.busword, region.obj)) - for csr_region in raw_regions: - documented_regions.append(DocumentedCSRRegion( - csr_region, csr_data_width=soc.csr_data_width)) - +def generate_svd(soc, buildpath, filename=None, name="soc", **kwargs): if filename is None: filename = name + ".svd" + kwargs["name"] = name with open(buildpath + "/" + filename, "w", encoding="utf-8") as svd: - print('', file=svd) - print('', file=svd) - print('', file=svd) - print(' {}'.format(vendor), file=svd) - print(' {}'.format(name.upper()), file=svd) - if description is not None: - print( - ' '.format(reflow(description)), file=svd) - print('', file=svd) - print(' 8', file=svd) - print(' 32', file=svd) - print(' 32', file=svd) - print(' read-write', file=svd) - print(' 0x00000000', file=svd) - print(' 0xFFFFFFFF', file=svd) - print('', file=svd) - print(' ', file=svd) - - for region in documented_regions: - csr_address = 0 - print(' ', file=svd) - print(' {}'.format(region.name.upper()), file=svd) - print( - ' 0x{:08X}'.format(region.origin), file=svd) - print( - ' {}'.format(region.name.upper()), file=svd) - if len(region.sections) > 0: - print(' '.format( - reflow(region.sections[0].body())), file=svd) - print(' ', file=svd) - for csr in region.csrs: - description = None - if hasattr(csr, "description"): - description = csr.description - if isinstance(csr, _CompoundCSR) and len(csr.simple_csrs) > 1: - is_first = True - for i in range(len(csr.simple_csrs)): - (start, length, name) = sub_csr_bit_range( - region.busword, csr, i) - if length > 0: - bits_str = "Bits {}-{} of `{}`.".format( - start, start+length, csr.name) - else: - bits_str = "Bit {} of `{}`.".format( - start, csr.name) - if is_first: - if description is not None: - print_svd_register( - csr.simple_csrs[i], csr_address, bits_str + " " + description, length, svd) - else: - print_svd_register( - csr.simple_csrs[i], csr_address, bits_str, length, svd) - is_first = False - else: - print_svd_register( - csr.simple_csrs[i], csr_address, bits_str, length, svd) - csr_address = csr_address + 4 - else: - length = ((csr.size + region.busword - 1) // - region.busword) * region.busword - print_svd_register( - csr, csr_address, description, length, svd) - csr_address = csr_address + 4 - print(' ', file=svd) - print(' ', file=svd) - print(' 0', file=svd) - print( - ' 0x{:x}'.format(csr_address), file=svd) - print(' registers', file=svd) - print(' ', file=svd) - if region.name in interrupts: - print(' ', file=svd) - print(' {}'.format(region.name), file=svd) - print( - ' {}'.format(interrupts[region.name]), file=svd) - print(' ', file=svd) - print(' ', file=svd) - print(' ', file=svd) - print('', file=svd) + svd.write(export.get_svd(soc, **kwargs)) def generate_docs(soc, base_dir, project_name="LiteX SoC Project", @@ -282,7 +130,7 @@ Documentation for {} if len(additional_modules) > 0: print(""" Modules -------- +======= .. toctree:: :maxdepth: 1 @@ -293,7 +141,7 @@ Modules if len(documented_regions) > 0: print(""" Register Groups ---------------- +=============== .. toctree:: :maxdepth: 1 @@ -303,7 +151,7 @@ Register Groups print(""" Indices and tables ------------------- +================== * :ref:`genindex` * :ref:`modindex` diff --git a/litex/soc/integration/export.py b/litex/soc/integration/export.py index d38c32d6..c91f5352 100644 --- a/litex/soc/integration/export.py +++ b/litex/soc/integration/export.py @@ -23,6 +23,11 @@ from litex.soc.interconnect.csr import CSRStatus from litex.build.tools import generated_banner +from litex.soc.doc.rst import reflow +from litex.soc.doc.module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts +from litex.soc.doc.csr import DocumentedCSRRegion +from litex.soc.interconnect.csr import _CompoundCSR + # CPU files ---------------------------------------------------------------------------------------- def get_cpu_mak(cpu, compile_software): @@ -273,3 +278,150 @@ def get_csr_csv(csr_regions={}, constants={}, mem_regions={}): d["memories"][name]["type"], ) return r + +# SVD Export -------------------------------------------------------------------------------------- + +def get_svd(soc, vendor="litex", name="soc", description=None): + def sub_csr_bit_range(busword, csr, offset): + nwords = (csr.size + busword - 1)//busword + i = nwords - offset - 1 + nbits = min(csr.size - i*busword, busword) - 1 + name = (csr.name + str(i) if nwords > 1 else csr.name).upper() + origin = i*busword + return (origin, nbits, name) + + def print_svd_register(csr, csr_address, description, length, svd): + svd.append(' ') + svd.append(' {}'.format(csr.short_numbered_name)) + if description is not None: + svd.append(' '.format(description)) + svd.append(' 0x{:04x}'.format(csr_address)) + svd.append(' 0x{:02x}'.format(csr.reset_value)) + svd.append(' {}'.format(length)) + svd.append(' {}'.format(csr.access)) + csr_address = csr_address + 4 + svd.append(' ') + if hasattr(csr, "fields") and len(csr.fields) > 0: + for field in csr.fields: + svd.append(' ') + svd.append(' {}'.format(field.name)) + svd.append(' {}'.format(field.offset + + field.size - 1)) + svd.append(' [{}:{}]'.format( + field.offset + field.size - 1, field.offset)) + svd.append(' {}'.format(field.offset)) + svd.append(' '.format( + reflow(field.description))) + svd.append(' ') + else: + field_size = csr.size + field_name = csr.short_name.lower() + # Strip off "ev_" from eventmanager fields + if field_name == "ev_enable": + field_name = "enable" + elif field_name == "ev_pending": + field_name = "pending" + elif field_name == "ev_status": + field_name = "status" + svd.append(' ') + svd.append(' {}'.format(field_name)) + svd.append(' {}'.format(field_size - 1)) + svd.append(' [{}:{}]'.format(field_size - 1, 0)) + svd.append(' {}'.format(0)) + svd.append(' ') + svd.append(' ') + svd.append(' ') + + interrupts = {} + for csr, irq in sorted(soc.soc_interrupt_map.items()): + interrupts[csr] = irq + + documented_regions = [] + + raw_regions = [] + if hasattr(soc, "get_csr_regions"): + raw_regions = soc.get_csr_regions() + else: + for region_name, region in soc.csr_regions.items(): + raw_regions.append((region_name, region.origin, + region.busword, region.obj)) + for csr_region in raw_regions: + documented_regions.append(DocumentedCSRRegion( + csr_region, csr_data_width=soc.csr_data_width)) + + svd = [] + svd.append('') + svd.append('') + svd.append('') + svd.append(' {}'.format(vendor)) + svd.append(' {}'.format(name.upper())) + if description is not None: + svd.append(' '.format(reflow(description))) + svd.append('') + svd.append(' 8') + svd.append(' 32') + svd.append(' 32') + svd.append(' read-write') + svd.append(' 0x00000000') + svd.append(' 0xFFFFFFFF') + svd.append('') + svd.append(' ') + + for region in documented_regions: + csr_address = 0 + svd.append(' ') + svd.append(' {}'.format(region.name.upper())) + svd.append(' 0x{:08X}'.format(region.origin)) + svd.append(' {}'.format(region.name.upper())) + if len(region.sections) > 0: + svd.append(' '.format( + reflow(region.sections[0].body()))) + svd.append(' ') + for csr in region.csrs: + description = None + if hasattr(csr, "description"): + description = csr.description + if isinstance(csr, _CompoundCSR) and len(csr.simple_csrs) > 1: + is_first = True + for i in range(len(csr.simple_csrs)): + (start, length, name) = sub_csr_bit_range( + region.busword, csr, i) + if length > 0: + bits_str = "Bits {}-{} of `{}`.".format( + start, start+length, csr.name) + else: + bits_str = "Bit {} of `{}`.".format( + start, csr.name) + if is_first: + if description is not None: + print_svd_register( + csr.simple_csrs[i], csr_address, bits_str + " " + description, length, svd) + else: + print_svd_register( + csr.simple_csrs[i], csr_address, bits_str, length, svd) + is_first = False + else: + print_svd_register( + csr.simple_csrs[i], csr_address, bits_str, length, svd) + csr_address = csr_address + 4 + else: + length = ((csr.size + region.busword - 1) // + region.busword) * region.busword + print_svd_register( + csr, csr_address, description, length, svd) + csr_address = csr_address + 4 + svd.append(' ') + svd.append(' ') + svd.append(' 0') + svd.append(' 0x{:x}'.format(csr_address)) + svd.append(' registers') + svd.append(' ') + if region.name in interrupts: + svd.append(' ') + svd.append(' {}'.format(region.name)) + svd.append(' {}'.format(interrupts[region.name])) + svd.append(' ') + svd.append(' ') + svd.append(' ') + svd.append('') + return "\n".join(svd)