scripts: zephyr_flash_debug: refactor into new "runner" package

Factor the classes which do the work into a new "runner" package. This
package has a core module where ZephyrBinaryRunner and common helpers
will live, and one file per subclass / runner front-end.

The top-level script, zephyr_flash_debug.py, still exists, but just
delegates its work to the core.

Signed-off-by: Marti Bolivar <marti.bolivar@linaro.org>
This commit is contained in:
Marti Bolivar 2017-10-24 12:29:03 -04:00 committed by Anas Nashif
commit 32b475ebb9
11 changed files with 1158 additions and 1013 deletions

View file

@ -0,0 +1,22 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
from .core import ZephyrBinaryRunner
# We import these here to ensure the ZephyrBinaryRunner subclasses are
# defined; otherwise, ZephyrBinaryRunner.create_for_shell_script()
# won't work.
# Explicitly silence the unused import warning.
# flake8: noqa: F401
from . import arc
from . import bossac
from . import dfu
from . import esp32
from . import nios2
from . import nrfjprog
from . import openocd
from . import pyocd
__all__ = ['ZephyrBinaryRunner']

View file

@ -0,0 +1,155 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''ARC architecture-specific runner.'''
from os import path
import os
import shlex
from .core import ZephyrBinaryRunner, get_env_or_bail
DEFAULT_ARC_TCL_PORT = 6333
DEFAULT_ARC_TELNET_PORT = 4444
DEFAULT_ARC_GDB_PORT = 3333
class ArcBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for the ARC architecture, using openocd.'''
# This unusual 'flash' implementation matches the original shell script.
#
# It works by starting a GDB server in a separate session, connecting a
# client to it to load the program, and running 'continue' within the
# client to execute the application.
#
# TODO: exit immediately when flashing is done, leaving Zephyr running.
def __init__(self, elf, zephyr_base, arch, board_name, python,
gdb, openocd='openocd', extra_init=None, default_path=None,
tui=None, tcl_port=DEFAULT_ARC_TCL_PORT,
telnet_port=DEFAULT_ARC_TELNET_PORT,
gdb_port=DEFAULT_ARC_GDB_PORT, debug=False):
super(ArcBinaryRunner, self).__init__(debug=debug)
self.elf = elf
self.zephyr_base = zephyr_base
self.arch = arch
self.board_name = board_name
self.python = python
self.gdb = gdb
search_args = []
if default_path is not None:
search_args = ['-s', default_path]
self.openocd_cmd = [openocd] + search_args
self.extra_init = extra_init if extra_init is not None else []
self.tui = tui
self.tcl_port = tcl_port
self.telnet_port = telnet_port
self.gdb_port = gdb_port
def replaces_shell_script(shell_script, command):
return (command in {'flash', 'debug', 'debugserver'} and
shell_script == 'arc_debugger.sh')
def create_from_env(command, debug):
'''Create runner from environment.
Required:
- O: build output directory
- KERNEL_ELF_NAME: zephyr kernel binary in ELF format
- ZEPHYR_BASE: zephyr Git repository base directory
- ARCH: board architecture
- BOARD_NAME: zephyr name of board
- PYTHON: python executable
- GDB: gdb executable
Optional:
- OPENOCD: path to openocd, defaults to openocd
- OPENOCD_EXTRA_INIT: initialization command for GDB server
- OPENOCD_DEFAULT_PATH: openocd search path to use
- TUI: if present, passed to gdb server used to flash
- TCL_PORT: openocd TCL port, defaults to 6333
- TELNET_PORT: openocd telnet port, defaults to 4444
- GDB_PORT: openocd gdb port, defaults to 3333
'''
elf = path.join(get_env_or_bail('O'),
get_env_or_bail('KERNEL_ELF_NAME'))
zephyr_base = get_env_or_bail('ZEPHYR_BASE')
arch = get_env_or_bail('ARCH')
board_name = get_env_or_bail('BOARD_NAME')
python = get_env_or_bail('PYTHON')
gdb = get_env_or_bail('GDB')
openocd = os.environ.get('OPENOCD', 'openocd')
extra_init = os.environ.get('OPENOCD_EXTRA_INIT', None)
if extra_init is not None:
extra_init = shlex.split(extra_init)
default_path = os.environ.get('OPENOCD_DEFAULT_PATH', None)
tui = os.environ.get('TUI', None)
tcl_port = int(os.environ.get('TCL_PORT',
str(DEFAULT_ARC_TCL_PORT)))
telnet_port = int(os.environ.get('TELNET_PORT',
str(DEFAULT_ARC_TELNET_PORT)))
gdb_port = int(os.environ.get('GDB_PORT',
str(DEFAULT_ARC_GDB_PORT)))
return ArcBinaryRunner(elf, zephyr_base, arch, board_name, python,
gdb, openocd=openocd, extra_init=extra_init,
default_path=default_path, tui=tui,
tcl_port=tcl_port, telnet_port=telnet_port,
gdb_port=gdb_port, debug=debug)
def run(self, command, **kwargs):
if command not in {'flash', 'debug', 'debugserver'}:
raise ValueError('{} is not supported'.format(command))
kwargs['openocd-cfg'] = path.join(self.zephyr_base, 'boards',
self.arch, self.board_name,
'support', 'openocd.cfg')
if command in {'flash', 'debug'}:
self.flash_debug(command, **kwargs)
else:
self.debugserver(**kwargs)
def flash_debug(self, command, **kwargs):
config = kwargs['openocd-cfg']
server_cmd = (self.openocd_cmd +
['-f', config] +
self.extra_init +
['-c', 'tcl_port {}'.format(self.tcl_port),
'-c', 'telnet_port {}'.format(self.telnet_port),
'-c', 'gdb_port {}'.format(self.gdb_port),
'-c', 'init',
'-c', 'targets',
'-c', 'halt'])
tui_arg = []
if self.tui is not None:
tui_arg = [self.tui]
continue_arg = []
if command == 'flash':
continue_arg = ['-ex', 'c']
gdb_cmd = ([self.gdb] +
tui_arg +
['-ex', 'target remote :{}'.format(self.gdb_port),
'-ex', 'load'] +
continue_arg +
[self.elf])
self.run_server_and_client(server_cmd, gdb_cmd)
def debugserver(self, **kwargs):
config = kwargs['openocd-cfg']
cmd = (self.openocd_cmd +
['-f', config,
'-c', 'init',
'-c', 'targets',
'-c', 'reset halt'])
self.check_call(cmd)

