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