nsdk_utils.py 35 KB

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