Files
ATtiny814-USB-PD-Adapter/software/tools/pymcuprog/libs/pyedbglib/protocols/edbgprotocol.py
2022-09-11 11:42:08 +02:00

211 lines
8.2 KiB
Python

"""Implements EDBG Protocol, a sub-protocol in the JTAGICE3 family of protocols."""
from logging import getLogger
from ..util.binary import unpack_be16
from .jtagice3protocol import Jtagice3Protocol
class EdbgProtocol(Jtagice3Protocol):
"""Implements EDBG protocol functionality on the JTAGICE3 protocol family"""
CMD_EDBG_QUERY = 0x00 # Capability discovery
CMD_EDBG_SET = 0x01 # Set parameters
CMD_EDBG_GET = 0x02 # Get parameters
CMD_EDBG_PROGRAM_ID_CHIP = 0x50 # Programs an ID chip
CMD_EDBG_REFRESH_ID_CHIP = 0x51 # Triggers ID chip refresh
CMD_EDBG_READ_ID_CHIP = 0x7E # Retrieve ID chip info
AVR_GET_CONFIG = 0x83 # CMSIS vendor 3 get config command
RSP_EDBG_OK = 0x80 # All OK
RSP_EDBG_LIST = 0x81 # List of items returned
RSP_EDBG_DATA = 0x84 # Data returned
RSP_EDBG_FAILED = 0xA0 # Command failed to execute
EDBG_QUERY_COMMANDS = 0x00
EDBG_CTXT_CONTROL = 0x00 # Control
EDBG_CONTROL_LED_USAGE = 0x00
EDBG_CONTROL_EXT_PROG = 0x01
EDBG_CONTROL_TARGET_POWER = 0x10
EDBG_CONFIG_KIT_DATA = 0x20 # Read the kit info flash page
"""Mapping EDBG error codes to more human friendly strings"""
EDBG_ERRORS = {0: 'SUCCESS'}
"""Mapping SHA204 response codes to more human friendly strings"""
RESPONSE_CODE = {0x00: 'SHA204_SUCCESS',
0xD2: 'SHA204_PARSE_ERROR',
0xD3: 'SHA204_CMD_FAIL',
0xD4: 'SHA204_STATUS_CRC',
0xE0: 'SHA204_FUNC_FAIL',
0xE2: 'SHA204_BAD_PARAM',
0xE4: 'SHA204_INVALID_SIZE',
0xE5: 'SHA204_BAD_CRC',
0xE6: 'SHA204_RX_FAIL',
0xE7: 'SHA204_RX_NO_RESPONSE',
0xE8: 'SHA204_RESYNC_WITH_WAKEUP',
0xF0: 'SHA204_COMM_FAIL',
0xF1: 'SHA204_TIMEOUT',
0xFA: 'ID_DATA_LOCKED',
0xFB: 'ID_CONFIG_LOCKED',
0xFC: 'ID_INVALID_SLOT',
0xFD: 'ID_DATA_PARSING_ERROR',
0xFE: 'ID_DATA_NOT_EQUAL'}
def __init__(self, transport):
self.logger = getLogger(__name__)
super(EdbgProtocol, self).__init__(
transport, Jtagice3Protocol.HANDLER_EDBG)
def check_command_exists(self, command):
"""
Check if command is supported
Runs a query to the tool to get a list of supported commands, then looks for
the input command in the list. If not supported, it raises NotImplementedError.
:param command: The command to test.
:return: None
"""
commands_supported = self.query(self.EDBG_QUERY_COMMANDS)
if command not in commands_supported:
raise NotImplementedError("Invalid command: 0x{:02X}".format(command))
def error_as_string(self, code):
"""
Get the response error as a string (error code translated to descriptive string)
:param code: error code
:return: error code as descriptive string
"""
try:
return self.EDBG_ERRORS[code]
except KeyError:
return "Unknown error!"
def response_as_string(self, code):
"""
Get the response code as a string (response code translated to descriptive string)
:param code: response code
:return: error code as descriptive string
"""
try:
return self.RESPONSE_CODE[code]
except KeyError:
return "Unknown response!"
def program_id_chip(self, id_number, data):
"""
Program the connected ID device located at the id_number with data.
:param id_number: Extension header ID number (Range 1 - 16)
:param data: A 64-byte data array to be programmed
:return: Response status from the programming
"""
self.logger.info("Programming ID chip...")
try:
self.check_command_exists(self.CMD_EDBG_PROGRAM_ID_CHIP)
except NotImplementedError as err:
self.logger.warning("Non-compliant command: %s", err)
# Old EDBG implementations contained a non-compliant version of this command
# Version 0 command
packet = bytearray([self.CMD_EDBG_PROGRAM_ID_CHIP, self.CMD_VERSION0, id_number - 1] + data)
resp = self.jtagice3_command_response_raw(packet)
self.logger.debug("Program ID response: %s", self.response_as_string(resp[3]))
return resp[3]
else:
# Version 1 command
packet = bytearray([self.CMD_EDBG_PROGRAM_ID_CHIP, self.CMD_VERSION1, id_number] + data)
status = self.check_response(self.jtagice3_command_response(packet))
self.logger.debug("Program ID response: %s", self.response_as_string(status[0]))
return status[0]
def refresh_id_chip(self):
"""
Forces a refresh of the list of connected ID devices.
:return: None
"""
self.logger.info("Refreshing ID chip...")
try:
self.check_command_exists(self.CMD_EDBG_REFRESH_ID_CHIP)
except NotImplementedError as err:
self.logger.warning("Non-compliant command: %s", err)
# Old EDBG implementations contained a non-compliant version of this command
# Version 0 command
packet = bytearray([self.CMD_EDBG_REFRESH_ID_CHIP, self.CMD_VERSION0])
resp = self.jtagice3_command_response_raw(packet)
if not resp[3] == self.RSP_EDBG_OK:
raise IOError("Invalid response from CMD_EDBG_REFRESH_ID_CHIP")
else:
# Version 1 command
packet = bytearray([self.CMD_EDBG_REFRESH_ID_CHIP, self.CMD_VERSION1])
self.check_response(self.jtagice3_command_response(packet))
def read_id_chip(self, id_number):
"""
Reads the ID information from the ID chip connected at id_number
:param id_number: Extension header ID number (Range 1 - 16)
:return: A 64-byte data array
"""
self.logger.info("Reading ID chip...")
try:
self.check_command_exists(self.CMD_EDBG_READ_ID_CHIP)
except NotImplementedError as err:
self.logger.warning("Non-compliant command: %s", err)
# Old EDBG implementations contained a non-compliant version of this command
# Version 0 command
packet = bytearray([self.CMD_EDBG_READ_ID_CHIP, self.CMD_VERSION0, id_number - 1])
resp = self.jtagice3_command_response_raw(packet)
if resp[4] == self.RSP_EDBG_DATA:
return resp[6:]
return False
else:
# Version 1 command
packet = bytearray([self.CMD_EDBG_READ_ID_CHIP, self.CMD_VERSION1, id_number])
data = self.check_response(self.jtagice3_command_response(packet))
return data
def read_edbg_extra_info(self):
"""
Reads the kit info flash page, containing board specific data
:return: A data array containing the kit info
"""
self.logger.info("Reading kit info...")
# The second parameter tells the debugger it is the only command
# The last parameter tells what to read. If zero a whole page is read, and
# if non-zero 32-bytes is fetched from offset 32 * parameter. The parameter
# cannot be greater than 8
response = self.dap_command_response(bytearray([self.AVR_GET_CONFIG, 0x01,
self.EDBG_CONFIG_KIT_DATA, 0x0]))
# Remove unused data
if len(response) >= 256 + 6:
self.logger.info("Response size is truncated")
response = response[:256 + 6]
# Byte 0 will echo the current command
# Byte 1 show the command status
if response[0] == self.AVR_GET_CONFIG:
# Check the status code
if response[1] == 0:
# Bytes [3..2] contain the received size
size = unpack_be16(response[2:4])
return response[6:size]
self.logger.warning("Command failed with error: %i", response[1])
self.logger.warning("Command was not echoed back")
return False