View file

@ -0,0 +1,64 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''bossac-specific runner (flash only) for Atmel SAM microcontrollers.'''
from os import path
import os
import platform
from .core import ZephyrBinaryRunner, get_env_or_bail
DEFAULT_BOSSAC_PORT = '/dev/ttyACM0'
class BossacBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for bossac.'''
def __init__(self, bin_name, bossac='bossac',
port=DEFAULT_BOSSAC_PORT, debug=False):
super(BossacBinaryRunner, self).__init__(debug=debug)
self.bin_name = bin_name
self.bossac = bossac
self.port = port
def replaces_shell_script(shell_script, command):
return command == 'flash' and shell_script == 'bossa-flash.sh'
def create_from_env(command, debug):
'''Create flasher from environment.
Required:
- O: build output directory
- KERNEL_BIN_NAME: name of kernel binary
Optional:
- BOSSAC: path to bossac, default is bossac
- BOSSAC_PORT: serial port to use, default is /dev/ttyACM0
'''
bin_name = path.join(get_env_or_bail('O'),
get_env_or_bail('KERNEL_BIN_NAME'))
bossac = os.environ.get('BOSSAC', 'bossac')
port = os.environ.get('BOSSAC_PORT', DEFAULT_BOSSAC_PORT)
return BossacBinaryRunner(bin_name, bossac=bossac, port=port,
debug=debug)
def run(self, command, **kwargs):
if command != 'flash':
raise ValueError('only flash is supported')
if platform.system() != 'Linux':
msg = 'CAUTION: No flash tool for your host system found!'
raise NotImplementedError(msg)
cmd_stty = ['stty', '-F', self.port, 'raw', 'ispeed', '1200',
'ospeed', '1200', 'cs8', '-cstopb', 'ignpar', 'eol', '255',
'eof', '255']
cmd_flash = [self.bossac, '-p', self.port, '-R', '-e', '-w', '-v',
'-b', self.bin_name]
self.check_call(cmd_stty)
self.check_call(cmd_flash)

View file

