]> begriffs open source - cmsis/blob - CMSIS/CoreValidation/Project/build.py
Possible bugs in MMU_MemorySection(), MMU_MemoryPage() (#219)
[cmsis] / CMSIS / CoreValidation / Project / build.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 import logging
5
6 from datetime import datetime
7 from enum import Enum
8 from glob import glob, iglob
9 from pathlib import Path
10
11 from lxml.etree import XMLSyntaxError
12 from zipfile import ZipFile
13
14 from 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.")
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')
49
50     def has_bl(self):
51         return self in [
52             DeviceAxis.CM23NS,
53             DeviceAxis.CM33NS,
54             DeviceAxis.CM35PNS,
55             DeviceAxis.CM52NS,
56             DeviceAxis.CM55NS,
57             DeviceAxis.CM85NS
58         ]
59
60     @property
61     def bl_device(self):
62         bld = {
63             DeviceAxis.CM23NS: 'CM23S',
64             DeviceAxis.CM33NS: 'CM33S',
65             DeviceAxis.CM35PNS: 'CM35PS',
66             DeviceAxis.CM52NS: 'CM52S',
67             DeviceAxis.CM55NS: 'CM55S',
68             DeviceAxis.CM85NS: 'CM85S'
69         }
70         return bld[self]
71
72
73 @matrix_axis("compiler", "c", "Compiler(s) to be considered.")
74 class CompilerAxis(Enum):
75     AC6 = ('AC6')
76     GCC = ('GCC')
77     IAR = ('IAR')
78     CLANG = ('Clang')
79
80     @property
81     def image_ext(self):
82         ext = {
83             CompilerAxis.AC6: 'axf',
84             CompilerAxis.GCC: 'elf',
85             CompilerAxis.IAR: 'elf',
86             CompilerAxis.CLANG: 'elf',
87         }
88         return ext[self]
89
90     @property
91     def toolchain(self):
92         ext = {
93             CompilerAxis.AC6: 'AC6',
94             CompilerAxis.GCC: 'GCC',
95             CompilerAxis.IAR: 'IAR',
96             CompilerAxis.CLANG: 'CLANG'
97         }
98         return ext[self]
99
100
101 @matrix_axis("optimize", "o", "Optimization level(s) to be considered.")
102 class OptimizationAxis(Enum):
103     NONE = ('none')
104     BALANCED = ('balanced')
105     SPEED = ('speed')
106     SIZE = ('size')
107
108
109 MODEL_EXECUTABLE = {
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", [])
139 }
140
141 QEMU_MACHINE = {
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")
147 }
148
149 def config_suffix(config, timestamp=True):
150     suffix = f"{config.compiler[0]}-{config.optimize[0]}-{config.device[1]}"
151     if timestamp:
152         suffix += f"-{datetime.now().strftime('%Y%m%d%H%M%S')}"
153     return suffix
154
155
156 def project_name(config):
157     return f"Validation.{config.compiler}_{config.optimize}+{config.device[1]}"
158
159
160 def bl_project_name(config):
161     return f"Bootloader.{config.compiler}_{config.optimize}+{config.device.bl_device}"
162
163
164 def output_dir(config):
165     return f"Validation/outdir"
166
167
168 def bl_output_dir(config):
169     return f"Bootloader/outdir"
170
171
172 def model_config(config):
173     return f"../Layer/Target/{config.device[1]}/model_config.txt"
174
175
176 def build_dir(config):
177     return f"build/{config.device[1]}/{config.compiler.toolchain}/{config.optimize}"
178
179
180 @matrix_action
181 def clean(config):
182     """Build the selected configurations using CMSIS-Build."""
183     yield cbuild_clean(f"{project_name(config)}/{project_name(config)}.cprj")
184
185
186 @matrix_action
187 def build(config, results):
188     """Build the selected configurations using CMSIS-Build."""
189
190     logging.info("Compiling Tests...")
191
192     yield cbuild(config)
193
194     if not all(r.success for r in results):
195         return
196
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)
203
204
205 @matrix_action
206 def extract(config):
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])
210
211
212 @matrix_action
213 def run(config, results):
214     """Run the selected configurations."""
215     logging.info("Running Core Validation on Arm model ...")
216     yield model_exec(config)
217
218     try:
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!")
223         else:
224             logging.exception(ex)
225
226
227 @matrix_action
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)
232         return
233     logging.info("Running Core Validation on Qemu ...")
234     yield qemu_exec(config)
235
236
237 @matrix_command()
238 def cbuild_clean(project):
239     return ["cbuild", "-C", project]
240
241
242 @matrix_command()
243 def unzip(archive):
244     return ["bash", "-c", f"unzip {archive}"]
245
246
247 @matrix_command()
248 def preprocess(infile, outfile):
249     return ["arm-none-eabi-gcc", "-xc", "-E", infile, "-P", "-o", outfile]
250
251
252 @matrix_command()
253 def cbuild(config):
254     return ["cbuild", "--toolchain", config.compiler.toolchain, "--update-rte", \
255              "--context", f".{config.optimize}+{config.device[1]}", \
256              "Validation.csolution.yml" ]
257
258
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}."
265                                                                     f"{title}"))
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}"]
272     return cmdline
273
274
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}."
281                                                                     f"{title}"))
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}"]
286     return cmdline
287
288
289 @matrix_filter
290 def filter_iar(config):
291     return config.compiler == CompilerAxis.IAR
292
293
294 @matrix_filter
295 def filter_gcc_cm52(config):
296     device = config.device.match('CM52*')
297     compiler = config.compiler == CompilerAxis.GCC
298     return device and compiler
299
300
301 if __name__ == "__main__":
302     main()