nsdk_utils.py 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461
  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 collections import OrderedDict
  19. from threading import Thread
  20. import subprocess
  21. import asyncio
  22. import glob
  23. import json
  24. import yaml
  25. import importlib.util
  26. if sys.platform != "win32":
  27. import fcntl
  28. import stat
  29. except Exception as exc:
  30. print("Import Error: %s" % (exc))
  31. print("Please install requried packages using: pip3 install -r %s" % (requirement_file))
  32. sys.exit(1)
  33. try:
  34. from collections.abc import Mapping
  35. except ImportError: # Python 2.7 compatibility
  36. from collections import Mapping
  37. SDK_GLOBAL_VARIABLES = {
  38. "sdk_checktag": "Nuclei SDK Build Time:",
  39. "sdk_check": True,
  40. "sdk_banner_tmout": 15,
  41. "sdk_copy_objects": "elf,map",
  42. "sdk_copy_objects_flag": False,
  43. "sdk_ttyerr_maxcnt": 3,
  44. "sdk_fpgaprog_maxcnt": 3,
  45. "sdk_gdberr_maxcnt": 10,
  46. "sdk_uploaderr_maxcnt": 10,
  47. "sdk_bannertmout_maxcnt": 100,
  48. "sdk_verb_buildmsg": True,
  49. "sdk_copy_failobj": True
  50. }
  51. INVAILD_SERNO = "xxxxx"
  52. BANNER_TMOUT = "banner_timeout"
  53. TTY_OP_ERR = "tty_operate_error"
  54. TTY_UNKNOWN_ERR = "tty_unknown_error"
  55. # get ci url information
  56. def get_ci_info():
  57. cijoburl = os.environ.get("CI_JOB_URL")
  58. cipipelineurl = os.environ.get("CI_PIPELINE_URL")
  59. if cijoburl and cipipelineurl:
  60. return {"joburl": cijoburl, "pipelineurl": cipipelineurl}
  61. else:
  62. return {}
  63. def get_global_variables():
  64. return SDK_GLOBAL_VARIABLES
  65. def get_sdk_checktag():
  66. checktag = os.environ.get("SDK_CHECKTAG")
  67. if checktag is None:
  68. checktag = SDK_GLOBAL_VARIABLES.get("sdk_checktag")
  69. return checktag
  70. def get_sdk_copyobjects():
  71. cpobjs = os.environ.get("SDK_COPY_OBJECTS")
  72. if cpobjs is None:
  73. cpobjs = SDK_GLOBAL_VARIABLES.get("sdk_copy_objects")
  74. return cpobjs
  75. def get_env_flag(envar, deft=None):
  76. flag = os.environ.get(envar)
  77. if flag is None:
  78. return deft
  79. return flag.lower() in ('true', '1', 't')
  80. def get_sdk_check():
  81. check = get_env_flag("SDK_CHECK")
  82. if check is None:
  83. check = SDK_GLOBAL_VARIABLES.get("sdk_check")
  84. return check
  85. def get_sdk_verb_buildmsg():
  86. check = get_env_flag("SDK_VERB_BUILDMSG")
  87. if check is None:
  88. check = SDK_GLOBAL_VARIABLES.get("sdk_verb_buildmsg")
  89. return check
  90. def get_sdk_copyobjects_flag():
  91. cpflag = get_env_flag("SDK_COPY_OBJECTS_FLAG")
  92. if cpflag is None:
  93. cpflag = SDK_GLOBAL_VARIABLES.get("sdk_copy_objects_flag")
  94. return cpflag
  95. def get_sdk_need_copyobjects(appconfig):
  96. try:
  97. needed = appconfig.get("copy_objects")
  98. except:
  99. needed = False
  100. if needed != True:
  101. # use global flag
  102. needed = get_sdk_copyobjects_flag()
  103. return needed
  104. def get_sdk_copy_failobj():
  105. cpflag = get_env_flag("SDK_COPY_FAILOBJ")
  106. if cpflag is None:
  107. cpflag = SDK_GLOBAL_VARIABLES.get("sdk_copy_failobj")
  108. return cpflag
  109. def get_sdk_banner_tmout():
  110. tmout = os.environ.get("SDK_BANNER_TMOUT")
  111. if tmout is not None:
  112. tmout = int(tmout)
  113. else:
  114. tmout = SDK_GLOBAL_VARIABLES.get("sdk_banner_tmout")
  115. return tmout
  116. def get_sdk_fpga_prog_tmout():
  117. tmout = os.environ.get("FPGA_PROG_TMOUT")
  118. return tmout
  119. def get_sdk_ttyerr_maxcnt():
  120. num = os.environ.get("SDK_TTYERR_MAXCNT")
  121. if num is not None:
  122. num = int(num)
  123. else:
  124. num = SDK_GLOBAL_VARIABLES.get("sdk_ttyerr_maxcnt")
  125. return num
  126. def get_sdk_fpgaprog_maxcnt():
  127. num = os.environ.get("SDK_FPGAPROG_MAXCNT")
  128. if num is not None:
  129. num = int(num)
  130. else:
  131. num = SDK_GLOBAL_VARIABLES.get("sdk_fpgaprog_maxcnt")
  132. return num
  133. def get_sdk_gdberr_maxcnt():
  134. num = os.environ.get("SDK_GDBERR_MAXCNT")
  135. if num is not None:
  136. num = int(num)
  137. else:
  138. num = SDK_GLOBAL_VARIABLES.get("sdk_gdberr_maxcnt")
  139. return num
  140. def get_sdk_bannertmout_maxcnt():
  141. num = os.environ.get("SDK_BANNERTMOUT_MAXCNT")
  142. if num is not None:
  143. num = int(num)
  144. else:
  145. num = SDK_GLOBAL_VARIABLES.get("sdk_bannertmout_maxcnt")
  146. return num
  147. def get_sdk_uploaderr_maxcnt():
  148. num = os.environ.get("SDK_UPLOADERR_MAXCNT")
  149. if num is not None:
  150. num = int(num)
  151. else:
  152. num = SDK_GLOBAL_VARIABLES.get("sdk_uploaderr_maxcnt")
  153. return num
  154. def parse_riscv_arch(arch_str):
  155. """Parse RISC-V architecture string to standardized format"""
  156. if not arch_str:
  157. return None
  158. arch_str = arch_str.lower()
  159. if not arch_str.startswith('rv32') and not arch_str.startswith('rv64'):
  160. return None
  161. features = {
  162. 'xlen': arch_str[:4],
  163. 'base': '',
  164. 'exts': set()
  165. }
  166. # Parse standard ISA string
  167. std_isa = arch_str[4:].split('_')[0]
  168. for c in std_isa:
  169. if c in 'iemafdcbpkv':
  170. # don't add b k p into base architecture
  171. if c == 'b':
  172. # for nuclei b extension contains zba/zbb/zbc/zbs
  173. features['exts'].add('zba')
  174. features['exts'].add('zbb')
  175. features['exts'].add('zbc')
  176. features['exts'].add('zbs')
  177. elif c == 'k':
  178. # for nuclei k extension contains zba/zbb/zbc/zbs
  179. features['exts'].add('zk') # zk -> zkn zkr zkt
  180. features['exts'].add('zks') # zks -> zbkb-sc zbkc-sc zbkx-sc zksed zksh
  181. features['exts'].add('zkn') # zkn -> zbkb-sc zbkc-sc zbkx-sc zkne zknd zknh
  182. features['exts'].add('zkr')
  183. features['exts'].add('zkt')
  184. features['exts'].add('zkne')
  185. features['exts'].add('zknd')
  186. features['exts'].add('zknh')
  187. features['exts'].add('zksed')
  188. features['exts'].add('zksh')
  189. features['exts'].add('zbkb-sc')
  190. features['exts'].add('zbkc-sc')
  191. features['exts'].add('zbkx-sc')
  192. elif c == 'v':
  193. features['exts'].add('zve64d')
  194. features['exts'].add('zvl128b')
  195. features['base'] += 'v'
  196. elif c == 'p':
  197. features['exts'].add('xxldsp')
  198. else:
  199. features['base'] += c
  200. # when base architecture has i extension, then e extension is implied
  201. if 'i' in features['base']:
  202. features['base'] += 'e'
  203. # Parse extensions
  204. if '_' in arch_str:
  205. exts = arch_str.split('_')[1:]
  206. for ext in exts:
  207. ext = ext.strip()
  208. if ext == "":
  209. continue
  210. if ext in ('zvl128', 'zvl256', 'zvl512', 'zvl1024'):
  211. ext = ext + 'b'
  212. elif ext in ('zvb', 'zvk', 'zc'):
  213. ext = ext + '*'
  214. elif ext in ('dsp'):
  215. ext = 'xxl' + ext
  216. elif ext in ('dspn1', 'dspn2', 'dspn3'):
  217. ext = 'xxl' + ext + 'x'
  218. features['exts'].add(ext)
  219. # For nuclei zc* can also configured as c extension via mmisc_ctl csr ZCMT_ZCMP_EN bit
  220. if 'zc*' in features['exts']:
  221. features['base'] += 'c'
  222. # For nuclei cpu, zifencei and zicsr are implied
  223. features['exts'].add('zicsr')
  224. features['exts'].add('zifencei')
  225. # zve64d imply zve64f, zve64f imply zve64x and zve32f
  226. # zve64x imply zve32x, zve32f imply zve32x
  227. if 'zve64d' in features['exts']:
  228. features['exts'].add('zve64f')
  229. if 'zve64f' in features['exts']:
  230. features['exts'].add('zve32f')
  231. features['exts'].add('zve64x')
  232. if 'zve64x' in features['exts']:
  233. features['exts'].add('zve32x')
  234. if 'zve32f' in features['exts']:
  235. features['exts'].add('zve32x')
  236. if 'xxldspn3x' in features['exts']:
  237. features['exts'].add('xxldspn2x')
  238. if 'xxldspn2x' in features['exts']:
  239. features['exts'].add('xxldspn1x')
  240. if 'xxldspn1x' in features['exts']:
  241. features['exts'].add('xxldsp')
  242. if 'zvl1024b' in features['exts']:
  243. features['exts'].add('zvl512b')
  244. if 'zvl512b' in features['exts']:
  245. features['exts'].add('zvl256b')
  246. if 'zvl256b' in features['exts']:
  247. features['exts'].add('zvl128b')
  248. if 'zve64d' in features['exts'] and 'zvl128b' in features['exts']:
  249. features['base'] += 'v'
  250. return features
  251. def get_nuclei_sdk_root():
  252. sdk_root = os.environ.get("NUCLEI_SDK_ROOT")
  253. if not sdk_root:
  254. sdk_root = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "..", ".."))
  255. return sdk_root
  256. def parse_makefile_core():
  257. sdk_root = get_nuclei_sdk_root()
  258. makefile_core = os.path.join(sdk_root, "Build", "Makefile.core")
  259. core_archs = {}
  260. if not os.path.exists(makefile_core):
  261. return core_archs
  262. with open(makefile_core, 'r') as f:
  263. for line in f:
  264. line = line.strip()
  265. if not line or line.startswith('#'):
  266. continue
  267. if '_CORE_ARCH_ABI' in line:
  268. parts = line.split('=')
  269. if len(parts) == 2:
  270. core_name = parts[0].split('_CORE_ARCH_ABI')[0].lower()
  271. arch_parts = parts[1].strip().split()
  272. if len(arch_parts) >= 2:
  273. core_archs[core_name] = arch_parts[0]
  274. return core_archs
  275. def check_arch_compatibility(core_arch, arch_ext, supported_arch):
  276. """Check if core architecture with extensions is compatible with supported architecture"""
  277. if not supported_arch:
  278. return True
  279. supported = parse_riscv_arch(supported_arch)
  280. if not supported:
  281. return True
  282. # Combine core_arch with arch_ext
  283. full_arch = core_arch
  284. if arch_ext:
  285. full_arch += arch_ext
  286. current = parse_riscv_arch(full_arch)
  287. if not current:
  288. return False
  289. # Check XLEN compatibility
  290. if current['xlen'] != supported['xlen']:
  291. return False
  292. # Check base ISA compatibility
  293. for c in current['base']:
  294. if c not in supported['base']:
  295. return False
  296. # Check extensions compatibility
  297. # For current['exts'] containing extensions (no * suffix)
  298. # For supported['exts'] containing extensions (may have * suffix)
  299. # Extension matching should handle wildcards (*) in supported extensions
  300. for ext in current['exts']:
  301. found_match = False
  302. for supported_ext in supported['exts']:
  303. if supported_ext.endswith('*'):
  304. # Handle wildcard matching
  305. if ext.startswith(supported_ext[:-1]):
  306. found_match = True
  307. break
  308. elif ext == supported_ext:
  309. # Handle exact matching
  310. found_match = True
  311. break
  312. if not found_match:
  313. return False
  314. return True
  315. def filter_app_config(appconfig):
  316. """
  317. Filter application configurations based on architecture and extension compatibility.
  318. This function examines the build configuration of an application and determines if it should
  319. be filtered out based on architecture support and extension compatibility.
  320. Parameters:
  321. appconfig (dict): A dictionary containing application configuration.
  322. Expected to have a 'build_config' key with CORE, ARCH_EXT details.
  323. Returns:
  324. tuple: A pair of (bool, str) where:
  325. - bool: True if the configuration should be filtered out, False otherwise
  326. - str: A message explaining why the configuration was filtered (empty if not filtered)
  327. Environment Variables Used:
  328. - SDK_SUPPORT_ARCH: Supported architecture specifications
  329. - SDK_IGNORED_EXTS: Underscore-separated list of extensions to ignore
  330. Example:
  331. >>> config = {
  332. ... "build_config": {
  333. ... "CORE": "n307",
  334. ... "ARCH_EXT": "p_zfh"
  335. ... }
  336. ... }
  337. >>> filter_app_config(config)
  338. (False, "")
  339. Notes:
  340. - The function handles both single-letter and multi-letter extensions
  341. - Architecture extensions can be specified with or without leading underscore
  342. - Returns (False, "") if any required configuration is missing or in case of errors
  343. """
  344. if not isinstance(appconfig, dict):
  345. return False, ""
  346. try:
  347. build_config = appconfig.get("build_config", None)
  348. if build_config is None or len(build_config) == 0:
  349. return False, ""
  350. # Check SDK_SUPPORT_ARCH compatibility
  351. core = build_config.get("CORE", "").lower()
  352. arch_ext = build_config.get("ARCH_EXT", "")
  353. supported_arch = os.environ.get("SDK_SUPPORT_ARCH")
  354. if core and supported_arch:
  355. core_archs = parse_makefile_core()
  356. if core in core_archs:
  357. core_arch = core_archs[core]
  358. if not check_arch_compatibility(core_arch, arch_ext, supported_arch):
  359. return True, f"Core {core} with extensions {arch_ext} not supported by {supported_arch}"
  360. # Continue with existing extension filtering
  361. archext = build_config.get("ARCH_EXT", None)
  362. if archext is None or archext.strip() == "":
  363. return False, ""
  364. first_part = None
  365. rest_part = None
  366. if archext.startswith("_") == False:
  367. if "_" in archext:
  368. first_part, rest_part = archext.split("_", 1)
  369. else:
  370. if archext.startswith("z"):
  371. rest_part = archext
  372. else:
  373. first_part = archext
  374. else:
  375. rest_part = archext
  376. ignored_exts = os.environ.get("SDK_IGNORED_EXTS")
  377. if ignored_exts is None:
  378. return False, ""
  379. unique_exts = list(
  380. OrderedDict.fromkeys(part.strip() for part in ignored_exts.split('_'))
  381. )
  382. if len(unique_exts) == 1 and unique_exts[0] == "":
  383. return False, ""
  384. for ext in unique_exts:
  385. if len(ext) == 0:
  386. continue
  387. if len(ext) == 1:
  388. # handle single letter
  389. if first_part and ext in first_part:
  390. return True, "Filtered by %s extension" %(ext)
  391. else:
  392. # handle multi letter
  393. if rest_part and ext in rest_part:
  394. return True, "Filtered by %s extension" % (ext)
  395. except:
  396. pass
  397. return False, ""
  398. class NThread(Thread):
  399. def __init__(self, func, args):
  400. super(NThread, self).__init__()
  401. self.func = func
  402. self.args = args
  403. def run(self):
  404. self.result = self.func(*self.args)
  405. def get_result(self):
  406. try:
  407. return self.result
  408. except Exception:
  409. return None
  410. YAML_OK=0
  411. YAML_NOFILE=1
  412. YAML_INVAILD=2
  413. def load_yaml(file):
  414. if isinstance(file, str) == False or os.path.isfile(file) == False:
  415. return YAML_NOFILE, None
  416. try:
  417. data = yaml.load(open(file, 'r'), Loader=yaml.FullLoader)
  418. return YAML_OK, data
  419. except:
  420. print("Error: %s is an invalid yaml file!" % (file))
  421. return YAML_INVAILD, None
  422. def save_yaml(file, data):
  423. if isinstance(file, str) == False:
  424. return False
  425. try:
  426. with open(file, "w") as cf:
  427. yaml.dump(data, cf, indent=4)
  428. return True
  429. except:
  430. print("Error: Data can't be serialized to yaml file!")
  431. return False
  432. def get_specific_key_value(dictdata:dict, key):
  433. if not dictdata:
  434. print("Error: dictdata doesn't exist!")
  435. return None
  436. value = dictdata.get(key, None)
  437. if not value:
  438. print("Error, key %s has no value!" % (key))
  439. return None
  440. return value
  441. JSON_OK=0
  442. JSON_NOFILE=1
  443. JSON_INVAILD=2
  444. def load_json(file):
  445. if isinstance(file, str) == False or os.path.isfile(file) == False:
  446. return JSON_NOFILE, None
  447. try:
  448. data = json.load(open(file, 'r'))
  449. return JSON_OK, data
  450. except:
  451. print("Error: %s is an invalid json file!" % (file))
  452. return JSON_INVAILD, None
  453. def save_json(file, data):
  454. if isinstance(file, str) == False:
  455. return False
  456. try:
  457. with open(file, "w") as cf:
  458. json.dump(data, cf, indent=4)
  459. return True
  460. except:
  461. print("Error: Data can't be serialized to json file!")
  462. return False
  463. def save_csv(file, csvlines, display=True):
  464. if isinstance(csvlines, list) == False:
  465. return False
  466. # Flush stdout buffer
  467. sys.stdout.flush()
  468. try:
  469. with open(file, "w") as cf:
  470. for line in csvlines:
  471. csvline = line + "\n"
  472. cf.write(csvline)
  473. cf.flush()
  474. if display:
  475. try:
  476. # sometimes facing issue BlockingIOError: [Errno 11] write could not complete without blocking here
  477. # maybe related to https://bugs.python.org/issue40634 since we are using async in this tool
  478. sys.stdout.flush()
  479. print("CSV, %s" % line)
  480. except:
  481. pass
  482. return True
  483. except:
  484. print("Error: Data can't be saved to file!")
  485. return False
  486. # Return possible serports, return a list of possible serports
  487. def find_possible_serports():
  488. comports = serial.tools.list_ports.comports()
  489. serports = [ port.device for port in comports ]
  490. return serports
  491. def find_serport_by_no(serno):
  492. comports = serial.tools.list_ports.comports()
  493. serport = None
  494. for port in comports:
  495. cur_serno = port.serial_number
  496. cur_dev = port.device
  497. cur_loc = port.location
  498. if cur_serno is None:
  499. continue
  500. if sys.platform == "win32":
  501. if (serno + 'B') == cur_serno:
  502. serport = cur_dev
  503. break
  504. else:
  505. if serno != cur_serno:
  506. continue
  507. # serial is the second device of the composite device
  508. if cur_loc.endswith(".1"):
  509. serport = cur_dev
  510. break
  511. # serport founded
  512. return serport
  513. def find_most_possible_serport():
  514. serports = find_possible_serports()
  515. if len(serports) > 0:
  516. # sort the ports
  517. serports.sort()
  518. # get the biggest port
  519. # for /dev/ttyUSB0, /dev/ttyUSB1, get /dev/ttyUSB1
  520. # for COM16, COM17, get COM17
  521. return serports[-1]
  522. else:
  523. return None
  524. # get from https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
  525. def dict_merge(dct, merge_dct):
  526. """ Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
  527. updating only top-level keys, dict_merge recurses down into dicts nested
  528. to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
  529. ``dct``.
  530. :param dct: dict onto which the merge is executed
  531. :param merge_dct: dct merged into dct
  532. :return: None
  533. """
  534. for k, v in merge_dct.items():
  535. if (k in dct and isinstance(dct[k], dict)
  536. and isinstance(merge_dct[k], Mapping)):
  537. dict_merge(dct[k], merge_dct[k])
  538. else:
  539. dct[k] = merge_dct[k]
  540. def get_make_csv(app, config):
  541. make_options = " "
  542. SUPPORT_KEYS = ["SOC", "BOARD", "CORE", "DOWNLOAD", "VARIANT", \
  543. "BENCH_UNIT", "BENCH_FLAGS", "ARCH_EXT", "STDCLIB", "SILENT", "V"]
  544. csv_print = "CSV, APP=%s" % (app)
  545. if isinstance(config, dict):
  546. for key in config:
  547. if key not in SUPPORT_KEYS:
  548. continue
  549. option = "%s=%s"%(key, config[key])
  550. make_options = " %s %s " % (make_options, option)
  551. csv_print = "%s, %s" % (csv_print, option)
  552. return make_options, csv_print
  553. def try_decode_bytes(bytes):
  554. ENCODING_LIST = ['utf-8', 'gbk', 'gb18030']
  555. destr = ""
  556. for encoding in ENCODING_LIST:
  557. try:
  558. destr = bytes.decode(encoding)
  559. break
  560. except:
  561. continue
  562. return destr
  563. def kill_async_subprocess(proc):
  564. startticks = time.time()
  565. if proc is not None:
  566. try:
  567. kill_sig = signal.SIGTERM
  568. if sys.platform != "win32":
  569. kill_sig = signal.SIGKILL
  570. print("Try to Kill process id %d now" %(proc.pid))
  571. parent_proc = psutil.Process(proc.pid)
  572. try:
  573. # This might cause PermissionError: [Errno 1] Operation not permitted: '/proc/1/stat' issue
  574. child_procs = parent_proc.children(recursive=True)
  575. for child_proc in child_procs:
  576. print("Kill child process %s, pid %d" %(child_proc.name(), child_proc.pid))
  577. try:
  578. os.kill(child_proc.pid, kill_sig) # kill child process
  579. except:
  580. continue
  581. except Exception as exc:
  582. print("Warning: kill child process failed with %s" %(exc))
  583. if parent_proc.is_running():
  584. print("Kill parent process %s, pid %d" %(parent_proc.name(), parent_proc.pid))
  585. if sys.platform != "win32":
  586. try:
  587. os.killpg(parent_proc.pid, kill_sig) # kill parent process
  588. except:
  589. os.kill(parent_proc.pid, kill_sig) # kill parent process
  590. else:
  591. os.kill(parent_proc.pid, kill_sig) # kill parent process
  592. # kill using process.kill again
  593. if parent_proc.is_running():
  594. proc.kill()
  595. except psutil.NoSuchProcess:
  596. pass
  597. except Exception as exc:
  598. print("Warning: kill process failed with %s" %(exc))
  599. # show time cost for kill process
  600. print("kill process used %.2f seconds" %((time.time() - startticks)))
  601. sys.stdout.flush()
  602. pass
  603. def kill_subprocess(proc):
  604. try:
  605. if proc.poll() is None: # process is still running
  606. kill_async_subprocess(proc)
  607. except:
  608. pass
  609. pass
  610. def import_module(module_name, file_path):
  611. if file_path is None or os.path.isfile(file_path) == False:
  612. return None
  613. try:
  614. spec = importlib.util.spec_from_file_location(module_name, file_path)
  615. module = importlib.util.module_from_spec(spec)
  616. spec.loader.exec_module(module)
  617. except:
  618. module = None
  619. return module
  620. def import_function(func_name, file_path):
  621. module_name = "tempmodule_%s" % (random.randint(0, 10000))
  622. tmpmodule = import_module(module_name, file_path)
  623. if tmpmodule is None:
  624. return None
  625. if func_name not in dir(tmpmodule):
  626. return None
  627. return getattr(tmpmodule, func_name)
  628. COMMAND_RUNOK=0
  629. COMMAND_INVALID=1
  630. COMMAND_FAIL=2
  631. COMMAND_INTERRUPTED=3
  632. COMMAND_EXCEPTION=4
  633. COMMAND_NOTAPP=5
  634. COMMAND_TIMEOUT=6
  635. COMMAND_TIMEOUT_READ=7
  636. RUNSTATUS_OK=0
  637. RUNSTATUS_FAIL=1
  638. RUNSTATUS_NOTSTART=2
  639. def run_command(command, show_output=True, logfile=None, append=False):
  640. logfh = None
  641. ret = COMMAND_RUNOK
  642. cmd_elapsed_ticks = 0
  643. if isinstance(command, str) == False:
  644. return COMMAND_INVALID, cmd_elapsed_ticks
  645. startticks = time.time()
  646. process = None
  647. try:
  648. if isinstance(logfile, str):
  649. if append:
  650. logfh = open(logfile, "ab")
  651. else:
  652. logfh = open(logfile, "wb")
  653. if logfh:
  654. # record command run in log file
  655. logfh.write(("Execute Command %s\n" % (command)).encode())
  656. process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, \
  657. stderr=subprocess.STDOUT)
  658. while True:
  659. line = process.stdout.readline()
  660. if (not line) and process.poll() is not None:
  661. break
  662. if show_output:
  663. print(try_decode_bytes(line), end="")
  664. if logfh:
  665. logfh.write(line)
  666. time.sleep(0.01)
  667. process.communicate(30)
  668. if process.returncode != 0:
  669. ret = COMMAND_FAIL
  670. except (KeyboardInterrupt):
  671. print("Key CTRL-C pressed, command executing stopped!")
  672. ret = COMMAND_INTERRUPTED
  673. except subprocess.TimeoutExpired:
  674. ret = COMMAND_TIMEOUT
  675. except Exception as exc:
  676. print("Unexpected exception happened: %s" %(str(exc)))
  677. ret = COMMAND_EXCEPTION
  678. finally:
  679. kill_subprocess(process)
  680. if process:
  681. del process
  682. if logfh:
  683. logfh.close()
  684. cmd_elapsed_ticks = time.time() - startticks
  685. return ret, cmd_elapsed_ticks
  686. 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):
  687. logfh = None
  688. ret = COMMAND_FAIL
  689. cmd_elapsed_ticks = 0
  690. if isinstance(command, str) == False:
  691. return COMMAND_INVALID, cmd_elapsed_ticks
  692. startticks = time.time()
  693. process = None
  694. check_status = False
  695. pass_checks = checks.get("PASS", [])
  696. fail_checks = checks.get("FAIL", [])
  697. def test_in_check(string, checks):
  698. if type(checks) == list:
  699. for check in checks:
  700. if check in string:
  701. return True
  702. return False
  703. NSDK_CHECK_TAG = get_sdk_checktag()
  704. if get_sdk_verb_buildmsg():
  705. print("Checker used: ", checks)
  706. print("SDK Checker Tag \"%s\", checker enable %s" % (NSDK_CHECK_TAG, sdk_check))
  707. print("SDK run timeout %s, banner timeout %s" % (timeout, banner_timeout))
  708. check_finished = False
  709. start_time = time.time()
  710. serial_log = ""
  711. nsdk_check_timeout = banner_timeout
  712. sdk_checkstarttime = time.time()
  713. try:
  714. if isinstance(logfile, str):
  715. logfh = open(logfile, "wb")
  716. if sys.platform != "win32":
  717. # add exec to running command to avoid create a process called /bin/sh -c
  718. # and if you kill that process it will kill this sh process not the really
  719. # command process you want to kill
  720. process = await asyncio.create_subprocess_shell("exec " + command, \
  721. stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
  722. else:
  723. process = await asyncio.create_subprocess_shell(command, \
  724. stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
  725. while (time.time() - start_time) < timeout:
  726. try:
  727. linebytes = await asyncio.wait_for(process.stdout.readline(), 1)
  728. except asyncio.TimeoutError:
  729. if sdk_check == True:
  730. linebytes = None
  731. else:
  732. continue
  733. except KeyboardInterrupt:
  734. print("Key CTRL-C pressed, command executing stopped!")
  735. break
  736. except:
  737. break
  738. if linebytes:
  739. line = str(try_decode_bytes(linebytes)).replace('\r', '')
  740. else:
  741. line = ""
  742. if sdk_check == True:
  743. if (time.time() - sdk_checkstarttime) > nsdk_check_timeout:
  744. print("No SDK banner found in %s s, quit now!" % (nsdk_check_timeout))
  745. ret = COMMAND_TIMEOUT
  746. check_status = False
  747. break
  748. if line == "":
  749. continue
  750. if show_output:
  751. print("XXX Check " + line, end='')
  752. if NSDK_CHECK_TAG in line:
  753. timestr = line.split(NSDK_CHECK_TAG)[-1].strip()
  754. cur_time = time.mktime(time.strptime(timestr, "%b %d %Y, %H:%M:%S"))
  755. if int(cur_time) >= int(checktime):
  756. sdk_check = False
  757. line = NSDK_CHECK_TAG + " " + timestr + "\n"
  758. serial_log = serial_log + str(line)
  759. else:
  760. serial_log = serial_log + str(line)
  761. if show_output:
  762. print(line, end='')
  763. if check_finished == False:
  764. if test_in_check(line, fail_checks):
  765. check_status = False
  766. check_finished = True
  767. if test_in_check(line, pass_checks):
  768. check_status = True
  769. check_finished = True
  770. if check_finished:
  771. ret = COMMAND_RUNOK
  772. # record another 2 seconds by reset start_time and timeout to 2
  773. start_time = time.time()
  774. timeout = 1
  775. if logfh and linebytes:
  776. logfh.write(linebytes)
  777. time.sleep(0.01)
  778. except (KeyboardInterrupt):
  779. print("Key CTRL-C pressed, command executing stopped!")
  780. ret = COMMAND_INTERRUPTED
  781. except Exception as exc:
  782. print("Unexpected exception happened: %s" %(str(exc)))
  783. ret = COMMAND_EXCEPTION
  784. finally:
  785. # kill this process
  786. kill_async_subprocess(process)
  787. if logfh:
  788. logfh.close()
  789. cmd_elapsed_ticks = time.time() - startticks
  790. return check_status, cmd_elapsed_ticks
  791. def run_cmd_and_check(command, timeout:int, checks:dict, checktime=time.time(), sdk_check=False, logfile=None, show_output=False, banner_timeout=30):
  792. loop = asyncio.get_event_loop()
  793. try:
  794. ret, cmd_elapsed_ticks = loop.run_until_complete( \
  795. run_cmd_and_check_async(command, timeout, checks, checktime, sdk_check, logfile, show_output, banner_timeout))
  796. except KeyboardInterrupt:
  797. print("Key CTRL-C pressed, command executing stopped!")
  798. ret, cmd_elapsed_ticks = False, 0
  799. finally:
  800. if sys.platform != "win32":
  801. os.system("stty echo 2> /dev/null")
  802. return ret, cmd_elapsed_ticks
  803. def find_files(fndir, pattern, recursive=False):
  804. fndir = os.path.normpath(fndir)
  805. files = glob.glob(os.path.join(fndir, pattern), recursive=recursive)
  806. return files
  807. def get_logfile(appdir, startdir, logdir, logname):
  808. relpath = os.path.relpath(appdir, startdir)
  809. _, startdir_basename = os.path.splitdrive(startdir)
  810. applogdir = os.path.join(os.path.relpath(logdir + os.sep + startdir_basename), relpath)
  811. applog = os.path.relpath(os.path.join(applogdir, logname))
  812. applogdir = os.path.dirname(applog)
  813. if os.path.isdir(applogdir) == False:
  814. os.makedirs(applogdir)
  815. return applog
  816. def strtofloat(value):
  817. fval = 0.0
  818. try:
  819. match = re.search(r'[+-]?\d*\.?\d+([Ee][+-]?\d+)?', value.strip())
  820. if match:
  821. fval = float(match.group())
  822. except:
  823. pass
  824. return fval
  825. def check_tool_version(ver_cmd, ver_check):
  826. vercmd_log = tempfile.mktemp()
  827. ret, _ = run_command(ver_cmd, show_output=False, logfile=vercmd_log)
  828. check_sts = False
  829. verstr = None
  830. if ret == COMMAND_RUNOK:
  831. with open(vercmd_log, 'r', errors='ignore') as vlf:
  832. for line in vlf.readlines():
  833. if ver_check in line:
  834. verstr = line.strip()
  835. check_sts = True
  836. break
  837. os.remove(vercmd_log)
  838. return check_sts, verstr
  839. def get_elfsize(elf):
  840. sizeinfo = {"text": -1, "data": -1, "bss": -1, "total": -1}
  841. if os.path.isfile(elf) == False:
  842. return sizeinfo
  843. for sizetool in [ "riscv-nuclei-elf-size", "riscv64-unknown-elf-size", "size" ]:
  844. sizecmd = "%s %s" % (sizetool, elf)
  845. sizelog = tempfile.mktemp()
  846. ret, _ = run_command(sizecmd, show_output=False, logfile=sizelog)
  847. if ret == COMMAND_RUNOK:
  848. with open(sizelog, "r", errors='ignore') as sf:
  849. lines = sf.readlines()
  850. datas = lines[-1].strip().split()
  851. sizeinfo["text"] = int(datas[0])
  852. sizeinfo["data"] = int(datas[1])
  853. sizeinfo["bss"] = int(datas[2])
  854. sizeinfo["total"] = int(datas[3])
  855. os.remove(sizelog)
  856. break
  857. else:
  858. os.remove(sizelog)
  859. return sizeinfo
  860. def merge_config_with_makeopts(config, make_options):
  861. opt_splits=make_options.strip().split()
  862. passed_buildcfg = dict()
  863. for opt in opt_splits:
  864. if "=" in opt:
  865. values = opt.split("=")
  866. # Make new build config
  867. if (len(values) == 2):
  868. passed_buildcfg[values[0]] = values[1]
  869. build_cfg = config.get("build_config", None)
  870. if build_cfg is None:
  871. config["build_config"] = passed_buildcfg
  872. else:
  873. # update build_config using parsed config via values specified in make_options
  874. config["build_config"].update(passed_buildcfg)
  875. return config
  876. # merge config dict and args dict
  877. # args will overwrite config
  878. def merge_config_with_args(config, args_dict):
  879. if isinstance(config, dict) == False:
  880. return None
  881. if isinstance(args_dict, dict) == False:
  882. return config
  883. serport = args_dict.get("serport", None)
  884. baudrate = args_dict.get("baudrate", None)
  885. make_options = args_dict.get("make_options", None)
  886. parallel = args_dict.get("parallel", None)
  887. build_target = args_dict.get("build_target", None)
  888. run_target = args_dict.get("run_target", None)
  889. timeout = args_dict.get("timeout", None)
  890. ncycm = args_dict.get("ncycm", None)
  891. if isinstance(config, dict) == False:
  892. return None
  893. new_config = copy.deepcopy(config)
  894. if serport or baudrate or run_target:
  895. run_cfg = new_config.get("run_config", None)
  896. if run_cfg is None:
  897. new_config["run_config"] = {"hardware":{}}
  898. elif "hardware" not in run_cfg:
  899. new_config["run_config"]["hardware"] = {}
  900. if serport:
  901. new_config["run_config"]["hardware"]["serport"] = str(serport)
  902. if baudrate:
  903. new_config["run_config"]["hardware"]["serport"] = int(baudrate)
  904. if run_target:
  905. new_config["run_config"]["target"] = str(run_target)
  906. run_target = new_config["run_config"].get("target", "hardware")
  907. if run_target not in new_config["run_config"]:
  908. new_config["run_config"][run_target] = dict()
  909. if ncycm:
  910. if "ncycm" not in new_config["run_config"]:
  911. new_config["run_config"]["ncycm"] = dict()
  912. new_config["run_config"]["ncycm"]["ncycm"] = os.path.abspath(ncycm)
  913. if timeout: # set timeout
  914. try:
  915. timeout = int(timeout)
  916. except:
  917. timeout = 60
  918. new_config["run_config"][run_target]["timeout"] = timeout
  919. if build_target is not None:
  920. new_config["build_target"] = build_target
  921. if parallel is not None:
  922. new_config["parallel"] = parallel
  923. if make_options:
  924. new_config = merge_config_with_makeopts(new_config, make_options)
  925. return new_config
  926. # merge two config, now is appcfg, another is hwcfg
  927. # hwcfg will overwrite configuration in appcfg
  928. def merge_two_config(appcfg, hwcfg):
  929. if isinstance(appcfg, dict) == True and isinstance(hwcfg, dict) == False:
  930. return appcfg
  931. if isinstance(appcfg, dict) == False and isinstance(hwcfg, dict) == True:
  932. return hwcfg
  933. merged_appcfg = copy.deepcopy(appcfg)
  934. dict_merge(merged_appcfg, hwcfg)
  935. return merged_appcfg
  936. def set_global_variables(config):
  937. global SDK_GLOBAL_VARIABLES
  938. if isinstance(config, dict) == False:
  939. return False
  940. if "global_variables" in config:
  941. dict_merge(SDK_GLOBAL_VARIABLES, config["global_variables"])
  942. print("Using global variables: %s" % SDK_GLOBAL_VARIABLES)
  943. return True
  944. def get_app_runresult(apprst):
  945. if not isinstance(apprst, dict):
  946. return "unknown", "-"
  947. if "type" not in apprst:
  948. return "unknown", "-"
  949. rsttype = apprst["type"]
  950. rstvaluedict = apprst.get("value", dict())
  951. if rstvaluedict and len(rstvaluedict) < 3:
  952. rstval = ""
  953. for key in rstvaluedict:
  954. rstval += "%s : %s;" %(key, rstvaluedict[key])
  955. rstval = rstval.rstrip(';')
  956. else:
  957. rstval = "-"
  958. return rsttype, rstval
  959. def save_execute_csv(result, csvfile):
  960. if isinstance(result, dict) == False:
  961. return False
  962. csvlines = ["App, buildstatus, runstatus, buildtime, runtime, type, value, total, text, data, bss"]
  963. for app in result:
  964. size = result[app]["size"]
  965. app_status = result[app]["status"]
  966. app_time = result[app]["time"]
  967. apprsttype, apprstval = get_app_runresult(result[app].get("result", dict()))
  968. csvline ="%s, %s, %s, %s, %s, %s, %s, %d, %d, %d, %d" % (app, app_status["build"], \
  969. app_status.get("run", False), app_time.get("build", "-"), app_time.get("run", "-"), \
  970. apprsttype, apprstval, size["total"], size["text"], size["data"], size["bss"])
  971. csvlines.append(csvline)
  972. display = get_sdk_verb_buildmsg()
  973. save_csv(csvfile, csvlines, display)
  974. return True
  975. def save_bench_csv(result, csvfile):
  976. if isinstance(result, dict) == False:
  977. return False
  978. csvlines = ["App, case, buildstatus, runstatus, buildtime, runtime, type, value, total, text, data, bss"]
  979. for app in result:
  980. appresult = result[app]
  981. for case in appresult:
  982. size = appresult[case]["size"]
  983. app_status = appresult[case]["status"]
  984. app_time = appresult[case]["time"]
  985. apprsttype, apprstval = get_app_runresult(appresult[case].get("result", dict()))
  986. csvline = "%s, %s, %s, %s, %s, %s, %s, %s, %d, %d, %d, %d" % (app, case, app_status["build"], \
  987. app_status.get("run", False), app_time.get("build", "-"), app_time.get("run", "-"), \
  988. apprsttype, apprstval, size["total"], size["text"], size["data"], size["bss"])
  989. csvlines.append(csvline)
  990. # save csv file
  991. display = get_sdk_verb_buildmsg()
  992. save_csv(csvfile, csvlines, display)
  993. return True
  994. def find_local_appconfig(appdir, localcfgs):
  995. if isinstance(appdir, str) and isinstance(localcfgs, dict):
  996. if appdir in localcfgs:
  997. return appdir
  998. else:
  999. foundcfg = None
  1000. for localcfg in localcfgs:
  1001. localcfgtp = localcfg.strip('/')
  1002. striped_dir = appdir.split(localcfgtp, 1)
  1003. if len(striped_dir) == 2:
  1004. striped_dir = striped_dir[1]
  1005. else:
  1006. striped_dir = appdir
  1007. if striped_dir != appdir:
  1008. if striped_dir.startswith('/'):
  1009. if foundcfg is None:
  1010. foundcfg = localcfg
  1011. else:
  1012. if len(foundcfg) < len(localcfg):
  1013. foundcfg = localcfg
  1014. return foundcfg
  1015. else:
  1016. return None
  1017. def fix_evalsoc_verilog_ncycm(verilog):
  1018. if os.path.isfile(verilog) == False:
  1019. return ""
  1020. vfct = ""
  1021. with open(verilog, "r", errors='ignore') as vf:
  1022. for line in vf.readlines():
  1023. line = line.replace("@80", "@00").replace("@90", "@08")
  1024. vfct += line
  1025. verilog_new = verilog + ".ncycm"
  1026. with open(verilog_new, "w") as vf:
  1027. vf.write(vfct)
  1028. return verilog_new
  1029. PROGRAM_UNKNOWN="unknown"
  1030. PROGRAM_BAREBENCH="barebench"
  1031. PROGRAM_COREMARK="coremark"
  1032. PROGRAM_DHRYSTONE="dhrystone"
  1033. PROGRAM_WHETSTONE="whetstone"
  1034. def parse_benchmark_compatiable(lines):
  1035. result = None
  1036. program_type = PROGRAM_UNKNOWN
  1037. subtype = PROGRAM_UNKNOWN
  1038. try:
  1039. for line in lines:
  1040. # Coremark
  1041. if "CoreMark" in line:
  1042. program_type = PROGRAM_BAREBENCH
  1043. subtype = PROGRAM_COREMARK
  1044. if "Iterations*1000000/total_ticks" in line:
  1045. value = line.split("=")[1].strip().split()[0]
  1046. result = dict()
  1047. result["CoreMark/MHz"] = strtofloat(value)
  1048. # Dhrystone
  1049. if "Dhrystone" in line:
  1050. program_type = PROGRAM_BAREBENCH
  1051. subtype = PROGRAM_DHRYSTONE
  1052. if "1000000/(User_Cycle/Number_Of_Runs)" in line:
  1053. value = line.split("=")[1].strip().split()[0]
  1054. result = dict()
  1055. result["DMIPS/MHz"] = strtofloat(value)
  1056. # Whetstone
  1057. if "Whetstone" in line:
  1058. program_type = PROGRAM_BAREBENCH
  1059. subtype = PROGRAM_WHETSTONE
  1060. if "MWIPS/MHz" in line:
  1061. value = line.split("MWIPS/MHz")[-1].strip().split()[0]
  1062. result = dict()
  1063. result["MWIPS/MHz"] = strtofloat(value)
  1064. except:
  1065. return program_type, subtype, result
  1066. return program_type, subtype, result
  1067. def parse_benchmark_baremetal(lines):
  1068. result = None
  1069. program_type = PROGRAM_UNKNOWN
  1070. subtype = PROGRAM_UNKNOWN
  1071. try:
  1072. unit = "unknown"
  1073. for line in lines:
  1074. stripline = line.strip()
  1075. if "csv," in stripline.lower():
  1076. csv_values = stripline.split(',')
  1077. if len(csv_values) >= 3:
  1078. key = csv_values[1].strip()
  1079. value = csv_values[-1].strip()
  1080. if key.lower() == "benchmark":
  1081. program_type = PROGRAM_BAREBENCH
  1082. unit = value
  1083. else:
  1084. subtype = key.lower()
  1085. result = dict()
  1086. result[unit] = strtofloat(value)
  1087. break
  1088. except:
  1089. return program_type, subtype, result
  1090. return program_type, subtype, result
  1091. def parse_benchmark_baremetal_csv(lines):
  1092. result = None
  1093. program_type = PROGRAM_UNKNOWN
  1094. try:
  1095. result = dict()
  1096. for line in lines:
  1097. stripline = line.strip()
  1098. if "csv," in stripline.lower():
  1099. csv_values = stripline.split(',')
  1100. if len(csv_values) >= 3:
  1101. key = csv_values[1].strip()
  1102. value = csv_values[-1].strip()
  1103. if "BENCH" not in key.upper():
  1104. result[key] = value
  1105. except:
  1106. return program_type, result
  1107. return program_type, result
  1108. def find_index(key, arr):
  1109. try:
  1110. index = arr.index(key)
  1111. except:
  1112. index = -1
  1113. return index
  1114. def parse_benchmark_runlog(lines, lgf=""):
  1115. if isinstance(lines, list) == False:
  1116. return PROGRAM_UNKNOWN, PROGRAM_UNKNOWN, None
  1117. if len(lines) == 0:
  1118. return PROGRAM_UNKNOWN, PROGRAM_UNKNOWN, None
  1119. subtype = ""
  1120. if lgf.strip() == "": # old style
  1121. program_type, subtype, result = parse_benchmark_compatiable(lines)
  1122. else:
  1123. lgf = lgf.replace("\\", "/")
  1124. appnormdirs = os.path.dirname(os.path.normpath(lgf)).replace('\\', '/').split('/')
  1125. if "baremetal/benchmark" in lgf:
  1126. # baremetal benchmark
  1127. program_type, subtype, result = parse_benchmark_baremetal(lines)
  1128. if program_type == PROGRAM_UNKNOWN:
  1129. # fallback to previous parser
  1130. program_type, subtype, result = parse_benchmark_compatiable(lines)
  1131. elif "baremetal/demo_dsp" in lgf:
  1132. program_type, result = parse_benchmark_baremetal_csv(lines)
  1133. program_type = "demo_dsp"
  1134. elif "DSP/Examples/RISCV" in lgf:
  1135. program_type, result = parse_benchmark_baremetal_csv(lines)
  1136. program_type = "nmsis_dsp_example"
  1137. index = find_index("RISCV", appnormdirs)
  1138. if index >= 0:
  1139. subtype = appnormdirs[index + 1]
  1140. elif "DSP/Test" in lgf:
  1141. program_type, result = parse_benchmark_baremetal_csv(lines)
  1142. program_type = "nmsis_dsp_tests"
  1143. index = find_index("Test", appnormdirs)
  1144. if index >= 0:
  1145. subtype = appnormdirs[index + 1]
  1146. elif "NN/Examples/RISCV" in lgf:
  1147. program_type, result = parse_benchmark_baremetal_csv(lines)
  1148. program_type = "nmsis_nn_example"
  1149. index = find_index("RISCV", appnormdirs)
  1150. if index >= 0:
  1151. subtype = appnormdirs[index + 1]
  1152. elif "NN/Tests" in lgf:
  1153. program_type, result = parse_benchmark_baremetal_csv(lines)
  1154. if "full" in appnormdirs:
  1155. program_type = "nmsis_nn_test_full"
  1156. subtype = "full"
  1157. else:
  1158. program_type = "nmsis_nn_test_percase"
  1159. index = find_index("percase", appnormdirs)
  1160. if index >= 0:
  1161. subtype = appnormdirs[index + 1]
  1162. else:
  1163. program_type, subtype, result = parse_benchmark_compatiable(lines)
  1164. return program_type, subtype, result
  1165. def parse_benchmark_use_pyscript(lines, lgf, pyscript):
  1166. if isinstance(lines, list) == False:
  1167. return PROGRAM_UNKNOWN, PROGRAM_UNKNOWN, None
  1168. if len(lines) == 0:
  1169. return PROGRAM_UNKNOWN, PROGRAM_UNKNOWN, None
  1170. # function should named parse_benchmark
  1171. # function argument and return like parse_benchmark_runlog
  1172. parsefunc = import_function("parse_benchmark", pyscript)
  1173. if parsefunc is None:
  1174. return PROGRAM_UNKNOWN, PROGRAM_UNKNOWN, None
  1175. try:
  1176. program_type, subtype, result = parsefunc(lines, lgf)
  1177. return program_type, subtype, result
  1178. except Exception as exc:
  1179. print("ERROR: Parse using %s script error: %s" %(pyscript, exc))
  1180. return PROGRAM_UNKNOWN, PROGRAM_UNKNOWN, None
  1181. def check_tool_exist(tool):
  1182. exist = False
  1183. if sys.platform == 'win32':
  1184. if os.system("where %s" % (tool)) == 0:
  1185. exist = True
  1186. else:
  1187. if os.system("which %s" % (tool)) == 0:
  1188. exist = True
  1189. return exist
  1190. def find_vivado_cmd():
  1191. for vivado_cmd in ("vivado", "vivado_lab"):
  1192. if sys.platform == 'win32':
  1193. if os.system("where %s" % (vivado_cmd)) == 0:
  1194. return vivado_cmd
  1195. else:
  1196. if os.system("which %s" % (vivado_cmd)) == 0:
  1197. return vivado_cmd
  1198. return None
  1199. def program_fpga(bit, target):
  1200. if os.path.isfile(bit) == False:
  1201. print("Can't find bitstream in %s" % (bit))
  1202. return False
  1203. print("Try to program fpga bitstream %s to target board %s" % (bit, target))
  1204. sys.stdout.flush()
  1205. vivado_cmd = find_vivado_cmd()
  1206. # check vivado is found or not
  1207. if vivado_cmd == None:
  1208. print("vivado is not found in PATH, please check!")
  1209. return False
  1210. tcl = os.path.join(os.path.dirname(os.path.realpath(__file__)), "program_bit.tcl")
  1211. target = "*%s" % (target)
  1212. progcmd = "%s -mode batch -nolog -nojournal -source %s -tclargs %s %s" % (vivado_cmd, tcl, bit, target)
  1213. tmout = get_sdk_fpga_prog_tmout()
  1214. if sys.platform != 'win32' and tmout is not None and tmout.strip() != "":
  1215. print("Timeout %s do fpga program" % (tmout))
  1216. progcmd = "timeout --foreground -s SIGKILL %s %s" % (tmout, progcmd)
  1217. print("Do fpga program using command: %s" % (progcmd))
  1218. sys.stdout.flush()
  1219. ret = os.system(progcmd)
  1220. sys.stdout.flush()
  1221. if ret != 0:
  1222. print("Program fpga bit failed, error code %d" % ret)
  1223. return False
  1224. print("Program fpga bit successfully")
  1225. return True
  1226. def find_fpgas():
  1227. vivado_cmd = find_vivado_cmd()
  1228. if vivado_cmd == None:
  1229. print("vivado is not found in PATH, please check!")
  1230. return dict()
  1231. tcl = os.path.join(os.path.dirname(os.path.realpath(__file__)), "find_devices.tcl")
  1232. sys.stdout.flush()
  1233. tmp_log = tempfile.mktemp()
  1234. os.system("%s -mode batch -nolog -nojournal -source %s -notrace > %s" % (vivado_cmd, tcl, tmp_log))
  1235. sys.stdout.flush()
  1236. fpgadevices = dict()
  1237. with open(tmp_log, "r", errors='ignore') as tf:
  1238. for line in tf.readlines():
  1239. line = line.strip()
  1240. if line.startswith("CSV,") == False:
  1241. continue
  1242. splits = line.split(",")
  1243. if len(splits) != 3:
  1244. continue
  1245. fpga_serial = "/".join(splits[1].split("/")[2:])
  1246. fpgadevices[fpga_serial] = splits[2].strip()
  1247. return fpgadevices
  1248. def check_serial_port(serport):
  1249. if serport in find_possible_serports():
  1250. return True
  1251. return False
  1252. def modify_openocd_cfg(cfg, ftdi_serial):
  1253. cfg_bk = cfg + ".backup"
  1254. if (os.path.isfile(cfg)) == False:
  1255. return False
  1256. if os.path.isfile(cfg_bk) == True:
  1257. print("Restore openocd cfg %s" %(cfg))
  1258. shutil.copyfile(cfg_bk, cfg)
  1259. else:
  1260. print("Backup openocd cfg %s" %(cfg))
  1261. shutil.copyfile(cfg, cfg_bk)
  1262. found = False
  1263. contents = []
  1264. index = 0
  1265. with open(cfg, 'r', errors='ignore') as cf:
  1266. contents = cf.readlines()
  1267. for line in contents:
  1268. if line.strip().startswith("transport select"):
  1269. found = True
  1270. break
  1271. index += 1
  1272. if found == False:
  1273. return False
  1274. if sys.platform == 'win32':
  1275. ftdi_serial = "%sA" % (ftdi_serial)
  1276. contents.insert(index, "ftdi_serial %s\ntcl_port disabled\ntelnet_port disabled\n" %(ftdi_serial))
  1277. with open(cfg, 'w') as cf:
  1278. contents = "".join(contents)
  1279. cf.write(contents)
  1280. return True
  1281. GL_CPUCFGs = os.path.join(SCRIPT_DIR, "configs", "cpu")
  1282. def gen_runcfg(cpucfg, runcfg, buildconfig=dict()):
  1283. _, cpucfgdict = load_json(cpucfg)
  1284. _, runcfgdict = load_json(runcfg)
  1285. if cpucfgdict is None:
  1286. return { "build_configs": { "default": {} } }
  1287. if runcfgdict is None:
  1288. return cpucfgdict
  1289. matrixcfgs = runcfgdict.get("matrix", None)
  1290. expectedcfg = runcfgdict.get("expected", dict())
  1291. expectedscfg = runcfgdict.get("expecteds", dict())
  1292. finalruncfg = copy.deepcopy(cpucfgdict)
  1293. # merge buildconfig
  1294. finalruncfg["build_config"] = merge_two_config(finalruncfg.get("build_config", dict()), buildconfig)
  1295. finalruncfg["expected"] = merge_two_config(finalruncfg.get("expected", dict()), expectedcfg)
  1296. finalruncfg["expecteds"] = merge_two_config(finalruncfg.get("expecteds", dict()), expectedscfg)
  1297. if matrixcfgs is None:
  1298. return finalruncfg
  1299. bcfgs = cpucfgdict.get("build_configs", dict())
  1300. newbcfgs = dict()
  1301. for bkey in bcfgs:
  1302. for key in matrixcfgs:
  1303. cfgkey = "%s-%s" % (bkey, key)
  1304. newbcfgs[cfgkey] = merge_two_config(bcfgs[bkey], matrixcfgs[key])
  1305. if len(newbcfgs) > 1:
  1306. finalruncfg["build_configs"] = newbcfgs
  1307. else:
  1308. finalruncfg["build_configs"] = bcfgs
  1309. return finalruncfg
  1310. def gen_coreruncfg(core, runcfg, choice="mini", buildconfig=dict(), casedir=None):
  1311. cpucfgsloc = os.path.join(GL_CPUCFGs, choice)
  1312. if casedir is not None:
  1313. tmp = os.path.join(casedir, choice)
  1314. if os.path.isdir(tmp) == True:
  1315. cpucfgsloc = os.path.realpath(tmp)
  1316. print("Use cpu configs in location %s directory" % (cpucfgsloc))
  1317. cpucfg = os.path.join(cpucfgsloc, "%s.json" % (core))
  1318. return gen_runcfg(cpucfg, runcfg, buildconfig)
  1319. def gen_coreruncfg_custom(core, runcfg, customcfgdir, buildconfig=dict()):
  1320. cpucfg = os.path.join(customcfgdir, "%s.json" % (core))
  1321. return gen_runcfg(cpucfg, runcfg, buildconfig)
  1322. def gen_runyaml(core, locs, fpga_serial, ftdi_serial, cycm, fpgabit, boardtype, ocdcfg, appcfg, hwcfg):
  1323. runyaml = { "runcfg": {"runner": "fpga"},
  1324. "fpga_runners": { core: {
  1325. "board_type": boardtype, "fpga_serial": fpga_serial,
  1326. "ftdi_serial": ftdi_serial, "serial_port": ""}
  1327. },
  1328. "ncycm_runners": { core: {
  1329. "model": cycm if cycm else "" }
  1330. },
  1331. "configs": { core: {
  1332. "fpga": boardtype, "bitstream": fpgabit,
  1333. "ncycm": core, "openocd_cfg": ocdcfg,
  1334. "appcfg": appcfg, "hwcfg": hwcfg }
  1335. },
  1336. "environment": {
  1337. "fpgaloc": locs.get("fpgaloc", ""),
  1338. "ncycmloc": locs.get("ncycmloc", ""),
  1339. "cfgloc": locs.get("cfgloc", "")
  1340. }
  1341. }
  1342. if cycm is not None:
  1343. runyaml["runcfg"]["runner"] = "ncycm"
  1344. return runyaml