@ -0,0 +1,219 @@
#! /usr/bin/env python3
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
"""Zephyr binary runner core interfaces
This provides the core ZephyrBinaryRunner class meant for public use,
as well as some other helpers for concrete runner classes.
"""
import abc
import os
import platform
import pprint
import shlex
import signal
import subprocess
import sys
def get_env_or_bail(env_var):
'''Get an environment variable, or raise an error.
In case of KeyError, an error message is printed, along with the
environment, and the exception is re-raised.
'''
try:
return os.environ[env_var]
except KeyError:
print('Variable {} not in environment:'.format(
env_var), file=sys.stderr)
pprint.pprint(dict(os.environ), stream=sys.stderr)
raise
def get_env_bool_or(env_var, default_value):
'''Get an environment variable as a boolean, or return a default value.
Get an environment variable, interpret it as a base ten
integer, and convert that to a boolean.
In case the environment variable is not defined, return default_value.
'''
try:
return bool(int(os.environ[env_var]))
except KeyError:
return default_value
def get_env_strip_or(env_var, to_strip, default_value):
'''Get and clean up an environment variable, or return a default value.
Get the value of env_var from the environment. If it is
defined, return that value with to_strip stripped off. If it
is undefined, return default_value (without any stripping).
'''
value = os.environ.get(env_var, None)
if value is not None:
return value.strip(to_strip)
else:
return default_value
def quote_sh_list(cmd):
'''Transform a command from list into shell string form.'''
fmt = ' '.join('{}' for _ in cmd)
args = [shlex.quote(s) for s in cmd]
return fmt.format(*args)
class ZephyrBinaryRunner(abc.ABC):
'''Abstract superclass for binary runners (flashers, debuggers).
With some exceptions, boards supported by Zephyr must provide
generic means to be flashed (have a Zephyr firmware binary
permanently installed on the device for running) and debugged
(have a breakpoint debugger and program loader on a host
workstation attached to a running target). This is supported by
three top-level commands managed by the Zephyr build system:
- 'flash': flash a previously configured binary to the board,
start execution on the target, then return.
- 'debug': connect to the board via a debugging protocol, then
drop the user into a debugger interface with symbol tables
loaded from the current binary, and block until it exits.
- 'debugserver': connect via a board-specific debugging protocol,
then reset and halt the target. Ensure the user is now able to
connect to a debug server with symbol tables loaded from the
binary.
Runner functionality relies on a variety of target-specific tools
and configuration values, the user interface to which is
abstracted by this class. Each runner subclass should take any
values it needs to execute one of these commands in its
constructor. The actual command execution is handled in the run()
method.
This functionality is also replacing the legacy Zephyr runners,
which are shell scripts.
At present, the Zephyr build system uses a variety of
tool-specific environment variables to control runner behavior.
To support a transition to ZephyrBinaryRunner and subclasses, this
class provides a create_for_shell_script() static factory method.
This method iterates over ZephyrBinaryRUnner subclasses,
determines which (if any) can provide equivalent functionality to
the shell-based runner, and returns a subclass instance with its
configuration determined from the environment.
To support this, subclasess currently must provide a pair of
static methods, replaces_shell_script() and create_from_env(). The
first allows the runner subclass to declare which commands and
scripts it can replace. The second is called by
create_for_shell_script() to create a concrete runner instance.
The environment-based factories are for legacy use *only*; the
user must be able to construct and use a runner using only the
constructor and run() method.'''
def __init__(self, debug=False):
self.debug = debug
@staticmethod
def create_for_shell_script(shell_script, command, debug):
'''Factory for using as a drop-in replacement to a shell script.
Command is one of 'flash', 'debug', 'debugserver'.
Get runner instance to use in place of shell_script, deriving
configuration from the environment.'''
for sub_cls in ZephyrBinaryRunner.__subclasses__():
if sub_cls.replaces_shell_script(shell_script, command):
return sub_cls.create_from_env(command, debug)
raise ValueError('cannot implement script {} command {}'.format(
shell_script, command))
@staticmethod
@abc.abstractmethod
def replaces_shell_script(shell_script, command):
'''Check if this class replaces shell_script for the given command.'''
@staticmethod
@abc.abstractmethod
def create_from_env(command, debug):
'''Create new flasher instance from environment variables.
This class must be able to replace the current shell script
(FLASH_SCRIPT or DEBUG_SCRIPT, depending on command). The
environment variables expected by that script are used to build
the flasher in a backwards-compatible manner.'''
@abc.abstractmethod
def run(self, command, **kwargs):
'''Run a command ('flash', 'debug', 'debugserver').
In case of an unsupported command, raise a ValueError.'''
def run_server_and_client(self, server, client):
'''Run a server that ignores SIGINT, and a client that handles it.
This routine portably:
- creates a Popen object for the `server' command which ignores SIGINT
- runs `client' in a subprocess while temporarily ignoring SIGINT
- cleans up the server after the client exits.
It's useful to e.g. open a GDB server and client.'''
server_proc = self.popen_ignore_int(server)
previous = signal.signal(signal.SIGINT, signal.SIG_IGN)
try:
self.check_call(client)
finally:
signal.signal(signal.SIGINT, previous)
server_proc.terminate()
server_proc.wait()
def check_call(self, cmd):
'''Subclass subprocess.check_call() wrapper.
Subclasses should use this command to run command in a
subprocess and check that it executed correctly, rather than
using subprocess directly, to keep accurate debug logs.
'''
if self.debug:
print(quote_sh_list(cmd))
subprocess.check_call(cmd)
def check_output(self, cmd):
'''Subclass subprocess.check_output() wrapper.
Subclasses should use this command to run command in a
subprocess and check that it executed correctly, rather than
using subprocess directly, to keep accurate debug logs.
'''
if self.debug:
print(quote_sh_list(cmd))
return subprocess.check_output(cmd)
def popen_ignore_int(self, cmd):
'''Spawn a child command, ensuring it ignores SIGINT.
The returned subprocess.Popen object must be manually terminated.'''
cflags = 0
preexec = None
system = platform.system()
if system == 'Windows':
cflags |= subprocess.CREATE_NEW_PROCESS_GROUP
elif system in {'Linux', 'Darwin'}:
preexec = os.setsid
if self.debug:
print(quote_sh_list(cmd))
return subprocess.Popen(cmd, creationflags=cflags, preexec_fn=preexec)

