nsdk_runner.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. #!/bin/env python3
  2. import os
  3. import sys
  4. SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
  5. requirement_file = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "requirements.txt"))
  6. try:
  7. import json
  8. import argparse
  9. import pprint
  10. import glob
  11. import yaml
  12. import usb
  13. import yamale
  14. except:
  15. print("Please install requried packages using: pip3 install -r %s" % (requirement_file))
  16. sys.exit(1)
  17. from nsdk_utils import *
  18. from nsdk_report import *
  19. from nsdk_bench import nsdk_bench
  20. RUNNER_LIST = ["fpga", "ncycm", "qemu", "xlspike"]
  21. GLOBAL_BITSTRAM = None
  22. GLOBAL_FPGASERIAL = None
  23. def global_program_bit():
  24. global GLOBAL_BITSTRAM
  25. global GLOBAL_FPGASERIAL
  26. if GLOBAL_BITSTRAM and GLOBAL_FPGASERIAL:
  27. return program_fpga(GLOBAL_BITSTRAM, GLOBAL_FPGASERIAL)
  28. return False
  29. def set_fpga_bit(bit, serial):
  30. global GLOBAL_BITSTRAM
  31. global GLOBAL_FPGASERIAL
  32. GLOBAL_BITSTRAM = bit
  33. GLOBAL_FPGASERIAL = serial
  34. pass
  35. def yaml_validate(sf, yf):
  36. try:
  37. schema = yamale.make_schema(sf)
  38. data = yamale.make_data(yf)
  39. yamale.validate(schema, data)
  40. return True
  41. except:
  42. return False
  43. def check_usb_serial(serno):
  44. return True
  45. # TODO: pyusb will make usb in virtualbox lost, disable it for now
  46. busses = usb.busses()
  47. for bus in busses:
  48. devices = bus.devices
  49. try:
  50. for dev in devices:
  51. serialnum = usb.util.get_string(dev.dev, dev.iSerialNumber)
  52. if serialnum == serno:
  53. return True
  54. except:
  55. continue
  56. return False
  57. class nsdk_runner(object):
  58. def __init__(self, sdk, makeopts, runyaml, locations):
  59. if os.path.isdir(sdk) == False:
  60. print("Invalid sdk path %s" % (sdk))
  61. sys.exit(1)
  62. runner_schema = os.path.join(os.path.dirname(os.path.realpath(__file__)), "runner_schema.yaml")
  63. if yaml_validate(runner_schema, runyaml) == False:
  64. print("Invalid runner yaml file %s" % (runyaml))
  65. sys.exit(1)
  66. yret, self.runcfg = load_yaml(runyaml)
  67. self.sdk = sdk
  68. self.makeopts = makeopts
  69. if yret != YAML_OK:
  70. print("Invalid yaml configuration file %s" % (runyaml))
  71. sys.exit(1)
  72. if locations["fpgaloc"]:
  73. self.runcfg["environment"]["fpgaloc"] = locations["fpgaloc"]
  74. if locations["ncycmloc"]:
  75. self.runcfg["environment"]["ncycmloc"] = locations["ncycmloc"]
  76. if locations["cfgloc"]:
  77. self.runcfg["environment"]["cfgloc"] = locations["cfgloc"]
  78. self.cpuruncfgs = dict()
  79. for key in self.runcfg["configs"]:
  80. self.cpuruncfgs[key] = copy.deepcopy(self.get_runcfg(key))
  81. pass
  82. def get_runcfg(self, config):
  83. if config not in self.runcfg.get("configs", dict()):
  84. print("ERROR: %s not found in runner yaml config" % (config))
  85. return None
  86. cur_runcfg = self.runcfg["configs"][config]
  87. fpga = cur_runcfg.get("fpga", None)
  88. bitstream = cur_runcfg.get("bitstream", None)
  89. openocdcfg = cur_runcfg.get("openocd_cfg", None)
  90. appcfg = cur_runcfg.get("appcfg", None)
  91. hwcfg = cur_runcfg.get("hwcfg", None)
  92. ncycm = cur_runcfg.get("ncycm", None)
  93. fpgas2run = dict()
  94. for runner in self.runcfg["fpga_runners"]:
  95. if self.runcfg["fpga_runners"][runner]["board_type"] == fpga:
  96. fpgas2run[runner] = self.runcfg["fpga_runners"][runner]
  97. fpgas2run[runner]["bitstream"] = bitstream
  98. fpgas2run[runner]["openocd_cfg"] = openocdcfg
  99. ncycm2run = dict()
  100. for runner in self.runcfg["ncycm_runners"]:
  101. if runner == ncycm:
  102. ncycm2run = self.runcfg["ncycm_runners"][runner]
  103. break
  104. benchcfg = {"appcfg": appcfg, "hwcfg": hwcfg}
  105. return {"benchcfg": benchcfg, "fpga": fpgas2run, "ncycm": ncycm2run}
  106. pass
  107. def get_configs(self):
  108. return list(self.cpuruncfgs.keys())
  109. def run_config(self, config, logdir, runon=""):
  110. runcfg = self.cpuruncfgs.get(config, None)
  111. if runcfg is None:
  112. return False
  113. if runon not in RUNNER_LIST:
  114. runon = "NOTRUN"
  115. print("No need to run on any target")
  116. benchcfg = runcfg["benchcfg"]
  117. appcfg_file = benchcfg["appcfg"]
  118. hwcfg_file = benchcfg["hwcfg"]
  119. cfgloc = self.runcfg["environment"]["cfgloc"]
  120. def get_file_path(file, loc):
  121. if os.path.isfile(file) == False:
  122. file = os.path.join(loc, file)
  123. return file
  124. appcfg_file = get_file_path(appcfg_file, cfgloc)
  125. hwcfg_file = get_file_path(hwcfg_file, cfgloc)
  126. ret, run_appcfg = load_json(appcfg_file)
  127. if ret != JSON_OK:
  128. print("ERROR: appcfg json file is not a valid configuration file!")
  129. ret, run_hwcfg = load_json(hwcfg_file)
  130. if ret != JSON_OK:
  131. print("ERROR: hwcfg json file is not a valid configuration file!")
  132. final_appcfg = merge_two_config(run_appcfg, run_hwcfg)
  133. need2run = True
  134. # check fpga/ncycm runner
  135. if runon == "fpga":
  136. if len(runcfg["fpga"]) == 0:
  137. print("ERROR: No fpga board available for this cpu")
  138. return False
  139. fpgaready = False
  140. ftdi_serial = serport = ""
  141. for fpga in runcfg["fpga"]:
  142. # check fpga and bitstream, serial port, ftdi_serial
  143. ftdi_serial = runcfg["fpga"][fpga]["ftdi_serial"]
  144. fpga_serial = runcfg["fpga"][fpga]["fpga_serial"]
  145. bitstream = runcfg["fpga"][fpga]["bitstream"]
  146. fpgaloc = self.runcfg["environment"]["fpgaloc"]
  147. serport = runcfg["fpga"][fpga]["serial_port"]
  148. openocdcfg = os.path.join(self.sdk, runcfg["fpga"][fpga]["openocd_cfg"])
  149. if (os.path.isfile(openocdcfg)) == False:
  150. print("OpenOCD Configuration File %s not found" % (openocdcfg))
  151. continue
  152. if os.path.isfile(bitstream) == False:
  153. bitstream = os.path.join(fpgaloc, bitstream)
  154. if os.path.isfile(bitstream) == False:
  155. print("Bitstream %s not found!" % (bitstream))
  156. continue
  157. if check_usb_serial(ftdi_serial) == False:
  158. print("FDTI Serial %s not found!" % (ftdi_serial))
  159. continue
  160. # program fpga
  161. if program_fpga(bitstream, fpga_serial) == False:
  162. print("Failed to program fpga using bit %s to target %s" % (bitstream, fpga_serial))
  163. continue
  164. else:
  165. print("Successfully program fpga using bit %s to target %s" % (bitstream, fpga_serial))
  166. # after program bit, serial port might change, view in virtual machine
  167. # so just check the serial port after program fpga
  168. if serport is None or serport.strip() == "":
  169. serport = find_serport_by_no(ftdi_serial)
  170. if serport is None:
  171. continue
  172. if check_serial_port(serport) == False:
  173. print("Serial port %s not found!" % (serport))
  174. continue
  175. print("Using Serial Port %s" %(serport))
  176. # only fpga programmed and serial port ready make this fpgaready to true
  177. fpgaready = True
  178. break
  179. # check fpga and serial port is ready or not
  180. if fpgaready == False:
  181. print("FPGA is not ready for running, please check!")
  182. return False
  183. # Modify openocd config
  184. if modify_openocd_cfg(openocdcfg, ftdi_serial) == False:
  185. print("Not a valid openocd configuration file %s" % (openocdcfg))
  186. return False
  187. # set run target to hardware
  188. final_appcfg["run_config"]["target"] = "hardware"
  189. if "hardware" not in final_appcfg["run_config"]:
  190. final_appcfg["run_config"]["hardware"] = {"baudrate": 115200, "timeout": 60, "serport": ""}
  191. final_appcfg["run_config"]["hardware"]["serport"] = serport
  192. final_appcfg["run_config"]["hardware"]["fpgabit"] = bitstream
  193. final_appcfg["run_config"]["hardware"]["fpgaserial"] = fpga_serial
  194. # set bitstream and fpga serial
  195. #set_fpga_bit(bitstream, fpga_serial)
  196. elif runon == "ncycm":
  197. # check ncycm
  198. if len(runcfg["ncycm"]) == 0:
  199. print("ERROR: No cycle model available for this cpu")
  200. return False
  201. ncycm_path = runcfg["ncycm"]["model"]
  202. ncycm_loc = self.runcfg["environment"]["ncycmloc"]
  203. if os.path.isfile(ncycm_path) == False:
  204. ncycm_path = os.path.join(ncycm_loc, ncycm_path)
  205. if os.path.isfile(ncycm_path) == False:
  206. print("cycle model %s not found!" % (ncycm_path))
  207. return False
  208. # set run target to ncycm
  209. final_appcfg["run_config"]["target"] = "ncycm"
  210. if "ncycm" not in final_appcfg["run_config"]:
  211. final_appcfg["run_config"]["ncycm"] = {"timeout": 600, "ncycm": "ncycm"}
  212. final_appcfg["run_config"]["ncycm"]["ncycm"] = ncycm_path
  213. elif runon == "qemu" or runon == "xlspike":
  214. # set run target to ncycm
  215. final_appcfg["run_config"]["target"] = runon
  216. if runon not in final_appcfg["run_config"]:
  217. final_appcfg["run_config"][runon] = {"timeout": 600}
  218. else:
  219. # don't need to run
  220. need2run = False
  221. # run on fpga/ncycm
  222. nsdk_ext = nsdk_bench()
  223. ret = True
  224. if self.makeopts is None:
  225. mkopts = ""
  226. else:
  227. mkopts = self.makeopts
  228. if runon == "ncycm" or runon == "xlspike":
  229. for opts in ("SIMULATION=1", "SIMU=xlspike"):
  230. if opts not in mkopts:
  231. mkopts = "%s %s" % (mkopts, opts)
  232. elif runon == "qemu":
  233. for opts in ("SIMULATION=1", "SIMU=qemu"):
  234. if opts not in mkopts:
  235. mkopts = "%s %s" % (mkopts, opts)
  236. subappcfg = final_appcfg
  237. if mkopts != "":
  238. subappcfg = merge_config_with_makeopts(subappcfg, mkopts)
  239. sublogdir = os.path.join(logdir, config)
  240. start_time = time.time()
  241. if need2run:
  242. #if runon == "fpga":
  243. # nsdk_ext.set_cpu_hangup_action(global_program_bit)
  244. cmdsts, result = nsdk_ext.run_apps(subappcfg, False, sublogdir, False)
  245. else:
  246. cmdsts, result = nsdk_ext.build_apps(subappcfg, False, sublogdir, False)
  247. runtime = round(time.time() - start_time, 2)
  248. print("Build or Run application for config %s run status: %s, time cost %s seconds" % (config, cmdsts, runtime))
  249. locret = check_expected(result, subappcfg, need2run)
  250. print("Application build as expected: %s" % (locret))
  251. if locret == False:
  252. ret = False
  253. save_results(subappcfg, None, subappcfg, result, sublogdir)
  254. if result:
  255. # Generate build or run report
  256. save_report_files(sublogdir, subappcfg, result, need2run)
  257. return ret
  258. pass
  259. def merge_cfgyaml(appyaml, runyaml):
  260. yret, appcfg = load_yaml(appyaml)
  261. if yret != YAML_OK:
  262. print("Invalid yaml configuration file %s" % (appyaml))
  263. sys.exit(1)
  264. yret, runcfg = load_yaml(runyaml)
  265. if yret != YAML_OK:
  266. print("Invalid yaml configuration file %s" % (runyaml))
  267. sys.exit(1)
  268. for runner in ["ncycm_runners", "fpga_runners"]:
  269. if runner in appcfg and runner in runcfg:
  270. appcfg[runner] = runcfg[runner]
  271. return merge_two_config(appcfg, runcfg)
  272. if __name__ == '__main__':
  273. parser = argparse.ArgumentParser(description="Nuclei SDK Benchmark and Report Tool")
  274. parser.add_argument('--appyaml', required=True, help="Application YAML Configuration File")
  275. parser.add_argument('--runyaml', help="Runner YAML Configuration File")
  276. parser.add_argument('--logdir', default='logs', help="logs directory, default logs")
  277. parser.add_argument('--fpgaloc', help="Where fpga bitstream located in")
  278. parser.add_argument('--ncycmloc', help="Where nuclei cycle model located in")
  279. parser.add_argument('--cfgloc', help="Where nsdk bench configurations located in")
  280. parser.add_argument('--sdk', help="Where SDK located in")
  281. parser.add_argument('--make_options', help="Extra make options passed to overwrite default build configuration passed via appcfg and hwcfg")
  282. parser.add_argument('--config', help="run configuration")
  283. parser.add_argument('--runon', default='hardware', help="Where to run these application")
  284. parser.add_argument('--show', action='store_true', help="Show configurations")
  285. args = parser.parse_args()
  286. if args.sdk is None:
  287. args.sdk = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../../"))
  288. if (os.path.isfile(args.appyaml) == False):
  289. print("Invalid application yaml, please check!")
  290. sys.exit(1)
  291. runneryaml = args.appyaml
  292. if args.runyaml:
  293. rundict = merge_cfgyaml(args.appyaml, args.runyaml)
  294. if os.path.isdir(args.logdir) == False:
  295. os.makedirs(args.logdir)
  296. runneryaml = os.path.join(args.logdir, "runner.yaml")
  297. save_yaml(runneryaml, rundict)
  298. pp = pprint.PrettyPrinter(compact=True)
  299. ret = True
  300. locations = {"fpgaloc": args.fpgaloc, "ncycmloc": args.ncycmloc, "cfgloc": args.cfgloc }
  301. nsdk_ext = nsdk_runner(args.sdk, args.make_options, runneryaml, locations)
  302. if args.show:
  303. print("Here are the supported configs:")
  304. pp.pprint(nsdk_ext.get_configs())
  305. else:
  306. start_time = time.time()
  307. if args.config:
  308. sel_configs = list(set([ key.strip() for key in args.config.split(',')]))
  309. print("Run selected configurations: %s" % (sel_configs))
  310. else:
  311. sel_configs = nsdk_ext.get_configs()
  312. print("Run all the configurations as below:")
  313. pp.pprint(sel_configs)
  314. runcnt = 0
  315. for config in sel_configs:
  316. if config in nsdk_ext.get_configs():
  317. runcnt += 1
  318. print("Run for configuration %s now" % (config))
  319. if nsdk_ext.run_config(config, args.logdir, runon=args.runon) == False:
  320. print("Configuration %s failed" % (config))
  321. ret = False
  322. if runcnt > 1:
  323. # generate total results for all the configs
  324. print("Generate all the reports for this run")
  325. need2run = False
  326. if args.runon in RUNNER_LIST:
  327. need2run = True
  328. generate_report_for_logs(args.logdir, need2run, True)
  329. runtime = round(time.time() - start_time, 2)
  330. print("Cost about %s seconds to do this running!" % (runtime))
  331. # Exit with ret value
  332. if ret:
  333. sys.exit(0)
  334. else:
  335. sys.exit(1)