mdb.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. # Copyright (c) 2020 Synopsys.
  2. #
  3. # SPDX-License-Identifier: Apache-2.0
  4. '''Runners for Synopsys Metaware Debugger(mdb).'''
  5. import shutil
  6. import time
  7. import os
  8. from os import path
  9. from runners.core import ZephyrBinaryRunner, RunnerCaps
  10. try:
  11. import psutil
  12. MISSING_REQUIREMENTS = False
  13. except ImportError:
  14. MISSING_REQUIREMENTS = True
  15. # normally we should create class with common functionality inherited from
  16. # ZephyrBinaryRunner and inherit MdbNsimBinaryRunner and MdbHwBinaryRunner
  17. # from it. However as we do lookup for runners with
  18. # ZephyrBinaryRunner.__subclasses__() such sub-sub-classes won't be found.
  19. # So, we move all common functionality to helper functions instead.
  20. def simulation_run(mdb_runner):
  21. return mdb_runner.nsim_args != ''
  22. def get_cld_pid(mdb_process):
  23. try:
  24. parent = psutil.Process(mdb_process.pid)
  25. children = parent.children(recursive=True)
  26. for process in children:
  27. if process.name().startswith("cld"):
  28. return (True, process.pid)
  29. except psutil.Error:
  30. pass
  31. return (False, -1)
  32. # MDB creates child process (cld) which won't be terminated if we simply
  33. # terminate parents process (mdb). 'record_cld_pid' is provided to record 'cld'
  34. # process pid to file (mdb.pid) so this process can be terminated correctly by
  35. # twister infrastructure
  36. def record_cld_pid(mdb_runner, mdb_process):
  37. for _i in range(100):
  38. found, pid = get_cld_pid(mdb_process)
  39. if found:
  40. mdb_pid_file = path.join(mdb_runner.build_dir, 'mdb.pid')
  41. mdb_runner.logger.debug("MDB CLD pid: " + str(pid) + " " + mdb_pid_file)
  42. with open(mdb_pid_file, 'w') as f:
  43. f.write(str(pid))
  44. return
  45. time.sleep(0.05)
  46. def mdb_do_run(mdb_runner, command):
  47. commander = "mdb"
  48. mdb_runner.require(commander)
  49. mdb_basic_options = ['-nooptions', '-nogoifmain',
  50. '-toggle=include_local_symbols=1']
  51. # remove previous .sc.project folder which has temporary settings
  52. # for MDB. This is useful for troubleshooting situations with
  53. # unexpected behavior of the debugger
  54. mdb_cfg_dir = path.join(mdb_runner.build_dir, '.sc.project')
  55. if path.exists(mdb_cfg_dir):
  56. shutil.rmtree(mdb_cfg_dir)
  57. # nsim
  58. if simulation_run(mdb_runner):
  59. mdb_target = ['-nsim', '@' + mdb_runner.nsim_args]
  60. # hardware target
  61. else:
  62. if mdb_runner.jtag == 'digilent':
  63. mdb_target = ['-digilent', mdb_runner.dig_device]
  64. else:
  65. # \todo: add support of other debuggers
  66. mdb_target = ['']
  67. if command == 'flash':
  68. if simulation_run(mdb_runner):
  69. # for nsim , can't run and quit immediately
  70. mdb_run = ['-run', '-cl']
  71. else:
  72. mdb_run = ['-run', '-cmd=-nowaitq run', '-cmd=quit', '-cl']
  73. elif command == 'debug':
  74. # use mdb gui to debug
  75. mdb_run = ['-OKN']
  76. if mdb_runner.cores == 1:
  77. # single core's mdb command is different with multicores
  78. mdb_cmd = ([commander] + mdb_basic_options + mdb_target +
  79. mdb_run + [mdb_runner.elf_name])
  80. elif 1 < mdb_runner.cores <= 4:
  81. mdb_multifiles = '-multifiles='
  82. for i in range(mdb_runner.cores):
  83. # note that: mdb requires -pset starting from 1, not 0 !!!
  84. mdb_sub_cmd = ([commander] +
  85. ['-pset={}'.format(i + 1),
  86. '-psetname=core{}'.format(i),
  87. # -prop=download=2 is used for SMP application debug, only the 1st
  88. # core will download the shared image.
  89. ('-prop=download=2' if i > 0 else '')] +
  90. mdb_basic_options + mdb_target + [mdb_runner.elf_name])
  91. mdb_runner.check_call(mdb_sub_cmd)
  92. mdb_multifiles += ('core{}'.format(mdb_runner.cores-1-i) if i == 0 else ',core{}'.format(mdb_runner.cores-1-i))
  93. # to enable multi-core aware mode for use with the MetaWare debugger,
  94. # need to set the NSIM_MULTICORE environment variable to a non-zero value
  95. if simulation_run(mdb_runner):
  96. os.environ["NSIM_MULTICORE"] = '1'
  97. mdb_cmd = ([commander] + [mdb_multifiles] + mdb_run)
  98. else:
  99. raise ValueError('unsupported cores {}'.format(mdb_runner.cores))
  100. process = mdb_runner.popen_ignore_int(mdb_cmd)
  101. record_cld_pid(mdb_runner, process)
  102. class MdbNsimBinaryRunner(ZephyrBinaryRunner):
  103. '''Runner front-end for nSIM via mdb.'''
  104. def __init__(self, cfg, cores=1, nsim_args=''):
  105. super().__init__(cfg)
  106. self.jtag = ''
  107. self.cores = int(cores)
  108. if nsim_args != '':
  109. self.nsim_args = path.join(cfg.board_dir, 'support', nsim_args)
  110. else:
  111. self.nsim_args = ''
  112. self.elf_name = cfg.elf_file
  113. self.build_dir = cfg.build_dir
  114. self.dig_device = ''
  115. @classmethod
  116. def name(cls):
  117. return 'mdb-nsim'
  118. @classmethod
  119. def capabilities(cls):
  120. return RunnerCaps(commands={'flash', 'debug'})
  121. @classmethod
  122. def do_add_parser(cls, parser):
  123. parser.add_argument('--cores', default=1,
  124. help='''choose the cores that target has, e.g.
  125. --cores=1''')
  126. parser.add_argument('--nsim_args', default='',
  127. help='''if given, arguments for nsim simulator
  128. through mdb which should be in
  129. <board_dir>/support, e.g. --nsim-args=
  130. mdb_em.args''')
  131. @classmethod
  132. def do_create(cls, cfg, args):
  133. return MdbNsimBinaryRunner(
  134. cfg,
  135. cores=args.cores,
  136. nsim_args=args.nsim_args)
  137. def do_run(self, command, **kwargs):
  138. mdb_do_run(self, command)
  139. class MdbHwBinaryRunner(ZephyrBinaryRunner):
  140. '''Runner front-end for mdb.'''
  141. def __init__(self, cfg, cores=1, jtag='digilent', dig_device=''):
  142. super().__init__(cfg)
  143. self.jtag = jtag
  144. self.cores = int(cores)
  145. self.nsim_args = ''
  146. self.elf_name = cfg.elf_file
  147. if dig_device != '':
  148. self.dig_device = '-prop=dig_device=' + dig_device
  149. else:
  150. self.dig_device = ''
  151. self.build_dir = cfg.build_dir
  152. @classmethod
  153. def name(cls):
  154. return 'mdb-hw'
  155. @classmethod
  156. def capabilities(cls):
  157. return RunnerCaps(commands={'flash', 'debug'})
  158. @classmethod
  159. def do_add_parser(cls, parser):
  160. parser.add_argument('--jtag', default='digilent',
  161. help='''choose the jtag interface for hardware
  162. targets, e.g. --jtat=digilent for digilent
  163. jtag adapter''')
  164. parser.add_argument('--cores', default=1,
  165. help='''choose the number of cores that target has,
  166. e.g. --cores=1''')
  167. parser.add_argument('--dig-device', default='',
  168. help='''choose the the specific digilent device to
  169. connect, this is useful when multiple
  170. targets are connected''')
  171. @classmethod
  172. def do_create(cls, cfg, args):
  173. return MdbHwBinaryRunner(
  174. cfg,
  175. cores=args.cores,
  176. jtag=args.jtag,
  177. dig_device=args.dig_device)
  178. def do_run(self, command, **kwargs):
  179. if MISSING_REQUIREMENTS:
  180. raise RuntimeError('one or more Python dependencies were missing; '
  181. "see the getting started guide for details on "
  182. "how to fix")
  183. mdb_do_run(self, command)