nsdk_runner.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. import time
  5. import serial
  6. import json
  7. import argparse
  8. from threading import Thread
  9. class NThread(Thread):
  10. def __init__(self, func, args):
  11. super(NThread, self).__init__()
  12. self.func = func
  13. self.args = args
  14. def run(self):
  15. self.result = self.func(*self.args)
  16. def get_result(self):
  17. try:
  18. return self.result
  19. except Exception:
  20. return None
  21. def get_make_csv(app, config):
  22. make_options = " "
  23. SUPPORT_KEYS = ["DOWNLOAD", "CORE", "BENCH_UNIT", "DSP_ENABLE", "SOC", "BOARD", "SILENT", "V"]
  24. csv_print = "CSV, APP=%s" % (app)
  25. if config:
  26. for key in config:
  27. if key not in SUPPORT_KEYS:
  28. continue
  29. option = "%s=%s"%(key, config[key])
  30. make_options = " %s %s " % (make_options, option)
  31. csv_print = "%s, %s" % (csv_print, option)
  32. return make_options, csv_print
  33. def nsdk_run_app(app, make_options="", target="clean"):
  34. if os.path.isdir(app) == False:
  35. return False
  36. run_cmd = "make -C %s %s %s" % (app, make_options, target)
  37. print("NSDK Run application %s using %s" % (app, run_cmd))
  38. if os.system(run_cmd) == 0:
  39. return True
  40. else:
  41. return False
  42. def upload_sdk_app(app, make_options=""):
  43. if os.path.isdir(app) == False:
  44. return None
  45. clean_cmd = "make -C %s %s clean" % (app, make_options)
  46. run_cmd = "make -C %s %s upload" % (app, make_options)
  47. print("Clean application %s using %s" % (app, clean_cmd))
  48. os.system(clean_cmd)
  49. print("Run application %s using %s" % (app, run_cmd))
  50. #print(csv_print)
  51. os.system(run_cmd)
  52. return None
  53. def read_serial(port="/dev/ttyUSB1", baudrate=115200, timeout=60, checks=None, sdk_check=False, checktime=time.time()):
  54. start_time = time.time()
  55. bench_log = ""
  56. bench_status = False
  57. pass_checks = checks.get("PASS", []) if checks else []
  58. fail_checks = checks.get("FAIL", []) if checks else []
  59. def test_in_check(string, checks):
  60. if type(checks) == list:
  61. for check in checks:
  62. if check in string:
  63. return True
  64. return False
  65. print("Read serial log from %s, baudrate %s" %(port, baudrate))
  66. nsdk_check_tag = "Nuclei SDK Build Time:"
  67. try:
  68. ser = serial.Serial(port, baudrate, timeout=5)
  69. while (time.time() - start_time) < timeout:
  70. # Remove '\r' in serial read line
  71. sline = ser.readline()
  72. line = ""
  73. try:
  74. line = str(sline.decode()).replace('\r', '')
  75. except:
  76. pass
  77. if sdk_check== True:
  78. print("XXX Check " + line)
  79. if nsdk_check_tag in line:
  80. timestr = line.split(nsdk_check_tag)[-1].strip()
  81. cur_time = time.mktime(time.strptime(timestr, "%b %d %Y, %H:%M:%S"))
  82. if int(cur_time) >= int(checktime):
  83. sdk_check = False
  84. bench_log = bench_log + str(line)
  85. else:
  86. bench_log = bench_log + str(line)
  87. if test_in_check(line, fail_checks):
  88. bench_status = False
  89. break
  90. if test_in_check(line, pass_checks):
  91. bench_status = True
  92. break
  93. except serial.serialutil.SerialException:
  94. # https://stackoverflow.com/questions/21050671/how-to-check-if-device-is-connected-pyserial
  95. print("serial port %s might not exist or in use" % port)
  96. else:
  97. print("Some error happens during serial operations")
  98. finally:
  99. ser.close()
  100. return bench_status, bench_log
  101. def run_sdk_app(app, config=None, logfile=None, dry_run=False):
  102. if os.path.isdir(app) == False:
  103. return False, ""
  104. if config is None:
  105. config = dict()
  106. port = config.get("port", "/dev/ttyUSB1")
  107. baudrate = config.get("baudrate", 115200)
  108. timeout = config.get("timeout", 60)
  109. checks = config.get("checks", dict())
  110. pass_checks = checks.get("PASS", [])
  111. fail_checks = checks.get("FAIL", [])
  112. checks = {"PASS": pass_checks, "FAIL": fail_checks}
  113. appschecks = config.get("appchecks", None)
  114. if appschecks and app in appschecks:
  115. print("Add application check for %s" % app)
  116. appchecks = appschecks[app]
  117. pass_checks = appchecks.get("PASS", [])
  118. fail_checks = appchecks.get("FAIL", [])
  119. config["appchecks"][app] = {"PASS": pass_checks, "FAIL": fail_checks}
  120. if checks:
  121. pass_checks.extend(checks.get("PASS", []))
  122. fail_checks.extend(checks.get("FAIL", []))
  123. checks = dict()
  124. checks["PASS"] = pass_checks
  125. checks["FAIL"] = fail_checks
  126. else:
  127. if "appchecks" not in config:
  128. config["appchecks"] = dict()
  129. config["appchecks"][app] = {"PASS": [], "FAIL": []}
  130. print("Here are pass checks: %s" % checks["PASS"])
  131. print("Here are fail checks: %s" % checks["FAIL"])
  132. make_options, csv_print = get_make_csv(app, config)
  133. if dry_run:
  134. print("Dry run for application %s" % app)
  135. return True, ""
  136. # record checktime before build application
  137. checktime = time.time()
  138. if nsdk_run_app(app, make_options, "clean") == False:
  139. return False, ""
  140. if nsdk_run_app(app, make_options, "all") == False:
  141. return False, ""
  142. ser_thread = NThread(read_serial, (port, baudrate, timeout, checks, True, checktime))
  143. ser_thread.start()
  144. nsdk_run_app(app, make_options, "upload")
  145. ser_thread.join()
  146. status, log = ser_thread.get_result()
  147. del ser_thread
  148. log = csv_print + "\n" + log
  149. print(log)
  150. if logfile:
  151. logdir = os.path.dirname(logfile)
  152. if os.path.isdir(logdir) == False:
  153. os.makedirs(logdir)
  154. print("Record serial log to %s" % (logfile))
  155. with open(logfile, 'w') as lf:
  156. lf.write(log)
  157. return status, log
  158. def find_file(path, filename):
  159. finded = []
  160. for root, dirs, files in os.walk(path):
  161. for fln in files:
  162. if fln.lower() == filename.lower():
  163. finded.append(root)
  164. break
  165. return finded
  166. def run_and_parse_apps(appdir, config=None, logname="runapps", logdir="logs", dry_run=False):
  167. nsdk_apps = find_file(appdir, "Makefile")
  168. full_log = ""
  169. fail_list = []
  170. pass_list = []
  171. if os.path.isdir(logdir) == False:
  172. os.makedirs(logdir)
  173. appslist = config.get("applist", None)
  174. for app in nsdk_apps:
  175. # Force to change windows path to linux path, \ -> /
  176. app = app.replace("\\", "/")
  177. if appslist and app not in appslist:
  178. print("Ignore application %s, which is not defined in applist" % app)
  179. continue
  180. print("Build, upload and run application %s" % (app))
  181. appname = app
  182. if appname == ".":
  183. appname = os.path.basename(os.path.relpath(appname))
  184. applog = appname.replace('/', '_').replace('\\','_') + ".log"
  185. applog = os.path.join(logdir, applog)
  186. status, log = run_sdk_app(app, config, applog, dry_run)
  187. if status:
  188. full_log = full_log + log
  189. pass_list.append(app)
  190. else:
  191. fail_list.append(app)
  192. if config:
  193. if appslist is None:
  194. config["applist"] = nsdk_apps
  195. cfg_file = os.path.join(logdir, "cfg.json")
  196. with open(cfg_file, "w") as cf:
  197. json.dump(config, cf, indent=4)
  198. if pass_list:
  199. print("The following applications are passed:")
  200. for ps in pass_list:
  201. print(ps)
  202. if fail_list:
  203. print("The following applications are failed:")
  204. for fail in fail_list:
  205. print(fail)
  206. flog_name = os.path.join(logdir, logname + ".log")
  207. flog_csv = os.path.join(logdir, logname + ".csv")
  208. run_log = os.path.join(logdir, "runner_report" + ".log")
  209. with open(flog_name, 'w') as lf:
  210. lf.write(full_log)
  211. log_lines = full_log.split("\n")
  212. with open(flog_csv, 'w') as cf:
  213. for log_line in log_lines:
  214. log_line = log_line.strip()
  215. if "CSV" in log_line:
  216. cf.write(log_line + "\n")
  217. with open(run_log, 'w') as rf:
  218. rf.write("Passed applications as below\n")
  219. for ps in pass_list:
  220. rf.write(ps+'\n')
  221. rf.write("Failed applications as below\n")
  222. for fl in fail_list:
  223. rf.write(fl+'\n')
  224. pass
  225. if __name__ == '__main__':
  226. parser = argparse.ArgumentParser(description="Nuclei SDK Runner")
  227. parser.add_argument('-a', '--appdir', default="application", help="Root application directories, default application")
  228. parser.add_argument('-c', '--core', default="n205", help="Benchmark Core, default n205")
  229. parser.add_argument('-d', '--download', default='ilm', help="Download mode, default ilm")
  230. parser.add_argument('-p', '--dsp_enable', default='ON', help="DSP_ENABLE ON or OFF, default ON")
  231. parser.add_argument('-b', '--bench_unit', default='cycles', help="BENCH_UNIT, cycles or instret, default cycles")
  232. parser.add_argument('-j', '--jsonconfig', default=None, help="JSON Configuration File, default no configuration")
  233. parser.add_argument('-l', '--logdir', default='logs', help="logs directory, default logs")
  234. parser.add_argument('--port', help="Serial port for monitor")
  235. parser.add_argument('--baudrate', help="Serial port baudrate for monitor")
  236. parser.add_argument('--dry_run', action="store_true", help="Dry run, will generate a empty json configuration file in <logdir>")
  237. args = parser.parse_args()
  238. checks = {"PASS":["all test are passed"], "FAIL":["MEPC:", "test error apprears"]}
  239. config = {
  240. "CORE": args.core.lower(),
  241. "DOWNLOAD": args.download.lower(),
  242. "DSP_ENABLE": args.dsp_enable.upper(),
  243. "BENCH_UNIT": args.bench_unit.lower(),
  244. "checks": checks
  245. }
  246. if args.jsonconfig and os.path.isfile(args.jsonconfig):
  247. jsoncfg = json.load(open(args.jsonconfig, 'r'))
  248. if "checks" in jsoncfg:
  249. config["checks"] = jsoncfg["checks"]
  250. jsoncfg.update(config)
  251. config = jsoncfg
  252. if args.port:
  253. config["port"] = args.port
  254. if args.baudrate:
  255. config["baudrate"] = args.baudrate
  256. app_names = [os.path.basename(os.path.relpath(args.appdir)), args.core.lower(), args.download.lower(), \
  257. args.dsp_enable.lower(), args.bench_unit.lower()]
  258. applog = "_".join(app_names)
  259. print("Log file name prefix is " + applog)
  260. run_and_parse_apps(args.appdir, config, applog, args.logdir, args.dry_run)