1# Copyright (c) 2017 Linaro Limited. 2# 3# SPDX-License-Identifier: Apache-2.0 4 5'''Modified openocd and gdb runner for Cyclone V SoC DevKit.''' 6 7import subprocess 8import re 9import os 10 11from os import path 12from pathlib import Path 13 14try: 15 from elftools.elf.elffile import ELFFile 16except ImportError: 17 ELFFile = None 18 19from runners.core import ZephyrBinaryRunner, RunnerCaps 20 21DEFAULT_OPENOCD_TCL_PORT = 6333 22DEFAULT_OPENOCD_TELNET_PORT = 4444 23DEFAULT_OPENOCD_GDB_PORT = 3333 24DEFAULT_OPENOCD_RESET_HALT_CMD = 'reset halt' 25 26class IntelCycloneVBinaryRunner(ZephyrBinaryRunner): 27 '''Runner front-end for openocd.''' 28 29 def __init__(self, cfg, pre_init=None, reset_halt_cmd=DEFAULT_OPENOCD_RESET_HALT_CMD, 30 pre_load=None, load_cmd=None, verify_cmd=None, post_verify=None, 31 do_verify=False, do_verify_only=False, 32 tui=None, config=None, serial=None, use_elf=True, 33 no_halt=False, no_init=False, no_targets=False, 34 tcl_port=DEFAULT_OPENOCD_TCL_PORT, 35 telnet_port=DEFAULT_OPENOCD_TELNET_PORT, 36 gdb_port=DEFAULT_OPENOCD_GDB_PORT, 37 gdb_init=None, no_load=False): 38 super().__init__(cfg) 39 40 support = path.join(cfg.board_dir, 'support') 41 42 if not config: 43 default = path.join(support, 'openocd.cfg') 44 default2 = path.join(support, 'download_all.gdb') 45 default3 = path.join(support, 'appli_dl_cmd.gdb') 46 default4 = path.join(support, 'appli_debug_cmd.gdb') 47 if path.exists(default): 48 config = [default] 49 gdb_commands = [default2] 50 gdb_commands2 = [default3] 51 gdb_commands_deb = [default4] 52 self.openocd_config = config 53 self.gdb_cmds = gdb_commands 54 self.gdb_cmds2 = gdb_commands2 55 self.gdb_cmds_deb = gdb_commands_deb 56 57 search_args = [] 58 if path.exists(support): 59 search_args.append('-s') 60 search_args.append(support) 61 62 if self.openocd_config is not None: 63 for i in self.openocd_config: 64 if path.exists(i) and not path.samefile(path.dirname(i), support): 65 search_args.append('-s') 66 search_args.append(path.dirname(i)) 67 68 if cfg.openocd_search is not None: 69 for p in cfg.openocd_search: 70 search_args.extend(['-s', p]) 71 self.openocd_cmd = [cfg.openocd or 'openocd'] + search_args 72 # openocd doesn't cope with Windows path names, so convert 73 # them to POSIX style just to be sure. 74 self.elf_name = Path(cfg.elf_file).as_posix() 75 self.pre_init = pre_init or [] 76 self.reset_halt_cmd = reset_halt_cmd 77 self.pre_load = pre_load or [] 78 self.load_cmd = load_cmd 79 self.verify_cmd = verify_cmd 80 self.post_verify = post_verify or [] 81 self.do_verify = do_verify or False 82 self.do_verify_only = do_verify_only or False 83 self.tcl_port = tcl_port 84 self.telnet_port = telnet_port 85 self.gdb_port = gdb_port 86 self.gdb_cmd = [cfg.gdb] if cfg.gdb else None 87 self.tui_arg = ['-tui'] if tui else [] 88 self.halt_arg = [] if no_halt else ['-c halt'] 89 self.init_arg = [] if no_init else ['-c init'] 90 self.targets_arg = [] if no_targets else ['-c targets'] 91 self.serial = ['-c set _ZEPHYR_BOARD_SERIAL ' + serial] if serial else [] 92 self.use_elf = use_elf 93 self.gdb_init = gdb_init 94 self.load_arg = [] if no_load else ['-ex', 'load'] 95 96 @classmethod 97 def name(cls): 98 return 'intel_cyclonev' 99 100 @classmethod 101 def capabilities(cls): 102 return RunnerCaps(commands={'flash', 'debug', 'attach'}, 103 dev_id=False, flash_addr=False, erase=False) 104 105 @classmethod 106 def do_add_parser(cls, parser): 107 parser.add_argument('--config', action='append', 108 help='''if given, override default config file; 109 may be given multiple times''') 110 parser.add_argument('--serial', default="", 111 help='if given, selects FTDI instance by its serial number, defaults to empty') 112 parser.add_argument('--use-elf', default=False, action='store_true', 113 help='if given, Elf file will be used for loading instead of HEX image') 114 # Options for flashing: 115 parser.add_argument('--cmd-pre-init', action='append', 116 help='''Command to run before calling init; 117 may be given multiple times''') 118 parser.add_argument('--cmd-reset-halt', default=DEFAULT_OPENOCD_RESET_HALT_CMD, 119 help=f'''Command to run for resetting and halting the target, 120 defaults to "{DEFAULT_OPENOCD_RESET_HALT_CMD}"''') 121 parser.add_argument('--cmd-pre-load', action='append', 122 help='''Command to run before flashing; 123 may be given multiple times''') 124 parser.add_argument('--cmd-load', 125 help='''Command to load/flash binary 126 (required when flashing)''') 127 parser.add_argument('--cmd-verify', 128 help='''Command to verify flashed binary''') 129 parser.add_argument('--cmd-post-verify', action='append', 130 help='''Command to run after verification; 131 may be given multiple times''') 132 parser.add_argument('--verify', action='store_true', 133 help='if given, verify after flash') 134 parser.add_argument('--verify-only', action='store_true', 135 help='if given, do verify and verify only. No flashing') 136 137 # Options for debugging: 138 parser.add_argument('--tui', default=False, action='store_true', 139 help='if given, GDB uses -tui') 140 parser.add_argument('--tcl-port', default=DEFAULT_OPENOCD_TCL_PORT, 141 help='openocd TCL port, defaults to 6333') 142 parser.add_argument('--telnet-port', 143 default=DEFAULT_OPENOCD_TELNET_PORT, 144 help='openocd telnet port, defaults to 4444') 145 parser.add_argument('--gdb-port', default=DEFAULT_OPENOCD_GDB_PORT, 146 help='openocd gdb port, defaults to 3333') 147 parser.add_argument('--gdb-init', action='append', 148 help='if given, add GDB init commands') 149 parser.add_argument('--no-halt', action='store_true', 150 help='if given, no halt issued in gdb server cmd') 151 parser.add_argument('--no-init', action='store_true', 152 help='if given, no init issued in gdb server cmd') 153 parser.add_argument('--no-targets', action='store_true', 154 help='if given, no target issued in gdb server cmd') 155 parser.add_argument('--no-load', action='store_true', 156 help='if given, no load issued in gdb server cmd') 157 158 @classmethod 159 def do_create(cls, cfg, args): 160 return IntelCycloneVBinaryRunner( 161 cfg, 162 pre_init=args.cmd_pre_init, reset_halt_cmd=args.cmd_reset_halt, 163 pre_load=args.cmd_pre_load, load_cmd=args.cmd_load, 164 verify_cmd=args.cmd_verify, post_verify=args.cmd_post_verify, 165 do_verify=args.verify, do_verify_only=args.verify_only, 166 tui=args.tui, config=args.config, serial=args.serial, 167 use_elf=args.use_elf, no_halt=args.no_halt, no_init=args.no_init, 168 no_targets=args.no_targets, tcl_port=args.tcl_port, 169 telnet_port=args.telnet_port, gdb_port=args.gdb_port, 170 gdb_init=args.gdb_init, no_load=args.no_load) 171 172 def print_gdbserver_message(self): 173 if not self.thread_info_enabled: 174 thread_msg = '; no thread info available' 175 elif self.supports_thread_info(): 176 thread_msg = '; thread info enabled' 177 else: 178 thread_msg = '; update OpenOCD software for thread info' 179 self.logger.info('OpenOCD GDB server running on port ' 180 f'{self.gdb_port}{thread_msg}') 181 182 # pylint: disable=R0201 183 def to_num(self, number): 184 dev_match = re.search(r"^\d*\+dev", number) 185 dev_version = not dev_match is None 186 187 num_match = re.search(r"^\d*", number) 188 num = int(num_match.group(0)) 189 190 if dev_version: 191 num += 1 192 193 return num 194 195 def read_version(self): 196 self.require(self.openocd_cmd[0]) 197 198 # OpenOCD prints in stderr, need redirect to get output 199 out = self.check_output([self.openocd_cmd[0], '--version'], 200 stderr=subprocess.STDOUT).decode() 201 202 return out.split('\n')[0] 203 204 def supports_thread_info(self): 205 # Zephyr rtos was introduced after 0.11.0 206 version_str = self.read_version().split(' ')[3] 207 version = version_str.split('.') 208 (major, minor, rev) = [self.to_num(i) for i in version] 209 return (major, minor, rev) > (0, 11, 0) 210 211 def do_run(self, command, **kwargs): 212 self.require(self.openocd_cmd[0]) 213 if ELFFile is None: 214 raise RuntimeError( 215 'elftools missing; please "pip3 install elftools"') 216 217 self.cfg_cmd = [] 218 if self.openocd_config is not None: 219 for i in self.openocd_config: 220 self.cfg_cmd.append('-f') 221 self.cfg_cmd.append(i) 222 223 if command == 'flash' and self.use_elf: 224 self.do_flash_elf(**kwargs) 225 elif command == 'flash': 226 self.do_flash_elf(**kwargs) 227 elif command in ('attach', 'debug'): 228 self.do_attach_debug(command, **kwargs) 229 230 def do_flash_elf(self, **kwargs): 231 if self.gdb_cmd is None: 232 raise ValueError('Cannot debug; no gdb specified') 233 if self.elf_name is None: 234 raise ValueError('Cannot debug; no .elf specified') 235 236 pre_init_cmd = [] 237 for i in self.pre_init: 238 pre_init_cmd.append("-c") 239 pre_init_cmd.append(i) 240 pre_init_cmd.append("-q") 241 242 if self.thread_info_enabled and self.supports_thread_info(): 243 pre_init_cmd.append("-c") 244 pre_init_cmd.append("$_TARGETNAME configure -rtos Zephyr") 245 246 server_cmd = (self.openocd_cmd + self.serial + self.cfg_cmd + #added mevalver 247 pre_init_cmd) 248 temp_str = '--cd=' + os.environ.get('ZEPHYR_BASE') #Go to Zephyr base Dir 249 gdb_cmd = (self.gdb_cmd + self.tui_arg + 250 [temp_str,'-ex', 'target extended-remote localhost:{}'.format(self.gdb_port) , '-batch']) #Execute First Script in Zephyr Base Dir 251 252 gdb_cmd2 = (self.gdb_cmd + self.tui_arg + 253 ['-ex', 'target extended-remote localhost:{}'.format(self.gdb_port) , '-batch']) #Execute Second Script in Build Dir 254 echo = ['echo'] 255 if self.gdb_init is not None: 256 for i in self.gdb_init: 257 gdb_cmd.append("-ex") 258 gdb_cmd.append(i) 259 gdb_cmd2.append("-ex") 260 gdb_cmd2.append(i) 261 262 if self.gdb_cmds is not None: 263 for i in self.gdb_cmds: 264 gdb_cmd.append("-x") 265 gdb_cmd.append(i) 266 267 if self.gdb_cmds2 is not None: 268 for i in self.gdb_cmds2: 269 gdb_cmd2.append("-x") 270 gdb_cmd2.append(i) 271 272 self.require(gdb_cmd[0]) 273 self.print_gdbserver_message() 274 275 cmd1 = (echo + server_cmd) 276 self.check_call(cmd1) 277 cmd2 = (echo + gdb_cmd) 278 self.check_call(cmd2) 279 cmd3 = (echo + gdb_cmd2) 280 self.check_call(cmd3) 281 282 self.run_server_and_client(server_cmd, gdb_cmd) 283 self.run_server_and_client(server_cmd, gdb_cmd2) 284 285 def do_attach_debug(self, command, **kwargs): 286 if self.gdb_cmd is None: 287 raise ValueError('Cannot debug; no gdb specified') 288 if self.elf_name is None: 289 raise ValueError('Cannot debug; no .elf specified') 290 291 pre_init_cmd = [] 292 for i in self.pre_init: 293 pre_init_cmd.append("-c") 294 pre_init_cmd.append(i) 295 296 if self.thread_info_enabled and self.supports_thread_info(): 297 pre_init_cmd.append("-c") 298 pre_init_cmd.append("$_TARGETNAME configure -rtos Zephyr") 299 pre_init_cmd.append("-q") 300 301 server_cmd = (self.openocd_cmd + self.serial + self.cfg_cmd + 302 pre_init_cmd) 303 304 gdb_attach = (self.gdb_cmd + self.tui_arg + 305 ['-ex', 'target extended-remote :{}'.format(self.gdb_port), 306 self.elf_name, '-q']) 307 308 temp_str = '--cd=' + os.environ.get('ZEPHYR_BASE') #Go to Zephyr base Dir 309 310 gdb_cmd = (self.gdb_cmd + self.tui_arg + 311 [temp_str,'-ex', 'target extended-remote localhost:{}'.format(self.gdb_port) , '-batch']) #Execute First Script in Zephyr Base Dir 312 313 gdb_cmd2 = (self.gdb_cmd + self.tui_arg + 314 ['-ex', 'target extended-remote :{}'.format(self.gdb_port) , '-batch']) #Execute Second Script in Build Dir 315 316 317 if self.gdb_init is not None: 318 for i in self.gdb_init: 319 gdb_cmd.append("-ex") 320 gdb_cmd.append(i) 321 gdb_cmd2.append("-ex") 322 gdb_cmd2.append(i) 323 324 if self.gdb_cmds is not None: 325 for i in self.gdb_cmds: 326 gdb_cmd.append("-x") 327 gdb_cmd.append(i) 328 329 if self.gdb_cmds_deb is not None: 330 for i in self.gdb_cmds_deb: 331 gdb_cmd2.append("-x") 332 gdb_cmd2.append(i) 333 334 self.require(gdb_cmd[0]) 335 self.print_gdbserver_message() 336 337 if command == 'attach': 338 self.run_server_and_client(server_cmd, gdb_attach) 339 elif command == 'debug': 340 self.run_server_and_client(server_cmd, gdb_cmd) 341 self.run_server_and_client(server_cmd, gdb_cmd2) 342 self.run_server_and_client(server_cmd, gdb_attach) 343