#!/usr/bin/python # pylint: disable=line-too-long, C0325, missing-docstring import os import re import json import argparse import fnmatch import logging from collections import Counter logging.basicConfig(level=logging.INFO) # parameters URI_PREFIX = "https://github.com/renode/renode/tree/master/" PERIPHERALS_URI_PREFIX = "https://github.com/renode/renode-infrastructure/tree/master/" PERIPHERAL_LINE_PATTERN = r'^[A-Za-z0-9_]+\s*:\s*([^\s]+)\s*@' USING_LINE_PATTERN = r'^using "([.A-Za-z0-9_/-]+)"' TOP_DIR = None GROUPS = [] PLATFORMS = [] PERIPHERALS = [] CATEGORIES = [] def get_or_create_platform(path): for p in PLATFORMS: if p._path == path: return p p = Platform(path) PLATFORMS.append(p) return p def get_or_create_peripheral(kind_name, class_name): p = Peripheral(kind_name, class_name) return p def try_get_category(platform): mapping = { "A2_CV32E40P": "OHG", "a20": "ALLWINNER", "ambiq-apollo4": "AMBIQ", "arduino_101-shield": "NRF", "arduino_nano_33_ble": "NRF", "arty_litex_vexriscv": "LITEX", "arvsom": "STARFIVE", "at91rm9200": "MICROCHIP", "atsamd21j17d-aft": "MICROCHIP", "atsamd51g19a": "MICROCHIP", "beaglev-fire": "PFSOC", "beaglev_starlight": "STARFIVE", "brd4116a": "EFM", "brd4117a": "EFM", "brd4118a": "EFM", "brd4120a": "EFM", "brd4121a": "EFM", "brd4162a": "EFM", "brd4186c": "EFM", "brd4402a": "EFM", "cc2538": "TI", "colibri-vf61": "NXP I.MX", "core-v-mcu": "OHG", "cortex-a53": "GENERIC ARM", "cortex-a53-gicv2": "GENERIC ARM", "cortex-a53-gicv3": "GENERIC ARM", "cortex-a53-gicv3_smp": "GENERIC ARM", "cortex_a53_virtio": "GENERIC ARM", "cortex_a53_console": "GENERIC ARM", "cortex-a78": "GENERIC ARM", "cortex-r52": "GENERIC ARM", "cortex-r52_smp": "GENERIC ARM", "cortex-r52_smp_4": "GENERIC ARM", "cortex-r8": "GENERIC ARM", "crosslink-nx-evn": "LITEX", "efm32g210": "EFM", "efm32g222": "EFM", "efm32g232": "EFM", "efm32g842": "EFM", "efm32g890": "EFM", "efm32gg942": "EFM", "efm32gg995": "EFM", "efm32hg350": "EFM", "efm32jg1": "EFM", "efm32jg12": "EFM", "efm32lg942": "EFM", "efm32lg995": "EFM", "efm32pg1": "EFM", "efm32pg12": "EFM", "efm32tg840": "EFM", "efm32wg995": "EFM", "efm32zg222": "EFM", "efr32mg1": "EFM", "efr32mg12": "EFM", "efr32mg13": "EFM", "efr32mg24": "EFM", "efr32mg26": "EFM", "efr32xg22": "EFM", "egis_et171": "EGIS", "focaltech_ft9001": "FOCALTECH", "eos-s3": "EOS", "eos-s3-qomu": "EOS", "eos-s3-quickfeather": "EOS", "ezr32hg320": "EFM", "ezr32lg330": "EFM", "ezr32wg330": "EMF", "fomu": "LITEX", "fsl_lx2160ardb": "NXP LAYERSCAPE", "gr716": "GAISLER", "gr716-devboard": "GAISLER", "gr712rc": "GAISLER", "x86": "X86", "ice40up5k-mdp-evn": "LITEX", "imxrt1064": "NXP I.MX", "imxrt500": "NXP I.MX", "mimxrt798s": "NXP I.MX", "mimxrt700_evk": "NXP I.MX", "kendryte_k210": "KENDRYTE", "leon3": "GAISLER", "leon3-externals": "GAISLER", "litex_common": "LITEX", "litex_ibex": "LITEX", "litex_linux_vexriscv_sdcard": "LITEX", "litex_microwatt": "LITEX", "litex_minerva": "LITEX", "litex_nexys_video_vexriscv_linux": "LITEX", "litex_picorv32": "LITEX", "litex_tock": "LITEX", "litex_vexriscv": "LITEX", "litex_vexriscv_linux": "LITEX", "litex_vexriscv_micropython": "LITEX", "litex_vexriscv_smp": "LITEX", "litex_vexriscv_tftp": "LITEX", "litex_vexriscv_verilated_cfu": "LITEX", "litex_vexriscv_verilated_liteuart": "LITEX", "litex_vexriscv_zephyr": "LITEX", "litex_zephyr_vexriscv_i2s": "LITEX", "lpc2294": "NXP LPC", "mars_zx3": "ZYNQ", "mars_zx3-externals": "ZYNQ", "max32652": "MAXIM", "max32652-evkit": "MAXIM", "microwatt": "POWERPC", "mimxrt1064_evk": "NXP I.MX", "miv": "MIV", "miv_rv32": "MIV", "miv-board": "MIV", "miv-board-additional-uarts": "MIV", "mpc5567": "NXP PPC", "mpfs-icicle-kit": "PFSOC", "msp430f2619": "MSP430", "murax_vexriscv": "OTHER RISC-V", "murax_vexriscv_verilated_uart": "OTHER RISC-V", "nrf52840": "NORDIC", "nrf52840dk_nrf52840": "NORDIC", "nucleo_wba52cg": "STM", "nucleo_h753zi": "STM", "nxp-k6xf": "NXP KINETIS", "opentitan-earlgrey": "OTHER RISC-V", "opentitan-earlgrey-cw310": "OTHER RISC-V", "picosoc": "OTHER RISC-V", "polarfire-soc": "PFSOC", "quark-c1000": "X86", "quark_c1000-cc2520": "X86", "acrn_x86_64": "X86-64", "up_squared_x86_64": "X86-64", "renesas_rz_t2m": "RENESAS", "renesas_rz_t2m_rsk": "RENESAS", "renesas_rz_g2l": "RENESAS", "ri5cy": "OTHER RISC-V", "riscv_verilated_uartlite": "OTHER RISC-V", "riscv_virt": "OTHER RISC-V", "s32k118": "NXP S32K", "nxp-s32k388": "NXP S32K", "sam_e70": "MICROCHIP", "sam4s": "MICROCHIP", "sam4s8b": "MICROCHIP", "sam4s16c": "MICROCHIP", "sam4s_xplained": "MICROCHIP", "sifive-fe310": "SIFIVE", "sifive-fu540": "SIFIVE", "sifive-fu740": "SIFIVE", "sltb001a": "EFM", "sltb004a": "EFM", "slwstk6220a": "EFM", "starfive-jh7100": "STARFIVE", "stk3200": "EFM", "stk3600": "EFM", "stk3700": "EFM", "stk3800": "EFM", "stm32f0": "STM", "stm32f042": "STM", "stm32f072": "STM", "stm32f072b_discovery": "STM", "stm32f103": "STM", "stm32f4": "STM", "stm32f412": "STM", "stm32f429": "STM", "stm32f4_discovery": "STM", "stm32f4_discovery-additional_gpios": "STM", "stm32f4_discovery-bb": "STM", "stm32f4_discovery-kit": "STM", "stm32f746": "STM", "stm32f7_discovery-bb": "STM", "stm32g0": "STM", "stm32h753": "STM", "stm32h743": "STM", "stm32l071": "STM", "stm32l072": "STM", "stm32l151": "STM", "stm32l552": "STM", "stm32w108": "STM", "stm32wba52": "STM", "tegra2": "TEGRA", "tegra3": "TEGRA", "tegra_externals": "TEGRA", "tock_veer_el2_sim": "OTHER RISC-V", "ut32m0r500": "GAISLER", "verilated_ibex": "LITEX", "versatile": "GENERIC ARM", "vexpress": "GENERIC ARM", "vexpress-externals": "GENERIC ARM", "vybrid": "NXP I.MX", "xtensa-sample-controller": "XTENSA", "zedboard": "ZYNQ", "zedboard-externals": "ZYNQ", "zolertia-firefly": "ZYNQ", "xilinx_zynqmp_r5": "ZYNQ", "zynq-7000": "ZYNQ", "zynqmp": "ZYNQ", "zynqmp-zcu102-revA": "ZYNQ", "zynqmp-zcu102-revB": "ZYNQ", "zynqmp-zcu104": "ZYNQ", "cortex-a9_smp": "GENERIC ARM", "cortex-a9": "GENERIC ARM", "cortex-r8_smp": "GENERIC ARM", "cortex-r8": "GENERIC ARM", "nuvoton_npcx9": "NUVOTON", "nuvoton_npcx9m6fb_evb": "NUVOTON", "andes_ae350_n25": "ANDES", "renesas-r7fa8m1a": "RENESAS", "renesas-r7fa2l1a": "RENESAS", "renesas-r7fa2e1a9": "RENESAS", "renesas-r7fa6m5b": "RENESAS", "renesas-r7fa4m1a": "RENESAS", "ck-ra6m5": "RENESAS", "renesas-da14592": "RENESAS", "ek-ra2e1": "RENESAS", "ek-ra8m1": "RENESAS", "arduino_uno_r4_minima": "RENESAS", "renesas_ck_ra6m5_sensors_example": "RENESAS", "renesas-ck_ra6m5": "RENESAS", "renesas-ek_ra2e1": "RENESAS", "renesas-ek_ra8m1": "RENESAS", "renesas-rz_t2m_rsk": "RENESAS", "vegaboard_ri5cy": "OTHER RISC-V", "x86-kvm": "X86", "x86_64-kvm": "X86-64", "x86_64-kvm-virtio": "X86-64", } if platform.get_name() not in mapping: raise Exception("don't know category for: {} ({})".format(platform.get_name(), platform._path)) result = mapping[platform.get_name()] if result not in CATEGORIES: CATEGORIES.append(result) return result class Platform: def __init__(self, path): self._path = path self._peripherals = [] self._usings = [] self._cached_res = None self._category = try_get_category(self) self._parse() def get_name(self): return os.path.splitext(os.path.basename(self._path))[0] def get_path(self): return self._path def get_peripherals(self): if self._cached_res: return self._cached_res res = {} for u in self._usings: ps = u.get_peripherals() for k in ps: if k not in res: res[k] = ps[k] else: res[k].extend(ps[k]) for p in self._peripherals: if p._kind not in res: res[p._kind] = [p._type] else: res[p._kind].append(p._type) self._cached_res = res return res def get_all_peripherals(self): # if self._cached_res: # return self._cached_res res = [] for u in self._usings: ps = u.get_all_peripherals() for k in ps: found = False for x in res: if x == k: x._count += 1 found = True break if not found: res.append(k) for k in self._peripherals: found = False for x in res: if x == k: x._count += 1 found = True break if not found: res.append(k) # self._cached_res = res return res def _parse(self): with open(self._path, 'r') as f: for line in f.readlines(): # maybe it's a using statement m = re.search(USING_LINE_PATTERN, line) if m: match = m.group(1) if match.startswith('.'): path = os.path.join(os.path.dirname(self._path), match) else: path = os.path.join(TOP_DIR, match) self._usings.append(get_or_create_platform(path)) continue # maybe it's a peripheral definition p = Peripheral.try_parse(line) if p: added = False for xp in self._peripherals: if xp == p: xp._count += 1 added = True break if not added: self._peripherals.append(p) continue def __str__(self): res = "[PLATFORM: {}]".format(self._path) for u in self._usings: res += "\n [USING: {}]".format(u) for p in self._peripherals: res += "\n {}".format(p) return res class Peripheral: def __init__(self, kind, type): self._kind = kind self._type = type self._count = 1 path = find_file(TOP_DIR + '/src/Infrastructure', self._type + '.cs') self._uri = '{}{}'.format(PERIPHERALS_URI_PREFIX, path) @staticmethod def try_parse(line): m = re.search(PERIPHERAL_LINE_PATTERN, line) if not m: return None type_name = m.group(1) # put all peripherals without a namespace into 'Others' if '.' not in type_name: type_name = "Others." + type_name kind_name, class_name = type_name.split('.', 1) return get_or_create_peripheral(kind_name, class_name) def __str__(self): res = "[PERIPHERAL: {} / {}]".format(self._kind, self._type) return res def __hash__(self): return hash(self._kind + self._type) def __eq__(self, other): return self._kind == other._kind and self._type == other._type def find_file(root_folder, fname): for _root, _, _filenames in os.walk(root_folder): if fname in _filenames: return os.path.join(_root[len(root_folder) + 1:], fname) return None def scan(folder): # scan for json files for root, dirnames, filenames in os.walk(folder): for filename in fnmatch.filter(filenames, '*.repl'): if 'fomu_led' in filename: continue platform = get_or_create_platform(os.path.join(root, filename)) for kind in platform.get_peripherals(): if kind not in GROUPS: GROUPS.append(kind) def generate_json(): return json.dumps(PLATFORMS, default=lambda x: {i:x.__dict__[i] for i in x.__dict__ if i != '_cached_res'}, indent=4, sort_keys=True) def platform_to_html(platform): res = '' res += '

