blob: cdb0e059cc97cbb27dcb044dc1b983085ddf6084 [file] [log] [blame]
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +02001#!/usr/bin/env python3
2
3# Copyright (c) 2023 Nordic Semiconductor ASA
4# SPDX-License-Identifier: Apache-2.0
5
6import argparse
7from dataclasses import dataclass
8from pathlib import Path, PurePath
9import pykwalify.core
10import sys
11from typing import List
12import yaml
Jamie McCrae50333992024-03-26 11:21:13 +000013import re
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +020014
Lukasz Mrugalab2f43212024-04-19 10:37:20 +000015try:
16 from yaml import CSafeLoader as SafeLoader
17except ImportError:
18 from yaml import SafeLoader
19
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +020020
21SOC_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'soc-schema.yml')
22with open(SOC_SCHEMA_PATH, 'r') as f:
Lukasz Mrugalab2f43212024-04-19 10:37:20 +000023 soc_schema = yaml.load(f.read(), Loader=SafeLoader)
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +020024
25ARCH_SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'arch-schema.yml')
26with open(ARCH_SCHEMA_PATH, 'r') as f:
Lukasz Mrugalab2f43212024-04-19 10:37:20 +000027 arch_schema = yaml.load(f.read(), Loader=SafeLoader)
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +020028
29SOC_YML = 'soc.yml'
30ARCHS_YML_PATH = PurePath('arch/archs.yml')
31
32class Systems:
33
34 def __init__(self, folder='', soc_yaml=None):
35 self._socs = []
36 self._series = []
37 self._families = []
38
39 if soc_yaml is None:
40 return
41
42 try:
Lukasz Mrugalab2f43212024-04-19 10:37:20 +000043 data = yaml.load(soc_yaml, Loader=SafeLoader)
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +020044 pykwalify.core.Core(source_data=data,
45 schema_data=soc_schema).validate()
46 except (yaml.YAMLError, pykwalify.errors.SchemaError) as e:
47 sys.exit(f'ERROR: Malformed yaml {soc_yaml.as_posix()}', e)
48
Jamie McCrae50333992024-03-26 11:21:13 +000049 # Ensure that any runner configuration matches socs and cpuclusters declared in the same
50 # soc.yml file
51 if 'runners' in data and 'run_once' in data['runners']:
52 for grp in data['runners']['run_once']:
53 for item_data in data['runners']['run_once'][grp]:
54 for group in item_data['groups']:
55 for qualifiers in group['qualifiers']:
56 components = qualifiers.split('/')
57 soc = components.pop(0)
58 found_match = False
59
60 # Allow 'ns' as final qualifier until "virtual" CPUs are ported to soc.yml
61 # https://github.com/zephyrproject-rtos/zephyr/issues/70721
62 if len(components) > 0 and components[len(components)-1] == 'ns':
63 components.pop(len(components)-1)
64
65 for f in data.get('family', []):
66 for s in f.get('series', []):
67 for socs in s.get('socs', []):
68 if re.match(fr'^{soc}$', socs.get('name')) is not None:
69 if 'cpuclusters' in socs and len(components) > 0:
70 check_string = '/'.join(components)
71 for cpucluster in socs.get('cpuclusters', []):
72 if re.match(fr'^{check_string}$', cpucluster.get('name')) is not None:
73 found_match = True
74 break
75 elif 'cpuclusters' not in socs and len(components) == 0:
76 found_match = True
77 break
78
79
80 if found_match is False:
81 sys.exit(f'ERROR: SoC qualifier match unresolved: {qualifiers}')
82
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +020083 for f in data.get('family', []):
84 family = Family(f['name'], folder, [], [])
85 for s in f.get('series', []):
86 series = Series(s['name'], folder, f['name'], [])
87 socs = [(Soc(soc['name'],
88 [c['name'] for c in soc.get('cpuclusters', [])],
89 folder, s['name'], f['name']))
90 for soc in s.get('socs', [])]
91 series.socs.extend(socs)
92 self._series.append(series)
93 self._socs.extend(socs)
94 family.series.append(series)
95 family.socs.extend(socs)
96 socs = [(Soc(soc['name'],
97 [c['name'] for c in soc.get('cpuclusters', [])],
98 folder, None, f['name']))
99 for soc in f.get('socs', [])]
100 self._socs.extend(socs)
101 self._families.append(family)
102
103 for s in data.get('series', []):
104 series = Series(s['name'], folder, '', [])
105 socs = [(Soc(soc['name'],
106 [c['name'] for c in soc.get('cpuclusters', [])],
107 folder, s['name'], ''))
108 for soc in s.get('socs', [])]
109 series.socs.extend(socs)
110 self._series.append(series)
111 self._socs.extend(socs)
112
113 socs = [(Soc(soc['name'],
114 [c['name'] for c in soc.get('cpuclusters', [])],
115 folder, '', ''))
116 for soc in data.get('socs', [])]
117 self._socs.extend(socs)
118
119 @staticmethod
120 def from_file(socs_file):
121 '''Load SoCs from a soc.yml file.
122 '''
123 try:
124 with open(socs_file, 'r') as f:
125 socs_yaml = f.read()
126 except FileNotFoundError as e:
127 sys.exit(f'ERROR: socs.yml file not found: {socs_file.as_posix()}', e)
128
129 return Systems(str(socs_file.parent), socs_yaml)
130
131 @staticmethod
132 def from_yaml(socs_yaml):
133 '''Load socs from a string with YAML contents.
134 '''
135 return Systems('', socs_yaml)
136
137 def extend(self, systems):
138 self._families.extend(systems.get_families())
139 self._series.extend(systems.get_series())
140 self._socs.extend(systems.get_socs())
141
142 def get_families(self):
143 return self._families
144
145 def get_series(self):
146 return self._series
147
148 def get_socs(self):
149 return self._socs
150
151 def get_soc(self, name):
152 try:
153 return next(s for s in self._socs if s.name == name)
154 except StopIteration:
155 sys.exit(f"ERROR: SoC '{name}' is not found, please ensure that the SoC exists "
156 f"and that soc-root containing '{name}' has been correctly defined.")
157
158
159@dataclass
160class Soc:
161 name: str
162 cpuclusters: List[str]
163 folder: str
164 series: str = ''
165 family: str = ''
166
167
168@dataclass
169class Series:
170 name: str
171 folder: str
172 family: str
173 socs: List[Soc]
174
175
176@dataclass
177class Family:
178 name: str
179 folder: str
180 series: List[Series]
181 socs: List[Soc]
182
183
Grzegorz Swiderski9dabce42024-03-22 20:17:03 +0100184def unique_paths(paths):
185 # Using dict keys ensures both uniqueness and a deterministic order.
186 yield from dict.fromkeys(map(Path.resolve, paths)).keys()
187
188
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200189def find_v2_archs(args):
190 ret = {'archs': []}
Grzegorz Swiderski9dabce42024-03-22 20:17:03 +0100191 for root in unique_paths(args.arch_roots):
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200192 archs_yml = root / ARCHS_YML_PATH
193
194 if Path(archs_yml).is_file():
195 with Path(archs_yml).open('r') as f:
Lukasz Mrugalab2f43212024-04-19 10:37:20 +0000196 archs = yaml.load(f.read(), Loader=SafeLoader)
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200197
198 try:
199 pykwalify.core.Core(source_data=archs, schema_data=arch_schema).validate()
200 except pykwalify.errors.SchemaError as e:
201 sys.exit('ERROR: Malformed "build" section in file: {}\n{}'
202 .format(archs_yml.as_posix(), e))
203
204 if args.arch is not None:
205 archs = {'archs': list(filter(
206 lambda arch: arch.get('name') == args.arch, archs['archs']))}
207 for arch in archs['archs']:
208 arch.update({'path': root / 'arch' / arch['path']})
209 arch.update({'hwm': 'v2'})
210 arch.update({'type': 'arch'})
211
212 ret['archs'].extend(archs['archs'])
213
214 return ret
215
216
217def find_v2_systems(args):
218 yml_files = []
219 systems = Systems()
Grzegorz Swiderski9dabce42024-03-22 20:17:03 +0100220 for root in unique_paths(args.soc_roots):
Marc Herbert251f52c2024-03-12 23:08:40 +0000221 yml_files.extend(sorted((root / 'soc').rglob(SOC_YML)))
Torsten Rasmussen8dc3f852022-09-14 22:23:15 +0200222
223 for soc_yml in yml_files:
224 if soc_yml.is_file():
225 systems.extend(Systems.from_file(soc_yml))
226
227 return systems
228
229
230def parse_args():
231 parser = argparse.ArgumentParser(allow_abbrev=False)
232 add_args(parser)
233 return parser.parse_args()
234
235
236def add_args(parser):
237 default_fmt = '{name}'
238
239 parser.add_argument("--soc-root", dest='soc_roots', default=[],
240 type=Path, action='append',
241 help='add a SoC root, may be given more than once')
242 parser.add_argument("--soc", default=None, help='lookup the specific soc')
243 parser.add_argument("--soc-series", default=None, help='lookup the specific soc series')
244 parser.add_argument("--soc-family", default=None, help='lookup the specific family')
245 parser.add_argument("--socs", action='store_true', help='lookup all socs')
246 parser.add_argument("--arch-root", dest='arch_roots', default=[],
247 type=Path, action='append',
248 help='add a arch root, may be given more than once')
249 parser.add_argument("--arch", default=None, help='lookup the specific arch')
250 parser.add_argument("--archs", action='store_true', help='lookup all archs')
251 parser.add_argument("--format", default=default_fmt,
252 help='''Format string to use to list each soc.''')
253 parser.add_argument("--cmakeformat", default=None,
254 help='''CMake format string to use to list each arch/soc.''')
255
256
257def dump_v2_archs(args):
258 archs = find_v2_archs(args)
259
260 for arch in archs['archs']:
261 if args.cmakeformat is not None:
262 info = args.cmakeformat.format(
263 TYPE='TYPE;' + arch['type'],
264 NAME='NAME;' + arch['name'],
265 DIR='DIR;' + str(arch['path'].as_posix()),
266 HWM='HWM;' + arch['hwm'],
267 # Below is non exising for arch but is defined here to support
268 # common formatting string.
269 SERIES='',
270 FAMILY='',
271 ARCH='',
272 VENDOR=''
273 )
274 else:
275 info = args.format.format(
276 type=arch.get('type'),
277 name=arch.get('name'),
278 dir=arch.get('path'),
279 hwm=arch.get('hwm'),
280 # Below is non exising for arch but is defined here to support
281 # common formatting string.
282 series='',
283 family='',
284 arch='',
285 vendor=''
286 )
287
288 print(info)
289
290
291def dump_v2_system(args, type, system):
292 if args.cmakeformat is not None:
293 info = args.cmakeformat.format(
294 TYPE='TYPE;' + type,
295 NAME='NAME;' + system.name,
296 DIR='DIR;' + Path(system.folder).as_posix(),
297 HWM='HWM;' + 'v2'
298 )
299 else:
300 info = args.format.format(
301 type=type,
302 name=system.name,
303 dir=system.folder,
304 hwm='v2'
305 )
306
307 print(info)
308
309
310def dump_v2_systems(args):
311 systems = find_v2_systems(args)
312
313 for f in systems.get_families():
314 dump_v2_system(args, 'family', f)
315
316 for s in systems.get_series():
317 dump_v2_system(args, 'series', s)
318
319 for s in systems.get_socs():
320 dump_v2_system(args, 'soc', s)
321
322
323if __name__ == '__main__':
324 args = parse_args()
325 if any([args.socs, args.soc, args.soc_series, args.soc_family]):
326 dump_v2_systems(args)
327 if args.archs or args.arch is not None:
328 dump_v2_archs(args)