1# Copyright (c) 2020 Synopsys. 2# 3# SPDX-License-Identifier: Apache-2.0 4 5'''Runners for Synopsys Metaware Debugger(mdb).''' 6 7 8import os 9import shutil 10from os import path 11 12from runners.core import RunnerCaps, ZephyrBinaryRunner 13 14 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. 20def is_simulation_run(mdb_runner): 21 return mdb_runner.nsim_args != '' 22 23def is_hostlink_used(mdb_runner): 24 return mdb_runner.build_conf.getboolean('CONFIG_UART_HOSTLINK') 25 26def is_flash_cmd_need_exit_immediately(mdb_runner): 27 if is_simulation_run(mdb_runner): 28 # for nsim, we can't run and quit immediately 29 return False 30 31 # if hostlink is used we can't run and quit immediately, as we still need MDB process 32 # attached to process hostlink IO 33 return not is_hostlink_used(mdb_runner) 34 35def smp_core_order(mdb_runner, id): 36 if is_simulation_run(mdb_runner): 37 # for simulation targets we start cores in direct order 38 # (core 0 first, core 1 second, etc...) 39 # otherwise we face mismatch arcnum (code ID) with ARConnect ID 40 # and core ID in instruction traces 41 return id 42 else: 43 # for HW targets we want to start the primary core last, 44 # to avoid ARConnect initialization interfere 45 # with secondary cores startup - so we reverse start order 46 return mdb_runner.cores - 1 - id 47 48def mdb_do_run(mdb_runner, command): 49 commander = "mdb64" 50 51 mdb_runner.require(commander) 52 53 mdb_basic_options = ['-nooptions', '-nogoifmain', '-toggle=include_local_symbols=1'] 54 55 # remove previous .sc.project folder which has temporary settings 56 # for MDB. This is useful for troubleshooting situations with 57 # unexpected behavior of the debugger 58 mdb_cfg_dir = path.join(mdb_runner.build_dir, '.sc.project') 59 if path.exists(mdb_cfg_dir): 60 shutil.rmtree(mdb_cfg_dir) 61 62 # nsim 63 if is_simulation_run(mdb_runner): 64 mdb_target = ['-nsim', '@' + mdb_runner.nsim_args] 65 # hardware target 66 else: 67 if mdb_runner.jtag == 'digilent': 68 mdb_target = ['-digilent'] 69 if mdb_runner.dig_device: 70 mdb_target += [mdb_runner.dig_device] 71 else: 72 # \todo: add support of other debuggers 73 raise ValueError(f'unsupported jtag adapter {mdb_runner.jtag}') 74 75 if command == 'flash': 76 if is_flash_cmd_need_exit_immediately(mdb_runner): 77 mdb_run = ['-run', '-cmd=-nowaitq run', '-cmd=quit', '-cl'] 78 else: 79 mdb_run = ['-run', '-cl'] 80 elif command == 'debug': 81 # use mdb gui to debug 82 mdb_run = ['-OKN'] 83 84 if mdb_runner.cores == 1: 85 # single core's mdb command is different with multicores 86 mdb_cmd = [commander] + mdb_basic_options + mdb_target + mdb_run + [mdb_runner.elf_name] 87 elif 1 < mdb_runner.cores <= 12: 88 mdb_multifiles = '-multifiles=' 89 for i in range(mdb_runner.cores): 90 mdb_sub_cmd = [commander] + [f'-pset={i + 1}', f'-psetname=core{i}'] 91 # -prop=download=2 is used for SMP application debug, only the 1st core 92 # will download the shared image. 93 if i > 0: 94 mdb_sub_cmd += ['-prop=download=2'] 95 mdb_sub_cmd += mdb_basic_options + mdb_target + [mdb_runner.elf_name] 96 mdb_runner.check_call(mdb_sub_cmd, cwd=mdb_runner.build_dir) 97 mdb_multifiles += f'{"" if i == 0 else ","}core{smp_core_order(mdb_runner, i)}' 98 99 # to enable multi-core aware mode for use with the MetaWare debugger, 100 # need to set the NSIM_MULTICORE environment variable to a non-zero value 101 if is_simulation_run(mdb_runner): 102 os.environ["NSIM_MULTICORE"] = '1' 103 104 mdb_cmd = [commander] + [mdb_multifiles] + mdb_run 105 else: 106 raise ValueError(f'unsupported cores {mdb_runner.cores}') 107 108 mdb_runner.call(mdb_cmd, cwd=mdb_runner.build_dir) 109 110 111class MdbNsimBinaryRunner(ZephyrBinaryRunner): 112 '''Runner front-end for nSIM via mdb.''' 113 114 def __init__(self, cfg, cores=1, nsim_args=''): 115 super().__init__(cfg) 116 self.jtag = '' 117 self.cores = int(cores) 118 if nsim_args != '': 119 self.nsim_args = path.join(cfg.board_dir, 'support', nsim_args) 120 else: 121 self.nsim_args = '' 122 self.elf_name = cfg.elf_file 123 self.build_dir = cfg.build_dir 124 self.dig_device = '' 125 126 @classmethod 127 def name(cls): 128 return 'mdb-nsim' 129 130 @classmethod 131 def capabilities(cls): 132 return RunnerCaps(commands={'flash', 'debug'}) 133 134 @classmethod 135 def do_add_parser(cls, parser): 136 parser.add_argument('--cores', default=1, 137 help='''choose the cores that target has, e.g. 138 --cores=1''') 139 parser.add_argument('--nsim_args', default='', 140 help='''if given, arguments for nsim simulator 141 through mdb which should be in 142 <board_dir>/support, e.g. --nsim-args= 143 mdb_em.args''') 144 145 @classmethod 146 def do_create(cls, cfg, args): 147 return MdbNsimBinaryRunner( 148 cfg, 149 cores=args.cores, 150 nsim_args=args.nsim_args) 151 152 def do_run(self, command, **kwargs): 153 mdb_do_run(self, command) 154 155 156class MdbHwBinaryRunner(ZephyrBinaryRunner): 157 '''Runner front-end for mdb.''' 158 159 def __init__(self, cfg, cores=1, jtag='digilent', dig_device=''): 160 super().__init__(cfg) 161 self.jtag = jtag 162 self.cores = int(cores) 163 self.nsim_args = '' 164 self.elf_name = cfg.elf_file 165 if dig_device != '': 166 self.dig_device = '-prop=dig_device=' + dig_device 167 else: 168 self.dig_device = '' 169 self.build_dir = cfg.build_dir 170 171 @classmethod 172 def name(cls): 173 return 'mdb-hw' 174 175 @classmethod 176 def capabilities(cls): 177 return RunnerCaps(commands={'flash', 'debug'}) 178 179 @classmethod 180 def do_add_parser(cls, parser): 181 parser.add_argument('--jtag', default='digilent', 182 help='''choose the jtag interface for hardware 183 targets, e.g. --jtag=digilent for digilent 184 jtag adapter''') 185 parser.add_argument('--cores', default=1, 186 help='''choose the number of cores that target has, 187 e.g. --cores=1''') 188 parser.add_argument('--dig-device', default='', 189 help='''choose the specific digilent device to 190 connect, this is useful when multiple 191 targets are connected''') 192 193 @classmethod 194 def do_create(cls, cfg, args): 195 return MdbHwBinaryRunner( 196 cfg, 197 cores=args.cores, 198 jtag=args.jtag, 199 dig_device=args.dig_device) 200 201 def do_run(self, command, **kwargs): 202 mdb_do_run(self, command) 203