scripts: introduce list_hardware.py for listing of architectures and SoCs
The list_hardware.py script parses archs.yml in all <arch-root>/arch folders and soc.yml in all <soc-root>/soc sub-folders. The archs.yml and soc.yml are introduced with hw model v2. Hw model v2 removes the need for architecture knowledge of the SoCs, and as part of this makes multi-arch and multi-core SoCs possible. Hw model v2 also allows for greater flexibility in arch and SoC organization as they can be organized freely. As example SoCs can be organized by vendors, architecture, or any other way as the socs.yml contains the path to the location of the SoC, instead of relying on a specific arch. Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
This commit is contained in:
committed by
Jamie McCrae
parent
a4d1980c35
commit
61bbfb5ba2
4
arch/archs.yml
Normal file
4
arch/archs.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
archs:
|
||||
- name: empty
|
||||
folder: empty
|
||||
comment: initial file, no arch v2 defined.
|
||||
108
cmake/modules/hwm_v2.cmake
Normal file
108
cmake/modules/hwm_v2.cmake
Normal file
@@ -0,0 +1,108 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright (c) 2023, Nordic Semiconductor ASA
|
||||
|
||||
# This CMake module works together with the list_hardware.py script to obtain
|
||||
# all archs and SoC implementations defined in the Zephyr build system.
|
||||
#
|
||||
# The result from list_hardware.py is then used to generate Kconfig files for
|
||||
# the build system.
|
||||
#
|
||||
# The following files are generated in '<kconfig-binary-dir>/soc'
|
||||
# - Kconfig.defconfig: Contains references to SoC defconfig files for Zephyr integration.
|
||||
# - Kconfig: Contains references to regular SoC Kconfig files for Zephyr integration.
|
||||
# - Kconfig.soc: Contains references to generic SoC Kconfig files.
|
||||
#
|
||||
# The following file is generated in '<kconfig-binary-dir>/arch'
|
||||
# - Kconfig: Contains references to regular arch Kconfig files for Zephyr integration.
|
||||
|
||||
include_guard(GLOBAL)
|
||||
|
||||
if(NOT HWMv2)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Internal helper function for creation of Kconfig files.
|
||||
function(kconfig_gen bin_dir file dirs)
|
||||
file(MAKE_DIRECTORY "${bin_dir}")
|
||||
set(kconfig_file ${bin_dir}/${file})
|
||||
foreach(dir ${dirs})
|
||||
file(APPEND ${kconfig_file} "osource \"${dir}/${file}\"\n")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# 'SOC_ROOT' and 'ARCH_ROOT' are prioritized lists of directories where their
|
||||
# implementations may be found. It always includes ${ZEPHYR_BASE}/[arch|soc]
|
||||
# at the lowest priority.
|
||||
list(APPEND SOC_ROOT ${ZEPHYR_BASE})
|
||||
list(APPEND ARCH_ROOT ${ZEPHYR_BASE})
|
||||
|
||||
list(TRANSFORM ARCH_ROOT PREPEND "--arch-root=" OUTPUT_VARIABLE arch_root_args)
|
||||
list(TRANSFORM SOC_ROOT PREPEND "--soc-root=" OUTPUT_VARIABLE soc_root_args)
|
||||
|
||||
execute_process(COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/list_hardware.py
|
||||
${arch_root_args} ${soc_root_args}
|
||||
--archs --socs
|
||||
--cmakeformat={TYPE}\;{NAME}\;{DIR}\;{HWM}
|
||||
OUTPUT_VARIABLE ret_hw
|
||||
ERROR_VARIABLE err_hw
|
||||
RESULT_VARIABLE ret_val
|
||||
)
|
||||
if(ret_val)
|
||||
message(FATAL_ERROR "Error listing hardware.\nError message: ${err_hw}")
|
||||
endif()
|
||||
|
||||
set(kconfig_soc_source_dir)
|
||||
|
||||
while(TRUE)
|
||||
string(FIND "${ret_hw}" "\n" idx REVERSE)
|
||||
math(EXPR start "${idx} + 1")
|
||||
string(SUBSTRING "${ret_hw}" ${start} -1 line)
|
||||
string(SUBSTRING "${ret_hw}" 0 ${idx} ret_hw)
|
||||
|
||||
cmake_parse_arguments(HWM "" "TYPE" "" ${line})
|
||||
if(HWM_TYPE STREQUAL "arch")
|
||||
cmake_parse_arguments(ARCH_V2 "" "NAME;DIR" "" ${line})
|
||||
|
||||
list(APPEND kconfig_arch_source_dir "${ARCH_V2_DIR}")
|
||||
list(APPEND ARCH_V2_NAME_LIST ${ARCH_V2_NAME})
|
||||
string(TOUPPER "${ARCH_V2_NAME}" ARCH_V2_NAME_UPPER)
|
||||
set(ARCH_V2_${ARCH_V2_NAME_UPPER}_DIR ${ARCH_V2_DIR})
|
||||
elseif(HWM_TYPE MATCHES "^soc|^series|^family")
|
||||
cmake_parse_arguments(SOC_V2 "" "NAME;DIR;HWM" "" ${line})
|
||||
|
||||
list(APPEND kconfig_soc_source_dir "${SOC_V2_DIR}")
|
||||
|
||||
if(HWM_TYPE STREQUAL "soc")
|
||||
set(setting_name SOC_${SOC_V2_NAME}_DIR)
|
||||
else()
|
||||
set(setting_name SOC_${HWM_TYPE}_${SOC_V2_NAME}_DIR)
|
||||
endif()
|
||||
string(TOUPPER ${setting_name} setting_name)
|
||||
set(${setting_name} ${SOC_V2_DIR})
|
||||
endif()
|
||||
|
||||
if(idx EQUAL -1)
|
||||
break()
|
||||
endif()
|
||||
endwhile()
|
||||
list(REMOVE_DUPLICATES kconfig_soc_source_dir)
|
||||
|
||||
# Support multiple ARCH_ROOT and SOC_ROOT
|
||||
set(arch_kconfig_file Kconfig)
|
||||
set(soc_defconfig_file Kconfig.defconfig)
|
||||
set(soc_zephyr_file Kconfig)
|
||||
set(soc_kconfig_file Kconfig.soc)
|
||||
set(arch_kconfig_header "# Load arch Kconfig descriptions.\n")
|
||||
set(defconfig_header "# Load Zephyr SoC Kconfig defconfig.\n")
|
||||
set(soc_zephyr_header "# Load Zephyr SoC Kconfig descriptions.\n")
|
||||
set(soc_kconfig_header "# Load SoC Kconfig descriptions.\n")
|
||||
file(WRITE ${KCONFIG_BINARY_DIR}/arch/${arch_kconfig_file} "${arch_kconfig_header}")
|
||||
file(WRITE ${KCONFIG_BINARY_DIR}/soc/${soc_defconfig_file} "${defconfig_header}")
|
||||
file(WRITE ${KCONFIG_BINARY_DIR}/soc/${soc_zephyr_file} "${soc_zephyr_header}")
|
||||
file(WRITE ${KCONFIG_BINARY_DIR}/soc/${soc_kconfig_file} "${soc_kconfig_header}")
|
||||
|
||||
kconfig_gen("${KCONFIG_BINARY_DIR}/arch" "${arch_kconfig_file}" "${kconfig_arch_source_dir}")
|
||||
kconfig_gen("${KCONFIG_BINARY_DIR}/soc" "${soc_defconfig_file}" "${kconfig_soc_source_dir}")
|
||||
kconfig_gen("${KCONFIG_BINARY_DIR}/soc" "${soc_zephyr_file}" "${kconfig_soc_source_dir}")
|
||||
kconfig_gen("${KCONFIG_BINARY_DIR}/soc" "${soc_kconfig_file}" "${kconfig_soc_source_dir}")
|
||||
@@ -96,6 +96,7 @@ list(APPEND zephyr_cmake_modules zephyr_module)
|
||||
list(APPEND zephyr_cmake_modules boards)
|
||||
list(APPEND zephyr_cmake_modules shields)
|
||||
list(APPEND zephyr_cmake_modules snippets)
|
||||
list(APPEND zephyr_cmake_modules hwm_v2)
|
||||
list(APPEND zephyr_cmake_modules arch)
|
||||
list(APPEND zephyr_cmake_modules configuration_files)
|
||||
list(APPEND zephyr_cmake_modules generated_file_directories)
|
||||
|
||||
283
scripts/list_hardware.py
Executable file
283
scripts/list_hardware.py
Executable file
@@ -0,0 +1,283 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import argparse
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path, PurePath
|
||||
import pykwalify.core
|
||||
import sys
|
||||
from typing import List
|
||||
import yaml
|
||||
|
||||
|
||||
SOC_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'soc-schema.yml')
|
||||
with open(SOC_SCHEMA_PATH, 'r') as f:
|
||||
soc_schema = yaml.safe_load(f.read())
|
||||
|
||||
ARCH_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'arch-schema.yml')
|
||||
with open(ARCH_SCHEMA_PATH, 'r') as f:
|
||||
arch_schema = yaml.safe_load(f.read())
|
||||
|
||||
SOC_YML = 'soc.yml'
|
||||
ARCHS_YML_PATH = PurePath('arch/archs.yml')
|
||||
|
||||
class Systems:
|
||||
|
||||
def __init__(self, folder='', soc_yaml=None):
|
||||
self._socs = []
|
||||
self._series = []
|
||||
self._families = []
|
||||
|
||||
if soc_yaml is None:
|
||||
return
|
||||
|
||||
try:
|
||||
data = yaml.safe_load(soc_yaml)
|
||||
pykwalify.core.Core(source_data=data,
|
||||
schema_data=soc_schema).validate()
|
||||
except (yaml.YAMLError, pykwalify.errors.SchemaError) as e:
|
||||
sys.exit(f'ERROR: Malformed yaml {soc_yaml.as_posix()}', e)
|
||||
|
||||
for f in data.get('family', []):
|
||||
family = Family(f['name'], folder, [], [])
|
||||
for s in f.get('series', []):
|
||||
series = Series(s['name'], folder, f['name'], [])
|
||||
socs = [(Soc(soc['name'],
|
||||
[c['name'] for c in soc.get('cpuclusters', [])],
|
||||
folder, s['name'], f['name']))
|
||||
for soc in s.get('socs', [])]
|
||||
series.socs.extend(socs)
|
||||
self._series.append(series)
|
||||
self._socs.extend(socs)
|
||||
family.series.append(series)
|
||||
family.socs.extend(socs)
|
||||
socs = [(Soc(soc['name'],
|
||||
[c['name'] for c in soc.get('cpuclusters', [])],
|
||||
folder, None, f['name']))
|
||||
for soc in f.get('socs', [])]
|
||||
self._socs.extend(socs)
|
||||
self._families.append(family)
|
||||
|
||||
for s in data.get('series', []):
|
||||
series = Series(s['name'], folder, '', [])
|
||||
socs = [(Soc(soc['name'],
|
||||
[c['name'] for c in soc.get('cpuclusters', [])],
|
||||
folder, s['name'], ''))
|
||||
for soc in s.get('socs', [])]
|
||||
series.socs.extend(socs)
|
||||
self._series.append(series)
|
||||
self._socs.extend(socs)
|
||||
|
||||
socs = [(Soc(soc['name'],
|
||||
[c['name'] for c in soc.get('cpuclusters', [])],
|
||||
folder, '', ''))
|
||||
for soc in data.get('socs', [])]
|
||||
self._socs.extend(socs)
|
||||
|
||||
@staticmethod
|
||||
def from_file(socs_file):
|
||||
'''Load SoCs from a soc.yml file.
|
||||
'''
|
||||
try:
|
||||
with open(socs_file, 'r') as f:
|
||||
socs_yaml = f.read()
|
||||
except FileNotFoundError as e:
|
||||
sys.exit(f'ERROR: socs.yml file not found: {socs_file.as_posix()}', e)
|
||||
|
||||
return Systems(str(socs_file.parent), socs_yaml)
|
||||
|
||||
@staticmethod
|
||||
def from_yaml(socs_yaml):
|
||||
'''Load socs from a string with YAML contents.
|
||||
'''
|
||||
return Systems('', socs_yaml)
|
||||
|
||||
def extend(self, systems):
|
||||
self._families.extend(systems.get_families())
|
||||
self._series.extend(systems.get_series())
|
||||
self._socs.extend(systems.get_socs())
|
||||
|
||||
def get_families(self):
|
||||
return self._families
|
||||
|
||||
def get_series(self):
|
||||
return self._series
|
||||
|
||||
def get_socs(self):
|
||||
return self._socs
|
||||
|
||||
def get_soc(self, name):
|
||||
try:
|
||||
return next(s for s in self._socs if s.name == name)
|
||||
except StopIteration:
|
||||
sys.exit(f"ERROR: SoC '{name}' is not found, please ensure that the SoC exists "
|
||||
f"and that soc-root containing '{name}' has been correctly defined.")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Soc:
|
||||
name: str
|
||||
cpuclusters: List[str]
|
||||
folder: str
|
||||
series: str = ''
|
||||
family: str = ''
|
||||
|
||||
|
||||
@dataclass
|
||||
class Series:
|
||||
name: str
|
||||
folder: str
|
||||
family: str
|
||||
socs: List[Soc]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Family:
|
||||
name: str
|
||||
folder: str
|
||||
series: List[Series]
|
||||
socs: List[Soc]
|
||||
|
||||
|
||||
def find_v2_archs(args):
|
||||
ret = {'archs': []}
|
||||
for root in args.arch_roots:
|
||||
archs_yml = root / ARCHS_YML_PATH
|
||||
|
||||
if Path(archs_yml).is_file():
|
||||
with Path(archs_yml).open('r') as f:
|
||||
archs = yaml.safe_load(f.read())
|
||||
|
||||
try:
|
||||
pykwalify.core.Core(source_data=archs, schema_data=arch_schema).validate()
|
||||
except pykwalify.errors.SchemaError as e:
|
||||
sys.exit('ERROR: Malformed "build" section in file: {}\n{}'
|
||||
.format(archs_yml.as_posix(), e))
|
||||
|
||||
if args.arch is not None:
|
||||
archs = {'archs': list(filter(
|
||||
lambda arch: arch.get('name') == args.arch, archs['archs']))}
|
||||
for arch in archs['archs']:
|
||||
arch.update({'path': root / 'arch' / arch['path']})
|
||||
arch.update({'hwm': 'v2'})
|
||||
arch.update({'type': 'arch'})
|
||||
|
||||
ret['archs'].extend(archs['archs'])
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def find_v2_systems(args):
|
||||
yml_files = []
|
||||
systems = Systems()
|
||||
for root in args.soc_roots:
|
||||
yml_files.extend((root / 'soc').rglob(SOC_YML))
|
||||
|
||||
for soc_yml in yml_files:
|
||||
if soc_yml.is_file():
|
||||
systems.extend(Systems.from_file(soc_yml))
|
||||
|
||||
return systems
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(allow_abbrev=False)
|
||||
add_args(parser)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def add_args(parser):
|
||||
default_fmt = '{name}'
|
||||
|
||||
parser.add_argument("--soc-root", dest='soc_roots', default=[],
|
||||
type=Path, action='append',
|
||||
help='add a SoC root, may be given more than once')
|
||||
parser.add_argument("--soc", default=None, help='lookup the specific soc')
|
||||
parser.add_argument("--soc-series", default=None, help='lookup the specific soc series')
|
||||
parser.add_argument("--soc-family", default=None, help='lookup the specific family')
|
||||
parser.add_argument("--socs", action='store_true', help='lookup all socs')
|
||||
parser.add_argument("--arch-root", dest='arch_roots', default=[],
|
||||
type=Path, action='append',
|
||||
help='add a arch root, may be given more than once')
|
||||
parser.add_argument("--arch", default=None, help='lookup the specific arch')
|
||||
parser.add_argument("--archs", action='store_true', help='lookup all archs')
|
||||
parser.add_argument("--format", default=default_fmt,
|
||||
help='''Format string to use to list each soc.''')
|
||||
parser.add_argument("--cmakeformat", default=None,
|
||||
help='''CMake format string to use to list each arch/soc.''')
|
||||
|
||||
|
||||
def dump_v2_archs(args):
|
||||
archs = find_v2_archs(args)
|
||||
|
||||
for arch in archs['archs']:
|
||||
if args.cmakeformat is not None:
|
||||
info = args.cmakeformat.format(
|
||||
TYPE='TYPE;' + arch['type'],
|
||||
NAME='NAME;' + arch['name'],
|
||||
DIR='DIR;' + str(arch['path']),
|
||||
HWM='HWM;' + arch['hwm'],
|
||||
# Below is non exising for arch but is defined here to support
|
||||
# common formatting string.
|
||||
SERIES='',
|
||||
FAMILY='',
|
||||
ARCH='',
|
||||
VENDOR=''
|
||||
)
|
||||
else:
|
||||
info = args.format.format(
|
||||
type=arch.get('type'),
|
||||
name=arch.get('name'),
|
||||
dir=arch.get('path'),
|
||||
hwm=arch.get('hwm'),
|
||||
# Below is non exising for arch but is defined here to support
|
||||
# common formatting string.
|
||||
series='',
|
||||
family='',
|
||||
arch='',
|
||||
vendor=''
|
||||
)
|
||||
|
||||
print(info)
|
||||
|
||||
|
||||
def dump_v2_system(args, type, system):
|
||||
if args.cmakeformat is not None:
|
||||
info = args.cmakeformat.format(
|
||||
TYPE='TYPE;' + type,
|
||||
NAME='NAME;' + system.name,
|
||||
DIR='DIR;' + system.folder,
|
||||
HWM='HWM;' + 'v2'
|
||||
)
|
||||
else:
|
||||
info = args.format.format(
|
||||
type=type,
|
||||
name=system.name,
|
||||
dir=system.folder,
|
||||
hwm='v2'
|
||||
)
|
||||
|
||||
print(info)
|
||||
|
||||
|
||||
def dump_v2_systems(args):
|
||||
systems = find_v2_systems(args)
|
||||
|
||||
for f in systems.get_families():
|
||||
dump_v2_system(args, 'family', f)
|
||||
|
||||
for s in systems.get_series():
|
||||
dump_v2_system(args, 'series', s)
|
||||
|
||||
for s in systems.get_socs():
|
||||
dump_v2_system(args, 'soc', s)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parse_args()
|
||||
if any([args.socs, args.soc, args.soc_series, args.soc_family]):
|
||||
dump_v2_systems(args)
|
||||
if args.archs or args.arch is not None:
|
||||
dump_v2_archs(args)
|
||||
29
scripts/schemas/arch-schema.yml
Normal file
29
scripts/schemas/arch-schema.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright (c) 2023, Nordic Semiconductor ASA
|
||||
|
||||
## A pykwalify schema for basic validation of the structure of a
|
||||
## arch metadata YAML file.
|
||||
##
|
||||
# The archs.yml file is a simple list of key value pairs containing architectures
|
||||
# and their location which is used by the build system.
|
||||
type: map
|
||||
mapping:
|
||||
archs:
|
||||
required: true
|
||||
type: seq
|
||||
sequence:
|
||||
- type: map
|
||||
mapping:
|
||||
name:
|
||||
required: true
|
||||
type: str
|
||||
desc: Name of the arch
|
||||
path:
|
||||
required: true
|
||||
type: str
|
||||
desc: Location of the arch implementation relative to the archs.yml file.
|
||||
comment:
|
||||
required: false
|
||||
type: str
|
||||
desc: Free form comment with extra information regarding the arch.
|
||||
72
scripts/schemas/soc-schema.yml
Normal file
72
scripts/schemas/soc-schema.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Copyright (c) 2023, Nordic Semiconductor ASA
|
||||
|
||||
## A pykwalify schema for basic validation of the structure of a SoC
|
||||
## metadata YAML file.
|
||||
##
|
||||
# The soc.yml file is a simple list of key value pairs containing SoCs
|
||||
# located and the current structure level.
|
||||
schema;cpucluster-schema:
|
||||
required: false
|
||||
type: seq
|
||||
sequence:
|
||||
- type: map
|
||||
mapping:
|
||||
name:
|
||||
required: true
|
||||
type: str
|
||||
|
||||
schema;soc-schema:
|
||||
required: false
|
||||
type: seq
|
||||
sequence:
|
||||
- type: map
|
||||
mapping:
|
||||
name:
|
||||
required: true
|
||||
type: str
|
||||
cpuclusters:
|
||||
include: cpucluster-schema
|
||||
|
||||
schema;series-schema:
|
||||
required: false
|
||||
type: seq
|
||||
sequence:
|
||||
- type: map
|
||||
mapping:
|
||||
name:
|
||||
required: true
|
||||
type: str
|
||||
socs:
|
||||
required: false
|
||||
include: soc-schema
|
||||
|
||||
type: map
|
||||
mapping:
|
||||
family:
|
||||
required: false
|
||||
type: seq
|
||||
sequence:
|
||||
- type: map
|
||||
mapping:
|
||||
name:
|
||||
required: true
|
||||
type: str
|
||||
series:
|
||||
include: series-schema
|
||||
socs:
|
||||
include: soc-schema
|
||||
series:
|
||||
include: series-schema
|
||||
socs:
|
||||
include: soc-schema
|
||||
vendor:
|
||||
required: false
|
||||
type: str
|
||||
desc: SoC series of the SoC.
|
||||
This field is of informational use and can be used for filtering of SoCs.
|
||||
comment:
|
||||
required: false
|
||||
type: str
|
||||
desc: Free form comment with extra information regarding the SoC.
|
||||
Reference in New Issue
Block a user