nsdk_utils.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. #!/usr/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 time
  8. import shutil
  9. import signal
  10. import psutil
  11. import re
  12. import copy
  13. import serial
  14. import serial.tools.list_ports
  15. import tempfile
  16. import collections
  17. from threading import Thread
  18. import subprocess
  19. import asyncio
  20. import glob
  21. import json
  22. import yaml
  23. except Exception as exc:
  24. print("Import Error: %s" % (exc))
  25. print("Please install requried packages using: pip3 install -r %s" % (requirement_file))
  26. sys.exit(1)
  27. try:
  28. from collections.abc import Mapping
  29. except ImportError: # Python 2.7 compatibility
  30. from collections import Mapping
  31. SDK_GLOBAL_VARIABLES = {
  32. "sdk_checktag": "Nuclei SDK Build Time:",
  33. "sdk_check": True,
  34. "sdk_banner_tmout": 15,
  35. "sdk_copy_objects": "elf,map,dasm,verilog"
  36. }
  37. class NThread(Thread):
  38. def __init__(self, func, args):
  39. super(NThread, self).__init__()
  40. self.func = func
  41. self.args = args
  42. def run(self):
  43. self.result = self.func(*self.args)
  44. def get_result(self):
  45. try:
  46. return self.result
  47. except Exception:
  48. return None
  49. YAML_OK=0
  50. YAML_NOFILE=1
  51. YAML_INVAILD=2
  52. def load_yaml(file):
  53. if isinstance(file, str) == False or os.path.isfile(file) == False:
  54. return YAML_NOFILE, None
  55. try:
  56. data = yaml.load(open(file, 'r'), Loader=yaml.FullLoader)
  57. return YAML_OK, data
  58. except:
  59. print("Error: %s is an invalid yaml file!" % (file))
  60. return YAML_INVAILD, None
  61. def save_yaml(file, data):
  62. if isinstance(file, str) == False:
  63. return False
  64. try:
  65. with open(file, "w") as cf:
  66. yaml.dump(data, cf, indent=4)
  67. return True
  68. except:
  69. print("Error: Data can't be serialized to yaml file!")
  70. return False
  71. JSON_OK=0
  72. JSON_NOFILE=1
  73. JSON_INVAILD=2
  74. def load_json(file):
  75. if isinstance(file, str) == False or os.path.isfile(file) == False:
  76. return JSON_NOFILE, None
  77. try:
  78. data = json.load(open(file, 'r'))
  79. return JSON_OK, data
  80. except:
  81. print("Error: %s is an invalid json file!" % (file))
  82. return JSON_INVAILD, None
  83. def save_json(file, data):
  84. if isinstance(file, str) == False:
  85. return False
  86. try:
  87. with open(file, "w") as cf:
  88. json.dump(data, cf, indent=4)
  89. return True
  90. except:
  91. print("Error: Data can't be serialized to json file!")
  92. return False
  93. def save_csv(file, csvlines, display=True):
  94. if isinstance(csvlines, list) == False:
  95. return False
  96. try:
  97. with open(file, "w") as cf:
  98. for line in csvlines:
  99. csvline = line + "\n"
  100. cf.write(csvline)
  101. if display:
  102. print("CSV, %s" % line)
  103. return True
  104. except:
  105. print("Error: Data can't be saved to file!")
  106. return False
  107. # Return possible serports, return a list of possible serports
  108. def find_possible_serports():
  109. comports = serial.tools.list_ports.comports()
  110. serports = [ port.device for port in comports ]
  111. return serports
  112. def find_serport_by_no(serno):
  113. comports = serial.tools.list_ports.comports()
  114. serport = None
  115. for port in comports:
  116. cur_serno = port.serial_number
  117. cur_dev = port.device
  118. cur_loc = port.location
  119. if cur_serno is None:
  120. continue
  121. if sys.platform == "win32":
  122. if (serno + 'B') == cur_serno:
  123. serport = cur_dev
  124. break
  125. else:
  126. if serno != cur_serno:
  127. continue
  128. # serial is the second device of the composite device
  129. if cur_loc.endswith(".1"):
  130. serport = cur_dev
  131. break
  132. # serport founded
  133. return serport
  134. def find_most_possible_serport():
  135. serports = find_possible_serports()
  136. if len(serports) > 0:
  137. # sort the ports
  138. serports.sort()
  139. # get the biggest port
  140. # for /dev/ttyUSB0, /dev/ttyUSB1, get /dev/ttyUSB1
  141. # for COM16, COM17, get COM17
  142. return serports[-1]
  143. else:
  144. return None
  145. # get from https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
  146. def dict_merge(dct, merge_dct):
  147. """ Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
  148. updating only top-level keys, dict_merge recurses down into dicts nested
  149. to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
  150. ``dct``.
  151. :param dct: dict onto which the merge is executed
  152. :param merge_dct: dct merged into dct
  153. :return: None
  154. """
  155. for k, v in merge_dct.items():
  156. if (k in dct and isinstance(dct[k], dict)
  157. and isinstance(merge_dct[k], Mapping)):
  158. dict_merge(dct[k], merge_dct[k])
  159. else:
  160. dct[k] = merge_dct[k]
  161. def get_make_csv(app, config):
  162. make_options = " "
  163. SUPPORT_KEYS = ["SOC", "BOARD", "CORE", "DOWNLOAD", "VARIANT", \
  164. "BENCH_UNIT", "BENCH_FLAGS", "ARCH_EXT", "STDCLIB", "SILENT", "V"]
  165. csv_print = "CSV, APP=%s" % (app)
  166. if isinstance(config, dict):
  167. for key in config:
  168. if key not in SUPPORT_KEYS:
  169. continue
  170. option = "%s=%s"%(key, config[key])
  171. make_options = " %s %s " % (make_options, option)
  172. csv_print = "%s, %s" % (csv_print, option)
  173. return make_options, csv_print
  174. def try_decode_bytes(bytes):
  175. ENCODING_LIST = ['utf-8', 'gbk', 'gb18030']
  176. destr = ""
  177. for encoding in ENCODING_LIST:
  178. try:
  179. destr = bytes.decode(encoding)
  180. break
  181. except:
  182. continue
  183. return destr
  184. def kill_async_subprocess(proc):
  185. startticks = time.time()
  186. if proc is not None:
  187. try:
  188. kill_sig = signal.SIGTERM
  189. if sys.platform != "win32":
  190. kill_sig = signal.SIGKILL
  191. print("Try to Kill process id %d now" %(proc.pid))
  192. parent_proc = psutil.Process(proc.pid)
  193. try:
  194. # This might cause PermissionError: [Errno 1] Operation not permitted: '/proc/1/stat' issue
  195. child_procs = parent_proc.children(recursive=True)
  196. for child_proc in child_procs:
  197. print("Kill child process %s, pid %d" %(child_proc.name(), child_proc.pid))
  198. try:
  199. os.kill(child_proc.pid, kill_sig) # kill child process
  200. except:
  201. continue
  202. except Exception as exc:
  203. print("Warning: kill child process failed with %s" %(exc))
  204. if parent_proc.is_running():
  205. print("Kill parent process %s, pid %d" %(parent_proc.name(), parent_proc.pid))
  206. if sys.platform != "win32":
  207. try:
  208. os.killpg(parent_proc.pid, kill_sig) # kill parent process
  209. except:
  210. os.kill(parent_proc.pid, kill_sig) # kill parent process
  211. else:
  212. os.kill(parent_proc.pid, kill_sig) # kill parent process
  213. # kill using process.kill again
  214. if parent_proc.is_running():
  215. proc.kill()
  216. except psutil.NoSuchProcess:
  217. pass
  218. except Exception as exc:
  219. print("Warning: kill process failed with %s" %(exc))
  220. # show time cost for kill process
  221. print("kill process used %.2f seconds" %((time.time() - startticks)))
  222. sys.stdout.flush()
  223. pass
  224. def kill_subprocess(proc):
  225. try:
  226. if proc.poll() is None: # process is still running
  227. kill_async_subprocess(proc)
  228. except:
  229. pass
  230. pass
  231. COMMAND_RUNOK=0
  232. COMMAND_INVALID=1
  233. COMMAND_FAIL=2
  234. COMMAND_INTERRUPTED=3
  235. COMMAND_EXCEPTION=4
  236. COMMAND_NOTAPP=5
  237. COMMAND_TIMEOUT=6
  238. COMMAND_TIMEOUT_READ=7
  239. RUNSTATUS_OK=0
  240. RUNSTATUS_FAIL=1
  241. RUNSTATUS_NOTSTART=2
  242. def run_command(command, show_output=True, logfile=None, append=False):
  243. logfh = None
  244. ret = COMMAND_RUNOK
  245. cmd_elapsed_ticks = 0
  246. if isinstance(command, str) == False:
  247. return COMMAND_INVALID, cmd_elapsed_ticks
  248. startticks = time.time()
  249. process = None
  250. try:
  251. if isinstance(logfile, str):
  252. if append:
  253. logfh = open(logfile, "ab")
  254. else:
  255. logfh = open(logfile, "wb")
  256. if logfh:
  257. # record command run in log file
  258. logfh.write(("Execute Command %s\n" % (command)).encode())
  259. process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, \
  260. stderr=subprocess.STDOUT)
  261. while True:
  262. line = process.stdout.readline()
  263. if (not line) and process.poll() is not None:
  264. break
  265. if show_output:
  266. print(try_decode_bytes(line), end="")
  267. if logfh:
  268. logfh.write(line)
  269. time.sleep(0.01)
  270. process.communicate(30)
  271. if process.returncode != 0:
  272. ret = COMMAND_FAIL
  273. except (KeyboardInterrupt):
  274. print("Key CTRL-C pressed, command executing stopped!")
  275. ret = COMMAND_INTERRUPTED
  276. except subprocess.TimeoutExpired:
  277. ret = COMMAND_TIMEOUT
  278. except Exception as exc:
  279. print("Unexpected exception happened: %s" %(str(exc)))
  280. ret = COMMAND_EXCEPTION
  281. finally:
  282. kill_subprocess(process)
  283. if process:
  284. del process
  285. if logfh:
  286. logfh.close()
  287. cmd_elapsed_ticks = time.time() - startticks
  288. return ret, cmd_elapsed_ticks
  289. async def run_cmd_and_check_async(command, timeout:int, checks:dict, checktime=time.time(), sdk_check=False, logfile=None, show_output=False, banner_timeout=3):
  290. logfh = None
  291. ret = COMMAND_FAIL
  292. cmd_elapsed_ticks = 0
  293. if isinstance(command, str) == False:
  294. return COMMAND_INVALID, cmd_elapsed_ticks
  295. startticks = time.time()
  296. process = None
  297. check_status = False
  298. pass_checks = checks.get("PASS", [])
  299. fail_checks = checks.get("FAIL", [])
  300. def test_in_check(string, checks):
  301. if type(checks) == list:
  302. for check in checks:
  303. if check in string:
  304. return True
  305. return False
  306. NSDK_CHECK_TAG = get_sdk_checktag()
  307. print("Checker used: ", checks)
  308. print("SDK Checker Tag \"%s\", checker enable %s" % (NSDK_CHECK_TAG, sdk_check))
  309. print("SDK run timeout %s, banner timeout %s" % (timeout, banner_timeout))
  310. check_finished = False
  311. start_time = time.time()
  312. serial_log = ""
  313. nsdk_check_timeout = banner_timeout
  314. sdk_checkstarttime = time.time()
  315. try:
  316. if isinstance(logfile, str):
  317. logfh = open(logfile, "wb")
  318. if sys.platform != "win32":
  319. # add exec to running command to avoid create a process called /bin/sh -c
  320. # and if you kill that process it will kill this sh process not the really
  321. # command process you want to kill
  322. process = await asyncio.create_subprocess_shell("exec " + command, \
  323. stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
  324. else:
  325. process = await asyncio.create_subprocess_shell(command, \
  326. stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
  327. while (time.time() - start_time) < timeout:
  328. try:
  329. linebytes = await asyncio.wait_for(process.stdout.readline(), 1)
  330. except asyncio.TimeoutError:
  331. if sdk_check == True:
  332. linebytes = None
  333. else:
  334. continue
  335. except KeyboardInterrupt:
  336. print("Key CTRL-C pressed, command executing stopped!")
  337. break
  338. except:
  339. break
  340. if linebytes:
  341. line = str(try_decode_bytes(linebytes)).replace('\r', '')
  342. else:
  343. line = ""
  344. if sdk_check == True:
  345. if (time.time() - sdk_checkstarttime) > nsdk_check_timeout:
  346. print("No SDK banner found in %s s, quit now!" % (nsdk_check_timeout))
  347. ret = COMMAND_TIMEOUT
  348. check_status = False
  349. break
  350. if line == "":
  351. continue
  352. if show_output:
  353. print("XXX Check " + line, end='')
  354. if NSDK_CHECK_TAG in line:
  355. timestr = line.split(NSDK_CHECK_TAG)[-1].strip()
  356. cur_time = time.mktime(time.strptime(timestr, "%b %d %Y, %H:%M:%S"))
  357. if int(cur_time) >= int(checktime):
  358. sdk_check = False
  359. line = NSDK_CHECK_TAG + " " + timestr + "\n"
  360. serial_log = serial_log + str(line)
  361. else:
  362. serial_log = serial_log + str(line)
  363. if show_output:
  364. print(line, end='')
  365. if check_finished == False:
  366. if test_in_check(line, fail_checks):
  367. check_status = False
  368. check_finished = True
  369. if test_in_check(line, pass_checks):
  370. check_status = True
  371. check_finished = True
  372. if check_finished:
  373. ret = COMMAND_RUNOK
  374. # record another 2 seconds by reset start_time and timeout to 2
  375. start_time = time.time()
  376. timeout = 1
  377. if logfh and linebytes:
  378. logfh.write(linebytes)
  379. time.sleep(0.01)
  380. except (KeyboardInterrupt):
  381. print("Key CTRL-C pressed, command executing stopped!")
  382. ret = COMMAND_INTERRUPTED
  383. except Exception as exc:
  384. print("Unexpected exception happened: %s" %(str(exc)))
  385. ret = COMMAND_EXCEPTION
  386. finally:
  387. # kill this process
  388. kill_async_subprocess(process)
  389. if logfh:
  390. logfh.close()
  391. cmd_elapsed_ticks = time.time() - startticks
  392. return check_status, cmd_elapsed_ticks
  393. def run_cmd_and_check(command, timeout:int, checks:dict, checktime=time.time(), sdk_check=False, logfile=None, show_output=False, banner_timeout=30):
  394. loop = asyncio.get_event_loop()
  395. try:
  396. ret, cmd_elapsed_ticks = loop.run_until_complete( \
  397. run_cmd_and_check_async(command, timeout, checks, checktime, sdk_check, logfile, show_output, banner_timeout))
  398. except KeyboardInterrupt:
  399. print("Key CTRL-C pressed, command executing stopped!")
  400. ret, cmd_elapsed_ticks = False, 0
  401. finally:
  402. if sys.platform != "win32":
  403. os.system("stty echo 2> /dev/null")
  404. return ret, cmd_elapsed_ticks
  405. def find_files(fndir, pattern, recursive=False):
  406. fndir = os.path.normpath(fndir)
  407. files = glob.glob(os.path.join(fndir, pattern), recursive=recursive)
  408. return files
  409. def get_logfile(appdir, startdir, logdir, logname):
  410. relpath = os.path.relpath(appdir, startdir)
  411. _, startdir_basename = os.path.splitdrive(startdir)
  412. applogdir = os.path.join(os.path.relpath(logdir + os.sep + startdir_basename), relpath)
  413. applog = os.path.relpath(os.path.join(applogdir, logname))
  414. applogdir = os.path.dirname(applog)
  415. if os.path.isdir(applogdir) == False:
  416. os.makedirs(applogdir)
  417. return applog
  418. def strtofloat(value):
  419. fval = 0.0
  420. try:
  421. match = re.search(r'[+-]?\d*\.?\d+([Ee][+-]?\d+)?', value.strip())
  422. if match:
  423. fval = float(match.group())
  424. except:
  425. pass
  426. return fval
  427. def check_tool_version(ver_cmd, ver_check):
  428. vercmd_log = tempfile.mktemp()
  429. ret, _ = run_command(ver_cmd, show_output=False, logfile=vercmd_log)
  430. check_sts = False
  431. verstr = None
  432. if ret == COMMAND_RUNOK:
  433. with open(vercmd_log, 'r') as vlf:
  434. for line in vlf.readlines():
  435. if ver_check in line:
  436. verstr = line.strip()
  437. check_sts = True
  438. break
  439. os.remove(vercmd_log)
  440. return check_sts, verstr
  441. def get_elfsize(elf):
  442. sizeinfo = {"text": -1, "data": -1, "bss": -1, "total": -1}
  443. if os.path.isfile(elf) == False:
  444. return sizeinfo
  445. sizecmd = "riscv-nuclei-elf-size %s" % (elf)
  446. sizelog = tempfile.mktemp()
  447. ret, _ = run_command(sizecmd, show_output=False, logfile=sizelog)
  448. if ret == COMMAND_RUNOK:
  449. with open(sizelog, "r") as sf:
  450. lines = sf.readlines()
  451. datas = lines[-1].strip().split()
  452. sizeinfo["text"] = int(datas[0])
  453. sizeinfo["data"] = int(datas[1])
  454. sizeinfo["bss"] = int(datas[2])
  455. sizeinfo["total"] = int(datas[3])
  456. os.remove(sizelog)
  457. return sizeinfo
  458. def merge_config_with_makeopts(config, make_options):
  459. opt_splits=make_options.strip().split()
  460. passed_buildcfg = dict()
  461. for opt in opt_splits:
  462. if "=" in opt:
  463. values = opt.split("=")
  464. # Make new build config
  465. if (len(values) == 2):
  466. passed_buildcfg[values[0]] = values[1]
  467. build_cfg = config.get("build_config", None)
  468. if build_cfg is None:
  469. config["build_config"] = passed_buildcfg
  470. else:
  471. # update build_config using parsed config via values specified in make_options
  472. config["build_config"].update(passed_buildcfg)
  473. return config
  474. # merge config dict and args dict
  475. # args will overwrite config
  476. def merge_config_with_args(config, args_dict):
  477. if isinstance(config, dict) == False:
  478. return None
  479. if isinstance(args_dict, dict) == False:
  480. return config
  481. serport = args_dict.get("serport", None)
  482. baudrate = args_dict.get("baudrate", None)
  483. make_options = args_dict.get("make_options", None)
  484. parallel = args_dict.get("parallel", None)
  485. build_target = args_dict.get("build_target", None)
  486. run_target = args_dict.get("run_target", None)
  487. timeout = args_dict.get("timeout", None)
  488. ncycm = args_dict.get("ncycm", None)
  489. if isinstance(config, dict) == False:
  490. return None
  491. new_config = copy.deepcopy(config)
  492. if serport or baudrate or run_target:
  493. run_cfg = new_config.get("run_config", None)
  494. if run_cfg is None:
  495. new_config["run_config"] = {"hardware":{}}
  496. elif "hardware" not in run_cfg:
  497. new_config["run_config"]["hardware"] = {}
  498. if serport:
  499. new_config["run_config"]["hardware"]["serport"] = str(serport)
  500. if baudrate:
  501. new_config["run_config"]["hardware"]["serport"] = int(baudrate)
  502. if run_target:
  503. new_config["run_config"]["target"] = str(run_target)
  504. run_target = new_config["run_config"].get("target", "hardware")
  505. if run_target not in new_config["run_config"]:
  506. new_config["run_config"][run_target] = dict()
  507. if ncycm:
  508. if "ncycm" not in new_config["run_config"]:
  509. new_config["run_config"]["ncycm"] = dict()
  510. new_config["run_config"]["ncycm"]["ncycm"] = os.path.abspath(ncycm)
  511. if timeout: # set timeout
  512. try:
  513. timeout = int(timeout)
  514. except:
  515. timeout = 60
  516. new_config["run_config"][run_target]["timeout"] = timeout
  517. if build_target is not None:
  518. new_config["build_target"] = build_target
  519. if parallel is not None:
  520. new_config["parallel"] = parallel
  521. if make_options:
  522. new_config = merge_config_with_makeopts(new_config, make_options)
  523. return new_config
  524. # merge two config, now is appcfg, another is hwcfg
  525. # hwcfg will overwrite configuration in appcfg
  526. def merge_two_config(appcfg, hwcfg):
  527. if isinstance(appcfg, dict) == True and isinstance(hwcfg, dict) == False:
  528. return appcfg
  529. if isinstance(appcfg, dict) == False and isinstance(hwcfg, dict) == True:
  530. return hwcfg
  531. merged_appcfg = copy.deepcopy(appcfg)
  532. dict_merge(merged_appcfg, hwcfg)
  533. return merged_appcfg
  534. def set_global_variables(config):
  535. global SDK_GLOBAL_VARIABLES
  536. if isinstance(config, dict) == False:
  537. return False
  538. if "global_variables" in config:
  539. dict_merge(SDK_GLOBAL_VARIABLES, config["global_variables"])
  540. print("Using global variables: %s" % SDK_GLOBAL_VARIABLES)
  541. return True
  542. def get_app_runresult(apprst):
  543. if not isinstance(apprst, dict):
  544. return "unknown", "-"
  545. if "type" not in apprst:
  546. return "unknown", "-"
  547. rsttype = apprst["type"]
  548. rstvaluedict = apprst.get("value", dict())
  549. if rstvaluedict and len(rstvaluedict) < 3:
  550. rstval = ""
  551. for key in rstvaluedict:
  552. rstval += "%s : %s;" %(key, rstvaluedict[key])
  553. rstval = rstval.rstrip(';')
  554. else:
  555. rstval = "-"
  556. return rsttype, rstval
  557. def save_execute_csv(result, csvfile):
  558. if isinstance(result, dict) == False:
  559. return False
  560. csvlines = ["App, buildstatus, runstatus, buildtime, runtime, type, value, total, text, data, bss"]
  561. for app in result:
  562. size = result[app]["size"]
  563. app_status = result[app]["status"]
  564. app_time = result[app]["time"]
  565. apprsttype, apprstval = get_app_runresult(result[app].get("result", dict()))
  566. csvline ="%s, %s, %s, %s, %s, %s, %s, %d, %d, %d, %d" % (app, app_status["build"], \
  567. app_status.get("run", False), app_time.get("build", "-"), app_time.get("run", "-"), \
  568. apprsttype, apprstval, size["total"], size["text"], size["data"], size["bss"])
  569. csvlines.append(csvline)
  570. save_csv(csvfile, csvlines)
  571. return True
  572. def save_bench_csv(result, csvfile):
  573. if isinstance(result, dict) == False:
  574. return False
  575. csvlines = ["App, case, buildstatus, runstatus, buildtime, runtime, type, value, total, text, data, bss"]
  576. for app in result:
  577. appresult = result[app]
  578. for case in appresult:
  579. size = appresult[case]["size"]
  580. app_status = appresult[case]["status"]
  581. app_time = appresult[case]["time"]
  582. apprsttype, apprstval = get_app_runresult(appresult[case].get("result", dict()))
  583. csvline = "%s, %s, %s, %s, %s, %s, %s, %s, %d, %d, %d, %d" % (app, case, app_status["build"], \
  584. app_status.get("run", False), app_time.get("build", "-"), app_time.get("run", "-"), \
  585. apprsttype, apprstval, size["total"], size["text"], size["data"], size["bss"])
  586. csvlines.append(csvline)
  587. # save csv file
  588. save_csv(csvfile, csvlines)
  589. return True
  590. def get_global_variables():
  591. return SDK_GLOBAL_VARIABLES
  592. def get_sdk_checktag():
  593. checktag = os.environ.get("SDK_CHECKTAG")
  594. if checktag is None:
  595. checktag = SDK_GLOBAL_VARIABLES.get("sdk_checktag")
  596. return checktag
  597. def get_sdk_copyobjects():
  598. cpobjs = os.environ.get("SDK_COPY_OBJECTS")
  599. if cpobjs is None:
  600. cpobjs = SDK_GLOBAL_VARIABLES.get("sdk_copy_objects")
  601. return cpobjs
  602. def get_sdk_check():
  603. check = os.environ.get("SDK_CHECK")
  604. if check is None:
  605. check = SDK_GLOBAL_VARIABLES.get("sdk_check")
  606. return check
  607. def get_sdk_banner_tmout():
  608. tmout = os.environ.get("SDK_BANNER_TMOUT")
  609. if tmout is not None:
  610. tmout = int(tmout)
  611. else:
  612. tmout = SDK_GLOBAL_VARIABLES.get("sdk_banner_tmout")
  613. return tmout
  614. def find_local_appconfig(appdir, localcfgs):
  615. if isinstance(appdir, str) and isinstance(localcfgs, dict):
  616. if appdir in localcfgs:
  617. return appdir
  618. else:
  619. foundcfg = None
  620. for localcfg in localcfgs:
  621. localcfgtp = localcfg.strip('/')
  622. striped_dir = appdir.split(localcfgtp, 1)
  623. if len(striped_dir) == 2:
  624. striped_dir = striped_dir[1]
  625. else:
  626. striped_dir = appdir
  627. if striped_dir != appdir:
  628. if striped_dir.startswith('/'):
  629. if foundcfg is None:
  630. foundcfg = localcfg
  631. else:
  632. if len(foundcfg) < len(localcfg):
  633. foundcfg = localcfg
  634. return foundcfg
  635. else:
  636. return None
  637. def fix_demosoc_verilog_ncycm(verilog):
  638. if os.path.isfile(verilog) == False:
  639. return ""
  640. vfct = ""
  641. with open(verilog, "r") as vf:
  642. for line in vf.readlines():
  643. line = line.replace("@80", "@00").replace("@90", "@08")
  644. vfct += line
  645. verilog_new = verilog + ".ncycm"
  646. with open(verilog_new, "w") as vf:
  647. vf.write(vfct)
  648. return verilog_new
  649. PROGRAM_UNKNOWN="unknown"
  650. PROGRAM_BAREBENCH="barebench"
  651. PROGRAM_COREMARK="coremark"
  652. PROGRAM_DHRYSTONE="dhrystone"
  653. PROGRAM_WHETSTONE="whetstone"
  654. def parse_benchmark_compatiable(lines):
  655. result = None
  656. program_type = PROGRAM_UNKNOWN
  657. subtype = PROGRAM_UNKNOWN
  658. try:
  659. for line in lines:
  660. # Coremark
  661. if "CoreMark" in line:
  662. program_type = PROGRAM_BAREBENCH
  663. subtype = PROGRAM_COREMARK
  664. if "Iterations*1000000/total_ticks" in line:
  665. value = line.split("=")[1].strip().split()[0]
  666. result = dict()
  667. result["CoreMark/MHz"] = strtofloat(value)
  668. # Dhrystone
  669. if "Dhrystone" in line:
  670. program_type = PROGRAM_BAREBENCH
  671. subtype = PROGRAM_DHRYSTONE
  672. if "1000000/(User_Cycle/Number_Of_Runs)" in line:
  673. value = line.split("=")[1].strip().split()[0]
  674. result = dict()
  675. result["DMIPS/MHz"] = strtofloat(value)
  676. # Whetstone
  677. if "Whetstone" in line:
  678. program_type = PROGRAM_BAREBENCH
  679. subtype = PROGRAM_WHETSTONE
  680. if "MWIPS/MHz" in line:
  681. value = line.split("MWIPS/MHz")[-1].strip().split()[0]
  682. result = dict()
  683. result["MWIPS/MHz"] = strtofloat(value)
  684. except:
  685. return program_type, subtype, result
  686. return program_type, subtype, result
  687. def parse_benchmark_baremetal(lines):
  688. result = None
  689. program_type = PROGRAM_UNKNOWN
  690. subtype = PROGRAM_UNKNOWN
  691. try:
  692. unit = "unknown"
  693. for line in lines:
  694. stripline = line.strip()
  695. if "csv," in stripline.lower():
  696. csv_values = stripline.split(',')
  697. if len(csv_values) >= 3:
  698. key = csv_values[1].strip()
  699. value = csv_values[-1].strip()
  700. if key.lower() == "benchmark":
  701. program_type = PROGRAM_BAREBENCH
  702. unit = value
  703. else:
  704. subtype = key.lower()
  705. result = dict()
  706. result[unit] = strtofloat(value)
  707. break
  708. except:
  709. return program_type, subtype, result
  710. return program_type, subtype, result
  711. def parse_benchmark_baremetal_csv(lines):
  712. result = None
  713. program_type = PROGRAM_UNKNOWN
  714. try:
  715. result = dict()
  716. for line in lines:
  717. stripline = line.strip()
  718. if "csv," in stripline.lower():
  719. csv_values = stripline.split(',')
  720. if len(csv_values) >= 3:
  721. key = csv_values[1].strip()
  722. value = csv_values[-1].strip()
  723. if "BENCH" not in key.upper():
  724. result[key] = value
  725. except:
  726. return program_type, result
  727. return program_type, result
  728. def find_index(key, arr):
  729. try:
  730. index = arr.index(key)
  731. except:
  732. index = -1
  733. return index
  734. def parse_benchmark_runlog(lines, lgf=""):
  735. if isinstance(lines, list) == False:
  736. return PROGRAM_UNKNOWN, PROGRAM_UNKNOWN, None
  737. subtype = ""
  738. if lgf.strip() == "": # old style
  739. program_type, subtype, result = parse_benchmark_compatiable(lines)
  740. else:
  741. lgf = lgf.replace("\\", "/")
  742. appnormdirs = os.path.dirname(os.path.normpath(lgf)).replace('\\', '/').split('/')
  743. if "baremetal/benchmark" in lgf:
  744. # baremetal benchmark
  745. program_type, subtype, result = parse_benchmark_baremetal(lines)
  746. if program_type == PROGRAM_UNKNOWN:
  747. # fallback to previous parser
  748. program_type, subtype, result = parse_benchmark_compatiable(lines)
  749. elif "baremetal/demo_dsp" in lgf:
  750. program_type, result = parse_benchmark_baremetal_csv(lines)
  751. program_type = "demo_dsp"
  752. elif "DSP/Examples/RISCV" in lgf:
  753. program_type, result = parse_benchmark_baremetal_csv(lines)
  754. program_type = "nmsis_dsp_example"
  755. index = find_index("RISCV", appnormdirs)
  756. if index >= 0:
  757. subtype = appnormdirs[index + 1]
  758. elif "DSP/Test" in lgf:
  759. program_type, result = parse_benchmark_baremetal_csv(lines)
  760. program_type = "nmsis_dsp_tests"
  761. index = find_index("Test", appnormdirs)
  762. if index >= 0:
  763. subtype = appnormdirs[index + 1]
  764. elif "NN/Examples/RISCV" in lgf:
  765. program_type, result = parse_benchmark_baremetal_csv(lines)
  766. program_type = "nmsis_nn_example"
  767. index = find_index("RISCV", appnormdirs)
  768. if index >= 0:
  769. subtype = appnormdirs[index + 1]
  770. elif "NN/Tests" in lgf:
  771. program_type, result = parse_benchmark_baremetal_csv(lines)
  772. if "full" in appnormdirs:
  773. program_type = "nmsis_nn_test_full"
  774. subtype = "full"
  775. else:
  776. program_type = "nmsis_nn_test_percase"
  777. index = find_index("percase", appnormdirs)
  778. if index >= 0:
  779. subtype = appnormdirs[index + 1]
  780. else:
  781. program_type, subtype, result = parse_benchmark_compatiable(lines)
  782. return program_type, subtype, result
  783. def check_tool_exist(tool):
  784. exist = False
  785. if sys.platform == 'win32':
  786. if os.system("where %s" % (tool)) == 0:
  787. exist = True
  788. else:
  789. if os.system("which %s" % (tool)) == 0:
  790. exist = True
  791. return exist
  792. def find_vivado_cmd():
  793. for vivado_cmd in ("vivado", "vivado_lab"):
  794. if sys.platform == 'win32':
  795. if os.system("where %s" % (vivado_cmd)) == 0:
  796. return vivado_cmd
  797. else:
  798. if os.system("which %s" % (vivado_cmd)) == 0:
  799. return vivado_cmd
  800. return None
  801. def program_fpga(bit, target):
  802. if os.path.isfile(bit) == False:
  803. print("Can't find bitstream in %s" % (bit))
  804. return False
  805. print("Try to program fpga bitstream %s to target board %s" % (bit, target))
  806. sys.stdout.flush()
  807. vivado_cmd = find_vivado_cmd()
  808. # check vivado is found or not
  809. if vivado_cmd == None:
  810. print("vivado is not found in PATH, please check!")
  811. return False
  812. tcl = os.path.join(os.path.dirname(os.path.realpath(__file__)), "program_bit.tcl")
  813. target = "*%s" % (target)
  814. sys.stdout.flush()
  815. ret = os.system("%s -mode batch -nolog -nojournal -source %s -tclargs %s %s" % (vivado_cmd, tcl, bit, target))
  816. sys.stdout.flush()
  817. if ret != 0:
  818. print("Program fpga bit failed, error code %d" % ret)
  819. return False
  820. print("Program fpga bit successfully")
  821. return True
  822. def find_fpgas():
  823. vivado_cmd = find_vivado_cmd()
  824. if vivado_cmd == None:
  825. print("vivado is not found in PATH, please check!")
  826. return dict()
  827. tcl = os.path.join(os.path.dirname(os.path.realpath(__file__)), "find_devices.tcl")
  828. sys.stdout.flush()
  829. tmp_log = tempfile.mktemp()
  830. os.system("%s -mode batch -nolog -nojournal -source %s -notrace > %s" % (vivado_cmd, tcl, tmp_log))
  831. sys.stdout.flush()
  832. fpgadevices = dict()
  833. with open(tmp_log, "r") as tf:
  834. for line in tf.readlines():
  835. line = line.strip()
  836. if line.startswith("CSV,") == False:
  837. continue
  838. splits = line.split(",")
  839. if len(splits) != 3:
  840. continue
  841. fpga_serial = "/".join(splits[1].split("/")[2:])
  842. fpgadevices[fpga_serial] = splits[2].strip()
  843. return fpgadevices
  844. def check_serial_port(serport):
  845. if serport in find_possible_serports():
  846. return True
  847. return False
  848. def modify_openocd_cfg(cfg, ftdi_serial):
  849. cfg_bk = cfg + ".backup"
  850. if (os.path.isfile(cfg)) == False:
  851. return False
  852. if os.path.isfile(cfg_bk) == True:
  853. print("Restore openocd cfg")
  854. shutil.copyfile(cfg_bk, cfg)
  855. else:
  856. print("Backup openocd cfg")
  857. shutil.copyfile(cfg, cfg_bk)
  858. found = False
  859. contents = []
  860. index = 0
  861. with open(cfg, 'r') as cf:
  862. contents = cf.readlines()
  863. for line in contents:
  864. if line.strip().startswith("transport select"):
  865. found = True
  866. break
  867. index += 1
  868. if found == False:
  869. return False
  870. contents.insert(index, "ftdi_serial %s\ntcl_port disabled\ntelnet_port disabled\n" %(ftdi_serial))
  871. with open(cfg, 'w') as cf:
  872. contents = "".join(contents)
  873. cf.write(contents)
  874. return True