nsdk_builder.py 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. import time
  5. import copy
  6. import shutil
  7. import glob
  8. SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
  9. requirement_file = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "requirements.txt"))
  10. try:
  11. import serial
  12. import tempfile
  13. import json
  14. import argparse
  15. from threading import Thread
  16. import subprocess
  17. except:
  18. print("Please install requried packages using: pip3 install -r %s" % (requirement_file))
  19. sys.exit(1)
  20. from nsdk_utils import *
  21. VALID_MAKEFILE_NAMES = ['Makefile', 'makefile', "GNUMakefile"]
  22. def is_nuclei_demosoc(soc):
  23. if soc == "hbird" or soc == "demosoc" or soc == "evalsoc" or soc == "xlspike":
  24. return True
  25. else:
  26. return False
  27. class nsdk_builder(object):
  28. def __init__(self):
  29. pass
  30. @staticmethod
  31. def is_app(appdir):
  32. if os.path.isdir(appdir) == False:
  33. return False
  34. appdir = os.path.realpath(appdir)
  35. for mkname in VALID_MAKEFILE_NAMES:
  36. mkfile_path = os.path.join(appdir, mkname)
  37. if os.path.isfile(mkfile_path):
  38. return True
  39. return False
  40. @staticmethod
  41. def copy_objects(appsts, copydir):
  42. if isinstance(appsts, dict) and "objects" in appsts:
  43. os.makedirs(copydir, exist_ok=True)
  44. objects = appsts["objects"]
  45. if "saved_objects" not in appsts:
  46. appsts["saved_objects"] = dict()
  47. cp_keys = get_sdk_copyobjects()
  48. if cp_keys != None:
  49. cp_keys = cp_keys.strip().split(",")
  50. for obj in objects:
  51. obj_file = objects[obj]
  52. if os.path.isfile(obj_file): # only copy when exist
  53. filename = os.path.basename(obj_file)
  54. filesuffix = os.path.splitext(filename)[-1].strip(".")
  55. newfile = os.path.join(copydir, filename)
  56. if cp_keys is None or filesuffix in cp_keys:
  57. shutil.copyfile(obj_file, newfile)
  58. appsts["saved_objects"][obj] = newfile
  59. pass
  60. @staticmethod
  61. def get_objects(appdir, target=None, timestamp=None):
  62. if nsdk_builder.is_app(appdir) == False:
  63. return None
  64. def find_app_object(pattern):
  65. files = find_files(appdir, pattern)
  66. found_file = ""
  67. latest_timestamp = 0
  68. for fl in files:
  69. flct = os.stat(fl).st_ctime
  70. if timestamp:
  71. if flct >= timestamp:
  72. found_file = fl
  73. break
  74. else:
  75. # find a latest file
  76. if flct > latest_timestamp:
  77. latest_timestamp = flct
  78. found_file = fl
  79. return found_file
  80. build_objects = dict()
  81. if target:
  82. build_objects["elf"] = find_app_object("*%s.elf" % (target))
  83. build_objects["map"] = find_app_object("*%s.map" % (target))
  84. build_objects["dump"] = find_app_object("*%s.dump" % (target) )
  85. build_objects["dasm"] = find_app_object("*%s.dasm" % (target))
  86. build_objects["verilog"] = find_app_object("*%s.verilog" % (target))
  87. else:
  88. build_objects["elf"] = find_app_object("*.elf")
  89. build_objects["map"] = find_app_object("*.map")
  90. build_objects["dump"] = find_app_object("*.dump")
  91. build_objects["dasm"] = find_app_object("*.dasm")
  92. build_objects["verilog"] = find_app_object("*.verilog")
  93. return build_objects
  94. def build_target_only(self, appdir, make_options="", target="clean", show_output=True, logfile=None, parallel=""):
  95. if self.is_app(appdir) == False:
  96. return COMMAND_NOTAPP, 0
  97. # Parallel must start with -j
  98. if isinstance(parallel, str):
  99. parallel = parallel.strip()
  100. if parallel != "" and parallel.startswith("-j") == False:
  101. parallel = ""
  102. else:
  103. parallel = ""
  104. if parallel != "": # need to split targets
  105. build_targets = target.strip().split()
  106. if get_sdk_verb_buildmsg():
  107. print("Target \"%s\" are split to seperated targets %s in parallel mode." %(target, build_targets))
  108. else:
  109. build_targets = [target]
  110. if os.path.isfile(logfile):
  111. os.remove(logfile)
  112. total_ticks = 0
  113. ignore_targets = ["info", "showtoolver", "showflags", "clean", "bin", "size"]
  114. for btg in build_targets:
  115. build_cmd = "make %s -C %s %s %s" % (parallel, appdir, make_options, btg)
  116. if get_sdk_verb_buildmsg() and (not ((show_output == False) and (btg in ignore_targets))):
  117. print("Build application %s, with target: %s" % (appdir, btg))
  118. print("Build command: %s" % (build_cmd))
  119. ret, ticks = run_command(build_cmd, show_output, logfile=logfile, append=True)
  120. if get_sdk_verb_buildmsg() and (not ((show_output == False) and (btg in ignore_targets))):
  121. print("Build command return value: %s" % (ret))
  122. total_ticks += ticks
  123. if ret != 0: # if one target failed, then stop
  124. break
  125. return ret, ticks
  126. def get_build_info(self, appdir, make_options=""):
  127. infolog = tempfile.mktemp()
  128. ret, _ = self.build_target_only(appdir, make_options, "info", False, infolog)
  129. build_info = dict()
  130. if ret != COMMAND_RUNOK:
  131. os.remove(infolog)
  132. return build_info
  133. build_info = dict()
  134. with open(infolog, "r") as inf:
  135. for line in inf.readlines():
  136. line = line.strip()
  137. INFO_TAG = "Current Configuration:"
  138. if line.startswith(INFO_TAG):
  139. infos = line.replace(INFO_TAG, "").strip().split()
  140. for info in infos:
  141. splits = info.split("=")
  142. if len(splits) == 2:
  143. build_info[splits[0]] = splits[1]
  144. os.remove(infolog)
  145. return build_info
  146. def get_build_flags(self, appdir, make_options=""):
  147. flagslog = tempfile.mktemp()
  148. ret, _ = self.build_target_only(appdir, make_options, "showflags", False, flagslog)
  149. build_flags = dict()
  150. if ret != COMMAND_RUNOK:
  151. os.remove(flagslog)
  152. return build_flags
  153. build_flags = dict()
  154. with open(flagslog, "r") as inf:
  155. for line in inf.readlines():
  156. line = line.strip()
  157. if ":" in line:
  158. splits = line.split(":")
  159. if len(splits) != 2:
  160. continue
  161. key, value = splits
  162. key = key.strip()
  163. value = value.strip()
  164. build_flags[key] = value
  165. os.remove(flagslog)
  166. return build_flags
  167. def get_build_toolver(self, appdir, make_options=""):
  168. log = tempfile.mktemp()
  169. ret, _ = self.build_target_only(appdir, make_options, "showtoolver", False, log)
  170. buildout = dict()
  171. if ret != COMMAND_RUNOK:
  172. os.remove(log)
  173. return buildout
  174. buildout = dict()
  175. toolname = ""
  176. with open(log, "r") as inf:
  177. for line in inf.readlines():
  178. line = line.strip()
  179. if line.startswith("Show"):
  180. toolname = line.split()[1].strip()
  181. buildout[toolname] = ""
  182. elif line.startswith("make:") == False:
  183. if toolname != "":
  184. buildout[toolname] += line + "\n"
  185. os.remove(log)
  186. return buildout
  187. def build_target(self, appdir, make_options="", target="clean", show_output=True, logfile=None, parallel=""):
  188. if self.is_app(appdir) == False:
  189. return False, None
  190. build_status = dict()
  191. ret, ticks = self.build_target_only(appdir, make_options, target, show_output, logfile, parallel)
  192. cmdsts = True
  193. if ret == COMMAND_INTERRUPTED:
  194. print("%s: Exit program due to CTRL - C pressed" % (sys._getframe().f_code.co_name))
  195. sys.exit(1)
  196. elif ret == COMMAND_RUNOK:
  197. cmdsts = True
  198. else:
  199. cmdsts = False
  200. build_status["app"] = { "path": appdir, \
  201. "make_options": make_options, \
  202. "target": target }
  203. build_status["status"] = {"build": cmdsts}
  204. build_status["status_code"] = {"build": ret}
  205. build_status["logs"] = {"build": logfile}
  206. build_status["time"] = {"build": round(ticks, 2)}
  207. build_status["info"] = self.get_build_info(appdir, make_options)
  208. build_status["toolver"] = self.get_build_toolver(appdir, make_options)
  209. build_status["flags"] = self.get_build_flags(appdir, make_options)
  210. apptarget = None
  211. if build_status["flags"]:
  212. apptarget = build_status["flags"].get("TARGET", None)
  213. build_status["objects"] = nsdk_builder.get_objects(appdir, apptarget)
  214. build_status["size"] = get_elfsize(build_status["objects"].get("elf", ""))
  215. return cmdsts, build_status
  216. def clean_app(self, appdir, make_options="", show_output=True, logfile=None):
  217. return self.build_target(appdir, make_options, "clean", show_output, logfile)
  218. def compile_app(self, appdir, make_options="", show_output=True, logfile=None, parallel=""):
  219. return self.build_target(appdir, make_options, "all", show_output, logfile, parallel)
  220. def upload_app(self, appdir, make_options="", show_output=True, logfile=None):
  221. if logfile is None:
  222. uploadlog = tempfile.mktemp()
  223. else:
  224. uploadlog = logfile
  225. cmdsts, build_status = self.build_target(appdir, make_options, "upload", show_output, uploadlog)
  226. uploader = dict()
  227. upload_sts = False
  228. with open(uploadlog, 'r') as uf:
  229. for line in uf.readlines():
  230. if "-ex" in line or "\\" in line:
  231. # strip extra newline and \
  232. uploader["cmd"] = uploader.get("cmd", "") + line.strip().strip("\\")
  233. if "On-Chip Debugger" in line:
  234. uploader["version"] = line.strip()
  235. if "A problem internal to GDB has been detected" in line:
  236. uploader["gdbstatus"] = "hang"
  237. if "Quit this debugging session?" in line:
  238. uploader["gdbstatus"] = "hang"
  239. if "Remote communication error" in line:
  240. uploader["gdbstatus"] = "lostcon"
  241. if "Start address" in line:
  242. uploader["gdbstatus"] = "ok"
  243. upload_sts = True
  244. break
  245. # append openocd log to upload log
  246. openocd_log = os.path.join(appdir, "openocd.log")
  247. if os.path.isfile(openocd_log):
  248. with open(uploadlog, 'a') as uf:
  249. uf.write("\n=====OpenOCD log content dumped as below:=====\n")
  250. with open(openocd_log, "r") as of:
  251. for line in of.readlines():
  252. if "Error: Target not examined yet" in line:
  253. uploader["cpustatus"] = "hang"
  254. if "Examined RISC-V core" in line:
  255. uploader["cpustatus"] = "ok"
  256. if "Unable to halt hart" in line:
  257. uploader["cpustatus"] = "hang"
  258. uf.write(line)
  259. if upload_sts == False: # actually not upload successfully
  260. cmdsts = False
  261. if "app" in build_status:
  262. build_status["app"]["uploader"] = uploader
  263. if logfile is None:
  264. os.remove(uploadlog)
  265. print("Upload application %s status: %s" % (appdir, cmdsts))
  266. return cmdsts, build_status
  267. class MonitorThread(Thread):
  268. def __init__(self, port:str, baudrate:str, timeout:int, checks:dict, checktime=time.time(), sdk_check=False, logfile=None, show_output=False):
  269. super().__init__()
  270. self.port = port
  271. self.baudrate = baudrate
  272. self.timeout = timeout
  273. self.checks = checks
  274. self.checktime = checktime
  275. self.tty_iserr = False
  276. self.sdk_check = sdk_check
  277. self.logfile = logfile
  278. self.show_output = show_output
  279. self._exit_req = False
  280. self._check_sdk = False
  281. self._check_sdk_timeout = 10
  282. pass
  283. def get_result(self):
  284. try:
  285. return self.result
  286. except Exception:
  287. return None
  288. def get_tty_iserror(self):
  289. return self.tty_iserr
  290. def exit_request(self):
  291. self._exit_req = True
  292. pass
  293. def set_check_sdk_timeout(self, timeout=10):
  294. self._check_sdk_timestart = time.time()
  295. self._check_sdk_timeout = timeout
  296. self._check_sdk = True # start to check timeout monitor
  297. pass
  298. def run(self):
  299. start_time = time.time()
  300. serial_log = ""
  301. check_status = False
  302. pass_checks = self.checks.get("PASS", [])
  303. fail_checks = self.checks.get("FAIL", [])
  304. def test_in_check(string, checks):
  305. if type(checks) == list:
  306. for check in checks:
  307. if check in string:
  308. return True
  309. return False
  310. NSDK_CHECK_TAG = get_sdk_checktag()
  311. if get_sdk_verb_buildmsg():
  312. print("Read serial log from %s, baudrate %s" %(self.port, self.baudrate))
  313. print("Checker used: ", self.checks)
  314. print("SDK Checker Tag \"%s\", checker enable %s" % (NSDK_CHECK_TAG, self.sdk_check))
  315. print("SDK run timeout %s, banner timeout %s" % (self.timeout, self._check_sdk_timeout))
  316. check_finished = False
  317. try:
  318. ser = None
  319. ser = serial.Serial(self.port, self.baudrate, timeout=3)
  320. while (time.time() - start_time) < self.timeout:
  321. if self._exit_req:
  322. break
  323. # Remove '\r' in serial read line
  324. sline = ser.readline()
  325. line = str(try_decode_bytes(sline)).replace('\r', '')
  326. if self.sdk_check == True:
  327. if self.show_output:
  328. print("XXX Check " + line, end='')
  329. if self._check_sdk:
  330. chk_time_cost = time.time() - self._check_sdk_timestart
  331. if chk_time_cost > self._check_sdk_timeout:
  332. print("No SDK banner found in %s s, quit now!" % (self._check_sdk_timeout))
  333. break
  334. if NSDK_CHECK_TAG in line:
  335. timestr = line.split(NSDK_CHECK_TAG)[-1].strip()
  336. if "Download" in timestr:
  337. print("Warning: Download and SDK tag in same line which should not happen!")
  338. #timestr = timestr.split("Download")[0].strip()
  339. cur_time = time.mktime(time.strptime(timestr, "%b %d %Y, %H:%M:%S"))
  340. if int(cur_time) >= int(self.checktime):
  341. self.sdk_check = False
  342. line = NSDK_CHECK_TAG + " " + timestr + "\n"
  343. serial_log = serial_log + str(line)
  344. else:
  345. serial_log = serial_log + str(line)
  346. if self.show_output:
  347. print(line, end='')
  348. if check_finished == False:
  349. if test_in_check(line, fail_checks):
  350. check_status = False
  351. check_finished = True
  352. if test_in_check(line, pass_checks):
  353. check_status = True
  354. check_finished = True
  355. if check_finished:
  356. # record another 2 seconds by reset start_time and timeout to 2
  357. start_time = time.time()
  358. self.timeout = 2
  359. except serial.serialutil.SerialException:
  360. # https://stackoverflow.com/questions/21050671/how-to-check-if-device-is-connected-pyserial
  361. print("serial port %s might not exist or in use" % self.port)
  362. # set tty is error
  363. self.tty_iserr = True
  364. except Exception as exc:
  365. print("Some error happens during serial operations, %s" % (str(exc)))
  366. finally:
  367. if ser:
  368. ser.close()
  369. if self.logfile:
  370. with open(self.logfile, 'w') as lf:
  371. lf.write(serial_log)
  372. self.result = check_status
  373. return check_status
  374. class nsdk_runner(nsdk_builder):
  375. def __init__(self):
  376. super().__init__()
  377. self.hangup_action = None
  378. self.ttyerrcnt = 0
  379. self.fpgaprogramcnt = 0
  380. self.gdberrcnt = 0
  381. self.uploaderrcnt = 0
  382. pass
  383. @staticmethod
  384. def find_apps(rootdir):
  385. subdirectories = [x[0] for x in os.walk(rootdir)]
  386. appdirs = []
  387. for subdir in subdirectories:
  388. if nsdk_runner.is_app(subdir):
  389. appdirs.append(os.path.normpath(subdir))
  390. return appdirs
  391. def reset_counters(self):
  392. self.ttyerrcnt = 0
  393. self.uploaderrcnt = 0
  394. self.fpgaprogramcnt = 0
  395. self.gdberrcnt = 0
  396. pass
  397. def need_exit_now(self):
  398. if self.ttyerrcnt > get_sdk_ttyerr_maxcnt():
  399. return True
  400. if self.fpgaprogramcnt > get_sdk_fpgaprog_maxcnt():
  401. return True
  402. if self.gdberrcnt > get_sdk_gdberr_maxcnt():
  403. return True
  404. if self.uploaderrcnt > get_sdk_uploaderr_maxcnt():
  405. return True
  406. return False
  407. def show_counters(self):
  408. print("TTY Error counter %d, limit count %d" % (self.ttyerrcnt, get_sdk_ttyerr_maxcnt()))
  409. print("GDB Internal Error counter %d, limit count %d" % (self.gdberrcnt, get_sdk_gdberr_maxcnt()))
  410. print("Upload Error counter %d, limit count %d" % (self.uploaderrcnt, get_sdk_uploaderr_maxcnt()))
  411. print("FPGA Program Error counter %d, limit count %d" % (self.fpgaprogramcnt, get_sdk_fpgaprog_maxcnt()))
  412. pass
  413. def set_cpu_hangup_action(self, hangaction):
  414. self.hangup_action = hangaction
  415. pass
  416. def build_target_in_directory(self, rootdir, make_options="", target="", \
  417. show_output=True, logdir=None, stoponfail=False):
  418. appdirs = self.find_apps(rootdir)
  419. if len(appdirs) == 0:
  420. return False, None
  421. cmdsts = True
  422. build_status = dict()
  423. createlog = False
  424. if isinstance(logdir, str):
  425. createlog = True
  426. if os.path.isdir(logdir) == False:
  427. os.makedirs(logdir)
  428. for appdir in appdirs:
  429. appdir = appdir.replace("\\", "/") # Change windows \\ path to /
  430. applogfile = None
  431. if createlog:
  432. applogfile = get_logfile(appdir, rootdir, logdir, "build.log")
  433. appcmdsts, appsts = self.build_target(appdir, make_options, \
  434. target, show_output, logfile=applogfile)
  435. build_status[appdir] = appsts
  436. if appcmdsts == False:
  437. cmdsts = appcmdsts
  438. if stoponfail == True:
  439. print("Stop build directory due to fail on application %s" %(appdir))
  440. return cmdsts, build_status
  441. return cmdsts, build_status
  442. def analyze_runlog(self, logfile, parsescript=None):
  443. result = {"type": "unknown", "value": {}}
  444. if os.path.isfile(logfile):
  445. result_lines = open(logfile).readlines()
  446. program_found, subtype, result_parsed = parse_benchmark_runlog(result_lines, lgf=logfile)
  447. if program_found == PROGRAM_UNKNOWN:
  448. program_found, subtype, result_parsed = parse_benchmark_use_pyscript(result_lines, logfile, parsescript)
  449. if program_found != PROGRAM_UNKNOWN:
  450. result = {"type": program_found, "subtype": subtype, "value": result_parsed}
  451. return result
  452. def run_app_onhw(self, appdir, runcfg:dict(), show_output=True, logfile=None, uploadlog=None):
  453. app_runcfg = runcfg.get("run_config", dict())
  454. app_runchecks = runcfg.get("checks", dict())
  455. make_options = runcfg["misc"]["make_options"]
  456. checktime = runcfg["misc"]["build_time"]
  457. hwconfig = app_runcfg.get("hardware", None)
  458. serport = None
  459. timeout = 60
  460. baudrate = 115200
  461. fpgabit = None
  462. fpgaserial = None
  463. if hwconfig is not None:
  464. most_possible_serport = find_most_possible_serport()
  465. serport = hwconfig.get("serport", most_possible_serport)
  466. baudrate = hwconfig.get("baudrate", 115200)
  467. timeout = hwconfig.get("timeout", 60)
  468. fpgabit = hwconfig.get("fpgabit", None)
  469. fpgaserial = hwconfig.get("fpgaserial", None)
  470. ser_thread = None
  471. uploader = None
  472. sdk_check = get_sdk_check()
  473. banner_tmout = get_sdk_banner_tmout()
  474. retry_cnt = 0
  475. max_retrycnt = 1
  476. while True:
  477. try:
  478. if retry_cnt > max_retrycnt: # do retry
  479. break
  480. retry_cnt += 1
  481. print("Hardware configuration: serial port %s, baudrate %s, timeout %ds, retry counter %d" % (serport, baudrate, timeout, retry_cnt))
  482. if serport: # only monitor serial port when port found
  483. ser_thread = MonitorThread(serport, baudrate, timeout, app_runchecks, checktime, \
  484. sdk_check, logfile, show_output)
  485. ser_thread.start()
  486. else:
  487. print("Warning: No available serial port found, please check!")
  488. self.ttyerrcnt += 1
  489. cmdsts, upload_sts = self.upload_app(appdir, make_options, show_output, uploadlog)
  490. uploader = upload_sts.get("app", dict()).get("uploader", None)
  491. uploader["retried"] = retry_cnt
  492. status = True
  493. if ser_thread:
  494. if cmdsts == False:
  495. ser_thread.exit_request()
  496. else:
  497. ser_thread.set_check_sdk_timeout(banner_tmout)
  498. while ser_thread.is_alive():
  499. ser_thread.join(1)
  500. status = ser_thread.get_result()
  501. if ser_thread.get_tty_iserror(): # tty is in use or not exist
  502. print("tty serial port error count %d" % (self.ttyerrcnt))
  503. self.ttyerrcnt += 1
  504. del ser_thread
  505. if uploader.get("cpustatus", "") == "hang": # cpu hangs then call cpu hangup action and retry this application
  506. self.uploaderrcnt += 1
  507. if self.hangup_action is not None:
  508. print("Execute hangup action for hangup case!")
  509. if self.hangup_action() == True:
  510. print("hangup action success!")
  511. continue
  512. else:
  513. print("hangup action failed!")
  514. elif fpgabit and fpgaserial:
  515. print("Reprogram fpga bit %s on fpga board serial number %s, total fpga reprogam count %d" % (fpgabit, fpgaserial, self.fpgaprogramcnt))
  516. self.fpgaprogramcnt += 1
  517. if program_fpga(fpgabit, fpgaserial) == True:
  518. print("Reprogram fpga sucessfully!")
  519. continue
  520. else:
  521. print("Reprogram fpga failed!")
  522. else:
  523. print("No cpu hangup action found, just continue with other cases")
  524. if uploader.get("gdbstatus", "") == "hang": # gdb hangs with internal error retry upload this application
  525. max_retrycnt = 2 # when gdb internal error happened, do retry twice
  526. print("GDB internal error happened, re-upload application, total re-upload count %d" % (self.gdberrcnt))
  527. self.gdberrcnt += 1
  528. continue
  529. # exit with upload status
  530. break
  531. except (KeyboardInterrupt, SystemExit):
  532. print("%s: Exit program due to CTRL - C pressed or SystemExit" % (sys._getframe().f_code.co_name))
  533. if ser_thread:
  534. ser_thread.exit_request()
  535. sys.exit(1)
  536. final_status = cmdsts and status
  537. return final_status, uploader
  538. def run_app_onqemu(self, appdir, runcfg:dict(), show_output=True, logfile=None):
  539. app_runcfg = runcfg.get("run_config", dict())
  540. app_runchecks = runcfg.get("checks", dict())
  541. build_info = runcfg["misc"]["build_info"]
  542. build_config = runcfg["misc"]["build_config"]
  543. build_objects = runcfg["misc"]["build_objects"]
  544. checktime = runcfg["misc"]["build_time"]
  545. hwconfig = app_runcfg.get("qemu", dict())
  546. timeout = 60
  547. qemu_exe = None
  548. qemu_extraopt = ""
  549. if hwconfig is not None:
  550. qemu32_exe = hwconfig.get("qemu32", "qemu-system-riscv32")
  551. qemu64_exe = hwconfig.get("qemu64", "qemu-system-riscv64")
  552. qemu_machine = hwconfig.get("qemu_machine", None)
  553. qemu_cpu = hwconfig.get("qemu_cpu", None)
  554. qemu_exe = qemu32_exe
  555. build_soc = build_info["SOC"]
  556. build_board = build_info["BOARD"]
  557. build_core = build_info["CORE"]
  558. build_download = build_info["DOWNLOAD"]
  559. build_smp = build_info.get("SMP", "")
  560. build_arch_ext = build_config.get("ARCH_EXT", "")
  561. if build_arch_ext == "":
  562. build_arch_ext = build_info.get("ARCH_EXT", "")
  563. if build_smp != "":
  564. qemu_extraopt = "%s -smp %s" % (qemu_extraopt, build_smp)
  565. if qemu_machine is None:
  566. if is_nuclei_demosoc(build_soc):
  567. machine = "nuclei_n"
  568. else:
  569. if build_board == "gd32vf103v_rvstar":
  570. machine = "gd32vf103_rvstar"
  571. elif build_board == "gd32vf103v_eval":
  572. machine = "gd32vf103_eval"
  573. else:
  574. machine = "nuclei_n"
  575. # machine combined with download
  576. machine = machine + ",download=%s" %(build_download.lower())
  577. else:
  578. machine = qemu_machine
  579. if qemu_cpu is None:
  580. qemu_sel_cpu = "nuclei-%s" % (build_core.lower())
  581. if build_arch_ext != "":
  582. qemu_sel_cpu = qemu_sel_cpu + ",ext=%s" %(build_arch_ext)
  583. else:
  584. qemu_sel_cpu = qemu_cpu
  585. if "rv64" in build_info["RISCV_ARCH"]:
  586. qemu_exe = qemu64_exe
  587. timeout = hwconfig.get("timeout", 60)
  588. runner = None
  589. cmdsts = False
  590. sdk_check = get_sdk_check()
  591. if qemu_exe:
  592. if os.path.isfile(build_objects["elf"]):
  593. vercmd = "%s --version" % (qemu_exe)
  594. verchk = "QEMU emulator version"
  595. ret, verstr = check_tool_version(vercmd, verchk)
  596. if ret:
  597. command = "%s %s -M %s -cpu %s -nodefaults -nographic -icount shift=0 -serial stdio -kernel %s" \
  598. % (qemu_exe, qemu_extraopt, machine, qemu_sel_cpu, build_objects["elf"])
  599. print("Run command: %s" %(command))
  600. runner = {"cmd": command, "version": verstr}
  601. cmdsts, _ = run_cmd_and_check(command, timeout, app_runchecks, checktime, \
  602. sdk_check, logfile, show_output)
  603. else:
  604. print("%s doesn't exist in PATH, please check!" % qemu_exe)
  605. else:
  606. print("ELF file %s doesn't exist, can't run on qemu" % (build_objects["elf"]))
  607. final_status = cmdsts
  608. return final_status, runner
  609. def run_app_onxlspike(self, appdir, runcfg:dict(), show_output=True, logfile=None):
  610. app_runcfg = runcfg.get("run_config", dict())
  611. app_runchecks = runcfg.get("checks", dict())
  612. build_info = runcfg["misc"]["build_info"]
  613. build_config = runcfg["misc"]["build_config"]
  614. build_objects = runcfg["misc"]["build_objects"]
  615. checktime = runcfg["misc"]["build_time"]
  616. hwconfig = app_runcfg.get("xlspike", dict())
  617. timeout = 60
  618. xlspike_exe = None
  619. xlspike_extraopt = ""
  620. if hwconfig is not None:
  621. xlspike_exe = hwconfig.get("xlspike", "xl_spike")
  622. build_soc = build_info["SOC"]
  623. build_board = build_info["BOARD"]
  624. riscv_arch = build_info["RISCV_ARCH"]
  625. # replace e with i for xlspike
  626. riscv_arch = riscv_arch.replace("e", "i")
  627. #build_arch_ext = build_info.get("ARCH_EXT", "")
  628. build_smp = build_info.get("SMP", "")
  629. if build_smp != "":
  630. xlspike_extraopt = "%s -p%s" % (xlspike_extraopt, build_smp)
  631. if not is_nuclei_demosoc(build_soc):
  632. xlspike_exe = None
  633. print("SOC=%s BOARD=%s is not supported by xlspike" % (build_soc, build_board))
  634. timeout = hwconfig.get("timeout", 60)
  635. runner = None
  636. cmdsts = False
  637. sdk_check = get_sdk_check()
  638. if xlspike_exe:
  639. if os.path.isfile(build_objects["elf"]):
  640. vercmd = "%s --help" % (xlspike_exe)
  641. verchk = "RISC-V ISA Simulator"
  642. ret, verstr = check_tool_version(vercmd, verchk)
  643. if ret:
  644. command = "%s %s --isa %s %s" % (xlspike_exe, xlspike_extraopt, riscv_arch, build_objects["elf"])
  645. print("Run command: %s" %(command))
  646. runner = {"cmd": command, "version": verstr}
  647. cmdsts, _ = run_cmd_and_check(command, timeout, app_runchecks, checktime, \
  648. sdk_check, logfile, show_output)
  649. else:
  650. print("%s doesn't exist in PATH, please check!" % xlspike_exe)
  651. else:
  652. print("ELF file %s doesn't exist, can't run on xlspike" % (build_objects["elf"]))
  653. else:
  654. print("Can't run on xlspike due to run config not exist or config not supported")
  655. final_status = cmdsts
  656. return final_status, runner
  657. def run_app_onncycm(self, appdir, runcfg:dict(), show_output=True, logfile=None):
  658. app_runcfg = runcfg.get("run_config", dict())
  659. app_runchecks = runcfg.get("checks", dict())
  660. build_info = runcfg["misc"]["build_info"]
  661. build_config = runcfg["misc"]["build_config"]
  662. build_objects = runcfg["misc"]["build_objects"]
  663. checktime = runcfg["misc"]["build_time"]
  664. hwconfig = app_runcfg.get("ncycm", dict())
  665. timeout = 60
  666. ncycm_exe = None
  667. if hwconfig is not None:
  668. ncycm_exe = hwconfig.get("ncycm", "ncycm")
  669. timeout = hwconfig.get("timeout", 600)
  670. runner = None
  671. cmdsts = False
  672. sdk_check = get_sdk_check()
  673. if ncycm_exe:
  674. if os.path.isfile(build_objects["elf"]):
  675. vercmd = "%s -v" % (ncycm_exe)
  676. verchk = "version:"
  677. ret, verstr = check_tool_version(vercmd, verchk)
  678. if ret == False:
  679. verstr = "v1"
  680. ret = check_tool_exist(ncycm_exe) or os.path.isfile(ncycm_exe)
  681. if ret:
  682. if (verstr == "v1"):
  683. ncycm_verilog = fix_demosoc_verilog_ncycm(build_objects["verilog"])
  684. if ncycm_verilog == "":
  685. command = ""
  686. else:
  687. command = "%s +TESTCASE=%s" % (ncycm_exe, ncycm_verilog)
  688. else:
  689. command = "%s %s" % (ncycm_exe, build_objects["elf"])
  690. if command != "":
  691. print("Run command: %s" %(command))
  692. runner = {"cmd": command, "version": verstr}
  693. cmdsts, _ = run_cmd_and_check(command, timeout, app_runchecks, checktime, \
  694. sdk_check, logfile, show_output, 480)
  695. else:
  696. print("Unable to run cycle model with %s" % (build_objects["elf"]))
  697. cmdsts = False
  698. else:
  699. print("%s doesn't exist in PATH, please check!" % ncycm_exe)
  700. else:
  701. print("ELF file %s doesn't exist, can't run on xlspike" % (build_objects["elf"]))
  702. else:
  703. print("Can't run on xlspike due to run config not exist or config not supported")
  704. final_status = cmdsts
  705. return final_status, runner
  706. def build_app_with_config(self, appdir, appconfig:dict, show_output=True, logfile=None):
  707. build_config = appconfig.get("build_config", None)
  708. target = appconfig.get("build_target", "all")
  709. parallel = appconfig.get("parallel", "")
  710. # Copy program objects if copy_objects is true
  711. copy_objects_required = appconfig.get("copy_objects", get_sdk_copyobjects_flag())
  712. make_options = ""
  713. if isinstance(build_config, dict):
  714. for key, value in build_config.items():
  715. value = str(value).strip()
  716. if " " in key:
  717. continue
  718. if " " in value:
  719. make_options += " %s=\"%s\""%(key, value)
  720. else:
  721. make_options += " %s=%s"%(key, value)
  722. appcmdsts, appsts = self.build_target(appdir, make_options, target, show_output, logfile, parallel)
  723. objs_copydir = os.path.dirname(logfile) # where objects are copied to
  724. # copy objects if copy_objects_required
  725. if copy_objects_required:
  726. nsdk_builder.copy_objects(appsts, objs_copydir)
  727. buildtime = appsts["time"]["build"]
  728. print("Build application %s with target %s, make options %s, time cost %s seconds, passed: %s" %(appdir, target, make_options, buildtime, appcmdsts))
  729. sys.stdout.flush()
  730. appsts["config"] = appconfig
  731. return appcmdsts, appsts
  732. def run_app_with_config(self, appdir, appconfig:dict, show_output=True, buildlog=None, runlog=None):
  733. appconfig["build_target"] = "clean dasm"
  734. # build application
  735. build_cktime = time.time()
  736. appcmdsts, appsts = self.build_app_with_config(appdir, appconfig, show_output, buildlog)
  737. # run application
  738. if appcmdsts == False:
  739. print("Failed to build application %s, so we can't run it!" % (appdir))
  740. return appcmdsts, appsts
  741. # get run config
  742. app_runcfg = appconfig.get("run_config", dict())
  743. app_runtarget = app_runcfg.get("target", "hardware")
  744. app_parsescript = app_runcfg.get("parsescript", None)
  745. if app_parsescript is not None:
  746. if os.path.isfile(app_parsescript) == False:
  747. app_parsescript = os.path.join(appdir, app_parsescript)
  748. # get run checks
  749. DEFAULT_CHECKS = { "PASS": [ ], "FAIL": [ "MCAUSE:" ] }
  750. app_runchecks = appconfig.get("checks", DEFAULT_CHECKS)
  751. misc_config = {"make_options": appsts["app"]["make_options"], "build_config": appconfig["build_config"],\
  752. "build_info": appsts["info"], "build_objects": appsts["objects"], "build_time": build_cktime}
  753. runcfg = {"run_config": app_runcfg, "checks": app_runchecks, "misc": misc_config}
  754. print("Run application on %s" % app_runtarget)
  755. runstarttime = time.time()
  756. runstatus = False
  757. appsts["status_code"]["run"] = RUNSTATUS_NOTSTART
  758. if app_runtarget == "hardware":
  759. uploadlog = None
  760. if runlog:
  761. uploadlog = os.path.join(os.path.dirname(runlog), "upload.log")
  762. runstatus, uploader = self.run_app_onhw(appdir, runcfg, show_output, runlog, uploadlog)
  763. # If run successfully, then do log analyze
  764. if runlog and runstatus:
  765. appsts["result"] = self.analyze_runlog(runlog, app_parsescript)
  766. appsts["logs"]["run"] = runlog
  767. appsts["logs"]["upload"] = uploadlog
  768. appsts["status_code"]["run"] = RUNSTATUS_OK if runstatus else RUNSTATUS_FAIL
  769. if uploader:
  770. appsts["app"]["uploader"] = uploader
  771. elif app_runtarget == "qemu":
  772. runstatus, runner = self.run_app_onqemu(appdir, runcfg, show_output, runlog)
  773. # If run successfully, then do log analyze
  774. if runlog and runstatus:
  775. appsts["result"] = self.analyze_runlog(runlog, app_parsescript)
  776. appsts["logs"]["run"] = runlog
  777. appsts["status_code"]["run"] = RUNSTATUS_OK if runstatus else RUNSTATUS_FAIL
  778. if runner:
  779. appsts["app"]["qemu"] = runner
  780. elif app_runtarget == "xlspike":
  781. runstatus, runner = self.run_app_onxlspike(appdir, runcfg, show_output, runlog)
  782. # If run successfully, then do log analyze
  783. if runlog and runstatus:
  784. appsts["result"] = self.analyze_runlog(runlog, app_parsescript)
  785. appsts["logs"]["run"] = runlog
  786. appsts["status_code"]["run"] = RUNSTATUS_OK if runstatus else RUNSTATUS_FAIL
  787. if runner:
  788. appsts["app"]["xlspike"] = runner
  789. elif app_runtarget == "ncycm":
  790. runstatus, runner = self.run_app_onncycm(appdir, runcfg, show_output, runlog)
  791. # If run successfully, then do log analyze
  792. if runlog and runstatus:
  793. appsts["result"] = self.analyze_runlog(runlog, app_parsescript)
  794. appsts["logs"]["run"] = runlog
  795. appsts["status_code"]["run"] = RUNSTATUS_OK if runstatus else RUNSTATUS_FAIL
  796. if runner:
  797. appsts["app"]["ncycm"] = runner
  798. else:
  799. print("Unsupported run target %s" %(app_runtarget))
  800. runtime = round(time.time() - runstarttime, 2)
  801. print("Run application %s on %s, time cost %s seconds, passed: %s" %(appdir, app_runtarget, runtime, runstatus))
  802. sys.stdout.flush()
  803. appsts["status"]["run"] = runstatus
  804. appsts["time"]["run"] = runtime
  805. return runstatus, appsts
  806. def build_apps_with_config(self, config:dict, show_output=True, logdir=None, stoponfail=False):
  807. # Build all the applications, each application only has one configuration
  808. # "app" : { the_only_config }
  809. cmdsts = True
  810. build_status = dict()
  811. apps_config = copy.deepcopy(config)
  812. for appdir in apps_config:
  813. appconfig = apps_config[appdir]
  814. applogs = appconfig.get("logs", dict())
  815. applogfile = applogs.get("build", None)
  816. appcmdsts, appsts = self.build_app_with_config(appdir, appconfig, show_output, applogfile)
  817. build_status[appdir] = appsts
  818. if appcmdsts == False:
  819. cmdsts = appcmdsts
  820. if stoponfail == True:
  821. print("Stop build apps with config due to fail on application %s" %(appdir))
  822. return cmdsts, build_status
  823. return cmdsts, build_status
  824. def build_apps_with_configs(self, config:dict, show_output=True, logdir=None, stoponfail=False):
  825. # Build all the applications, each application has more than one configuration
  826. # "app" : {"configs": {"case1": case1_config}}
  827. cmdsts = True
  828. build_status = dict()
  829. apps_config = copy.deepcopy(config)
  830. for appdir in apps_config:
  831. appconfigs = apps_config[appdir]
  832. if "configs" not in appconfigs:
  833. continue
  834. build_status[appdir] = dict()
  835. app_allconfigs = appconfigs["configs"]
  836. for cfgname in app_allconfigs:
  837. appconfig = app_allconfigs[cfgname] # get configuration for each case for single app
  838. applogs = appconfig.get("logs", dict())
  839. applogfile = applogs.get("build", None)
  840. appcmdsts, appsts = self.build_app_with_config(appdir, appconfig, show_output, applogfile)
  841. build_status[appdir][cfgname] = appsts
  842. if appcmdsts == False:
  843. cmdsts = appcmdsts
  844. if stoponfail == True:
  845. print("Stop build apps with config due to fail on application %s" %(appdir))
  846. return cmdsts, build_status
  847. return cmdsts, build_status
  848. def run_apps_with_config(self, config:dict, show_output=True, stoponfail=False):
  849. # Run all the applications, each application only has one configuration
  850. # "app" : { the_only_config }
  851. cmdsts = True
  852. # reset error counters
  853. self.reset_counters()
  854. build_status = dict()
  855. apps_config = copy.deepcopy(config)
  856. for appdir in apps_config:
  857. appconfig = apps_config[appdir]
  858. applogs = appconfig.get("logs", dict())
  859. app_buildlogfile = applogs.get("build", None)
  860. app_runlogfile = applogs.get("run", None)
  861. appcmdsts, appsts = self.run_app_with_config(appdir, appconfig, show_output, app_buildlogfile, app_runlogfile)
  862. build_status[appdir] = appsts
  863. if appcmdsts == False:
  864. cmdsts = appcmdsts
  865. if stoponfail == True:
  866. print("ERROR: Stop run apps with config due to fail on application %s" %(appdir))
  867. return cmdsts, build_status
  868. # error exit check
  869. if self.need_exit_now():
  870. print("ERROR: Need to exit now due to error counter exceed limit!")
  871. self.show_counters()
  872. cmdsts = False
  873. return cmdsts, build_status
  874. return cmdsts, build_status
  875. def run_apps_with_configs(self, config:dict, show_output=True, stoponfail=False):
  876. # Run all the applications, each application has more than one configuration
  877. # "app" : {"configs": {"case1": case1_config}}
  878. cmdsts = True
  879. # reset error counters
  880. self.reset_counters()
  881. build_status = dict()
  882. apps_config = copy.deepcopy(config)
  883. for appdir in apps_config:
  884. appconfigs = apps_config[appdir]
  885. if "configs" not in appconfigs:
  886. continue
  887. build_status[appdir] = dict()
  888. app_allconfigs = appconfigs["configs"]
  889. for cfgname in app_allconfigs:
  890. appconfig = app_allconfigs[cfgname] # get configuration for each case for single app
  891. applogs = appconfig.get("logs", dict())
  892. app_buildlogfile = applogs.get("build", None)
  893. app_runlogfile = applogs.get("run", None)
  894. appcmdsts, appsts = self.run_app_with_config(appdir, appconfig, show_output, app_buildlogfile, app_runlogfile)
  895. build_status[appdir][cfgname] = appsts
  896. if appcmdsts == False:
  897. cmdsts = appcmdsts
  898. if stoponfail == True:
  899. print("ERROR: Stop run apps with config due to fail on application %s" %(appdir))
  900. return cmdsts, build_status
  901. # error exit check
  902. if self.need_exit_now():
  903. print("ERROR: Need to exit now due to error counter exceed limit!")
  904. self.show_counters()
  905. cmdsts = False
  906. return cmdsts, build_status
  907. return cmdsts, build_status