View file

@ -0,0 +1,78 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for flashing with dfu-util.'''
import os
import sys
import time
from .core import ZephyrBinaryRunner, get_env_or_bail
class DfuUtilBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for dfu-util.'''
def __init__(self, pid, alt, img, dfuse=None, exe='dfu-util', debug=False):
super(DfuUtilBinaryRunner, self).__init__(debug=debug)
self.alt = alt
self.img = img
self.dfuse = dfuse
self.cmd = [exe, '-d,{}'.format(pid)]
try:
self.list_pattern = ', alt={},'.format(int(self.alt))
except ValueError:
self.list_pattern = ', name="{}",'.format(self.alt)
def replaces_shell_script(shell_script, command):
return command == 'flash' and shell_script == 'dfuutil.sh'
def create_from_env(command, debug):
'''Create flasher from environment.
Required:
- DFUUTIL_PID: USB VID:PID of the board
- DFUUTIL_ALT: interface alternate setting number or name
- DFUUTIL_IMG: binary to flash
Optional:
- DFUUTIL_DFUSE_ADDR: target address if the board is a
DfuSe device. Ignored if not present.
- DFUUTIL: dfu-util executable, defaults to dfu-util.
'''
pid = get_env_or_bail('DFUUTIL_PID')
alt = get_env_or_bail('DFUUTIL_ALT')
img = get_env_or_bail('DFUUTIL_IMG')
dfuse = os.environ.get('DFUUTIL_DFUSE_ADDR', None)
exe = os.environ.get('DFUUTIL', 'dfu-util')
return DfuUtilBinaryRunner(pid, alt, img, dfuse=dfuse, exe=exe,
debug=debug)
def find_device(self):
cmd = list(self.cmd) + ['-l']
output = self.check_output(cmd)
output = output.decode(sys.getdefaultencoding())
return self.list_pattern in output
def run(self, command, **kwargs):
if command != 'flash':
raise ValueError('only flash is supported')
reset = 0
if not self.find_device():
reset = 1
print('Please reset your board to switch to DFU mode...')
while not self.find_device():
time.sleep(0.1)
cmd = list(self.cmd)
if self.dfuse is not None:
cmd.extend(['-s', '{}:leave'.format(self.dfuse)])
cmd.extend(['-a', self.alt, '-D', self.img])
self.check_call(cmd)
if reset:
print('Now reset your board again to switch back to runtime mode.')

View file

