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