nsdk_runcpu.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. #!/bin/env python3
  2. import os
  3. import sys
  4. import traceback
  5. from itertools import combinations
  6. import random
  7. SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
  8. requirement_file = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "requirements.txt"))
  9. try:
  10. import json
  11. import argparse
  12. import shutil
  13. import time
  14. import datetime
  15. except:
  16. print("Please install requried packages using: pip3 install -r %s" % (requirement_file))
  17. sys.exit(1)
  18. from nsdk_utils import *
  19. from nsdk_report import *
  20. from nsdk_runner import nsdk_runner
  21. FPGACIROOT = os.path.join(SCRIPT_DIR, "configs", "fpgaci")
  22. def valid_cpuarch(cpuarch):
  23. cpuarch = cpuarch.lower()
  24. if not cpuarch.startswith("rv64") and not cpuarch.startswith("rv32"):
  25. print("CPU arch should start with rv64 or rv32")
  26. return False
  27. # remove rv64/rv32
  28. arch = re.sub(r'rv\d\d', "", cpuarch)
  29. rst = re.search(r'[^imafdcbpvg]', arch)
  30. if rst:
  31. print("%s not valid arch" %(cpuarch))
  32. return False
  33. return True
  34. def gen_buildcfg(cpu, full_arch):
  35. buildcfg = dict()
  36. # if 'g'(imafd) exist
  37. rv_arch = re.sub(r'g', "imafd", full_arch)
  38. # remove rv64/rv32
  39. arch = re.sub(r'rv\d\d', "", rv_arch)
  40. arch_ext = re.sub(r'[imafdc]', "", arch)
  41. # core + f/p or fp
  42. core = cpu.replace('u', 'n') + re.sub(r'[imac%s]' %(arch_ext), "", arch)
  43. if "" == arch_ext:
  44. buildcfg = {full_arch:{"CORE": core}}
  45. else:
  46. buildcfg = {full_arch:{"CORE": core, "ARCH_EXT": arch_ext}}
  47. return buildcfg
  48. def gencfg_from_arch(cfgloc, core, cpuarch, maxnum):
  49. DEFAULT_VALID_MAX = 0
  50. # single, just itself
  51. ARCH_VALID = 1
  52. ARCH_AT_LEAST_VALID = 2
  53. arch_ext_comb = []
  54. arch_full = []
  55. cpucfgdict = dict()
  56. cpuarch = cpuarch.lower()
  57. # remove rv64/rv32
  58. arch = re.sub(r'rv\d\d', "", cpuarch)
  59. cpu_series = re.search(r'\d+', core).group()
  60. arch_prefix = re.match(r'rv\d\d', cpuarch).group()
  61. arch_base = ["imac"]
  62. if "f" in arch:
  63. arch_base.append('imafc')
  64. # if "d" exists, "f" exists
  65. if "d" in arch:
  66. arch_base.append('imafdc')
  67. # b/p/v
  68. arch_ext = re.sub(r'[imafdc]', "", arch)
  69. for i in range(1, len(arch_ext) + 1):
  70. for val in combinations(arch_ext, i):
  71. arch_ext_comb.append(''.join(val))
  72. # empty string on purpose to generate ones without ARCH_EXT
  73. arch_ext_comb.append("")
  74. arch_ext_comb.sort(key = lambda val:len(val), reverse = False)
  75. if maxnum >= ARCH_AT_LEAST_VALID and len(arch_ext_comb) > 1:
  76. arch_base = [arch_base[-1]]
  77. print("ARCH_AT_LEAST_VALID base : %s" %(arch_base))
  78. if maxnum >= ARCH_AT_LEAST_VALID:
  79. arch_ext_rest_cnt = 0
  80. if len(arch_ext_comb) > 2:
  81. arch_ext_rest_cnt = random.randint(0, len(arch_ext_comb) - 2)
  82. rand_rest_ext = random.sample(arch_ext_comb[1:-1], arch_ext_rest_cnt)
  83. # empty string ensures at least one element
  84. arch_ext_comb = [arch_ext_comb[0],arch_ext_comb[-1]]
  85. print("ARCH_AT_LEAST_VALID at least ext : %s, random rest ext : %s" %(arch_ext_comb, rand_rest_ext))
  86. arch_ext_comb.extend(rand_rest_ext)
  87. arch_ext_comb = list(set(arch_ext_comb))
  88. arch_base.sort(key = lambda val:len(val), reverse = False)
  89. arch_ext_comb.sort(key = lambda val:len(val), reverse = False)
  90. for idx_base, base in enumerate(arch_base):
  91. if ARCH_VALID == maxnum and idx_base != len(arch_base) - 1:
  92. print("Not ARCH_VALID base type, skip %s" %(base))
  93. continue
  94. for idx_ext, ext in enumerate(arch_ext_comb):
  95. if ARCH_VALID == maxnum and idx_ext != len(arch_ext_comb) - 1:
  96. print("Not ARCH_VALID ext type, skip %s" %(ext))
  97. continue
  98. if "imac" == base and 'v' in ext:
  99. print("v ext should not be with base imac, skip")
  100. continue
  101. arch_full.append(arch_prefix+base+ext)
  102. cpucfgdict["build_config"] = {"CPU_SERIES": cpu_series}
  103. cpucfgdict["build_configs"] = dict()
  104. for arch in arch_full:
  105. buildcfg = gen_buildcfg(core, arch)
  106. cpucfgdict["build_configs"].update(buildcfg)
  107. print("CPU config generated from arch = %s, mode = %s, number = %d: %s" %(cpuarch, maxnum, len(cpucfgdict["build_configs"]), cpucfgdict))
  108. cpucfg_name = core + "_" + cpuarch + ".json"
  109. cpucfg = os.path.join(cfgloc, cpucfg_name)
  110. save_json(cpucfg, cpucfgdict)
  111. return cpucfg
  112. # caseconfig is a dict
  113. # {
  114. # "core": "n300",
  115. # "locations": {
  116. # "fpgaloc": "",
  117. # "ncycmloc": "",
  118. # "cfgloc": ""
  119. # },
  120. # "ncycm": "xxxx",
  121. # "bitstream": "fpga.bit",
  122. # "fpga_serial": "xxxx",
  123. # "ftdi_serial": "xxxx",
  124. # "boardtype": "ddr200t",
  125. # "ocdcfg": "SoC/evalsoc/Board/nuclei_fpga_eval/openocd_evalsoc.cfg",
  126. # "cpucfg": "n300.json"
  127. # }
  128. def gen_runner_configs(casedir, caseconfig, genloc):
  129. if os.path.isdir(casedir) == False:
  130. return False
  131. if "core" not in caseconfig:
  132. print("No core is specified, please check!")
  133. return False
  134. # for ux1000_3w like, _3w should be stripped
  135. core = caseconfig["core"].split('_')[0]
  136. ocdcfg = caseconfig.get("ocdcfg", "SoC/evalsoc/Board/nuclei_fpga_eval/openocd_evalsoc.cfg")
  137. defbldcfg = dict()
  138. try:
  139. pathkeys = ocdcfg.replace("\\","/").split("/")
  140. defbldcfg = {"SOC": pathkeys[1], "BOARD": pathkeys[3]}
  141. except:
  142. defbldcfg = {"SOC": "evalsoc"}
  143. # get build_config from caseconfig
  144. glbldcfg = caseconfig.get("build_config", defbldcfg)
  145. runcfg = os.path.join(casedir, "%s.json" % (core))
  146. appcfg = os.path.join(casedir, "app.json")
  147. cfgcfg = os.path.join(casedir, "config.json")
  148. _, cfgconfig = load_json(cfgcfg)
  149. if cfgconfig is None:
  150. cfgconfig = {"choice": "mini"}
  151. choice = cfgconfig.get("choice", "mini")
  152. if os.path.isdir(genloc) == False:
  153. os.makedirs(genloc)
  154. cpuarch = caseconfig.get("cpuarch", "")
  155. # if specified user own cpu config use it
  156. if caseconfig.get("cpucfg", None):
  157. runcfgdict = gen_runcfg(caseconfig.get("cpucfg"), runcfg, glbldcfg)
  158. elif valid_cpuarch(cpuarch):
  159. # maxnum: 0, means generate default reasonable arch sets, max sets
  160. # 1, means generate cpuarch-same set
  161. # >1, means generate at least reasonable arch sets
  162. maxnum = int(caseconfig.get("archmaxnum", 1))
  163. cpucfgpath = gencfg_from_arch(genloc, core, cpuarch, maxnum)
  164. runcfgdict = gen_runcfg(cpucfgpath, runcfg, glbldcfg)
  165. else:
  166. # if not cpuarch or cpucfg file specified, then select cpu config via choice defined path
  167. runcfgdict = gen_coreruncfg(core, runcfg, choice, glbldcfg, casedir)
  168. caseconfig["gencfgtimestamp"] = str(datetime.datetime.now())
  169. # save casecfg.json/app.json/hw.json
  170. save_json(os.path.join(genloc, "casecfg.json"), caseconfig)
  171. save_json(os.path.join(genloc, "hw.json"), runcfgdict)
  172. shutil.copy(appcfg, os.path.join(genloc, "app.json"))
  173. locs = caseconfig.get("locations", dict())
  174. locs["cfgloc"] = "."
  175. fpga_serial = caseconfig.get("fpga_serial", INVAILD_SERNO)
  176. ftdi_serial = caseconfig.get("ftdi_serial", INVAILD_SERNO)
  177. cycm = caseconfig.get("ncycm", None)
  178. fpgabit = caseconfig.get("bitstream", "fpga.bit")
  179. if core in ["n200", "n300"]:
  180. boardtype = "ddr200t"
  181. elif core in ["n600", "ux600"]:
  182. boardtype = "ku060"
  183. else:
  184. boardtype = "vcu118"
  185. for btype in ["mcu200t", "ddr200t", "ku060", "vcu118"]:
  186. if btype in fpgabit:
  187. boardtype = btype
  188. break
  189. boardtype = caseconfig.get("boardtype", boardtype)
  190. appcfg = "app.json"
  191. hwcfg = "hw.json"
  192. # generate required runner yaml
  193. runyamldict = gen_runyaml(core, locs, fpga_serial, ftdi_serial, cycm, fpgabit, boardtype, ocdcfg, appcfg, hwcfg)
  194. save_yaml(os.path.join(genloc, "core.yaml"), runyamldict)
  195. return True
  196. if __name__ == '__main__':
  197. parser = argparse.ArgumentParser(description="Nuclei SDK CPU Runner")
  198. parser.add_argument('--cases', required=True, help="Case names, splitted by comma, such as barebench,clibbench")
  199. parser.add_argument('--casecfg', required=True, help="Case configuration for cycle model or fpga")
  200. parser.add_argument('--caseroot', default=FPGACIROOT, help="Case configuration root")
  201. parser.add_argument('--logdir', default='logs', help="logs directory, default logs")
  202. parser.add_argument('--sdk', help="Where SDK located in")
  203. parser.add_argument('--make_options', help="Extra make options passed to overwrite default build configuration passed via appcfg and hwcfg")
  204. parser.add_argument('--runon', default='qemu', help="Where to run these application")
  205. parser.add_argument('--timeout', help="Runner timeout for each application run, if not specified will use default one specified in json configuration")
  206. parser.add_argument('--verbose', action='store_true', help="If specified, will show detailed build/run messsage")
  207. parser.add_argument('--uniqueid', default="", help="Pass pipeline$CI_PIPELINE_ID, such as pipeline123456")
  208. args = parser.parse_args()
  209. if args.sdk is None:
  210. sdk_path = os.environ.get("NUCLEI_SDK_ROOT")
  211. if sdk_path is None or os.path.isdir(sdk_path) == False:
  212. args.sdk = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../../"))
  213. else:
  214. args.sdk = sdk_path
  215. if not(args.sdk and os.path.isdir(args.sdk) and os.path.isfile(os.path.join(args.sdk, "npk.yml"))):
  216. print("SDK location %s is invalid, please check!" % (args.sdk))
  217. sys.exit(1)
  218. STOPONFAIL = get_env_flag("STOPONFAIL", True)
  219. print("Using sdk path in %s" % (args.sdk))
  220. ret, caseconfig = load_json(args.casecfg)
  221. if ret != JSON_OK:
  222. print("Invalid case configuration file %s, please check!" % (args.casecfg))
  223. sys.exit(1)
  224. passed_cases = []
  225. failed_cases = []
  226. torun_cases = list(set(args.cases.split(",")))
  227. tot_cases = []
  228. start_time = time.time()
  229. ret = True
  230. try:
  231. print("Prepare to do cases %s on %s, stop on fail %s" % (torun_cases, args.runon, STOPONFAIL))
  232. index = 0
  233. totlen = len(torun_cases)
  234. for case in torun_cases:
  235. case = case.strip()
  236. index += 1
  237. print("Run baremetal cpu test case %s now, progress %d/%d!" % (case, index, totlen))
  238. if case == "":
  239. print("Case %s is invaild, ignore it!" % (case))
  240. continue
  241. casedir = os.path.join(args.caseroot, case)
  242. if os.path.isdir(casedir) == False:
  243. print("Can't find case %s, ignore it!" % (casedir))
  244. continue
  245. tot_cases.append(case)
  246. caselogdir = os.path.join(args.logdir, case)
  247. casecfgdir = os.path.join(caselogdir, "gencfgs")
  248. caseconfig["execase"] = case
  249. if gen_runner_configs(casedir, caseconfig, casecfgdir) == False:
  250. print("No correct case configurations found in %s" % (casedir))
  251. ret = False
  252. # if stop on fail, it will break current execution and exit
  253. if STOPONFAIL:
  254. print("Stop early due to case %s failed" % (case))
  255. failed_cases.append(case)
  256. break
  257. else:
  258. print("Continue execution due to not stop on failed case")
  259. continue
  260. runneryaml = os.path.join(casecfgdir, "core.yaml")
  261. locations = dict()
  262. nsdk_ext = nsdk_runner(args.sdk, args.make_options, runneryaml, locations, args.verbose, args.timeout)
  263. casepassed = True
  264. for config in nsdk_ext.get_configs():
  265. print("Run case %s for configuration %s specified in yaml %s" % (case, config, runneryaml))
  266. if nsdk_ext.run_config(config, caselogdir, runon=args.runon, createsubdir=False) == False:
  267. print("Configuration %s failed" % (config))
  268. print("Case %s for configuration %s specified in yaml %s: FAILED" % (case, config, runneryaml))
  269. failed_cases.append(case)
  270. casepassed = False
  271. else:
  272. print("Case %s for configuration %s specified in yaml %s: PASSED" % (case, config, runneryaml))
  273. passed_cases.append(case)
  274. # only one configuration should be executed, so just break here
  275. break
  276. if casepassed == False:
  277. ret = False
  278. # if stop on fail, it will break current execution and exit
  279. if STOPONFAIL:
  280. print("Stop early due to case %s failed" % (case))
  281. break
  282. else:
  283. print("Continue execution due to not stop on failed case")
  284. except Exception as exc:
  285. print("Unexpected Error: %s during execute case %s" % (exc, case))
  286. if case not in failed_cases:
  287. failed_cases.append(case)
  288. # https://stackoverflow.com/questions/3702675/how-to-catch-and-print-the-full-exception-traceback-without-halting-exiting-the
  289. traceback.print_exc()
  290. ret = False
  291. runtime = round(time.time() - start_time, 2)
  292. print("Cost about %s seconds to do this running, passed %s!" % (runtime, ret))
  293. print("All the required cases are %s" % (torun_cases))
  294. print("Case %s passed out of executed %s" % (passed_cases, tot_cases))
  295. print("These following cases failed: %s" % (failed_cases))
  296. # At least passed or failed cases are not zero
  297. if (len(passed_cases) > 0 or len(failed_cases) > 0) and os.path.isdir(args.logdir):
  298. runcpustatustxt = os.path.join(args.logdir, "runcpustatus.txt")
  299. print("Save run cpu report status into %s" % (runcpustatustxt))
  300. with open(runcpustatustxt, "w") as rf:
  301. rf.write("PASSED:%s\n" % (",".join(passed_cases)))
  302. rf.write("FAILED:%s\n" % (",".join(failed_cases)))
  303. sys.stdout.flush()
  304. # Exit with ret value
  305. if ret:
  306. sys.exit(0)
  307. else:
  308. sys.exit(1)