Files
simulation_core/tools/peripherals_scanner.py

614 lines
18 KiB
Python
Raw Normal View History

#!/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 += ' <h4 onclick="{1}">{0}</h4>\n'.format(platform.get_name(), "showPeripherals(this, '{}')".format(platform.get_name()))
return res
def platform_peripherals_table(platform):
res = ''
res += '<table class="boards-table docutils align-default" style="margin-top: 10px">\n'
res += '<tr>\n'
res += '<th>kind</td>\n'
res += '<th>type</td>\n'
res += '</tr>\n'
for peripheral in platform.get_all_peripherals():
res += '<tr>\n'
res += '<td>{}</td>\n'.format(peripheral._kind)
res += '<td><a href="{}">{}</a>{}</td>\n'.format(peripheral._uri, peripheral._type, ' (x{})'.format(peripheral._count) if peripheral._count > 1 else '')
res += '</tr>\n'
res += '</table>\n'
return res
def generate_html():
res = ''
res +="""
<style>
.category h3 {
background-color: #666;
border: none;
// color: #404040;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 4px 2px;
cursor: pointer;
border-radius: 16px;
}
.platform h4 {
background-color: white;
border: solid 1px;
border-color: #007ded;
color: #007ded;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 4px 2px;
cursor: pointer;
border-radius: 16px;
}
.platform_selected h4 {
background-color: #007ded;
border: solid 1px;
border-color: #007ded;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 4px 2px;
cursor: pointer;
border-radius: 16px;
}
div.category {
display: inline-block;
}
div.platform {
display: none;
}
</style>
"""
PLATFORMS.sort(key=lambda x: x.get_name())
res += '<div style="margin-top: 10px; margin-bottom: 10px">\n'
for category in sorted(CATEGORIES):
res += '<div class="category">\n'.format(category)
res += ' <h3 onclick="toggleCategory(this)">{}</h3>\n'.format(category)
for platform in [x for x in PLATFORMS if x._category == category]:
res += ' <div class="platform">\n'.format(platform_to_html(platform))
res += platform_to_html(platform)
res += ' </div>\n'
res += '</div>\n'
res += '</div>\n'
for platform in PLATFORMS:
res += ' <div id="peripherals-{}" class="peripherals-table" style="display: none">\n'.format(platform.get_name())
res += platform_peripherals_table(platform)
res += ' </div>\n'
res +="""
<script>
function hideAllPeripherals () {
var kids = document.getElementsByClassName('peripherals-table');
for (kid of kids) {
kid.style.display = "none";
}
var kids = document.getElementsByClassName('platform_selected');
for (kid of kids) {
kid.className = "platform";
}
}
function showPeripherals (target, name) {
hideAllPeripherals();
target.parentElement.className = 'platform_selected';
var targetDiv = document.getElementById('peripherals-' + name);
targetDiv.style.display = '';
}
function toogleElementsByClass (element, className) {
var kids = element.getElementsByClassName(className);
for (kid of kids) {
if(kid.style.display == "none" || kid.style.display == "")
kid.style.display = "inline-block";
else
kid.style.display = "none";
}
}
function toggleCategory (element) {
hideAllPeripherals();
toogleElementsByClass(element.parentElement, "platform");
}
</script>
"""
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()