nsdk_utils.py 38 KB

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