{0}

\n'.format(platform.get_name(), "showPeripherals(this, '{}')".format(platform.get_name())) return res def platform_peripherals_table(platform): res = '' res += '\n' res += '\n' res += '\n' for peripheral in platform.get_all_peripherals(): res += '\n' res += '\n'.format(peripheral._kind) res += '\n'.format(peripheral._uri, peripheral._type, ' (x{})'.format(peripheral._count) if peripheral._count > 1 else '') res += '\n' res += '
kind\n' res += 'type\n' res += '
{}{}{}
\n' return res def generate_html(): res = '' res +=""" """ PLATFORMS.sort(key=lambda x: x.get_name()) res += '
\n' for category in sorted(CATEGORIES): res += '
\n'.format(category) res += '

{}

\n'.format(category) for platform in [x for x in PLATFORMS if x._category == category]: res += '
\n'.format(platform_to_html(platform)) res += platform_to_html(platform) res += '
\n' res += '
\n' res += '
\n' for platform in PLATFORMS: res += ' \n' res +=""" """ return res def main(): global TOP_DIR parser = argparse.ArgumentParser() parser.add_argument("-d", "--dir", dest="dir", action="store", default=".", help="Directory to scan") parser.add_argument("-J", "--json", dest="generate_json", action="store_true", default=False, help="Generate JSON output") parser.add_argument("-H", "--html", dest="generate_html", action="store_true", default=False, help="Generate HTML output") options = parser.parse_args() flag = False TOP_DIR = options.dir scan(options.dir + '/platforms') if options.generate_json: flag = True print(generate_json()) if options.generate_html: flag = True print(generate_html()) if not flag: parser.print_help() main()