@ -0,0 +1,88 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for flashing ESP32 devices with esptool/espidf.'''
from os import path
import os
from .core import ZephyrBinaryRunner, get_env_or_bail
class Esp32BinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for espidf.'''
def __init__(self, elf, device, baud=921600, flash_size='detect',
flash_freq='40m', flash_mode='dio', espidf='espidf',
debug=False):
super(Esp32BinaryRunner, self).__init__(debug=debug)
self.elf = elf
self.device = device
self.baud = baud
self.flash_size = flash_size
self.flash_freq = flash_freq
self.flash_mode = flash_mode
self.espidf = espidf
def replaces_shell_script(shell_script, command):
return command == 'flash' and shell_script == 'esp32.sh'
def create_from_env(command, debug):
'''Create flasher from environment.
Required:
- O: build output directory
- KERNEL_ELF_NAME: name of kernel binary in ELF format
Optional:
- ESP_DEVICE: serial port to flash, default /dev/ttyUSB0
- ESP_BAUD_RATE: serial baud rate, default 921600
- ESP_FLASH_SIZE: flash size, default 'detect'
- ESP_FLASH_FREQ: flash frequency, default '40m'
- ESP_FLASH_MODE: flash mode, default 'dio'
- ESP_TOOL: complete path to espidf, or set to 'espidf' to look for it
in $ESP_IDF_PATH/components/esptool_py/esptool/esptool.py
'''
elf = path.join(get_env_or_bail('O'),
get_env_or_bail('KERNEL_ELF_NAME'))
# TODO add sane device defaults on other platforms than Linux.
device = os.environ.get('ESP_DEVICE', '/dev/ttyUSB0')
baud = os.environ.get('ESP_BAUD_RATE', '921600')
flash_size = os.environ.get('ESP_FLASH_SIZE', 'detect')
flash_freq = os.environ.get('ESP_FLASH_FREQ', '40m')
flash_mode = os.environ.get('ESP_FLASH_MODE', 'dio')
espidf = os.environ.get('ESP_TOOL', 'espidf')
if espidf == 'espidf':
idf_path = get_env_or_bail('ESP_IDF_PATH')
espidf = path.join(idf_path, 'components', 'esptool_py', 'esptool',
'esptool.py')
return Esp32BinaryRunner(elf, device, baud=baud,
flash_size=flash_size, flash_freq=flash_freq,
flash_mode=flash_mode, espidf=espidf,
debug=debug)
def run(self, command, **kwargs):
if command != 'flash':
raise ValueError('only flash is supported')
bin_name = path.splitext(self.elf)[0] + path.extsep + 'bin'
cmd_convert = [self.espidf, '--chip', 'esp32', 'elf2image', self.elf]
cmd_flash = [self.espidf, '--chip', 'esp32', '--port', self.device,
'--baud', self.baud, '--before', 'default_reset',
'--after', 'hard_reset', 'write_flash', '-u',
'--flash_mode', self.flash_mode,
'--flash_freq', self.flash_freq,
'--flash_size', self.flash_size,
'0x1000', bin_name]
print("Converting ELF to BIN")
self.check_call(cmd_convert)
print("Flashing ESP32 on {} ({}bps)".format(self.device, self.baud))
self.check_call(cmd_flash)

View file

@ -0,0 +1,65 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for NIOS II.'''
from os import path
from .core import ZephyrBinaryRunner, get_env_or_bail
class Nios2BinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for NIOS II.'''
# From the original shell script:
#
# "XXX [flash] only support[s] cases where the .elf is sent
# over the JTAG and the CPU directly boots from __start. CONFIG_XIP
# and CONFIG_INCLUDE_RESET_VECTOR must be disabled."
def __init__(self, hex_, cpu_sof, zephyr_base, debug=False):
super(Nios2BinaryRunner, self).__init__(debug=debug)
self.hex_ = hex_
self.cpu_sof = cpu_sof
self.zephyr_base = zephyr_base
def replaces_shell_script(shell_script, command):
return command == 'flash' and shell_script == 'nios2.sh'
def create_from_env(command, debug):
'''Create runner from environment.
Required:
- O: build output directory
- KERNEL_HEX_NAME: name of kernel binary in HEX format
- NIOS2_CPU_SOF: location of the CPU .sof data
- ZEPHYR_BASE: zephyr Git repository base directory
'''
hex_ = path.join(get_env_or_bail('O'),
get_env_or_bail('KERNEL_HEX_NAME'))
cpu_sof = get_env_or_bail('NIOS2_CPU_SOF')
zephyr_base = get_env_or_bail('ZEPHYR_BASE')
return Nios2BinaryRunner(hex_, cpu_sof, zephyr_base, debug=debug)
def run(self, command, **kwargs):
if command not in {'flash', 'debug', 'debugserver'}:
raise ValueError('{} is not supported'.format(command))
if command == 'flash':
self.flash(**kwargs)
else:
self.debug_debugserver(command, **kwargs)
def flash(self, **kwargs):
cmd = [path.join(self.zephyr_base, 'scripts', 'support',
'quartus-flash.py'),
'--sof', self.cpu_sof,
'--kernel', self.hex_]
self.check_call(cmd)
def debug_debugserver(command, **kwargs):
raise NotImplementedError()

View file

