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