1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4import logging 5 6from datetime import datetime 7from enum import Enum 8from glob import glob, iglob 9from pathlib import Path 10 11from lxml.etree import XMLSyntaxError 12from zipfile import ZipFile 13 14from matrix_runner import main, matrix_axis, matrix_action, matrix_command, matrix_filter, \ 15 ConsoleReport, CropReport, TransformReport, JUnitReport 16 17 18@matrix_axis("device", "d", "Device(s) to be considered.") 19class DeviceAxis(Enum): 20 CM0 = ('Cortex-M0', 'CM0') 21 CM0plus = ('Cortex-M0plus', 'CM0plus') 22 CM3 = ('Cortex-M3', 'CM3') 23 CM4 = ('Cortex-M4', 'CM4') 24 CM7 = ('Cortex-M7', 'CM7') 25 CM23 = ('Cortex-M23', 'CM23') 26 CM23S = ('Cortex-M23S', 'CM23S') 27 CM23NS = ('Cortex-M23NS', 'CM23NS') 28 CM33 = ('Cortex-M33', 'CM33') 29 CM33S = ('Cortex-M33S', 'CM33S') 30 CM33NS = ('Cortex-M33NS', 'CM33NS') 31 CM35P = ('Cortex-M35P', 'CM35P') 32 CM35PS = ('Cortex-M35PS', 'CM35PS') 33 CM35PNS = ('Cortex-M35PNS', 'CM35PNS') 34 CM55 = ('Cortex-M55', 'CM55') 35 CM55S = ('Cortex-M55S', 'CM55S') 36 CM55NS = ('Cortex-M55NS', 'CM55NS') 37 CM85 = ('Cortex-M85', 'CM85') 38 CM85S = ('Cortex-M85S', 'CM85S') 39 CM85NS = ('Cortex-M85NS', 'CM85NS') 40 CA5 = ('Cortex-A5', 'CA5') 41 CA7 = ('Cortex-A7', 'CA7') 42 CA9 = ('Cortex-A9', 'CA9') 43# CA5NEON = ('Cortex-A5neon', 'CA5neon') 44# CA7NEON = ('Cortex-A7neon', 'CA7neon') 45# CA9NEON = ('Cortex-A9neon', 'CA9neon') 46 47 def has_bl(self): 48 return self in [ 49 DeviceAxis.CM23NS, 50 DeviceAxis.CM33NS, 51 DeviceAxis.CM35PNS, 52 DeviceAxis.CM55NS, 53 DeviceAxis.CM85NS 54 ] 55 56 @property 57 def bl_device(self): 58 bld = { 59 DeviceAxis.CM23NS: 'CM23S', 60 DeviceAxis.CM33NS: 'CM33S', 61 DeviceAxis.CM35PNS: 'CM35PS', 62 DeviceAxis.CM55NS: 'CM55S', 63 DeviceAxis.CM85NS: 'CM85S' 64 } 65 return bld[self] 66 67 68@matrix_axis("compiler", "c", "Compiler(s) to be considered.") 69class CompilerAxis(Enum): 70 AC6 = ('AC6') 71 GCC = ('GCC') 72 IAR = ('IAR') 73 CLANG = ('Clang') 74 75 @property 76 def image_ext(self): 77 ext = { 78 CompilerAxis.AC6: 'axf', 79 CompilerAxis.GCC: 'elf', 80 CompilerAxis.IAR: 'elf', 81 CompilerAxis.CLANG: 'elf', 82 } 83 return ext[self] 84 85 @property 86 def toolchain(self): 87 ext = { 88 CompilerAxis.AC6: 'AC6', 89 CompilerAxis.GCC: 'GCC', 90 CompilerAxis.IAR: 'IAR', 91 CompilerAxis.CLANG: 'CLANG' 92 } 93 return ext[self] 94 95 96@matrix_axis("optimize", "o", "Optimization level(s) to be considered.") 97class OptimizationAxis(Enum): 98 NONE = ('none') 99 BALANCED = ('balanced') 100 SPEED = ('speed') 101 SIZE = ('size') 102 103 104MODEL_EXECUTABLE = { 105 DeviceAxis.CM0: ("FVP_MPS2_Cortex-M0", []), 106 DeviceAxis.CM0plus: ("FVP_MPS2_Cortex-M0plus", []), 107 DeviceAxis.CM3: ("FVP_MPS2_Cortex-M3", []), 108 DeviceAxis.CM4: ("FVP_MPS2_Cortex-M4", []), 109 DeviceAxis.CM7: ("FVP_MPS2_Cortex-M7", []), 110 DeviceAxis.CM23: ("FVP_MPS2_Cortex-M23", []), 111 DeviceAxis.CM23S: ("FVP_MPS2_Cortex-M23", []), 112 DeviceAxis.CM23NS: ("FVP_MPS2_Cortex-M23", []), 113 DeviceAxis.CM33: ("FVP_MPS2_Cortex-M33", []), 114 DeviceAxis.CM33S: ("FVP_MPS2_Cortex-M33", []), 115 DeviceAxis.CM33NS: ("FVP_MPS2_Cortex-M33", []), 116 DeviceAxis.CM35P: ("FVP_MPS2_Cortex-M35P", []), 117 DeviceAxis.CM35PS: ("FVP_MPS2_Cortex-M35P", []), 118 DeviceAxis.CM35PNS: ("FVP_MPS2_Cortex-M35P", []), 119 DeviceAxis.CM55: ("FVP_MPS2_Cortex-M55", []), 120 DeviceAxis.CM55S: ("FVP_MPS2_Cortex-M55", []), 121 DeviceAxis.CM55NS: ("FVP_MPS2_Cortex-M55", []), 122 DeviceAxis.CM85: ("FVP_MPS2_Cortex-M85", []), 123 DeviceAxis.CM85S: ("FVP_MPS2_Cortex-M85", []), 124 DeviceAxis.CM85NS: ("FVP_MPS2_Cortex-M85", []), 125 DeviceAxis.CA5: ("FVP_VE_Cortex-A5x1", []), 126 DeviceAxis.CA7: ("FVP_VE_Cortex-A7x1", []), 127 DeviceAxis.CA9: ("FVP_VE_Cortex-A9x1", []), 128# DeviceAxis.CA5NEON: ("_VE_Cortex-A5x1", []), 129# DeviceAxis.CA7NEON: ("_VE_Cortex-A7x1", []), 130# DeviceAxis.CA9NEON: ("_VE_Cortex-A9x1", []) 131} 132 133def config_suffix(config, timestamp=True): 134 suffix = f"{config.compiler[0]}-{config.optimize[0]}-{config.device[1]}" 135 if timestamp: 136 suffix += f"-{datetime.now().strftime('%Y%m%d%H%M%S')}" 137 return suffix 138 139 140def project_name(config): 141 return f"Validation.{config.compiler}_{config.optimize}+{config.device[1]}" 142 143 144def bl_project_name(config): 145 return f"Bootloader.{config.compiler}_{config.optimize}+{config.device.bl_device}" 146 147 148def output_dir(config): 149 return f"Validation/outdir" 150 151 152def bl_output_dir(config): 153 return f"Bootloader/outdir" 154 155 156def model_config(config): 157 return f"../Layer/Target/{config.device[1]}/model_config.txt" 158 159 160def build_dir(config): 161 return f"build/{config.device[1]}/{config.compiler.toolchain}/{config.optimize}" 162 163 164@matrix_action 165def clean(config): 166 """Build the selected configurations using CMSIS-Build.""" 167 yield cbuild_clean(f"{project_name(config)}/{project_name(config)}.cprj") 168 169 170@matrix_action 171def build(config, results): 172 """Build the selected configurations using CMSIS-Build.""" 173 174 logging.info("Compiling Tests...") 175 176 yield cbuild(config) 177 178 if not all(r.success for r in results): 179 return 180 181 file = f"build/CoreValidation-{config_suffix(config)}.zip" 182 logging.info("Archiving build output to %s...", file) 183 with ZipFile(file, "w") as archive: 184 for content in iglob(f"{build_dir(config)}/**/*", recursive=True): 185 if Path(content).is_file(): 186 archive.write(content) 187 188 189@matrix_action 190def extract(config): 191 """Extract the latest build archive.""" 192 archives = sorted(glob(f"build/CoreValidation-{config_suffix(config, timestamp=False)}-*.zip"), reverse=True) 193 yield unzip(archives[0]) 194 195 196@matrix_action 197def run(config, results): 198 """Run the selected configurations.""" 199 logging.info("Running Core Validation on Arm model ...") 200 yield model_exec(config) 201 202 try: 203 results[0].test_report.write(f"build/CoreValidation-{config_suffix(config)}.junit") 204 except RuntimeError as ex: 205 if isinstance(ex.__cause__, XMLSyntaxError): 206 logging.error("No valid test report found in model output!") 207 else: 208 logging.exception(ex) 209 210 211@matrix_command() 212def cbuild_clean(project): 213 return ["cbuild", "-c", project] 214 215 216@matrix_command() 217def unzip(archive): 218 return ["bash", "-c", f"unzip {archive}"] 219 220 221@matrix_command() 222def preprocess(infile, outfile): 223 return ["arm-none-eabi-gcc", "-xc", "-E", infile, "-P", "-o", outfile] 224 225 226@matrix_command() 227def cbuild(config): 228 return ["cbuild", "--toolchain", config.compiler.toolchain, "--update-rte", \ 229 "--context", f".{config.optimize}+{config.device[1]}", \ 230 "Validation.csolution.yml" ] 231 232 233@matrix_command(test_report=ConsoleReport() | 234 CropReport('<\?xml version="1.0"\?>', '</report>') | 235 TransformReport('validation.xsl') | 236 JUnitReport(title=lambda title, result: f"{result.command.config.compiler}." 237 f"{result.command.config.optimize}." 238 f"{result.command.config.device}." 239 f"{title}")) 240def model_exec(config): 241 cmdline = [MODEL_EXECUTABLE[config.device][0], "-q", "--simlimit", 100, "-f", model_config(config)] 242 cmdline += MODEL_EXECUTABLE[config.device][1] 243 cmdline += ["-a", f"{build_dir(config)}/{output_dir(config)}/Validation.{config.compiler.image_ext}"] 244 if config.device.has_bl(): 245 cmdline += ["-a", f"{build_dir(config)}/{bl_output_dir(config)}/Bootloader.{config.compiler.image_ext}"] 246 return cmdline 247 248 249@matrix_filter 250def filter_iar(config): 251 return config.compiler == CompilerAxis.IAR 252 253 254if __name__ == "__main__": 255 main() 256