@ -0,0 +1,96 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for flashing with nrfjprog.'''
from os import path
import sys
from .core import ZephyrBinaryRunner, get_env_or_bail
class NrfJprogBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for nrfjprog.'''
def __init__(self, hex_, family, board, debug=False):
super(NrfJprogBinaryRunner, self).__init__(debug=debug)
self.hex_ = hex_
self.family = family
self.board = board
def replaces_shell_script(shell_script, command):
return command == 'flash' and shell_script == 'nrf_flash.sh'
def create_from_env(command, debug):
'''Create flasher from environment.
Required:
- O: build output directory
- KERNEL_HEX_NAME: name of kernel binary in ELF format
- NRF_FAMILY: e.g. NRF51 or NRF52
- BOARD: Zephyr board name
'''
hex_ = path.join(get_env_or_bail('O'),
get_env_or_bail('KERNEL_HEX_NAME'))
family = get_env_or_bail('NRF_FAMILY')
board = get_env_or_bail('BOARD')
return NrfJprogBinaryRunner(hex_, family, board, debug=debug)
def get_board_snr_from_user(self):
snrs = self.check_output(['nrfjprog', '--ids'])
snrs = snrs.decode(sys.getdefaultencoding()).strip().splitlines()
if len(snrs) == 1:
return snrs[0]
print('There are multiple boards connected.')
for i, snr in enumerate(snrs, 1):
print('{}. {}'.format(i, snr))
p = 'Please select one with desired serial number (1-{}): '.format(
len(snrs))
while True:
value = input(p)
try:
value = int(value)
except ValueError:
continue
if 1 <= value <= len(snrs):
break
return snrs[value - 1]
def run(self, command, **kwargs):
if command != 'flash':
raise ValueError('only flash is supported')
board_snr = self.get_board_snr_from_user()
print('Flashing file: {}'.format(self.hex_))
commands = [
['nrfjprog', '--eraseall', '-f', self.family, '--snr', board_snr],
['nrfjprog', '--program', self.hex_, '-f', self.family, '--snr',
board_snr],
]
if self.family == 'NRF52':
commands.extend([
# Set reset pin
['nrfjprog', '--memwr', '0x10001200', '--val', '0x00000015',
'-f', self.family, '--snr', board_snr],
['nrfjprog', '--memwr', '0x10001204', '--val', '0x00000015',
'-f', self.family, '--snr', board_snr],
['nrfjprog', '--reset', '-f', self.family, '--snr', board_snr],
])
commands.append(['nrfjprog',
'--pinreset',
'-f', self.family,
'--snr', board_snr])
for cmd in commands:
self.check_call(cmd)
print('{} Serial Number {} flashed with success.'.format(
self.board, board_snr))

View file

