1# Copyright (c) 2021, Telink Semiconductor 2# 3# SPDX-License-Identifier: Apache-2.0 4 5import re 6import os 7import time 8import subprocess 9 10from runners.core import ZephyrBinaryRunner, RunnerCaps, BuildConfiguration 11 12class SpiBurnBinaryRunner(ZephyrBinaryRunner): 13 '''Runner front-end for SPI_burn.''' 14 15 def __init__(self, cfg, addr, spiburn, iceman, timeout, gdb_port, gdb_ex, erase=False): 16 super().__init__(cfg) 17 18 self.spiburn = spiburn 19 self.iceman = iceman 20 self.addr = addr 21 self.timeout = int(timeout) 22 self.erase = bool(erase) 23 self.gdb_port = gdb_port 24 self.gdb_ex = gdb_ex 25 26 @classmethod 27 def name(cls): 28 return 'spi_burn' 29 30 @classmethod 31 def capabilities(cls): 32 return RunnerCaps(commands={'flash', 'debug'}, erase=True, flash_addr=True) 33 34 @classmethod 35 def do_add_parser(cls, parser): 36 parser.add_argument('--addr', default='0x0', 37 help='start flash address to write') 38 parser.add_argument('--timeout', default=10, 39 help='ICEman connection establishing timeout in seconds') 40 parser.add_argument('--telink-tools-path', help='path to Telink flash tools') 41 parser.add_argument('--gdb-port', default='1111', help='Port to connect for gdb-client') 42 parser.add_argument('--gdb-ex', default='', nargs='?', help='Additional gdb commands to run') 43 44 @classmethod 45 def do_create(cls, cfg, args): 46 47 if args.telink_tools_path: 48 spiburn = f'{args.telink_tools_path}/flash/bin/SPI_burn' 49 iceman = f'{args.telink_tools_path}/ice/ICEman' 50 else: 51 # If telink_tools_path arg is not specified then pass to tools shall be specified in PATH 52 spiburn = 'SPI_burn' 53 iceman = 'ICEman' 54 55 # Get flash address offset 56 if args.dt_flash == 'y': 57 build_conf = BuildConfiguration(cfg.build_dir) 58 address = hex(cls.get_flash_address(args, build_conf) - build_conf['CONFIG_FLASH_BASE_ADDRESS']) 59 else: 60 address = args.addr 61 62 return SpiBurnBinaryRunner(cfg, address, spiburn, iceman, args.timeout, args.gdb_port, args.gdb_ex, args.erase) 63 64 def do_run(self, command, **kwargs): 65 66 self.require(self.spiburn) 67 68 # Find path to ICEman with require call 69 self.iceman_path = self.require(self.iceman) 70 71 if command == "flash": 72 self._flash() 73 74 elif command == "debug": 75 self._debug() 76 77 else: 78 self.logger.error(f'{command} not supported!') 79 80 def start_iceman(self): 81 82 # Start ICEman as background process 83 self.ice_process = self.popen_ignore_int(["./ICEman", '-Z', 'v5', '-l', 'aice_sdp.cfg'], 84 cwd=os.path.dirname(self.iceman_path), 85 stdout=subprocess.PIPE) 86 87 # Wait till it ready or exit by timeout 88 start = time.time() 89 while True: 90 out = self.ice_process.stdout.readline() 91 if b'ICEman is ready to use.' in out: 92 break 93 if time.time() - start > self.timeout: 94 raise RuntimeError("TIMEOUT: ICEman is not ready") 95 96 def stop_iceman(self): 97 # Kill ICEman subprocess 98 self.ice_process.terminate() 99 100 def _flash(self): 101 102 try: 103 104 # Start ICEman 105 self.start_iceman() 106 107 # Compose flash command 108 cmd_flash = [self.spiburn, '--addr', str(self.addr), '--image', self.cfg.bin_file] 109 110 if self.erase: 111 cmd_flash += ["--erase-all"] 112 113 # Run SPI burn flash tool 114 self.check_call(cmd_flash) 115 116 finally: 117 self.stop_iceman() 118 119 def _debug(self): 120 121 try: 122 123 # Start ICEman 124 self.start_iceman() 125 126 # format -ex commands 127 gdb_ex = re.split("(-ex) ", self.gdb_ex)[1::] 128 129 # Compose gdb command 130 client_cmd = [self.cfg.gdb, self.cfg.elf_file, '-ex', f'target remote :{self.gdb_port}'] + gdb_ex 131 132 # Run gdb 133 self.run_client(client_cmd) 134 135 finally: 136 self.stop_iceman() 137