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