mirror of
https://github.com/wagiminator/ATtiny814-USB-PD-Adapter.git
synced 2025-08-16 13:27:03 +03:00
Initial commit
This commit is contained in:
561
software/tools/pymcuprog/libs/pymcuprog/pymcuprog_main.py
Normal file
561
software/tools/pymcuprog/libs/pymcuprog/pymcuprog_main.py
Normal file
@@ -0,0 +1,561 @@
|
||||
"""
|
||||
Python MCU programmer, CLI main program
|
||||
"""
|
||||
# Python 3 compatibility for Python 2
|
||||
from __future__ import print_function
|
||||
|
||||
# utils
|
||||
import time
|
||||
import os
|
||||
from copy import copy
|
||||
|
||||
from .backend import Backend, SessionConfig
|
||||
from .toolconnection import ToolUsbHidConnection, ToolSerialConnection
|
||||
from .deviceinfo.memorynames import MemoryNameAliases, MemoryNames
|
||||
from .deviceinfo.eraseflags import ChiperaseEffect
|
||||
from .deviceinfo.deviceinfo import get_supported_devices
|
||||
from .deviceinfo.deviceinfokeys import DeviceMemoryInfoKeys
|
||||
|
||||
from .utils import print_tool_info, showdata, verify_flash_from_bin, compare
|
||||
from .hexfileutils import write_memories_to_hex, write_memory_to_hex, read_memories_from_hex, verify_flash_from_hex
|
||||
from .pymcuprog_errors import PymcuprogNotSupportedError, PymcuprogSessionConfigError, \
|
||||
PymcuprogToolConnectionError, PymcuprogDeviceLockedError
|
||||
|
||||
try:
|
||||
from .version import VERSION, BUILD_DATE, COMMIT_ID
|
||||
except ImportError:
|
||||
VERSION = "0.0.0"
|
||||
COMMIT_ID = "N/A"
|
||||
BUILD_DATE = "N/A"
|
||||
|
||||
STATUS_SUCCESS = 0
|
||||
STATUS_FAILURE = 1
|
||||
STATUS_FAILURE_LOCKED = 2
|
||||
|
||||
# Only include memories that can be written when writing memories to hex file
|
||||
WRITE_TO_HEX_MEMORIES = [MemoryNames.EEPROM, MemoryNames.FLASH, MemoryNames.FUSES, MemoryNames.CONFIG_WORD]
|
||||
|
||||
|
||||
def pymcuprog(args):
|
||||
"""
|
||||
Main program
|
||||
"""
|
||||
if args.version or args.release_info:
|
||||
print("pymcuprog version {}".format(VERSION))
|
||||
if args.release_info:
|
||||
print("Build date: {}".format(BUILD_DATE))
|
||||
print("Commit ID: {}".format(COMMIT_ID))
|
||||
return STATUS_SUCCESS
|
||||
|
||||
backend = Backend()
|
||||
|
||||
toolconnection = _setup_tool_connection(args)
|
||||
|
||||
try:
|
||||
backend.connect_to_tool(toolconnection)
|
||||
except PymcuprogToolConnectionError as error:
|
||||
print(error)
|
||||
return STATUS_FAILURE
|
||||
|
||||
status = None
|
||||
if args.tool not in ['uart']:
|
||||
# This section can initialise all features requiring non-UART transports
|
||||
|
||||
# DAP info only available on native CMSIS-DAP
|
||||
dap_info = backend.read_tool_info()
|
||||
print_tool_info(dap_info)
|
||||
|
||||
# Targetless actions, only available on HID tools
|
||||
status = _debugger_actions(backend, args)
|
||||
|
||||
if status is not None:
|
||||
backend.disconnect_from_tool()
|
||||
return status
|
||||
|
||||
device_selected = _select_target_device(backend, args)
|
||||
if device_selected is None:
|
||||
backend.disconnect_from_tool()
|
||||
return STATUS_FAILURE
|
||||
|
||||
status = _start_session(backend, device_selected, args)
|
||||
if status != STATUS_SUCCESS:
|
||||
backend.disconnect_from_tool()
|
||||
return status
|
||||
|
||||
# -x timer argument
|
||||
time_start = None
|
||||
if args.timing:
|
||||
print("Starting timer")
|
||||
time_start = time.time()
|
||||
|
||||
_programming_actions(backend, args)
|
||||
|
||||
backend.end_session()
|
||||
backend.disconnect_from_tool()
|
||||
if args.timing:
|
||||
time_stop = time.time()
|
||||
print("Operation took {0:.03f}s".format(time_stop - time_start))
|
||||
|
||||
print("Done.")
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def _action_getvoltage(backend):
|
||||
voltage = backend.read_target_voltage()
|
||||
print("Measured voltage: {0:0.2f}V".format(voltage))
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_getsupplyvoltage(backend):
|
||||
voltage = backend.read_supply_voltage_setpoint()
|
||||
print("Supply voltage set to {0:0.2f}V".format(voltage))
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_getusbvoltage(backend):
|
||||
voltage = backend.read_usb_voltage()
|
||||
print("USB voltage is {0:0.2f}V".format(voltage))
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_setsupplyvoltage(backend, literal):
|
||||
voltage = backend.read_supply_voltage_setpoint()
|
||||
print("Supply voltage is currently set to {0:0.2f}V".format(voltage))
|
||||
if literal is None:
|
||||
print("Specify voltage in Volts using -l <literal>")
|
||||
else:
|
||||
setvoltage = literal[0]
|
||||
if setvoltage == voltage:
|
||||
print("Voltage is already right where you want it.")
|
||||
else:
|
||||
print("Setting supply voltage to {0:0.2f}V".format(setvoltage))
|
||||
backend.set_supply_voltage_setpoint(setvoltage)
|
||||
voltage = backend.read_supply_voltage_setpoint()
|
||||
print("Supply voltage is now set to {0:0.2f}V".format(voltage))
|
||||
|
||||
# Static delay to let the target voltage settle before reading it out
|
||||
# Alternatively a retry loop could be used, but it is difficult to know when to terminate
|
||||
# the loop as sometimes the final voltage is not known, for example if setting the voltage
|
||||
# to 5.5V the actual voltage will depend upon the USB voltage. If the USB voltage is only
|
||||
# 4.9V the target voltage will never reach more than 4.9V
|
||||
time.sleep(0.5)
|
||||
voltage = backend.read_target_voltage()
|
||||
print("Measured voltage: {0:0.2f}V".format(voltage))
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_reboot_debugger(backend):
|
||||
print("Rebooting tool...")
|
||||
backend.reboot_tool()
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_ping(backend):
|
||||
print("Pinging device...")
|
||||
response = backend.read_device_id()
|
||||
idstring = ''
|
||||
for idbyte in response:
|
||||
idstring = '{:02X}'.format(idbyte) + idstring
|
||||
print("Ping response: {}".format(idstring))
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_erase(backend, args):
|
||||
if args.memory is None or args.memory == MemoryNameAliases.ALL:
|
||||
print("Chip/Bulk erase,")
|
||||
for memname in MemoryNames.get_all():
|
||||
try:
|
||||
effect = backend.get_chiperase_effect(memname)
|
||||
except ValueError:
|
||||
# This memory type does not exist for this device, just continue
|
||||
continue
|
||||
else:
|
||||
if effect != ChiperaseEffect.NOT_ERASED:
|
||||
print("Memory type {} is {}".format(memname, effect))
|
||||
print("...")
|
||||
else:
|
||||
if backend.is_isolated_erase_possible(args.memory):
|
||||
print("Erasing {}...".format(args.memory))
|
||||
else:
|
||||
print("ERROR: {} memory can't be erased or "
|
||||
"can't be erased without affecting other memories".format(args.memory))
|
||||
chiperase_effect = backend.get_chiperase_effect(args.memory)
|
||||
if chiperase_effect != ChiperaseEffect.NOT_ERASED:
|
||||
print("{} memory is {} by a chip/bulk erase".format(args.memory, chiperase_effect))
|
||||
print("Use erase without -m option to erase this memory")
|
||||
return STATUS_FAILURE
|
||||
|
||||
backend.erase(args.memory, address=None)
|
||||
print("Erased.")
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_read(backend, args):
|
||||
# Reading with bytes argument requires that memory type is specified
|
||||
if args.bytes != 0 and args.memory == MemoryNameAliases.ALL:
|
||||
print("Memory area must be specified when number of bytes is specified.")
|
||||
return STATUS_FAILURE
|
||||
|
||||
print("Reading...")
|
||||
result = backend.read_memory(args.memory, args.offset, args.bytes, args.max_chunk_size)
|
||||
|
||||
# If a filename is specified, write to it
|
||||
hexfile = False
|
||||
binary = False
|
||||
filepath = None
|
||||
if args.filename is not None:
|
||||
filepath = os.path.normpath(args.filename)
|
||||
prefix, postfix = _get_file_prefix_and_postfix(filepath)
|
||||
# If it ends in hex, use intel hex format, else binary
|
||||
if postfix == 'hex':
|
||||
hexfile = True
|
||||
else:
|
||||
binary = True
|
||||
|
||||
# Print the data or save it to a file
|
||||
if hexfile:
|
||||
if args.memory == MemoryNameAliases.ALL:
|
||||
# Only memories that can be written should go into the hex file
|
||||
result_to_write = _extract_writeable_memories(result)
|
||||
write_memories_to_hex(filepath, result_to_write)
|
||||
else:
|
||||
write_memory_to_hex(filepath, result[0], args.offset)
|
||||
print("Data written to hex file: '{0:s}'".format(filepath))
|
||||
elif binary:
|
||||
for item in result:
|
||||
memory_name = item.memory_info[DeviceMemoryInfoKeys.NAME]
|
||||
data = item.data
|
||||
filepath = "{}_{}.{}".format(prefix, memory_name, postfix)
|
||||
# Binary files does not have addressing, and needs a split on memory type
|
||||
with open(filepath, "wb") as binfile:
|
||||
binfile.write(data)
|
||||
print("Data written to binary file: '{0:s}'".format(filepath))
|
||||
else:
|
||||
for item in result:
|
||||
memory_info = item.memory_info
|
||||
print("Memory type: {}".format(memory_info[DeviceMemoryInfoKeys.NAME]))
|
||||
showdata(item.data,
|
||||
args.offset + memory_info[DeviceMemoryInfoKeys.ADDRESS],
|
||||
memory_info[DeviceMemoryInfoKeys.PAGE_SIZE])
|
||||
print("\n")
|
||||
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _action_verify(backend, args):
|
||||
hexfile = False
|
||||
binary = False
|
||||
literal = False
|
||||
filepath = None
|
||||
if args.filename is not None:
|
||||
filepath = os.path.normpath(args.filename)
|
||||
_, postfix = _get_file_prefix_and_postfix(filepath)
|
||||
# If it ends in hex, use intel hex format, else binary
|
||||
if postfix == 'hex':
|
||||
hexfile = True
|
||||
else:
|
||||
binary = True
|
||||
if args.literal is not None:
|
||||
literal = True
|
||||
if args.filename is not None:
|
||||
print("Both file and literal value was specified. Literal verify will be ignored in favor of file verify")
|
||||
literal = False
|
||||
|
||||
if hexfile:
|
||||
print("Verifying...")
|
||||
verify_status = verify_flash_from_hex(args.filename, backend, max_read_chunk=args.max_read_chunk)
|
||||
if verify_status is True:
|
||||
print("Verify successful. Data in flash matches data in specified hex-file")
|
||||
elif binary:
|
||||
print("Verifying...")
|
||||
verify_status = verify_flash_from_bin(args.filename, backend, args.offset, max_read_chunk=args.max_read_chunk)
|
||||
if verify_status is True:
|
||||
print("Verify successful. Data in flash matches data in specified bin-file")
|
||||
elif literal:
|
||||
print("Verifying...")
|
||||
flash_data = backend.read_memory('flash', args.offset, len(args.literal), max_read_chunk=args.max_read_chunk)[0].data
|
||||
compare(flash_data, args.literal, args.offset)
|
||||
print("Verify successful. Data in flash matches literal data specified")
|
||||
else:
|
||||
raise Exception('No file or literal specified for verify')
|
||||
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _get_file_prefix_and_postfix(filepath):
|
||||
"""
|
||||
Get file prefix and postfix from the filepath
|
||||
|
||||
If the file name in the filepath has not file extension the file is supposed to be a binary file
|
||||
:param filepath: File name and full path
|
||||
:return: prefix, postfix
|
||||
"""
|
||||
prefix = filepath.split('.')[0]
|
||||
postfix = filepath.split('.')[-1].lower()
|
||||
# If no "." is found in the filepath
|
||||
if postfix == prefix:
|
||||
postfix = "bin"
|
||||
|
||||
return prefix, postfix
|
||||
|
||||
|
||||
def _extract_writeable_memories(memory_segments):
|
||||
"""
|
||||
Take a list of memory segments and return the segments that can be written
|
||||
|
||||
:param memory_segments: List of namedtuples with two fields: data and memory_info. data contains a byte array of
|
||||
raw data bytes and memory_info is a dictionary with memory information (as defined in
|
||||
deviceinfo.deviceinfo.DeviceMemoryInfo).
|
||||
:return: List of namedtuples (a subset of the memory_segments input parameter) only containing memory segments
|
||||
that can be written
|
||||
"""
|
||||
writeable_segments = []
|
||||
for segment in memory_segments:
|
||||
if segment.memory_info[DeviceMemoryInfoKeys.NAME] in WRITE_TO_HEX_MEMORIES:
|
||||
writeable_segments.append(segment)
|
||||
return writeable_segments
|
||||
|
||||
|
||||
def _action_write(backend, args):
|
||||
# If a filename is specified, read from it
|
||||
if args.filename is not None:
|
||||
filepath = os.path.normpath(args.filename)
|
||||
_, postfix = _get_file_prefix_and_postfix(filepath)
|
||||
# If it ends in hex, use intel hex format, else binary
|
||||
if postfix == 'hex':
|
||||
# Hexfiles contain addressing information that cannot be remapped, so offset/memory are not allowed here
|
||||
if args.offset:
|
||||
print("Offset cannot be specified when writing hex file")
|
||||
return STATUS_FAILURE
|
||||
|
||||
if args.memory != MemoryNameAliases.ALL:
|
||||
print("Memory area cannot be specified when writing hex file")
|
||||
return STATUS_FAILURE
|
||||
|
||||
result = read_memories_from_hex(args.filename, backend.device_memory_info)
|
||||
|
||||
print("Writing from hex file...")
|
||||
|
||||
_write_memory_segments(backend, result, args.verify, blocksize=args.blocksize, pagewrite_delay=args.pagewrite_delay)
|
||||
else:
|
||||
with open(filepath, "rb") as binfile:
|
||||
data_from_file = bytearray(binfile.read())
|
||||
|
||||
# Prepare and write data
|
||||
print("Writing from binary file...")
|
||||
# When writing data to target the data might be pagealigned so we make a copy to avoid verifying
|
||||
# more than needed (in case verify option is enabled)
|
||||
data_to_write = copy(data_from_file)
|
||||
backend.write_memory(data_to_write, args.memory, args.offset)
|
||||
if args.verify:
|
||||
print("Verifying from binary file...")
|
||||
# Verify content, an exception is thrown on mismatch
|
||||
backend.verify_memory(data_from_file, args.memory, args.offset)
|
||||
elif args.literal:
|
||||
# Prepare and write data
|
||||
print("Writing literal values...")
|
||||
backend.write_memory(bytearray(args.literal), args.memory, args.offset)
|
||||
if args.verify:
|
||||
print("Verifying literal values...")
|
||||
# Verify content, an exception is thrown on mismatch
|
||||
backend.verify_memory(bytearray(args.literal), args.memory, args.offset)
|
||||
else:
|
||||
print("Error: for writing use either -f <file> or -l <literal>")
|
||||
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _write_memory_segments(backend, memory_segments, verify, blocksize = 0, pagewrite_delay=0):
|
||||
"""
|
||||
Write content of list of memory segments
|
||||
|
||||
:param backend: pymcuprog Backend instance
|
||||
:param memory_segments: List of namedtuples with two fields: data and memory_info. data contains a byte array of
|
||||
raw data bytes and memory_info is a dictionary with memory information (as defined in
|
||||
deviceinfo.deviceinfo.DeviceMemoryInfo).
|
||||
:param verify: If True verify the written data by reading it back and compare
|
||||
:param blocksize: this is a signal to write_memory for updiserial when writing flash; if 0 or not supplied
|
||||
do not use blocks (equivalent to blocksize == 2 bytes or 1 word). If -1, it will set tje blocksize to
|
||||
the page size of the target chip, which can imcrease write speed more than 10:1. Any other number will
|
||||
be used as supplied. Even numbers up to the page size are recommended.
|
||||
Any other negative number is invalid, and is zero'ed out.
|
||||
"""
|
||||
for segment in memory_segments:
|
||||
memory_name = segment.memory_info[DeviceMemoryInfoKeys.NAME]
|
||||
print("Writing {}...".format(memory_name))
|
||||
backend.write_memory(segment.data, memory_name, segment.offset, blocksize=blocksize, pagewrite_delay=pagewrite_delay)
|
||||
if verify:
|
||||
print("Verifying {}...".format(memory_name))
|
||||
verify_ok = backend.verify_memory(segment.data, memory_name, segment.offset)
|
||||
if verify_ok:
|
||||
print("OK")
|
||||
else:
|
||||
print("Verification failed!")
|
||||
|
||||
|
||||
def _action_reset(backend):
|
||||
backend.hold_in_reset()
|
||||
# Wait a bit to make sure the device has entered reset
|
||||
# If needed this sleep could be made configurable by a CLI parameter,
|
||||
# but for now a hardcoded value is assumed to be sufficient
|
||||
time.sleep(0.1)
|
||||
backend.release_from_reset()
|
||||
return STATUS_SUCCESS
|
||||
|
||||
|
||||
def _debugger_actions(backend, args):
|
||||
"""
|
||||
Debugger related actions
|
||||
|
||||
Targetless actions only involving the debugger. Only available on HID tools
|
||||
"""
|
||||
status = None
|
||||
if args.action == 'getvoltage':
|
||||
status = _action_getvoltage(backend)
|
||||
if args.action == 'getsupplyvoltage':
|
||||
status = _action_getsupplyvoltage(backend)
|
||||
if args.action == 'getusbvoltage':
|
||||
status = _action_getusbvoltage(backend)
|
||||
if args.action == 'setsupplyvoltage':
|
||||
status = _action_setsupplyvoltage(backend, args.literal)
|
||||
if args.action == 'reboot-debugger':
|
||||
status = _action_reboot_debugger(backend)
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def _programming_actions(backend, args):
|
||||
status = None
|
||||
# Ping: checks that the device is there by reading its ID, or equivalent
|
||||
if args.action == "ping":
|
||||
status = _action_ping(backend)
|
||||
# Erase: perform a full chip erase, or memtype-only erase if specified
|
||||
elif args.action == "erase":
|
||||
status = _action_erase(backend, args)
|
||||
# Reading data:
|
||||
elif args.action == "read":
|
||||
status = _action_read(backend, args)
|
||||
elif args.action == "write":
|
||||
status = _action_write(backend, args)
|
||||
elif args.action == "reset":
|
||||
status = _action_reset(backend)
|
||||
elif args.action == "verify":
|
||||
status = _action_verify(backend, args)
|
||||
else:
|
||||
print("Unknown command '{0:s}'".format(args.action))
|
||||
status = STATUS_FAILURE
|
||||
|
||||
return status
|
||||
|
||||
|
||||
def _setup_tool_connection(args):
|
||||
toolconnection = None
|
||||
|
||||
# Parse the requested tool from the CLI
|
||||
if args.tool == "uart":
|
||||
# Embedded GPIO/UART tool (eg: raspberry pi) => no USB connection
|
||||
toolconnection = ToolSerialConnection(serialport=args.uart)
|
||||
else:
|
||||
usb_serial = args.serialnumber
|
||||
product = args.tool
|
||||
if usb_serial and product:
|
||||
print("Connecting to {0:s} ({1:s})'".format(product, usb_serial))
|
||||
else:
|
||||
if usb_serial:
|
||||
print("Connecting to any tool with USB serial number '{0:s}'".format(usb_serial))
|
||||
elif product:
|
||||
print("Connecting to any {0:s}".format(product))
|
||||
else:
|
||||
print("Connecting to anything possible")
|
||||
toolconnection = ToolUsbHidConnection(serialnumber=usb_serial, tool_name=product)
|
||||
|
||||
return toolconnection
|
||||
|
||||
|
||||
def _select_target_device(backend, args):
|
||||
device_mounted = None
|
||||
device_selected = None
|
||||
if args.tool not in ['uart']:
|
||||
# Find out from the board (kit) if a device is mounted
|
||||
device_mounted = backend.read_kit_device()
|
||||
if device_mounted is not None:
|
||||
device_mounted = device_mounted.lower()
|
||||
print("Device mounted: '{0:s}'".format(device_mounted))
|
||||
|
||||
# Parse device field. If unspecified, use the board's device
|
||||
if args.device:
|
||||
device_selected = args.device.lower()
|
||||
else:
|
||||
if device_mounted is None:
|
||||
print("Unable to determine on-board target! Please specify device using -d <device>")
|
||||
else:
|
||||
print("No device specified. Using on-board target ({0:s})".format(device_mounted))
|
||||
device_selected = device_mounted
|
||||
|
||||
# Mismatch. Allow user to proceed at own risk.
|
||||
if device_mounted is not None and device_selected != device_mounted:
|
||||
print("Warning: you are attempting to use a device which is not the one which was mounted on the kit!")
|
||||
print("Cut all straps between the debugger and the on-board target when accessing an external device!")
|
||||
|
||||
return device_selected
|
||||
|
||||
|
||||
def _start_session(backend, device, args):
|
||||
"""
|
||||
Setup the session and try to build the stack for this device
|
||||
"""
|
||||
sessionconfig = SessionConfig(device)
|
||||
|
||||
# -c clock argument
|
||||
# allow Hz, or kHz ending in 'k' (eg: 100k) or MHz ending in 'M' eg (1M)
|
||||
if args.clk:
|
||||
if args.clk[-1] == 'k':
|
||||
clk = int(args.clk.strip('k')) * 1000
|
||||
elif args.clk[-1] == 'M':
|
||||
clk = int(args.clk.strip('M')) * 1000000
|
||||
else:
|
||||
clk = int(args.clk)
|
||||
|
||||
sessionconfig.interface_speed = clk
|
||||
|
||||
# Translate args into "special_options" to pass down the stack
|
||||
sessionconfig.special_options = {}
|
||||
if args.high_voltage:
|
||||
sessionconfig.special_options['high-voltage'] = args.high_voltage
|
||||
if args.user_row_locked_device:
|
||||
sessionconfig.special_options['user-row-locked-device'] = args.user_row_locked_device
|
||||
if args.chip_erase_locked_device:
|
||||
sessionconfig.special_options['chip-erase-locked-device'] = args.chip_erase_locked_device
|
||||
|
||||
# Programming user row on locked parts and erasing to unlock are mutually exclusive
|
||||
if args.chip_erase_locked_device and args.user_row_locked_device:
|
||||
print("User row cannot be written on a locked device while erasing and unlocking.")
|
||||
return STATUS_FAILURE
|
||||
|
||||
if args.interface:
|
||||
sessionconfig.interface = args.interface
|
||||
|
||||
if args.packpath:
|
||||
sessionconfig.packpath = args.packpath
|
||||
|
||||
status = STATUS_SUCCESS
|
||||
try:
|
||||
backend.start_session(sessionconfig)
|
||||
except PymcuprogDeviceLockedError:
|
||||
print("The device is in a locked state and is not accessible; a chip erase is required.")
|
||||
print("Locked AVR UPDI devices can:")
|
||||
print(" - be unlocked using command: erase --chip-erase-locked-device")
|
||||
print(" - write user row values using command: write --user-row-locked-device")
|
||||
status = STATUS_FAILURE_LOCKED
|
||||
except PymcuprogNotSupportedError:
|
||||
print("Unable to setup stack for device {0:s}".format(sessionconfig.device))
|
||||
print("Currently supported devices (in 'devices' folder):")
|
||||
device_list = get_supported_devices()
|
||||
print(', '.join(map(str, device_list)))
|
||||
status = STATUS_FAILURE
|
||||
except PymcuprogSessionConfigError as error:
|
||||
print("Unable to start session: {}".format(error))
|
||||
status = STATUS_FAILURE
|
||||
|
||||
return status
|
Reference in New Issue
Block a user