@ -0,0 +1,209 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for openocd.'''
from os import path
import os
import shlex
from .core import ZephyrBinaryRunner, get_env_or_bail, get_env_strip_or
DEFAULT_OPENOCD_TCL_PORT = 6333
DEFAULT_OPENOCD_TELNET_PORT = 4444
DEFAULT_OPENOCD_GDB_PORT = 3333
class OpenOcdBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for openocd.'''
def __init__(self, openocd_config,
openocd='openocd', default_path=None,
bin_name=None, elf_name=None,
load_cmd=None, verify_cmd=None, pre_cmd=None, post_cmd=None,
extra_init=None,
tcl_port=DEFAULT_OPENOCD_TCL_PORT,
telnet_port=DEFAULT_OPENOCD_TELNET_PORT,
gdb_port=DEFAULT_OPENOCD_GDB_PORT,
gdb=None, tui=None, debug=False):
super(OpenOcdBinaryRunner, self).__init__(debug=debug)
self.openocd_config = openocd_config
search_args = []
if default_path is not None:
search_args = ['-s', default_path]
self.openocd_cmd = [openocd] + search_args
self.bin_name = bin_name
self.elf_name = elf_name
self.load_cmd = load_cmd
self.verify_cmd = verify_cmd
self.pre_cmd = pre_cmd
self.post_cmd = post_cmd
self.extra_init = extra_init if extra_init is not None else []
self.tcl_port = tcl_port
self.telnet_port = telnet_port
self.gdb_port = gdb_port
self.gdb_cmd = [gdb] if gdb is not None else None
self.tui_arg = [tui] if tui is not None else []
def replaces_shell_script(shell_script, command):
return (command in {'flash', 'debug', 'debugserver'} and
shell_script == 'openocd.sh')
def create_from_env(command, debug):
'''Create runner from environment.
Required:
- ZEPHYR_BASE: zephyr Git repository base directory
- ARCH: board architecture
- BOARD_NAME: zephyr name of board
Optional:
- OPENOCD: path to openocd, defaults to openocd
- OPENOCD_DEFAULT_PATH: openocd search path to use
Required for 'flash':
- O: build output directory
- KERNEL_BIN_NAME: zephyr kernel binary
- OPENOCD_LOAD_CMD: command to load binary into flash
- OPENOCD_VERIFY_CMD: command to verify flash executed correctly
Optional for 'flash':
- OPENOCD_PRE_CMD: command to run before any others
- OPENOCD_POST_CMD: command to run after verifying flash write
Required for 'debug':
- GDB: GDB executable
- O: build output directory
- KERNEL_ELF_NAME: zephyr kernel binary, ELF format
Optional for 'debug':
- TUI: one additional argument to GDB (e.g. -tui)
- OPENOCD_EXTRA_INIT: additional arguments to pass to openocd
- TCL_PORT: openocd TCL port, defaults to 6333
- TELNET_PORT: openocd telnet port, defaults to 4444
- GDB_PORT: openocd gdb port, defaults to 3333
'''
zephyr_base = get_env_or_bail('ZEPHYR_BASE')
arch = get_env_or_bail('ARCH')
board_name = get_env_or_bail('BOARD_NAME')
openocd_config = path.join(zephyr_base, 'boards', arch,
board_name, 'support', 'openocd.cfg')
openocd = os.environ.get('OPENOCD', 'openocd')
default_path = os.environ.get('OPENOCD_DEFAULT_PATH', None)
o = os.environ.get('O', None)
bin_ = os.environ.get('KERNEL_BIN_NAME', None)
elf = os.environ.get('KERNEL_ELF_NAME', None)
bin_name = None
elf_name = None
if o is not None:
if bin_ is not None:
bin_name = path.join(o, bin_)
if elf is not None:
elf_name = path.join(o, elf)
load_cmd = get_env_strip_or('OPENOCD_LOAD_CMD', '"', None)
verify_cmd = get_env_strip_or('OPENOCD_VERIFY_CMD', '"', None)
pre_cmd = get_env_strip_or('OPENOCD_PRE_CMD', '"', None)
post_cmd = get_env_strip_or('OPENOCD_POST_CMD', '"', None)
gdb = os.environ.get('GDB', None)
tui = os.environ.get('TUI', None)
extra_init = os.environ.get('OPENOCD_EXTRA_INIT', None)
if extra_init is not None:
extra_init = shlex.split(extra_init)
tcl_port = int(os.environ.get('TCL_PORT',
str(DEFAULT_OPENOCD_TCL_PORT)))
telnet_port = int(os.environ.get('TELNET_PORT',
str(DEFAULT_OPENOCD_TELNET_PORT)))
gdb_port = int(os.environ.get('GDB_PORT',
str(DEFAULT_OPENOCD_GDB_PORT)))
return OpenOcdBinaryRunner(openocd_config,
openocd=openocd, default_path=default_path,
bin_name=bin_name, elf_name=elf_name,
load_cmd=load_cmd, verify_cmd=verify_cmd,
pre_cmd=pre_cmd, post_cmd=post_cmd,
extra_init=extra_init, tcl_port=tcl_port,
telnet_port=telnet_port, gdb_port=gdb_port,
gdb=gdb, tui=tui, debug=debug)
def run(self, command, **kwargs):
if command not in {'flash', 'debug', 'debugserver'}:
raise ValueError('{} is not supported'.format(command))
if command == 'flash':
self.do_flash(**kwargs)
elif command == 'debug':
self.do_debug(**kwargs)
else:
self.do_debugserver(**kwargs)
def do_flash(self, **kwargs):
if self.bin_name is None:
raise ValueError('Cannot flash; binary name is missing')
if self.load_cmd is None:
raise ValueError('Cannot flash; load command is missing')
if self.verify_cmd is None:
raise ValueError('Cannot flash; verify command is missing')
pre_cmd = []
if self.pre_cmd is not None:
pre_cmd = ['-c', self.pre_cmd]
post_cmd = []
if self.post_cmd is not None:
post_cmd = ['-c', self.post_cmd]
cmd = (self.openocd_cmd +
['-f', self.openocd_config,
'-c', 'init',
'-c', 'targets'] +
pre_cmd +
['-c', 'reset halt',
'-c', self.load_cmd,
'-c', 'reset halt',
'-c', self.verify_cmd] +
post_cmd +
['-c', 'reset run',
'-c', 'shutdown'])
self.check_call(cmd)
def do_debug(self, **kwargs):
if self.gdb_cmd is None:
raise ValueError('Cannot debug; no gdb specified')
if self.elf_name is None:
raise ValueError('Cannot debug; no .elf specified')
server_cmd = (self.openocd_cmd +
['-f', self.openocd_config] +
self.extra_init +
['-c', 'tcl_port {}'.format(self.tcl_port),
'-c', 'telnet_port {}'.format(self.telnet_port),
'-c', 'gdb_port {}'.format(self.gdb_port),
'-c', 'init',
'-c', 'targets',
'-c', 'halt'])
gdb_cmd = (self.gdb_cmd + self.tui_arg +
['-ex', 'target remote :{}'.format(self.gdb_port),
self.elf_name])
self.run_server_and_client(server_cmd, gdb_cmd)
def do_debugserver(self, **kwargs):
cmd = (self.openocd_cmd +
['-f', self.openocd_config,
'-c', 'init',
'-c', 'targets',
'-c', 'reset halt'])
self.check_call(cmd)

View file

