1import argparse
2import pickle
3import sys
4import subprocess
5import os
6import colorama
7from colorama import init,Fore, Back, Style
8from os import environ
9
10parser = argparse.ArgumentParser(description='Parse test description')
11parser.add_argument('-avh', nargs='?',type = str, default="C:/Keil_v5/ARM/avh-fvp/bin/models", help="AVH folder")
12parser.add_argument('-d', action='store_true', help="Debug log")
13parser.add_argument('-n', action='store_true', help="No force rebuild")
14parser.add_argument('-r', action='store_true', help="Raw results only")
15parser.add_argument('-c', action='store_true', help="Display cycles (so passing test are displayed)")
16parser.add_argument('-g', nargs='?',type = str,help="AC6 / CLANG / GCC")
17parser.add_argument('-s', action='store_true', help="Take into account AVH error code")
18
19args = parser.parse_args()
20
21
22GHACTION = False
23
24if "AVH_FVP_PLUGINS" in os.environ:
25    GHACTION = True
26
27DEBUG=False
28if args.d:
29    DEBUG=True
30
31init()
32
33sys.path.append("..")
34
35from TestScripts.Parser import TreeElem
36
37ERROR_OCCURED = False
38
39def printTitle(s):
40    print("\n" + Fore.GREEN + Style.BRIGHT +  s + Style.RESET_ALL)
41
42def printSubTitle(s):
43    print("\n" + Fore.YELLOW + Style.BRIGHT + s + Style.RESET_ALL)
44
45def printError(s):
46    print("\n" + Fore.RED + Style.BRIGHT +  s + Style.RESET_ALL)
47
48# Load Output.pickle files for the test description
49def loadRoot(f):
50    root = None
51    with open(f,"rb") as inf:
52         root=pickle.load(inf)
53    return(root)
54
55# Get test suites from the test descriptions
56def getSuites(node,filterList,currentList=[]):
57    if node.kind==TreeElem.SUITE:
58        currentList.append(node.data["class"])
59    if node.kind==TreeElem.GROUP:
60       if not node.data["class"] in filterList:
61          for c in node.children:
62             getSuites(c,filterList,currentList)
63
64class Result:
65    def __init__(self,msg,error=False):
66        self._error = error
67        self._msg = msg
68
69    @property
70    def error(self):
71        return self._error
72
73    @property
74    def msg(self):
75        return self._msg
76
77
78
79# Run a command and get error or result
80# For the test report we don't need the stderr
81# in case of error since the test report is giving
82# all the details. So, there is an option to
83# disable the dump of stderr
84def run(*args,mustPrint=False,dumpStdErr=True,withExitCodeCheck=True):
85    global ERROR_OCCURED
86    global DEBUG
87    try:
88        if DEBUG:
89            print(" ".join(args))
90        result=subprocess.run(args,text=True,capture_output=True,timeout=600)
91        if withExitCodeCheck and (result.returncode !=0) :
92             ERROR_OCCURED = True
93             if dumpStdErr:
94                return(Result(result.stderr + f"\n\nSTDOUT (error code = {result.returncode}):\n\n" + result.stdout,error=True))
95             else:
96                return(Result(result.stdout,error=True))
97
98        if mustPrint:
99            print(result.stdout)
100        return(Result(result.stdout))
101    except Exception as e:
102        printError("Exception occured")
103        ERROR_OCCURED = True
104        return(Result(str(e),error=True))
105
106
107
108
109
110# Configuration file for AVH core
111configFiles={
112    "CS310":"ARM_VHT_Corstone_310_config.txt",
113    "CS300":{
114       "AC6":"ARM_VHT_Corstone_300_config_ext.txt",
115       "GCC":"ARM_VHT_Corstone_300_config_ext.txt",
116       "CLANG":"ARM_VHT_Corstone_300_config_ext.txt"
117    },
118    "M55":"ARM_VHT_MPS2_M55_config.txt",
119    "M33_DSP_FP":"ARM_VHT_MPS2_M33_DSP_FP_config.txt",
120    "M7DP":"ARM_VHT_MPS2_M7DP_config.txt",
121    "M4FP":"ARM_VHT_MPS2_M4FP_config.txt",
122    "M3":"ARM_VHT_MPS2_M3_config.txt",
123    "M23":"ARM_VHT_MPS2_M23_config.txt",
124    "M0plus":"ARM_VHT_MPS2_M0plus_config.txt",
125}
126
127# Windows executable
128avhUnixExe={
129    "CS310":"FVP_Corstone_SSE-310_Ethos-U65",
130    "CS300":"FVP_Corstone_SSE-300_Ethos-U55",
131    "M55":"FVP_MPS2_Cortex-M55",
132    "M33_DSP_FP":"FVP_MPS2_Cortex-M33",
133    "M7DP":"FVP_MPS2_Cortex-M7",
134    "M4FP":"FVP_MPS2_Cortex-M4",
135    "M3":"FVP_MPS2_Cortex-M3",
136    "M23":"FVP_MPS2_Cortex-M23",
137    "M0plus":"FVP_MPS2_Cortex-M0plus",
138}
139
140avhWindowsExe={
141    "CS310":"FVP_Corstone_SSE-310_Ethos-U65.exe",
142    "CS300":"FVP_Corstone_SSE-300_Ethos-U55.exe",
143    "M55":"FVP_MPS2_Cortex-M55.exe",
144    "M33_DSP_FP":"FVP_MPS2_Cortex-M33.exe",
145    "M7DP":"FVP_MPS2_Cortex-M7.exe",
146    "M4FP":"FVP_MPS2_Cortex-M4.exe",
147    "M3":"FVP_MPS2_Cortex-M3.exe",
148    "M23":"FVP_MPS2_Cortex-M23.exe",
149    "M0plus":"FVP_MPS2_Cortex-M0plus.exe",
150}
151
152AVHROOT = args.avh
153
154# Run AVH
155def runAVH(build,core,compiler):
156    axf="cprj/out/test/%s/Release/test.axf" % (build,)
157    elf="cprj/out/test/%s/Release/test.elf" % (build,)
158    app = axf
159    if os.path.exists(axf):
160        app = axf
161    if os.path.exists(elf):
162        app = elf
163    if isinstance(configFiles[core],str):
164       config = os.path.join("configs",configFiles[core])
165    else:
166       config = os.path.join("configs",configFiles[core][compiler])
167
168    if AVHROOT:
169       avhAttempt = os.path.join(AVHROOT,avhWindowsExe[core])
170       if os.path.exists(avhAttempt):
171          avh = avhAttempt
172
173       avhAttempt = os.path.join(AVHROOT,avhUnixExe[core])
174       if os.path.exists(avhAttempt):
175          avh = avhAttempt
176    else:
177       avh = avhUnixExe[core]
178
179
180
181    res=run(avh,"-f",config,app,withExitCodeCheck=args.s)
182    return(res)
183
184####################
185
186# Test descriptions to use
187tests=["../Output.pickle","../Output_f16.pickle"]
188# Test group to avoid
189filterList=["NNTests","ExampleTests"]
190allSuites=[]
191
192# Load all the test suite to run
193for t in tests:
194    root=loadRoot(t)
195    suites=[]
196    getSuites(root,filterList,suites)
197    allSuites += [(x,t) for x in suites ]
198
199# Test suite and output pickle needed to decode the result
200#print(allSuites)
201
202
203
204#allSuites=[
205#("MISCF32","../Output.pickle"),
206#("MISCQ31","../Output.pickle"),
207#("SupportTestsF16","../Output_f16.pickle"),
208###("BasicTestsF32","../Output.pickle"),
209##("BasicTestsF16","../Output_f16.pickle"),
210#]
211
212# Solution and build file for all
213# the tests
214# It is a pair : csolution target type and AVH identification
215# AVH identification is used to find the executable
216# and the configuration file
217compil_config={
218    'AC6':[
219      ("VHT-Corstone-300","CS300"),
220      ("VHT-Corstone-300-NOMVE","CS300"),
221      ("VHT_M33","M33_DSP_FP"),
222      ("VHT_M7","M7DP"),
223      ("VHT_M7_UNROLLED","M7DP"),
224      ("VHT_M4","M4FP"),
225      ("VHT_M0P","M0plus")
226    ],
227    'GCC':[
228      ("VHT-Corstone-300","CS300"),
229      #("VHT-Corstone-300-NOMVE","CS300"),
230      ("VHT_M33","M33_DSP_FP"),
231      ("VHT_M7","M7DP"),
232      ("VHT_M7_UNROLLED","M7DP"),
233      ("VHT_M4","M4FP"),
234      ("VHT_M0P","M0plus")
235    ],
236    'CLANG':[
237      ("VHT-Corstone-300","CS300"),
238      ("VHT-Corstone-300-NOMVE","CS300"),
239      ("VHT_M33","M33_DSP_FP"),
240      ("VHT_M7","M7DP"),
241      ("VHT_M7_UNROLLED","M7DP"),
242      ("VHT_M4","M4FP"),
243      ("VHT_M0P","M0plus")
244    ],
245}
246
247#Override previous solutions for more restricted testing.
248#compil_config={
249#    'AC6':[
250#      ("VHT-Corstone-300","CS300"),
251#      ("VHT_M33","M33_DSP_FP"),
252#    ],
253#    'GCC':[
254#      ("VHT-Corstone-300","CS300"),
255#      ("VHT_M33","M33_DSP_FP"),
256#    ],
257#    'CLANG':[
258#      ("VHT-Corstone-300","CS300"),
259#      ("VHT_M33","M33_DSP_FP"),
260#    ]
261#}
262
263
264
265HTMLHEADER="""<html>
266<header>
267<title>CMSIS-DSP Test summary</title>
268</header>
269<body>
270"""
271
272HTMLFOOTER="""
273</body></html>
274"""
275
276compilers = ["AC6", "CLANG", "GCC"]
277
278if args.g:
279    compilers=[args.g]
280
281
282# Run the tests and log the result
283# in a summary.html file
284
285if len(compilers)>1:
286   results_file = "summary.html"
287else:
288   results_file = f"summary_{compilers[0]}.html"
289
290with open(results_file,"w") as f:
291    print(HTMLHEADER,file=f)
292    for comp_nb,compiler in enumerate(compilers):
293        if compiler in compil_config:
294            solutions = compil_config[compiler]
295            printTitle("Process compiler %s (%d / %d)" % (compiler,comp_nb+1,len(compilers)))
296            print("<h1>Compiler %s</h1>" % compiler,file=f)
297            maxNbBuilds=len(solutions)
298            buildNb=0
299            for build,core in solutions:
300                buildNb = buildNb + 1
301                print("<h2>Core %s</h2>" % build,file=f)
302                printTitle("Process core %s (%d/%d)" % (build,buildNb,maxNbBuilds))
303                buildFile="test.Release+%s" % build
304                nb = 0
305                maxNb = len(allSuites)
306                for s,pickle in allSuites:
307                    nb = nb + 1
308                    printSubTitle("Process suite %s (%d/%d)" % (s,nb,maxNb))
309                    res=run(sys.executable,"../processTests.py","-gen","..","-p","../Patterns","-d","../Parameters","-f",pickle,"-e",s,mustPrint=True)
310                    if res.error:
311                        printError("Error processTests")
312                        print("<p><font color=\"red\">Error generating %s</font></p><PRE>" % s,file=f)
313                        print(res.msg,file=f)
314                        print("</PRE>",file=f)
315                        continue
316                    if nb==1:
317                       # -r is needed for first
318                       # build when we switch
319                       # between different solutions
320                       # (Like one using AC6 and the other
321                       # using gcc)
322                       if args.n:
323                          res=run("cbuild","-O", "cprj","test.csolution.yml","-c",buildFile,"--toolchain",compiler)
324                       else:
325                          res=run("cbuild","-O", "cprj","test.csolution.yml","-r","--update-rte","-c",buildFile,"--toolchain",compiler)
326                    else:
327                       res=run("cbuild","-O", "cprj","test.csolution.yml","-c",buildFile,"--toolchain",compiler)
328                    if res.error:
329                        printError("Error cbuild")
330                        print("<p><font color=\"red\">Error building %s</font></p><PRE>" % s,file=f)
331                        print(res.msg,file=f)
332                        print("</PRE>",file=f)
333                        continue
334                    printSubTitle("Run AVH")
335                    res=runAVH(build,core,compiler)
336                    if res.error:
337                        printError("Error running AVH")
338                        print("<p><font color=\"red\">Error running %s</font></p><PRE>" % s,file=f)
339                        print(res.msg,file=f)
340                        print("</PRE>",file=f)
341                        continue
342                    else:
343                        with open("results.txt","w") as o:
344                            print(res.msg,file=o)
345                        # Dump raw result
346                        if args.r:
347                            print(res.msg)
348                    # If raw result, no post processing
349                    if not args.r:
350                        res=run(sys.executable,"../processResult.py","-f",pickle,"-e","-ahtml","-r","results.txt",dumpStdErr=False)
351                        if res.error:
352                            printError("Error processResult")
353                            print("<p><font color=\"red\">Error processing %s result</font></p><PRE>" % s,file=f)
354                            print(res.msg,file=f)
355                            print("</PRE>",file=f)
356                            continue
357                        else:
358                            # When no error the section is
359                            # included in final file on when
360                            # cycles are requested
361                            if args.c:
362                               print(res.msg,file=f)
363    print(HTMLFOOTER,file=f)
364
365if not GHACTION:
366   if ERROR_OCCURED:
367       sys.exit("Error occurred")
368   else:
369       sys.exit(0)