1# Copyright (c) 2017 Linaro Limited. 2# 3# SPDX-License-Identifier: Apache-2.0 4 5'''Runner for NIOS II, based on quartus-flash.py and GDB.''' 6 7from runners.core import NetworkPortHelper, RunnerCaps, ZephyrBinaryRunner 8 9 10class Nios2BinaryRunner(ZephyrBinaryRunner): 11 '''Runner front-end for NIOS II.''' 12 13 # From the original shell script: 14 # 15 # "XXX [flash] only support[s] cases where the .elf is sent 16 # over the JTAG and the CPU directly boots from __start. CONFIG_XIP 17 # and CONFIG_INCLUDE_RESET_VECTOR must be disabled." 18 19 def __init__(self, cfg, quartus_py=None, cpu_sof=None, tui=False): 20 super().__init__(cfg) 21 self.hex_name = cfg.hex_file 22 self.elf_name = cfg.elf_file 23 self.cpu_sof = cpu_sof 24 self.quartus_py = quartus_py 25 self.gdb_cmd = [cfg.gdb] if cfg.gdb else None 26 self.tui_arg = ['-tui'] if tui else [] 27 28 @classmethod 29 def name(cls): 30 return 'nios2' 31 32 @classmethod 33 def capabilities(cls): 34 return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'}) 35 36 @classmethod 37 def do_add_parser(cls, parser): 38 # TODO merge quartus-flash.py script into this file. 39 parser.add_argument('--quartus-flash', required=True) 40 parser.add_argument('--cpu-sof', required=True, 41 help='path to the CPU .sof data') 42 parser.add_argument('--tui', default=False, action='store_true', 43 help='if given, GDB uses -tui') 44 45 @classmethod 46 def do_create(cls, cfg, args): 47 return Nios2BinaryRunner(cfg, 48 quartus_py=args.quartus_flash, 49 cpu_sof=args.cpu_sof, 50 tui=args.tui) 51 52 def do_run(self, command, **kwargs): 53 if command == 'flash': 54 self.flash(**kwargs) 55 else: 56 self.debug_debugserver(command, **kwargs) 57 58 def flash(self, **kwargs): 59 if self.quartus_py is None: 60 raise ValueError('Cannot flash; --quartus-flash not given.') 61 if self.cpu_sof is None: 62 raise ValueError('Cannot flash; --cpu-sof not given.') 63 self.ensure_output('hex') 64 65 self.logger.info(f'Flashing file: {self.hex_name}') 66 cmd = [self.quartus_py, 67 '--sof', self.cpu_sof, 68 '--kernel', self.hex_name] 69 self.require(cmd[0]) 70 self.check_call(cmd) 71 72 def print_gdbserver_message(self, gdb_port): 73 self.logger.info(f'Nios II GDB server running on port {gdb_port}') 74 75 def debug_debugserver(self, command, **kwargs): 76 # Per comments in the shell script, the NIOSII GDB server 77 # doesn't exit gracefully, so it's better to explicitly search 78 # for an unused port. The script picks a random value in 79 # between 1024 and 49151, but we'll start with the 80 # "traditional" 3333 choice. 81 gdb_start = 3333 82 nh = NetworkPortHelper() 83 gdb_port = nh.get_unused_ports([gdb_start])[0] 84 85 server_cmd = (['nios2-gdb-server', 86 '--tcpport', str(gdb_port), 87 '--stop', '--reset-target']) 88 self.require(server_cmd[0]) 89 90 if command == 'debugserver': 91 self.print_gdbserver_message(gdb_port) 92 self.check_call(server_cmd) 93 else: 94 if self.elf_name is None: 95 raise ValueError('Cannot debug; elf is missing') 96 if self.gdb_cmd is None: 97 raise ValueError('Cannot debug; no gdb specified') 98 99 gdb_cmd = (self.gdb_cmd + 100 self.tui_arg + 101 [self.elf_name, 102 '-ex', f'target remote :{gdb_port}']) 103 self.require(gdb_cmd[0]) 104 105 self.print_gdbserver_message(gdb_port) 106 self.run_server_and_client(server_cmd, gdb_cmd) 107