1#!/usr/bin/env python3
2# vim: set syntax=python ts=4 :
3#
4# Copyright (c) 2018-2022 Intel Corporation
5# Copyright (c) 2024 Arm Limited (or its affiliates). All rights reserved.
6#
7# SPDX-License-Identifier: Apache-2.0
8
9import logging
10import os
11import shutil
12
13import scl
14from twisterlib.constants import SUPPORTED_SIMS
15from twisterlib.environment import ZEPHYR_BASE
16
17logger = logging.getLogger('twister')
18logger.setLevel(logging.DEBUG)
19
20
21class Simulator:
22    """Class representing a simulator"""
23
24    def __init__(self, data: dict[str, str]):
25        assert "name" in data
26        assert data["name"] in SUPPORTED_SIMS
27        self.name = data["name"]
28        self.exec = data.get("exec")
29
30    def is_runnable(self) -> bool:
31        return not bool(self.exec) or bool(shutil.which(self.exec))
32
33    def __str__(self):
34        return f"Simulator(name: {self.name}, exec: {self.exec})"
35
36    def __eq__(self, other):
37        if isinstance(other, Simulator):
38            return self.name == other.name and self.exec == other.exec
39        else:
40            return False
41
42
43class Platform:
44    """Class representing metadata for a particular platform
45
46    Maps directly to BOARD when building"""
47
48    platform_schema = scl.yaml_load(
49        os.path.join(ZEPHYR_BASE, "scripts", "schemas", "twister", "platform-schema.yaml")
50    )
51
52    def __init__(self):
53        """Constructor.
54
55        """
56
57        self.name = ""
58        self.aliases = []
59        self.normalized_name = ""
60        # if sysbuild to be used by default on a given platform
61        self.sysbuild = False
62        self.twister = True
63        # if no RAM size is specified by the board, take a default of 128K
64        self.ram = 128
65
66        self.timeout_multiplier = 1.0
67        self.ignore_tags = []
68        self.only_tags = []
69        self.default = False
70        # if no flash size is specified by the board, take a default of 512K
71        self.flash = 512
72        self.supported = set()
73
74        self.arch = None
75        self.vendor = ""
76        self.tier = -1
77        self.type = "na"
78        self.simulators: list[Simulator] = []
79        self.simulation: str = "na"
80        self.supported_toolchains = []
81        self.env = []
82        self.env_satisfied = True
83        self.filter_data = dict()
84        self.uart = ""
85        self.resc = ""
86        self.qualifier = None
87
88    def load(self, board, target, aliases, data):
89        """Load the platform data from the board data and target data
90        board: the board object as per the zephyr build system
91        target: the target name of the board as per the zephyr build system
92        aliases: list of aliases for the target
93        data: the data from the twister.yaml file for the target
94        """
95        self.name = target
96        self.aliases = aliases
97
98        # Get data for various targets and use the main board data as a
99        # defauly. Individual variant information will replace the default data
100        # provded in the main twister configuration for this board.
101        variants = data.get("variants", {})
102        variant_data = {}
103        for alias in aliases:
104            variant_data = variants.get(alias, {})
105            if variant_data:
106                break
107
108        self.normalized_name = self.name.replace("/", "_")
109        self.sysbuild = variant_data.get("sysbuild", data.get("sysbuild", self.sysbuild))
110        self.twister = variant_data.get("twister", data.get("twister", self.twister))
111
112        # if no RAM size is specified by the board, take a default of 128K
113        self.ram = variant_data.get("ram", data.get("ram", self.ram))
114        # if no flash size is specified by the board, take a default of 512K
115        self.flash = variant_data.get("flash", data.get("flash", self.flash))
116
117        testing = variant_data.get("testing", data.get("testing", {}))
118        self.timeout_multiplier = testing.get("timeout_multiplier", self.timeout_multiplier)
119        self.ignore_tags = testing.get("ignore_tags", self.ignore_tags)
120        self.only_tags = testing.get("only_tags", self.only_tags)
121        self.default = testing.get("default", self.default)
122        self.binaries = testing.get("binaries", [])
123        renode = testing.get("renode", {})
124        self.uart = renode.get("uart", "")
125        self.resc = renode.get("resc", "")
126        self.supported = set()
127        for supp_feature in variant_data.get("supported", data.get("supported", [])):
128            for item in supp_feature.split(":"):
129                self.supported.add(item)
130
131        self.arch = variant_data.get('arch', data.get('arch', self.arch))
132        self.vendor = board.vendor
133        self.tier = variant_data.get("tier", data.get("tier", self.tier))
134        self.type = variant_data.get('type', data.get('type', self.type))
135
136        self.simulators = [
137            Simulator(data) for data in variant_data.get(
138                'simulation',
139                data.get('simulation', self.simulators)
140            )
141        ]
142        default_sim = self.simulator_by_name(None)
143        if default_sim:
144            self.simulation = default_sim.name
145
146        self.supported_toolchains = variant_data.get("toolchain", data.get("toolchain", []))
147        if self.supported_toolchains is None:
148            self.supported_toolchains = []
149
150        support_toolchain_variants = {
151          # we don't provide defaults for 'arc' intentionally: some targets can't be built with GNU
152          # toolchain ("zephyr", "cross-compile", "xtools" options) and for some targets we haven't
153          # provided MWDT compiler / linker options in corresponding SoC file in Zephyr, so these
154          # targets can't be built with ARC MWDT toolchain ("arcmwdt" option) by Zephyr build system
155          # Instead for 'arc' we rely on 'toolchain' option in board yaml configuration.
156          "arm": ["zephyr", "gnuarmemb", "xtools", "armclang", "llvm"],
157          "arm64": ["zephyr", "cross-compile"],
158          "mips": ["zephyr", "xtools"],
159          "nios2": ["zephyr", "xtools"],
160          "riscv": ["zephyr", "cross-compile"],
161          "posix": ["host", "llvm"],
162          "sparc": ["zephyr", "xtools"],
163          "x86": ["zephyr", "xtools", "llvm"],
164          # Xtensa is not listed on purpose, since there is no single toolchain
165          # that is supported on all board targets for xtensa.
166        }
167
168        if self.arch in support_toolchain_variants:
169            for toolchain in support_toolchain_variants[self.arch]:
170                if toolchain not in self.supported_toolchains:
171                    self.supported_toolchains.append(toolchain)
172
173        self.env = variant_data.get("env", data.get("env", []))
174        self.env_satisfied = True
175        for env in self.env:
176            if not os.environ.get(env, None):
177                self.env_satisfied = False
178
179    def simulator_by_name(self, sim_name: str | None) -> Simulator | None:
180        if sim_name:
181            return next(filter(lambda s: s.name == sim_name, iter(self.simulators)), None)
182        else:
183            return next(iter(self.simulators), None)
184
185    def __repr__(self):
186        return f"<{self.name} on {self.arch}>"
187