2 # -*- coding: utf-8 -*-
6 from datetime import datetime
8 from glob import glob, iglob
9 from pathlib import Path
11 from lxml.etree import XMLSyntaxError
12 from zipfile import ZipFile
14 from matrix_runner import main, matrix_axis, matrix_action, matrix_command, matrix_filter, \
15 ConsoleReport, CropReport, TransformReport, JUnitReport
18 @matrix_axis("device", "d", "Device(s) to be considered.")
19 class 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 CM52 = ('Cortex-M52', 'CM52')
35 CM52S = ('Cortex-M52S', 'CM52S')
36 CM52NS = ('Cortex-M52NS', 'CM52NS')
37 CM55 = ('Cortex-M55', 'CM55')
38 CM55S = ('Cortex-M55S', 'CM55S')
39 CM55NS = ('Cortex-M55NS', 'CM55NS')
40 CM85 = ('Cortex-M85', 'CM85')
41 CM85S = ('Cortex-M85S', 'CM85S')
42 CM85NS = ('Cortex-M85NS', 'CM85NS')
43 CA5 = ('Cortex-A5', 'CA5')
44 CA7 = ('Cortex-A7', 'CA7')
45 CA9 = ('Cortex-A9', 'CA9')
46 # CA5NEON = ('Cortex-A5neon', 'CA5neon')
47 # CA7NEON = ('Cortex-A7neon', 'CA7neon')
48 # CA9NEON = ('Cortex-A9neon', 'CA9neon')
63 DeviceAxis.CM23NS: 'CM23S',
64 DeviceAxis.CM33NS: 'CM33S',
65 DeviceAxis.CM35PNS: 'CM35PS',
66 DeviceAxis.CM52NS: 'CM52S',
67 DeviceAxis.CM55NS: 'CM55S',
68 DeviceAxis.CM85NS: 'CM85S'
73 @matrix_axis("compiler", "c", "Compiler(s) to be considered.")
74 class CompilerAxis(Enum):
83 CompilerAxis.AC6: 'axf',
84 CompilerAxis.GCC: 'elf',
85 CompilerAxis.IAR: 'elf',
86 CompilerAxis.CLANG: 'elf',
93 CompilerAxis.AC6: 'AC6',
94 CompilerAxis.GCC: 'GCC',
95 CompilerAxis.IAR: 'IAR',
96 CompilerAxis.CLANG: 'CLANG'
101 @matrix_axis("optimize", "o", "Optimization level(s) to be considered.")
102 class OptimizationAxis(Enum):
104 BALANCED = ('balanced')
110 DeviceAxis.CM0: ("FVP_MPS2_Cortex-M0", []),
111 DeviceAxis.CM0plus: ("FVP_MPS2_Cortex-M0plus", []),
112 DeviceAxis.CM3: ("FVP_MPS2_Cortex-M3", []),
113 DeviceAxis.CM4: ("FVP_MPS2_Cortex-M4", []),
114 DeviceAxis.CM7: ("FVP_MPS2_Cortex-M7", []),
115 DeviceAxis.CM23: ("FVP_MPS2_Cortex-M23", []),
116 DeviceAxis.CM23S: ("FVP_MPS2_Cortex-M23", []),
117 DeviceAxis.CM23NS: ("FVP_MPS2_Cortex-M23", []),
118 DeviceAxis.CM33: ("FVP_MPS2_Cortex-M33", []),
119 DeviceAxis.CM33S: ("FVP_MPS2_Cortex-M33", []),
120 DeviceAxis.CM33NS: ("FVP_MPS2_Cortex-M33", []),
121 DeviceAxis.CM35P: ("FVP_MPS2_Cortex-M35P", []),
122 DeviceAxis.CM35PS: ("FVP_MPS2_Cortex-M35P", []),
123 DeviceAxis.CM35PNS: ("FVP_MPS2_Cortex-M35P", []),
124 DeviceAxis.CM52: ("FVP_MPS2_Cortex-M52", []),
125 DeviceAxis.CM52S: ("FVP_MPS2_Cortex-M52", []),
126 DeviceAxis.CM52NS: ("FVP_MPS2_Cortex-M52", []),
127 DeviceAxis.CM55: ("FVP_MPS2_Cortex-M55", []),
128 DeviceAxis.CM55S: ("FVP_MPS2_Cortex-M55", []),
129 DeviceAxis.CM55NS: ("FVP_MPS2_Cortex-M55", []),
130 DeviceAxis.CM85: ("FVP_MPS2_Cortex-M85", []),
131 DeviceAxis.CM85S: ("FVP_MPS2_Cortex-M85", []),
132 DeviceAxis.CM85NS: ("FVP_MPS2_Cortex-M85", []),
133 DeviceAxis.CA5: ("FVP_VE_Cortex-A5x1", []),
134 DeviceAxis.CA7: ("FVP_VE_Cortex-A7x1", []),
135 DeviceAxis.CA9: ("FVP_VE_Cortex-A9x1", []),
136 # DeviceAxis.CA5NEON: ("_VE_Cortex-A5x1", []),
137 # DeviceAxis.CA7NEON: ("_VE_Cortex-A7x1", []),
138 # DeviceAxis.CA9NEON: ("_VE_Cortex-A9x1", [])
142 DeviceAxis.CM3: ("mps2-an385"),
143 DeviceAxis.CM4: ("mps2-an386"),
144 DeviceAxis.CM7: ("mps2-an500"),
145 DeviceAxis.CM33: ("mps2-an505"),
146 DeviceAxis.CM55: ("mps3-an547")
149 def config_suffix(config, timestamp=True):
150 suffix = f"{config.compiler[0]}-{config.optimize[0]}-{config.device[1]}"
152 suffix += f"-{datetime.now().strftime('%Y%m%d%H%M%S')}"
156 def project_name(config):
157 return f"Validation.{config.compiler}_{config.optimize}+{config.device[1]}"
160 def bl_project_name(config):
161 return f"Bootloader.{config.compiler}_{config.optimize}+{config.device.bl_device}"
164 def output_dir(config):
165 return f"Validation/outdir"
168 def bl_output_dir(config):
169 return f"Bootloader/outdir"
172 def model_config(config):
173 return f"../Layer/Target/{config.device[1]}/model_config.txt"
176 def build_dir(config):
177 return f"build/{config.device[1]}/{config.compiler.toolchain}/{config.optimize}"
182 """Build the selected configurations using CMSIS-Build."""
183 yield cbuild_clean(f"{project_name(config)}/{project_name(config)}.cprj")
187 def build(config, results):
188 """Build the selected configurations using CMSIS-Build."""
190 logging.info("Compiling Tests...")
194 if not all(r.success for r in results):
197 file = f"build/CoreValidation-{config_suffix(config)}.zip"
198 logging.info("Archiving build output to %s...", file)
199 with ZipFile(file, "w") as archive:
200 for content in iglob(f"{build_dir(config)}/**/*", recursive=True):
201 if Path(content).is_file():
202 archive.write(content)
207 """Extract the latest build archive."""
208 archives = sorted(glob(f"build/CoreValidation-{config_suffix(config, timestamp=False)}-*.zip"), reverse=True)
209 yield unzip(archives[0])
213 def run(config, results):
214 """Run the selected configurations."""
215 logging.info("Running Core Validation on Arm model ...")
216 yield model_exec(config)
219 results[0].test_report.write(f"build/CoreValidation-{config_suffix(config)}.junit")
220 except RuntimeError as ex:
221 if isinstance(ex.__cause__, XMLSyntaxError):
222 logging.error("No valid test report found in model output!")
224 logging.exception(ex)
228 def qemu(config, results):
229 """Run the selected configurations."""
230 if not config.device in QEMU_MACHINE:
231 logging.error("Qemu doesn't support target device '%s'!", config.device)
233 logging.info("Running Core Validation on Qemu ...")
234 yield qemu_exec(config)
238 def cbuild_clean(project):
239 return ["cbuild", "-C", project]
244 return ["bash", "-c", f"unzip {archive}"]
248 def preprocess(infile, outfile):
249 return ["arm-none-eabi-gcc", "-xc", "-E", infile, "-P", "-o", outfile]
254 return ["cbuild", "--toolchain", config.compiler.toolchain, "--update-rte", \
255 "--context", f".{config.optimize}+{config.device[1]}", \
256 "Validation.csolution.yml" ]
259 @matrix_command(test_report=ConsoleReport() |
260 CropReport('<\\?xml version="1.0"\\?>', '</report>') |
261 TransformReport('validation.xsl') |
262 JUnitReport(title=lambda title, result: f"{result.command.config.compiler}."
263 f"{result.command.config.optimize}."
264 f"{result.command.config.device}."
266 def model_exec(config):
267 cmdline = [MODEL_EXECUTABLE[config.device][0], "-q", "--simlimit", 100, "-f", model_config(config)]
268 cmdline += MODEL_EXECUTABLE[config.device][1]
269 cmdline += ["-a", f"{build_dir(config)}/{output_dir(config)}/Validation.{config.compiler.image_ext}"]
270 if config.device.has_bl():
271 cmdline += ["-a", f"{build_dir(config)}/{bl_output_dir(config)}/Bootloader.{config.compiler.image_ext}"]
275 @matrix_command(test_report=ConsoleReport() |
276 CropReport('<\\?xml version="1.0"\\?>', '</report>') |
277 TransformReport('validation.xsl') |
278 JUnitReport(title=lambda title, result: f"{result.command.config.compiler}."
279 f"{result.command.config.optimize}."
280 f"{result.command.config.device}."
282 def qemu_exec(config):
283 cmdline = ["qemu-system-arm", "-semihosting-config", "enable=on", "-monitor", "none", "-serial", "none", "-nographic"]
284 cmdline += ["-machine", QEMU_MACHINE[config.device]]
285 cmdline += ["-kernel", f"{build_dir(config)}/{output_dir(config)}/Validation.{config.compiler.image_ext}"]
290 def filter_iar(config):
291 return config.compiler == CompilerAxis.IAR
295 def filter_gcc_cm52(config):
296 device = config.device.match('CM52*')
297 compiler = config.compiler == CompilerAxis.GCC
298 return device and compiler
301 if __name__ == "__main__":