@ -0,0 +1,157 @@
# Copyright (c) 2017 Linaro Limited.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for pyOCD .'''
from os import path
import os
from .core import ZephyrBinaryRunner, get_env_or_bail
DEFAULT_PYOCD_GDB_PORT = 3333
class PyOcdBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for pyocd-flashtool.'''
def __init__(self, target, flashtool='pyocd-flashtool',
gdb=None, gdbserver='pyocd-gdbserver',
gdb_port=DEFAULT_PYOCD_GDB_PORT, tui=None,
bin_name=None, elf_name=None,
board_id=None, daparg=None, debug=False):
super(PyOcdBinaryRunner, self).__init__(debug=debug)
self.target_args = ['-t', target]
self.flashtool = flashtool
self.gdb_cmd = [gdb] if gdb is not None else None
self.gdbserver = gdbserver
self.gdb_port = gdb_port
self.tui_args = [tui] if tui is not None else []
self.bin_name = bin_name
self.elf_name = elf_name
board_args = []
if board_id is not None:
board_args = ['-b', board_id]
self.board_args = board_args
daparg_args = []
if daparg is not None:
daparg_args = ['-da', daparg]
self.daparg_args = daparg_args
def replaces_shell_script(shell_script, command):
return (command in {'flash', 'debug', 'debugserver'} and
shell_script == 'pyocd.sh')
def port_args(self):
return ['-p', str(self.gdb_port)]
def create_from_env(command, debug):
'''Create runner from environment.
Required:
- PYOCD_TARGET: target override
Optional:
- PYOCD_DAPARG: arguments to pass to pyocd tool, default is none
- PYOCD_BOARD_ID: ID of board to flash, default is to prompt
Required for 'flash':
- O: build output directory
- KERNEL_BIN_NAME: name of kernel binary
Optional for 'flash':
- PYOCD_FLASHTOOL: flash tool path, defaults to pyocd-flashtool
Required for 'debug':
- O: build output directory
- KERNEL_ELF_NAME
- GDB: gdb executable
Optional for 'debug', 'debugserver':
- TUI: one additional argument to GDB (e.g. -tui)
- GDB_PORT: pyocd gdb port, defaults to 3333
- PYOCD_GDBSERVER: gdb server executable, defaults to pyocd-gdbserver
'''
target = get_env_or_bail('PYOCD_TARGET')
o = os.environ.get('O', None)
bin_ = os.environ.get('KERNEL_BIN_NAME', None)
elf = os.environ.get('KERNEL_ELF_NAME', None)
bin_name = None
elf_name = None
if o is not None:
if bin_ is not None:
bin_name = path.join(o, bin_)
if elf is not None:
elf_name = path.join(o, elf)
flashtool = os.environ.get('PYOCD_FLASHTOOL', 'pyocd-flashtool')
board_id = os.environ.get('PYOCD_BOARD_ID', None)
daparg = os.environ.get('PYOCD_DAPARG', None)
gdb = os.environ.get('GDB', None)
gdbserver = os.environ.get('PYOCD_GDBSERVER', 'pyocd-gdbserver')
gdb_port = os.environ.get('GDB_PORT', DEFAULT_PYOCD_GDB_PORT)
tui = os.environ.get('TUI', None)
return PyOcdBinaryRunner(target, flashtool=flashtool, gdb=gdb,
gdbserver=gdbserver, gdb_port=gdb_port,
tui=tui, bin_name=bin_name, elf_name=elf_name,
board_id=board_id, daparg=daparg, debug=debug)
def run(self, command, **kwargs):
if command not in {'flash', 'debug', 'debugserver'}:
raise ValueError('{} is not supported'.format(command))
if command == 'flash':
self.flash(**kwargs)
else:
self.debug_debugserver(command, **kwargs)
def flash(self, **kwargs):
if self.bin_name is None:
raise ValueError('Cannot flash; bin_name is missing')
cmd = ([self.flashtool] +
self.daparg_args +
self.target_args +
self.board_args +
[self.bin_name])
print('Flashing Target Device')
self.check_call(cmd)
def print_gdbserver_message(self):
print('pyOCD GDB server running on port {}'.format(self.gdb_port))
def debug_debugserver(self, command, **kwargs):
server_cmd = ([self.gdbserver] +
self.daparg_args +
self.port_args() +
self.target_args +
self.board_args)
if command == 'debugserver':
self.print_gdbserver_message()
self.check_call(server_cmd)
else:
if self.gdb_cmd is None:
raise ValueError('Cannot debug; gdb is missing')
if self.elf_name is None:
raise ValueError('Cannot debug; elf is missing')
client_cmd = (self.gdb_cmd +
self.tui_args +
[self.elf_name] +
['-ex', 'target remote :{}'.format(self.gdb_port),
'-ex', 'load',
'-ex', 'monitor reset halt'])
self.print_gdbserver_message()
self.run_server_and_client(server_cmd, client_cmd)

File diff suppressed because it is too large Load diff