Initial commit

This commit is contained in:
wagiminator
2022-09-11 11:42:08 +02:00
parent 54d0f15ce8
commit 03e8a184a2
166 changed files with 86898 additions and 2 deletions

View File

@@ -0,0 +1,81 @@
"""
Python MCU programmer utility
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pymcuprog is a utility for programming various Microchip MCU devices using Microchip CMSIS-DAP based debuggers
pymcuprog can be used as a library using its "backend API". For example:
Setup logging - pymcuprog uses the Python logging module
>>> import logging
>>> logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.WARNING)
Configure the session:
>>> from pymcuprog.backend import SessionConfig
>>> sessionconfig = SessionConfig("atmega4808")
Instantiate USB transport (only 1 tool connected)
>>> from pymcuprog.toolconnection import ToolUsbHidConnection
>>> transport = ToolUsbHidConnection()
Instantiate backend
>>> from pymcuprog.backend import Backend
>>> backend = Backend()
Connect to tool using transport
>>> backend.connect_to_tool(transport)
Start the session
>>> backend.start_session(sessionconfig)
Read the target device_id
>>> device_id = backend.read_device_id()
>>> print ("Device ID is {0:06X}".format(int.from_bytes(d, byteorder="little")))
Print the pymcuprog package version:
>>> from pymcuprog.version import VERSION as pymcuprog_version
>>> print("pymcuprog version {}".format(pymcuprog_version))
In addition, the CLI-backend API is versioned for convenience:
>>> print("pymcuprog backend API version: {}".format(backend.get_api_version()))
Logging
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This package uses the Python logging module for publishing log messages to library users.
A basic configuration can be used (see example), but for best results a more thorough configuration is
recommended in order to control the verbosity of output from dependencies in the stack which also use logging.
See logging.yaml which is included in the package (although only used for CLI)
Dependencies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pymcuprog depends on pyedbglib for its transport protocol.
pyedbglib requires a USB transport library like libusb. See pyedbglib package for more information.
Supported devices and tools
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note: pymcuprog is primarily intended for use with PKOB nano (nEDBG) debuggers which
are found on Curiosity Nano kits and other development boards. This means that it is
continuously tested with a selection of AVR devices with UPDI interface as well as a
selection of PIC devices. However since the protocol is compatible between all
EDBG-based debuggers (pyedbglib) it is possible to use pymcuprog with a wide range of
debuggers and devices, although not all device families/interfaces have been implemented.
The following Atmel/Microchip debuggers are supported:
* JTAGICE3 (only firmware version 3.x)
* Atmel-ICE
* Power Debugger
* EDBG
* mEDBG
* PKOB nano (nEDBG)
* MPLAB PICkit 4 ICD (only when in 'AVR mode')
* MPLAB Snap ICD (only when in 'AVR mode')
Not all functionality is provided on all boards. See device support below.
The following device-types are supported:
* All UPDI devices, whether mounted on kits or standalone
* PIC devices mounted on Curiosity Nano kits, or similar board with PKOB nano (nEDBG) debugger
* Other devices (eg ATmega328P, ATsamd21e18a) may be partially supported for experimental purposes
"""
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())

View File

@@ -0,0 +1,658 @@
"""
Backend interface for the pymcuprog utility.
This module is the boundary between the Command Line Interface (CLI) part and
the backend part that does the actual job. Any external utility or script that
needs access to the functionality provided by pymcuprog should connect to the
interface provided by this backend module
"""
# Python 3 compatibility for Python 2
from __future__ import print_function
import os
from logging import getLogger
# pyedbglib dependencies
from pyedbglib.hidtransport.hidtransportfactory import hid_transport
from pyedbglib.hidtransport.hidtransportbase import HidTransportBase
from pyedbglib.protocols import housekeepingprotocol
from pyedbglib.protocols.jtagice3protocol import Jtagice3ResponseError
from .pymcuprog_errors import PymcuprogToolConfigurationError, PymcuprogToolConnectionError
from .pymcuprog_errors import PymcuprogNotSupportedError, PymcuprogEraseError
from .pymcuprog_errors import PymcuprogSessionConfigError, PymcuprogSessionError
from .programmer import Programmer
from .deviceinfo import deviceinfo
from .deviceinfo.memorynames import MemoryNames
from .deviceinfo.memorynames import MemoryNameAliases
from .deviceinfo.eraseflags import ChiperaseEffect
from .deviceinfo.deviceinfokeys import DeviceInfoKeys, DeviceMemoryInfoKeys
from .toolconnection import ToolUsbHidConnection, ToolSerialConnection
from .utils import read_tool_info
from .utils import read_target_voltage, read_supply_voltage_setpoint, read_usb_voltage
from .utils import set_supply_voltage_setpoint
from .hexfileutils import read_memories_from_hex
# Files in devices folder not representing devices
NON_DEVICEFILES = ["__init__.py"]
DEVICE_FOLDER = os.path.dirname(os.path.abspath(__file__)) + "//deviceinfo//devices"
# This class is a collection of parameters so no need for any methods
#pylint: disable=too-few-public-methods
class SessionConfig(object):
"""
Collection of all parameters needed when configuring a programming session
Used as input parameter for the start_session function
"""
device = None
interface = None
# For some interfaces this is baud in bits per second and for other interfaces this is clock frequency in Hz
interface_speed = None
# Path to python devicesupportscripts for PIC devices
packpath = None
# Content and format of special_options will depend on the device stack implementation.
# Normally these options are not in use.
special_options = None
def __init__(self, device):
"""
device name is mandatory
"""
self.device = device
# To achieve a single entry point for users of the backend part of pymcuprog it is accepted to exceed the maximum
# number of methods.
#pylint: disable=too-many-public-methods
class Backend(object):
"""
Backend interface of the pymcuprog utility.
This class provides access to all the functionality provided by pymcuprog
"""
API_VERSION = '2.0'
def __init__(self):
# Hook onto logger
self.logger = getLogger(__name__)
self.transport = None
self.connected_to_tool = False
self.session_active = False
self.programmer = None
self.device_memory_info = None
self.housekeeper = None
def get_api_version(self):
"""
Returns the current pymcuprog API version
"""
return self.API_VERSION
@staticmethod
def get_supported_devices():
"""
Return a list of devices supported by pymcuprog.
This will be the list of devices with a corresponding device file
:returns: List of device names
"""
devices = []
for filename in os.listdir(DEVICE_FOLDER):
if filename not in NON_DEVICEFILES and filename.endswith('.py'):
devices.append(filename.split('.py')[0])
return devices
@staticmethod
def get_available_hid_tools(serialnumber_substring='', tool_name=None):
"""
Return a list of Microchip USB HID tools (debuggers) connected to the host
:param serialnumber_substring: can be an empty string or a subset of a serial number. Not case sensitive
This function will do matching of the last part of the devices serial numbers to
the serialnumber_substring. Examples:
'123' will match "MCHP3252000000043123" but not "MCP32520001230000000"
'' will match any serial number
:param tool_name: tool type to connect to. If None any tool matching the serialnumber_substring
will be returned
:returns: List of pyedbglib.hidtransport.hidtransportbase.HidTool objects
"""
# Just use a temporary transport as the request is only to report connected Microchip HID tools,
# not to connect to any of them
transport = hid_transport()
return transport.get_matching_tools(serialnumber_substring, tool_name)
def connect_to_tool(self, toolconnection):
"""
Connect to a tool
The tool can either be a USB HID tool or a serial port.
:param ToolConnection: This is an instance of one of the ToolConnection sub-classes. This object wraps
parameters needed to identify which tool to connect to like tool name and USB serial or serial port
name (e.g. 'COM1').
For USB HID tools there are some special handling:
- If both tool name and usb_serial are None any tool will be picked.
- If usb_serial is None any tool matching the tool name will be picked
- If tool name is None any tool matching the usb_serial will be picked
- If more than one tool is connected that matches the tool name and usb_serial parameters a
PymcuprogToolConnectionError exception will be raised.
:raises: PymcuprogToolConnectionError if more than one matching tool is found or if no matching tool is found
:raises: PymcuprogToolConfigurationError if the toolconnection configuration is incorrect
"""
if isinstance(toolconnection, ToolSerialConnection):
# For serial port connection no connection action is needed, just need to store the
# Serial port number to be used (e.g. 'COM1')
self.transport = toolconnection.serialport
elif isinstance(toolconnection, ToolUsbHidConnection):
self.transport = hid_transport()
connect_status = False
try:
connect_status = self.transport.connect(serial_number=toolconnection.serialnumber,
product=toolconnection.tool_name)
except IOError as error:
raise PymcuprogToolConnectionError("Unable to connect to USB device ({})".format(error))
if not connect_status:
raise PymcuprogToolConnectionError("Unable to connect to USB device")
self.housekeeper = housekeepingprotocol.Jtagice3HousekeepingProtocol(self.transport)
self.housekeeper.start_session()
else:
raise PymcuprogToolConfigurationError("Unknown toolconnection argument type: {})".
format(type(toolconnection)))
self.connected_to_tool = True
def disconnect_from_tool(self):
"""
Disconnect the connected tool
If no tool is connected nothing is done (i.e. no exception raised when not connected)
"""
if self._is_connected_to_hid_tool():
self.housekeeper.end_session()
self.transport.disconnect()
self.connected_to_tool = False
def read_tool_info(self):
"""
Interrogates tool (debugger) for useful info
:returns: Dictionary with various info about the connected debugger
:raises PymcuprogToolConnectionError if not connected to any USB HID tool (connect_to_tool not run)
"""
self._is_hid_tool_not_connected_raise()
return read_tool_info(self.housekeeper)
def read_kit_device(self):
"""
Read out the device name from kit configuration.
If the connected tool does not have any kit configuration
(i.e. the tool is not an onboard debugger) None will be returned.
connect_to_tool must have been called before calling read_kit_device, but start_session is not necessary.
Typically read_kit_device is used to get the device name required to configure a session before calling
start_session.
:returns: Name of target device as given by the kit, None if the tool does not have any device configured.
:raises PymcuprogToolConnectionError if not connected to any USB HID tool (connect_to_tool not run)
"""
self._is_hid_tool_not_connected_raise()
dap_info = read_tool_info(self.housekeeper)
device_name = dap_info['device_name'].lower()
if device_name == '':
device_name = None
return device_name
def read_target_voltage(self):
"""
Read target voltage
:returns: Measured target voltage
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogNotSupportedError if the tool does not have supply capabilities
"""
self._is_hid_tool_not_connected_raise()
try:
voltage = read_target_voltage(self.housekeeper)
except Jtagice3ResponseError:
raise PymcuprogNotSupportedError("Connected debugger/board does not have target voltage read capability")
return voltage
def read_supply_voltage_setpoint(self):
"""
Read tool power supply voltage setpoint
:returns: Tool power supply voltage setpoint
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogNotSupportedError if the tool does not have supply capabilities
"""
self._is_hid_tool_not_connected_raise()
try:
voltage = read_supply_voltage_setpoint(self.housekeeper)
except Jtagice3ResponseError:
raise PymcuprogNotSupportedError("Connected debugger/board does not have supply voltage capability.")
return voltage
def read_usb_voltage(self):
"""
Read USB voltage
:returns: Measured USB voltage
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogNotSupportedError if the tool can't measure USB voltage
"""
self._is_hid_tool_not_connected_raise()
try:
voltage = read_usb_voltage(self.housekeeper)
except Jtagice3ResponseError:
raise PymcuprogNotSupportedError("Connected debugger/board does not have USB voltage read capability.")
return voltage
def set_supply_voltage_setpoint(self, setpoint):
"""
Set tool power supply voltage setpoint
:param setpoint: Power supply setpoint
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogNotSupportedError if the tool does not have supply capabilities
:raises: ValueError if the setpoint is out of range
"""
self._is_hid_tool_not_connected_raise()
set_supply_voltage_setpoint(self.housekeeper, setpoint)
def reboot_tool(self):
"""
Trigger a reboot of the tool (debugger)
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
"""
self._is_hid_tool_not_connected_raise()
self.housekeeper.end_session(reset_tool=True)
# A tool reboot will automatically disconnect the tool. Calling self.disconnect_from_tool
# would just fail as it would try to talk to a tool while it is rebooting
self.connected_to_tool = False
@staticmethod
def get_device_info(device):
"""
Get info about a device
:param device: Name of the device
:returns: dictionary with device info as defined in the device files in pymcuprog.deviceinfo.devices
:raises: PymcuprogNotSupportedError if device is not supported
"""
try:
info = deviceinfo.getdeviceinfo(device)
except ModuleNotFoundError:
raise PymcuprogNotSupportedError("No device info for device: {}".format(device))
return info
def start_session(self, sessionconfig, user_interaction_callback=None):
"""
Start a programming session.
This function will build the device model stack and initialize the tool for a
programming session. If a session is already started calling start_session will do an end_session and start
a new session from scratch.
Note connect_to_tool must have been called before start_session is called. If not an exception will be thrown.
:param sessionconfig: SessionConfig object wrapping the parameters configuring the session
:param user_interaction_callback: Callback to be called when user interaction is required,
for example when doing UPDI high-voltage activation with user target power toggle.
This function could ask the user to toggle power and halt execution waiting for the user
to respond (this is default behavior if the callback is None), or if the user is another
script it could toggle power automatically and then return.
:raises: PymcuprogSessionConfigError if starting the session failed due to incorrectly configured session
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogDeviceLockedError if unable to start the session due to the device being locked
:raises: PymcuprogNotSupportedError if configured device is not supported
"""
self._is_tool_not_connected_raise()
# Check that all session configuration parameters required are in place
if sessionconfig.device is None or sessionconfig.device == '':
raise PymcuprogSessionConfigError("Device must be specified")
if self.session_active:
# A session is already active so it must be ended before starting a new session
self.end_session()
# Setup the programmer
self.programmer = Programmer(self.transport)
if sessionconfig.special_options is not None:
self.programmer.set_options(sessionconfig.special_options)
# Try to build the stack for this device
self.programmer.load_device(sessionconfig.device)
self.programmer.setup_device(
sessionconfig.interface,
sessionconfig.packpath,
sessionconfig.interface_speed)
# Make contact
self.programmer.start(user_interaction_callback=user_interaction_callback)
# Get device memory info
self.device_memory_info = self.programmer.get_device_memory_info()
self.session_active = True
def end_session(self):
"""
End a programming session
This will take down the device model stack and stop the programming session on the tool. However the tool will
not be disconnected and it will be possible to do another start_session without another connect_to_tool call.
If no session has been started this function will do nothing (i.e. it won't fail even if a session has
not been started)
"""
if self.session_active:
# Lower the flag first to ensure it is updated as the rest of this function might fail with an exception
# for example if UPDI were disabled during the session
self.session_active = False
self.programmer.stop()
def read_device_id(self):
"""
Read out the device id
:return Byte array with device ID as raw byte values. Number of bytes will depend upon target type
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
return self.programmer.read_device_id()
def erase(self, memory_name=MemoryNameAliases.ALL, address=None):
"""
Erase target device memory
If a single memory is specified it will only be erased if it won't affect other memories
:param memory: name of memory to erase. To unlock a device use the MemoryNameAliases.ALL
MemoryNameAliases.ALL run the widest erase:
- For PIC the widest bulk erase will be run.
- For AVR a chip erase will be run
- The following memories will not be erased:
- AVR fuses
- EEPROM if EESAVE fuse is set for AVR
- EEPROM if the target device does not support EEPROM erase
- EEPROM if Data Code Protection (CPD_n) is not enabled for PIC
- PIC ICD memory (special memory used for Debug Executives)
:param address: optional address for erase command. If address is None the complete memory
segment will be erased. Note that the address parameter will just propagate through the stack down to the
device dependent implementation (devicesupportscripts for PIC and firmware for AVR). Normal use is to
leave the address as None.
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
:raises: ValueError if the specified memory is not defined for the target device
:raises: PymcuprogEraseError if the memory can't be erased or if the memory can't be erased without affecting
other memories
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
if memory_name is not None and memory_name != MemoryNameAliases.ALL:
if not self.is_isolated_erase_possible(memory_name):
message = "{} memory can't be erased or can't be erased without side effect".format(memory_name)
raise PymcuprogEraseError(message)
self.programmer.erase(memory_name, address)
def is_isolated_erase_possible(self, memory_name):
"""
Can the memory be erased without affecting other memories?
:param memory_name: name of memory
:return: True only if the memory can be erased without side effects, False if memory can't be erased at all or
if erasing it will erase other memories too.
:raises ValueError if memory is not defined for the configured device
"""
# The device model must have been loaded upfront
self._is_session_not_active_raise()
meminfo = self.device_memory_info.memory_info_by_name(memory_name)
isolated_erase_key = DeviceMemoryInfoKeys.ISOLATED_ERASE
if isolated_erase_key in meminfo:
return meminfo[isolated_erase_key] is True
self.logger.error('%s flag not found for %s memory', isolated_erase_key, memory_name)
return False
def get_chiperase_effect(self, memory_name):
"""
Get the effect of a chip erase (widest bulk erase) on the given memory
:param memory_name: name of memory
:return: One of the values defined by deviceinfo.eraseflags.ChiperaseEffect depending upon the settings in the
device model for the configured device. If the chiperase_effect flag is missing in the device model
ChiperaseEffect.NOT_ERASED will be returned.
:raises ValueError if memory is not defined for the configured device
"""
# The device model must have been loaded upfront
self._is_session_not_active_raise()
meminfo = self.device_memory_info.memory_info_by_name(memory_name)
chiperase_effect_key = DeviceMemoryInfoKeys.CHIPERASE_EFFECT
if chiperase_effect_key in meminfo:
return meminfo[chiperase_effect_key]
self.logger.error('%s flag not found for %s memory', chiperase_effect_key, memory_name)
return ChiperaseEffect.NOT_ERASED
def read_memory(self, memory_name=MemoryNameAliases.ALL, offset_byte=0, numbytes=0, max_chunk_size=None):
"""
Read target device memory
:param memory_name: Name of memory as defined in memorynames.py. MemoryNameAliases.ALL reads all memories
defined in the device model (numbytes and offset_byte will be ignored).
:param offset_byte: Byte offset within memory to start reading at.
:param numbytes: Number of bytes to read. 0 means read all memory locations from offset_byte and until end
of memory
:return: 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). Normally the list will contain one item, but when
memory_name parameter is MemoryNameAliases.ALL there will be one namedtuple item per memory
type read.
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
:raises: ValueError if trying to read outside the specified memory
:raises: ValueError if the specified memory is not defined for the target device
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
return self.programmer.read_memory(memory_name=memory_name, offset=offset_byte, numbytes=numbytes, max_chunk_size=max_chunk_size)
def write_memory(self, data, memory_name=MemoryNames.FLASH, offset_byte=0, blocksize=0, pagewrite_delay=0):
"""
Write target device memory
:param memory_name: Name of memory as defined in memorynames.py
:param offset_byte: Byte offset within memory to start writing to.
:param data: bytearray of raw data bytes to write
:param blocksize: max number of bytes to send at a time. Ignored if 0 or omitted, and not passed
to write_memory; only serialupdi supports this.
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
:raises: ValueError if trying to write outside the specified memory
:raises: ValueError if the specified memory is not defined for the target device
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
if blocksize == 0:
self.programmer.write_memory(data=data, memory_name=memory_name, offset=offset_byte, pagewrite_delay=pagewrite_delay)
else:
self.programmer.write_memory(data=data, memory_name=memory_name, offset=offset_byte, blocksize=blocksize, pagewrite_delay=pagewrite_delay)
def verify_memory(self, data, memory_name=MemoryNames.FLASH, offset_byte=0, max_read_chunk=None):
"""
Verify target device memory
:param memory_name: Name of memory as defined in DeviceMemoryInfo (deviceinfo.py)
:param offset_byte: Byte offset within memory to start verifying at.
:param data: bytearray of raw data bytes to verify against
:return: boolean compare status
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
:raises: ValueError if trying to verify outside the specified memory
:raises: ValueError if the specified memory is not defined for the target device
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
return self.programmer.verify_memory(data=data, memory_name=memory_name, offset=offset_byte, max_read_chunk=max_read_chunk)
def hold_in_reset(self):
"""
Hold target device in reset
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
self.programmer.hold_in_reset()
def release_from_reset(self):
"""
Release target device from reset
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
self.programmer.release_from_reset()
# Releasing the target from reset will take it out of programming mode. In other words the session
# is partly taken down. To keep housekeeping right and to take down the stack properly end_session
# must be called
self.end_session()
def write_hex_to_target(self, hexfile):
"""
Write hexfile to target device
Note no erase will be run (i.e. memory is assumed to already be erased)
:param hexfile: name of file to write
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
hex_memories = read_memories_from_hex(os.path.abspath(hexfile), self.device_memory_info)
for segment in hex_memories:
memory_name = segment.memory_info[DeviceInfoKeys.NAME]
self.logger.debug("Writing %s...", memory_name)
self.write_memory(segment.data, memory_name, segment.offset)
def verify_hex(self, hexfile):
"""
Verify target memory content against hexfile
:param hexfile: name of file to verify against
:return: boolean compare status
:raises: PymcuprogToolConnectionError if not connected to any tool (connect_to_tool not run)
:raises: PymcuprogSessionError if a session has not been started (session_start not run)
"""
self._is_tool_not_connected_raise()
self._is_session_not_active_raise()
hex_memories = read_memories_from_hex(os.path.abspath(hexfile), self.device_memory_info)
verify_ok = True
for segment in hex_memories:
memory_name = segment.memory_info[DeviceInfoKeys.NAME]
self.logger.debug("Verifying %s...", memory_name)
segment_ok = self.verify_memory(segment.data, memory_name, segment.offset, max_read_chunk=max_read_chunk)
if segment_ok:
self.logger.debug("OK!")
else:
verify_ok = False
return verify_ok
def _is_tool_not_connected_raise(self):
"""
Check if any tool is connected and if not raise an exception
:raises: PymcuprogToolConnectionError if not connected to any tool
"""
if not self._is_connected_to_hid_tool() and not self._is_connected_to_serialport():
raise PymcuprogToolConnectionError("Not connected to any tool")
def _is_hid_tool_not_connected_raise(self):
"""
Check if a USB HID tool is connected and if not raise an exception
:raises: PymcuprogToolConnectionError if not connected to any tool
"""
if not self._is_connected_to_hid_tool():
raise PymcuprogToolConnectionError("Not connected to any USB HID debugger")
def _is_connected_to_hid_tool(self):
"""
Check if a connection to a USB HID tool is active
"""
return self.connected_to_tool and isinstance(self.transport, HidTransportBase)
def _is_connected_to_serialport(self):
"""
Check if a connection to a Serial port is active
"""
# For Serial port communication transport is only set to a string with the name of the serial port
# to use (e.g. 'COM1').
return self.connected_to_tool and isinstance(self.transport, str)
def _is_session_not_active_raise(self):
"""
Check if a programming session is active and if not raise an exception
:raises: PymcuprogSessionError if programming session not active
"""
if not self.session_active:
raise PymcuprogSessionError("No programming session active")

View File

@@ -0,0 +1,290 @@
"""
deviceinfo.py
A simple Device Information service
Device information is stored in files named <devicename>.py in the devices sub-folder
Each device file contains a dict of values
These device files are [ideally] generated from DFP information by [running generate_device_info.py | hand]
"""
# Python 3 compatibility for Python 2
from __future__ import print_function
import os
import importlib
from logging import getLogger
from pymcuprog.pymcuprog_errors import PymcuprogError
from .memorynames import MemoryNames
from .deviceinfokeys import DeviceMemoryInfoKeys, DeviceInfoKeys, DeviceInfoKeysPic
def getdeviceinfo(devicename):
"""
Looks up device info for a given part
:param devicename: device to look up
:return: device information dict
"""
logger = getLogger(__name__)
logger.info("Looking for device %s", devicename)
devicename = devicename.lower()
try:
device_module = importlib.import_module("deviceinfo.devices.{}".format(devicename))
except ImportError:
try:
# When pymcuprog is used as a package in other scripts
# the deviceinfo module is part of the pymcuprog package
device_module = importlib.import_module("pymcuprog.deviceinfo.devices.{}".format(devicename))
except ImportError:
device_module = importlib.import_module("{}".format(devicename))
device_info = getattr(device_module, "DEVICE_INFO")
# For PIC devices there will be a default_bulk_erase_address outside any memory information
# This address needs to be converted to byte address
default_bulk_erase_address_byte = None
for param in device_info:
if param.startswith(DeviceInfoKeysPic.DEFAULT_BULK_ERASE_ADDRESS):
# Check if it's word or byte oriented data
mul = DeviceMemoryInfo.bytes_or_words(param)
if mul is not None:
default_bulk_erase_address_byte = int(device_info[param] * mul)
else:
default_bulk_erase_address_byte = device_info[param]
if default_bulk_erase_address_byte is not None:
device_info[DeviceInfoKeysPic.DEFAULT_BULK_ERASE_ADDRESS] = default_bulk_erase_address_byte
return device_info
def get_supported_devices():
"""
Return a list of all supported devices
A device is supported if it has a device model file in the devices folder
"""
root_folder = os.path.dirname(os.path.abspath(__file__))
dir_list = os.listdir(root_folder + "//devices")
ignore_list = ['__init__.py']
device_list = []
for devicefile in dir_list:
if devicefile.endswith(".py") and devicefile not in ignore_list:
devicename = devicefile.split('.')[0]
device_list.append(devicename)
return device_list
class DeviceMemoryInfo:
"""
API to fetch information about device memory segments
"""
def __init__(self, device_info):
self.device = device_info
self.memtypes = MemoryNames.get_all()
# hexfile_address is the start address for the memory segment in hex files.
# PIC and ARM devices usually does not need the parameter as all locations are mapped in a single address space.
# AVR8 devices does not map all memory types in a single address space.
# Memory types have defined offsets in hex files as defined below
self.avr8_hex_file_offsets = {
MemoryNames.FLASH: 0x000000,
MemoryNames.EEPROM: 0x810000,
MemoryNames.FUSES: 0x820000,
MemoryNames.LOCKBITS: 0x830000,
MemoryNames.SIGNATURES: 0x840000,
MemoryNames.USER_ROW: 0x850000
}
# erase_address is the address for the erase of the memory.
# Note that for PIC devices other memories might be erased in the same operation depending on the target,
# see the programming spec for the target device.
# erase_address, hexfile_address, hexfile_size and verify mask are optional in the device models.
# erase_address will be set to the memory address if it's missing.
# Hex file address will be set to the memory address if it's missing, unless it's an AVR device where
# the hex file offset is used instead.
# Hex file size will be set to the memory size if it's missing except for EEPROM on PIC16 devices where
# the hex file will contain phantom bytes so the hex file will contain twice as many EEPROM bytes as
# the actual EEPROM in the device
# verify_mask is set based on architecture
self.paramtypes = [DeviceMemoryInfoKeys.ADDRESS,
DeviceMemoryInfoKeys.SIZE,
DeviceMemoryInfoKeys.PAGE_SIZE,
DeviceMemoryInfoKeys.WRITE_SIZE,
DeviceMemoryInfoKeys.READ_SIZE,
DeviceMemoryInfoKeys.VERIFY_MASK,
DeviceMemoryInfoKeys.ERASE_ADDRESS,
DeviceMemoryInfoKeys.HEXFILE_ADDRESS,
DeviceMemoryInfoKeys.HEXFILE_SIZE,
DeviceMemoryInfoKeys.CHIPERASE_EFFECT,
DeviceMemoryInfoKeys.ISOLATED_ERASE]
self.mem_by_name = {}
# Find information about memory segments
for param in self.device:
for mtype in self.memtypes:
# Does this line describe a memory location?
if param.startswith(mtype):
self._configure_memory_param(mtype, param)
# erase_address and hexfile_address are optional and should default to the value of the address parameter
optional_params = [DeviceMemoryInfoKeys.VERIFY_MASK,
DeviceMemoryInfoKeys.HEXFILE_ADDRESS,
DeviceMemoryInfoKeys.ERASE_ADDRESS,
DeviceMemoryInfoKeys.HEXFILE_SIZE]
for optional_param in optional_params:
for memtype in self.mem_by_name:
if optional_param not in self.mem_by_name[memtype]:
# Set the verify mask based on architecture
if optional_param == DeviceMemoryInfoKeys.VERIFY_MASK:
verify_mask = self._get_verify_mask(self.device[DeviceInfoKeys.ARCHITECTURE], memtype)
self.mem_by_name[memtype][optional_param] = verify_mask
# Set the hexfile_address
elif optional_param == DeviceMemoryInfoKeys.HEXFILE_ADDRESS:
self._add_hexfile_address(memtype, optional_param)
# Set the hexfile_size
elif optional_param == DeviceMemoryInfoKeys.HEXFILE_SIZE:
self._add_hexfile_size(memtype, optional_param)
# Set the erase_address
elif optional_param == DeviceMemoryInfoKeys.ERASE_ADDRESS:
# By default the erase_address is the same as the address of the memory
address = self.mem_by_name[memtype][DeviceMemoryInfoKeys.ADDRESS]
self.mem_by_name[memtype][optional_param] = address
def _configure_memory_param(self, memorytype, param):
# Check if it's word or byte oriented data
mul = self.bytes_or_words(param)
# Create a dict for the memory type if it does not exist
if not self.mem_by_name.get(memorytype):
self.mem_by_name[memorytype] = {DeviceMemoryInfoKeys.NAME: memorytype}
# Parse and store parameter
for ptype in self.paramtypes:
if param.startswith("{}_{}".format(memorytype, ptype)):
if mul is not None:
self.mem_by_name[memorytype][ptype] = int(self.device[param] * mul)
else:
self.mem_by_name[memorytype][ptype] = self.device[param]
def _add_hexfile_address(self, memorytype, paramname):
# Inject hex file addresses for AVR memory areas
if self.device[DeviceInfoKeys.ARCHITECTURE].startswith('avr8'):
if memorytype in self.avr8_hex_file_offsets:
self.mem_by_name[memorytype][paramname] = self.avr8_hex_file_offsets[memorytype]
else:
# The hexfile_address for memory types that doesn't make sense in a hex file like SRAM
# and regular I/O space is defined to an address the other memory types will not reach
self.mem_by_name[memorytype][paramname] = 0xFFFFFF
# All other memory types are mapped 1 to 1 in the hex file
else:
self.mem_by_name[memorytype][paramname] = self.mem_by_name[memorytype][DeviceMemoryInfoKeys.ADDRESS]
def _add_hexfile_size(self, memorytype, paramname):
if self.device[DeviceInfoKeys.ARCHITECTURE].startswith('PIC16') and memorytype == MemoryNames.EEPROM:
# For PIC16 devices there will be one phantom byte in the hex file for each EEPROM byte, so
# the size of EEPROM in a hex file will be twice the size of the actual EEPROM memory
self.mem_by_name[memorytype][paramname] = self.mem_by_name[memorytype][DeviceMemoryInfoKeys.SIZE] * 2
else:
self.mem_by_name[memorytype][paramname] = self.mem_by_name[memorytype][DeviceMemoryInfoKeys.SIZE]
@staticmethod
def _get_verify_mask(architecture, memtype):
# byte oriented memory
mask = [0xFF]
# PIC16 is word addressed and has 14-bit flash, except EEPROM which is byte oriented
if architecture == 'PIC16' and memtype not in [MemoryNames.EEPROM]:
mask = [0xFF, 0x3F]
# PIC18 is word addressed and has 16-bit flash, except EEPROM which is byte oriented
elif architecture == 'PIC18' and memtype not in [MemoryNames.EEPROM]:
mask = [0xFF, 0xFF]
# PIC24 is word addressed and has 24-bit flash, except EEPROM which is word oriented
elif architecture == 'PIC24':
if memtype in [MemoryNames.EEPROM]:
mask = [0xFF, 0xFF]
else:
mask = [0xFF, 0xFF, 0xFF, 0x00]
return mask
@staticmethod
def bytes_or_words(address_param):
"""
Return multiplier for address parameter
The returned multiplier can be used to convert the address parameter to byte address
:param address_param: Address parameter (used as key in device info dict)
:return: Multiplier to convert the address to byte address
"""
if address_param.endswith("_byte") or address_param.endswith("_bytes"):
mul = 1
elif address_param.endswith("_word") or address_param.endswith("_words"):
mul = 2
else:
mul = None
return mul
def memory_info_by_address_range(self,
start,
stop,
address_type=DeviceMemoryInfoKeys.ADDRESS,
size_type=DeviceMemoryInfoKeys.SIZE):
"""
Returns a list of all memories applicable for the address range(start, stop)
:param start: Start address (byte)
:param stop: End address (byte)
:param address_type: Selects between normal addresses and addresses used in hex files
(address vs hexfile_address)
:param size_type: Selects between normal size and size used in hexfiles (size vs hexfile_size)
"""
# We do not support negative memory ranges
if start > stop:
raise PymcuprogError("Cannot parse reverse memory range {} to {}".format(start, stop))
memtypes = []
# Loop through all known memory types for this device
for memtype in self.mem_by_name:
address = self.mem_by_name[memtype][address_type]
size = self.mem_by_name[memtype][size_type]
# Check if any of the addresses between start and stop is within the memory type range
if start < address+size and stop > address:
memtypes.append(self.mem_by_name[memtype])
return memtypes
def memory_info_by_address(self,
byte_address,
address_type=DeviceMemoryInfoKeys.ADDRESS,
size_type=DeviceMemoryInfoKeys.SIZE):
"""
Returns information about the memory type for a given byte address
:param byte_address: Memory address to check
:param address_type: Selects between normal addresses and addresses used in hex files
(ADDRESS vs HEXFILE_ADDRESS)
:param size_type: Selects between normal size and size used in hexfiles (size vs hexfile_size)
"""
memtype = None
for memory in self.mem_by_name:
if self.mem_by_name[memory][address_type] <= byte_address < \
self.mem_by_name[memory][address_type] + self.mem_by_name[memory][size_type]:
if memtype is not None:
raise PymcuprogError("Duplicate memory area found for byte address '{}'".format(byte_address))
memtype = self.mem_by_name[memory]
return memtype
def memory_info_by_name(self, name):
"""
Returns information about the requested memory
"""
memory = self.mem_by_name.get(name)
if not memory:
message = "Memory type '{}' not defined for device '{}'".format(name, self.device[DeviceInfoKeys.NAME])
raise ValueError(message)
return memory

View File

@@ -0,0 +1,88 @@
#pylint: disable=too-few-public-methods
"""
Definitions of keys for device info dictionaries
"""
class DeviceInfoKeys(object):
"""
Base class with common device info keys
"""
NAME = 'name'
ARCHITECTURE = 'architecture'
INTERFACE = 'interface'
DEVICE_ID = 'device_id'
@classmethod
def get_all(cls):
"""
Get a list of all keys
:return List of all valid keys (baseclass and any subclass keys if run on a subclass)
"""
all_keys = []
for attribute in dir(cls):
if not attribute.startswith('__') and not callable(getattr(cls, attribute)):
all_keys.append(getattr(cls, attribute))
return all_keys
class DeviceInfoKeysAvr(DeviceInfoKeys):
"""
Keys specific to AVR device info files
"""
NVMCTRL_BASE = 'nvmctrl_base'
SYSCFG_BASE = 'syscfg_base'
OCD_BASE = 'ocd_base'
PROG_CLOCK_KHZ = 'prog_clock_khz'
ADDRESS_SIZE = 'address_size'
class DeviceInfoKeysAvr32(DeviceInfoKeys):
"""
Keys specific to 32-bit AVR device info files
"""
RESET_DOMAINS = 'reset_domains'
class DeviceInfoKeysPic(DeviceInfoKeys):
"""
Keys specific to PIC device info files
"""
# This key should have _byte or _word ending in device info files to specify byte or word address
# This ending will be removed by the getdeviceinfo function before returning the device info dictionary
DEFAULT_BULK_ERASE_ADDRESS = 'default_bulk_erase_address'
class DeviceMemoryInfoKeys(object):
"""
Keys for device memory info dictionary
These keys are found in the dictionaries returned by DeviceMemoryInfo for each memory type
"""
NAME = 'name'
ADDRESS = 'address'
SIZE = 'size'
PAGE_SIZE = 'page_size'
WRITE_SIZE = 'write_size'
READ_SIZE = 'read_size'
ERASE_ADDRESS = 'erase_address'
CHIPERASE_EFFECT = 'chiperase_effect'
ISOLATED_ERASE = 'isolated_erase'
HEXFILE_ADDRESS = 'hexfile_address'
HEXFILE_SIZE = 'hexfile_size'
VERIFY_MASK = 'verify_mask'
@classmethod
def get_all(cls):
"""
Get a list of all keys
:return List of all valid keys (baseclass and any subclass keys if run on a subclass)
"""
all_keys = []
for attribute in dir(cls):
if not attribute.startswith('__') and not callable(getattr(cls, attribute)):
all_keys.append(getattr(cls, attribute))
return all_keys

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1604 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1604',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3c00,
'internal_sram_size_bytes': 0x0400,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9425,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1606 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1606',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3c00,
'internal_sram_size_bytes': 0x0400,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9424,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1607 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1607',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3c00,
'internal_sram_size_bytes': 0x0400,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9423,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1614 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1614',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9422,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1616 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1616',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9421,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1617 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1617',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9420,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1624 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1624',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E942A,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1626 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1626',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9429,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny1627 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny1627',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x4000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9428,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny202 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny202',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0040,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f80,
'internal_sram_size_bytes': 0x0080,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x0800,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9123,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny204 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny204',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0040,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f80,
'internal_sram_size_bytes': 0x0080,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x0800,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9122,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny212 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny212',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0040,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f80,
'internal_sram_size_bytes': 0x0080,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x0800,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9121,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny214 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny214',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0040,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f80,
'internal_sram_size_bytes': 0x0080,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x0800,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9120,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny3216 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny3216',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x40,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x80,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x40,
'user_row_page_size_bytes': 0x40,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x8000,
'flash_page_size_bytes': 0x80,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x80,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9521,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny3217 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny3217',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x40,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3800,
'internal_sram_size_bytes': 0x0800,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x80,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x40,
'user_row_page_size_bytes': 0x40,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x8000,
'flash_page_size_bytes': 0x80,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x80,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9522,
}

View File

@@ -0,0 +1,85 @@
"""
Required device info for the attiny3224 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny3224',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x40,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3400,
'internal_sram_size_bytes': 0x0C00,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x80,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x40,
'user_row_page_size_bytes': 0x40,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x8000,
'flash_page_size_bytes': 0x80,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x80,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9528,
}

View File

@@ -0,0 +1,85 @@
"""
Required device info for the attiny3226 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny3226',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x40,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3400,
'internal_sram_size_bytes': 0x0C00,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x80,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x40,
'user_row_page_size_bytes': 0x40,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x8000,
'flash_page_size_bytes': 0x80,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x80,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9527,
}

View File

@@ -0,0 +1,85 @@
"""
Required device info for the attiny3227 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny3227',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0100,
'eeprom_page_size_bytes': 0x40,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3400,
'internal_sram_size_bytes': 0x0C00,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x80,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x40,
'user_row_page_size_bytes': 0x40,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x8000,
'flash_page_size_bytes': 0x80,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x80,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9526,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny402 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny402',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f00,
'internal_sram_size_bytes': 0x0100,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9227,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny404 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny404',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f00,
'internal_sram_size_bytes': 0x0100,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9226,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny406 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny406',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f00,
'internal_sram_size_bytes': 0x0100,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9225,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny412 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny412',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f00,
'internal_sram_size_bytes': 0x0100,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9223,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny414 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny414',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f00,
'internal_sram_size_bytes': 0x0100,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9222,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny416 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny416',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f00,
'internal_sram_size_bytes': 0x0100,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9221,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny417 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny417',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3f00,
'internal_sram_size_bytes': 0x0100,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9220,
}

View File

@@ -0,0 +1,86 @@
"""
Required device info for the attiny424 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, and his users were complaining,
it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny424',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E922C,
}

View File

@@ -0,0 +1,86 @@
"""
Required device info for the attiny426 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, and his users were complaining,
it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny426',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E922B,
}

View File

@@ -0,0 +1,86 @@
"""
Required device info for the attiny427 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, and his users were complaining,
it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny427',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x1000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E922A,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny804 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny804',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9325,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny806 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny806',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9324,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny807 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny807',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9323,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny814 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny814',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9322,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny816 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny816',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9321,
}

View File

@@ -0,0 +1,84 @@
"""
Required device info for the attiny817 devices
The following data was collected from device pack Microchip.ATtiny_DFP 2.4.111
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny817',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3e00,
'internal_sram_size_bytes': 0x0200,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9320,
}

View File

@@ -0,0 +1,86 @@
"""
Required device info for the attiny824 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, and his users were complaining,
it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny824',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3c00,
'internal_sram_size_bytes': 0x0400,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9329,
}

View File

@@ -0,0 +1,86 @@
"""
Required device info for the attiny826 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, and his users were complaining,
it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny826',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3c00,
'internal_sram_size_bytes': 0x0400,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9328,
}

View File

@@ -0,0 +1,86 @@
"""
Required device info for the attiny827 devices
The following data would normally have been collected from device packs.
But since Microchip hasn't done this, and his users were complaining,
it was deduced from device packs by Spence Konde.
"""
from pymcuprog.deviceinfo.eraseflags import ChiperaseEffect
DEVICE_INFO = {
'name': 'attiny827',
'architecture': 'avr8x',
# eeprom
'eeprom_address_byte': 0x00001400,
'eeprom_size_bytes': 0x0080,
'eeprom_page_size_bytes': 0x20,
'eeprom_read_size_bytes': 1,
'eeprom_write_size_bytes': 1,
'eeprom_chiperase_effect': ChiperaseEffect.CONDITIONALLY_ERASED_AVR,
'eeprom_isolated_erase': True,
# fuses
'fuses_address_byte': 0x00001280,
'fuses_size_bytes': 0xA,
'fuses_page_size_bytes': 1,
'fuses_read_size_bytes': 1,
'fuses_write_size_bytes': 1,
'fuses_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'fuses_isolated_erase': False,
# internal_sram
'internal_sram_address_byte': 0x3c00,
'internal_sram_size_bytes': 0x0400,
'internal_sram_page_size_bytes': 1,
'internal_sram_read_size_bytes': 1,
'internal_sram_write_size_bytes': 1,
'internal_sram_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'internal_sram_isolated_erase': False,
# lockbits
'lockbits_address_byte': 0x0000128A,
'lockbits_size_bytes': 0x1,
'lockbits_page_size_bytes': 1,
'lockbits_read_size_bytes': 1,
'lockbits_write_size_bytes': 1,
'lockbits_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'lockbits_isolated_erase': False,
# signatures
'signatures_address_byte': 0x00001100,
'signatures_size_bytes': 0x3,
'signatures_page_size_bytes': 0x40,
'signatures_read_size_bytes': 1,
'signatures_write_size_bytes': 0,
'signatures_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'signatures_isolated_erase': False,
# user_row
'user_row_address_byte': 0x00001300,
'user_row_size_bytes': 0x20,
'user_row_page_size_bytes': 0x20,
'user_row_read_size_bytes': 1,
'user_row_write_size_bytes': 1,
'user_row_chiperase_effect': ChiperaseEffect.NOT_ERASED,
'user_row_isolated_erase': True,
# flash
'flash_address_byte': 0x00008000,
'flash_size_bytes': 0x2000,
'flash_page_size_bytes': 0x40,
'flash_read_size_bytes': 2,
'flash_write_size_bytes': 0x40,
'flash_chiperase_effect': ChiperaseEffect.ALWAYS_ERASED,
'flash_isolated_erase': True,
# Some extra AVR specific fields
'nvmctrl_base': 0x00001000,
'syscfg_base': 0x00000F00,
'ocd_base': 0x00000F80,
'prog_clock_khz': 900,
'interface': 'UPDI',
'address_size': '16-bit',
'device_id': 0x1E9327,
}

View File

@@ -0,0 +1,25 @@
"""
Definitions of erase related flags for the device models
"""
import inspect
from pymcuprog.utils import enum
# Flag used to specify if a memory type will be erased by a chip erase (AVR) or the widest/default bulk erase (PIC)
ChiperaseEffect = enum(
ALWAYS_ERASED='always erased',
CONDITIONALLY_ERASED_AVR='conditionally erased (depending upon EESAVE fuse setting)',
CONDITIONALLY_ERASED_PIC='conditionally erased (depending upon Code Protect configuration bit(s) settings)',
NOT_ERASED='not erased')
def get_list_of_chiperase_effects():
"""Return a list of all ChiperaseEffect values"""
chiperase_effect_attributes = inspect.getmembers(ChiperaseEffect, lambda a: not inspect.isroutine(a))
chiperase_effect_values = []
for attribute in chiperase_effect_attributes:
# Builtin routines always starts and ends with double underscore (__)
if not (attribute[0].startswith('__') and attribute[0].endswith('__')):
# Only the attribute values are returned
chiperase_effect_values.append(attribute[1])
return chiperase_effect_values

View File

@@ -0,0 +1,41 @@
#pylint: disable=too-few-public-methods
"""
Memory name definitions
"""
class MemoryNameAliases(object):
"""
Memory names that are actually not real memories but an alias for several memories
"""
ALL = 'all'
class MemoryNames(object):
"""
Memory names corresponding to target device memories
"""
# Real memories
FLASH = 'flash'
CONFIG_WORD = 'config_words'
USER_ID = 'user_id'
USER_ROW = 'user_row'
EEPROM = 'eeprom'
FUSES = 'fuses'
CALIBRATION_ROW = 'calibration_row'
ICD = 'icd'
LOCKBITS = 'lockbits'
SIGNATURES = 'signatures'
INTERNAL_SRAM = 'internal_sram'
@classmethod
def get_all(cls):
"""
Get a list of all memories representing actual device memories
:return List of all memory names representing actual device memories
"""
all_memories = []
for attribute in dir(cls):
if not attribute.startswith('__') and not callable(getattr(cls, attribute)):
all_memories.append(getattr(cls, attribute))
return all_memories

View File

@@ -0,0 +1,181 @@
"""
Module providing read and write functionality towards hex files with data intended for target device memories
"""
import copy
import os
from array import array
from collections import namedtuple
from intelhex import IntelHex
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path # python 2 backport
from .deviceinfo.deviceinfokeys import DeviceMemoryInfoKeys, DeviceInfoKeys
def write_memories_to_hex(filename, memory_segments):
"""
Write a collection of memory segments to a hex file
Each segment will be written from relative offset 0 (i.e. start of each memory segment)
:param filename: Name/path of hex file to write to
: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.
"""
hexfile = IntelHex()
for memory_segment in memory_segments:
_add_data_to_hex(hexfile, memory_segment.data, memory_segment.memory_info)
_write_hex_to_file(hexfile, filename)
def write_memory_to_hex(filename, memory_segment, offset):
"""
Write one memory segment to a hex file with data starting at relative offset given by offset parameter.
:param filename: Name/path of hex file to write to
:param memory_segment: namedtuple 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 offset: Relative offset for the data within the memory segment
"""
hexfile = IntelHex()
_add_data_to_hex(hexfile, memory_segment.data, memory_segment.memory_info, offset)
_write_hex_to_file(hexfile, filename)
def read_memories_from_hex(filename, device_memory_info):
"""
Read the content of a hexfile
:param filename: Name/path of hex file to read from
:param device_memory_info: DeviceMemoryInfo instance for the device the hex file is intended for
:returns: list of namedtuples with three fields: data, offset and memory_info. data contains a byte array
of raw data bytes, offset is the start address within the memory the data starts at and memory_info
is a dictionary with the memory info as defined in pymcuprog.deviceinfo.deviceinfo
"""
hexfile = IntelHex()
hexfile.fromfile(filename, format='hex')
memory_segments = []
for segment in hexfile.segments():
start = segment[0]
stop = segment[1]
subsegment_start = start
subsegment_stop = start
while subsegment_stop < stop:
current_memory_info = device_memory_info.memory_info_by_address(subsegment_start,
DeviceMemoryInfoKeys.HEXFILE_ADDRESS,
DeviceMemoryInfoKeys.HEXFILE_SIZE)
if current_memory_info is None:
raise IndexError(
"Hexfile contains data at hex address 0x{:X} which is outside any memory".format(subsegment_start))
current_hexfile_address = current_memory_info[DeviceMemoryInfoKeys.HEXFILE_ADDRESS]
current_hexfile_size = current_memory_info[DeviceMemoryInfoKeys.HEXFILE_SIZE]
subsegment_stop = current_hexfile_address + current_hexfile_size
if stop < subsegment_stop:
# Reached end of segment
subsegment_stop = stop
memory_tuple = namedtuple('MemorySegment', 'data offset memory_info')
data = hexfile.tobinarray(start=subsegment_start, end=subsegment_stop - 1)
current_size = current_memory_info[DeviceMemoryInfoKeys.SIZE]
if current_hexfile_size == current_size*2:
# There are phantom bytes in the hexfile (PIC16 EEPROM), so every 2nd byte should be removed
data = remove_phantom_bytes(data)
memory_tuple.data = data
memory_tuple.memory_info = current_memory_info
memory_tuple.offset = subsegment_start - current_hexfile_address
memory_segments.append(copy.deepcopy(memory_tuple))
subsegment_start = subsegment_stop
return memory_segments
def verify_flash_from_hex(hex_filename, backend, max_read_chunk=None):
"""
Verify the contents of flash against a hex-file
:param filename: Name/path of hex-file to verify
:param device_memory_info: DeviceMemoryInfo instance for the device the hex file should be verified against
:param backend: Reference to the Backend class of pymcuprog
:returns: Boolean value indicating success or failure of the operation
"""
hexfile = IntelHex(hex_filename)
segments = hexfile.segments()
for i in range(len(segments)):
segment_data = []
for j in range(segments[i][1]-segments[i][0]):
segment_data.append(hexfile[segments[i][0]+j])
verify_status = backend.verify_memory(segment_data, 'flash', segments[i][0], max_read_chunk=max_read_chunk)
if verify_status is False:
return False
return True
def remove_phantom_bytes(data):
"""
Remove every 2nd byte from the data
"""
data_stripped = []
for index in range(0, len(data), 2):
data_stripped.append(data[index])
# Make a bin array out of the data list to be consistent with the data format of
# the data fetched directly from the hex file
data_stripped_binarray = array('B')
data_stripped_binarray.fromlist(data_stripped)
return data_stripped_binarray
def _add_data_to_hex(intelhex, data, memory_info, offset=0):
"""
Add given data starting at relative index offset to IntelHex instance intelhex
:param intelhex: IntelHex object
:param data: raw data bytes
:param memory_info: memory info as provided by pymcuprog.deviceinfo.deviceinfo
:param offset: relative offset within the memory
"""
hexfile_address_key = DeviceMemoryInfoKeys.HEXFILE_ADDRESS
hexfile_size_key = DeviceMemoryInfoKeys.HEXFILE_SIZE
size_key = DeviceMemoryInfoKeys.SIZE
name = memory_info[DeviceInfoKeys.NAME]
if offset+len(data) > memory_info[hexfile_size_key]:
raise IndexError(
"Attempting to write outside boundary of {} memory ({} bytes starting at offset {})".format(name,
len(data),
offset))
hex_offset = memory_info[hexfile_address_key] + offset
if memory_info[hexfile_size_key] == memory_info[size_key]*2:
# Hex file should contain one phantom byte per data byte in the hex file (PIC16 EEPROM)
for i, dat in enumerate(data):
intelhex[i*2 + hex_offset] = data[i]
intelhex[i*2 + 1 + hex_offset] = 0 & 0xFF
else:
for i, dat in enumerate(data):
intelhex[i + hex_offset] = dat
def _write_hex_to_file(intelhex, filename):
"""
Write intelhex object to file.
Directories will be created if path does not exist
:param intelhex: IntelHex instance
:param filename: Name/path to write intelhex object to
"""
directory = os.path.dirname(filename)
if directory != '' and not os.path.exists(directory):
Path(directory).mkdir(exist_ok=True, parents=True)
intelhex.write_hex_file(filename)

View File

@@ -0,0 +1,57 @@
version: 1
disable_existing_loggers: False
formatters:
timestamped:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
detailed:
format: "%(name)s - %(levelname)s - %(message)s"
simple:
format: "%(message)s"
handlers:
# Logging to the console is default to WARNING with detailed output:
console:
class: logging.StreamHandler
level: WARNING
formatter: detailed
stream: ext://sys.stdout
# Logging debug output to file
# Handler disabled by default - for reference only
debug_file_handler:
class: logging.FileHandler
level: DEBUG
formatter: timestamped
# File path will be user log directory for this application
filename: debug.log
encoding: utf8
# Logging errors to file
# Handler disabled by default - for reference only
error_file_handler:
class: logging.handlers.RotatingFileHandler
level: ERROR
formatter: timestamped
# File path will be user log directory for this application
filename: errors.log
maxBytes: 10485760 # 10MB
backupCount: 20
encoding: utf8
loggers:
# pyedbglib library should be kept to critical errors to console only
pyedbglib:
level: ERROR
handlers: [console]
propagate: no
root:
# Default level is warning
# this is increased with -v <level> in CLI usage
level: WARNING
# Default handlers is console only
handlers: [console]
# Add debug_file_handler for debug output to file
# Add error_file_handler for error output to file
# See configuration in handlers section above
#handlers: [console, debug_file_handler, error_file_handler]

View File

@@ -0,0 +1,130 @@
"""
NVM layer protocols
"""
# Python 3 compatibility for Python 2
from __future__ import print_function
from logging import getLogger
from .deviceinfo.deviceinfokeys import DeviceInfoKeys
def get_nvm_access_provider(transport, device_info, interface="", packpath=None, frequency=None, options=""):
"""
Returns an NVM provider with the requested properties
:param transport: transport layer object
:param device_info: device info dict
:param interface: physical interface for NVM
:param packpath: path to pack
:param frequency: interface clock
:param options: special options
:return: NVM access object
"""
# Although it is considered best practice to have imports at top level, in this case it makes sense to have the
# imports on the function level as in most cases only one import will be used. Having all imports at the top
# level will then be a waste of resources.
#pylint: disable=import-outside-toplevel
# There will be cyclic imports since the modules imported below containing NVM Access providers will import
# from the current module since all NVM Access providers inherits from the NVM Access provider base classes
# defined in the current module, but this should be ok since the imports below are late.
#pylint: disable=cyclic-import
accessprovider = None
architecture = device_info[DeviceInfoKeys.ARCHITECTURE].lower()
if DeviceInfoKeys.INTERFACE in device_info:
interface = device_info[DeviceInfoKeys.INTERFACE].lower()
if architecture in ['pic16', 'pic18', 'pic24']:
from .nvmpic import NvmAccessProviderCmsisDapPic
accessprovider = NvmAccessProviderCmsisDapPic(transport, device_info, packpath, options=options)
elif architecture == 'avr8x':
if isinstance(transport, str):
if interface == 'updi':
from .nvmserialupdi import NvmAccessProviderSerial
accessprovider = NvmAccessProviderSerial(transport, device_info, baud=frequency)
elif interface == 'updi':
from .nvmupdi import NvmAccessProviderCmsisDapUpdi
accessprovider = NvmAccessProviderCmsisDapUpdi(transport, device_info=device_info,
frequency=frequency, options=options)
elif architecture == 'avr8':
if interface == 'isp':
if interface == "debugwire":
from .nvmdebugwire import NvmAccessProviderCmsisDapDebugwire
accessprovider = NvmAccessProviderCmsisDapDebugwire(transport, device_info)
else:
from .nvmspi import NvmAccessProviderCmsisDapSpi
accessprovider = NvmAccessProviderCmsisDapSpi(transport, device_info)
elif architecture == 'cortex-m0plus':
from .nvmmzeroplus import NvmAccessProviderCmsisDapMZeroPlus
accessprovider = NvmAccessProviderCmsisDapMZeroPlus(transport, device_info, frequency)
elif architecture == 'avr32':
from .nvmavr32 import NvmAccessProviderCmsisDapAvr32
accessprovider = NvmAccessProviderCmsisDapAvr32(transport, device_info)
return accessprovider
class NvmAccessProvider:
"""
Wrapper for device info
"""
def __init__(self, device_info):
self.device_info = device_info
self.logger = getLogger(__name__)
def _log_incomplete_stack(self, device_stack):
"""
Used to tell the user this device stack is not completed yet
:param device_stack: User friendly name of target stack
"""
self.logger.warning("")
self.logger.warning("%s stack is in Alpha state", device_stack)
self.logger.warning("Expect some features to be missing")
self.logger.warning("")
def start(self, user_interaction_callback=None):
"""
Start (activate) session
:param user_interaction_callback: Callback to be called when user interaction is required,
for example when doing UPDI high-voltage activation with user target power toggle.
This function could ask the user to toggle power and halt execution waiting for the user
to respond (this is default behavior if the callback is None), or if the user is another
script it could toggle power automatically and then return.
"""
#pylint: disable=unused-argument
self.logger.info("No specific initializer for this provider")
def stop(self):
"""
Stop (deactivate) session
"""
self.logger.info("No specific de-initializer for this provider")
def hold_in_reset(self):
"""
Hold target in reset
"""
self.logger.info("hold_in_reset not implemented for this provider")
def release_from_reset(self):
"""
Release target from reset
"""
self.logger.info("release_from_reset not implemented for this provider")
class NvmAccessProviderCmsisDapTool(NvmAccessProvider):
"""
General CMSIS-DAP Tool
"""
def __init__(self, device_info):
NvmAccessProvider.__init__(self, device_info)
class NvmAccessProviderCmsisDapAvr(NvmAccessProviderCmsisDapTool):
"""
AVR CMSIS DAP Tool
"""
def __init__(self, device_info):
NvmAccessProviderCmsisDapTool.__init__(self, device_info)

View File

@@ -0,0 +1,248 @@
"""
pyupdi-esque NVM implementation
"""
import binascii
from pyedbglib.util import binary
from . import utils
from .nvm import NvmAccessProvider
from .deviceinfo import deviceinfo
from .deviceinfo.deviceinfokeys import DeviceInfoKeysAvr, DeviceMemoryInfoKeys
from .deviceinfo.memorynames import MemoryNames
from .serialupdi.application import UpdiApplication
import math
from . import progress_bar
# This is a data class so it should not need any methods but will have many instance variables
# pylint: disable=too-many-instance-attributes,too-few-public-methods
class Dut:
"""
Create a device object for UpdiApplication
"""
def __init__(self, dev_info):
# Parse the device info for memory descriptions
device_memory_info = deviceinfo.DeviceMemoryInfo(dev_info)
flash_info = device_memory_info.memory_info_by_name(MemoryNames.FLASH)
self.flash_start = flash_info[DeviceMemoryInfoKeys.ADDRESS]
self.flash_size = flash_info[DeviceMemoryInfoKeys.SIZE]
self.flash_pagesize = flash_info[DeviceMemoryInfoKeys.PAGE_SIZE]
self.syscfg_address = dev_info[DeviceInfoKeysAvr.SYSCFG_BASE]
self.nvmctrl_address = dev_info[DeviceInfoKeysAvr.NVMCTRL_BASE]
address_key = DeviceMemoryInfoKeys.ADDRESS
self.sigrow_address = device_memory_info.memory_info_by_name(MemoryNames.SIGNATURES)[address_key]
self.fuses_address = device_memory_info.memory_info_by_name(MemoryNames.FUSES)[address_key]
self.userrow_address = device_memory_info.memory_info_by_name(MemoryNames.USER_ROW)[address_key]
class NvmAccessProviderSerial(NvmAccessProvider):
"""
NVM Access the Python AVR way
"""
def __init__(self, port, device_info, baud):
self.avr = None
NvmAccessProvider.__init__(self, device_info)
if not baud:
baud = 115200
self.dut = Dut(device_info)
self.avr = UpdiApplication(port, baud, self.dut)
# Read the device info to set up the UPDI stack variant
self.avr.read_device_info()
try:
self.avr.enter_progmode()
except IOError as inst:
self.logger.error("Device is locked.\nError:\n%s", inst)
def read_device_id(self):
"""
Read and display (log) the device info
:returns: Device ID raw bytes (Little endian)
"""
self.avr.read_device_info()
signatures_base = self.dut.sigrow_address
# Read 3 bytes
sig = self.avr.read_data(signatures_base, 3)
device_id_read = binary.unpack_be24(sig)
self.logger.info("Device ID: '%06X'", device_id_read)
if not self.device_info.get(DeviceInfoKeysAvr.DEVICE_ID) == device_id_read:
self.logger.warning("ID read ('%06X') does not match expected device id! ('%06X')", device_id_read,
self.device_info.get(DeviceInfoKeysAvr.DEVICE_ID))
raise ValueError("Device ID does not match")
revision = self.avr.read_data(self.device_info.get(DeviceInfoKeysAvr.SYSCFG_BASE) + 1, 1)
self.logger.info("Device revision: '%s'", chr(revision[0] + ord('A')))
serial = self.avr.read_data(signatures_base + 3, 10)
self.logger.info("Device serial number: '%s'", binascii.hexlify(serial))
# Return the raw signature bytes, but swap the endianness as target sends ID as Big endian
return bytearray([sig[2], sig[1], sig[0]])
def erase(self, memory_info=None, address=None):
"""
Do a chip erase of the device
"""
_dummy = memory_info
_dummy = address
try:
self.avr.nvm.chip_erase()
except IOError as inst:
self.logger.error("Device is locked. Performing unlock with chip erase.\nError: ('%s')", inst)
self.avr.unlock()
def write(self, memory_info, offset, data, blocksize=0, pagewrite_delay=0):
"""
Write the memory with data
:param memory_info: dictionary for the memory as provided by the DeviceMemoryInfo class
:param offset: relative offset within the memory type
:param data: the data to program
:return: None
"""
# Make sure the data is aligned to a memory page
data_aligned, offset_aligned = utils.pagealign(data,
offset,
memory_info[DeviceMemoryInfoKeys.PAGE_SIZE],
memory_info[DeviceMemoryInfoKeys.WRITE_SIZE])
memtype_string = memory_info[DeviceMemoryInfoKeys.NAME]
offset_aligned += memory_info[DeviceMemoryInfoKeys.ADDRESS]
if memtype_string in (MemoryNames.FLASH, MemoryNames.EEPROM, MemoryNames.FUSES):
write_chunk_size = memory_info[DeviceMemoryInfoKeys.PAGE_SIZE]
else:
write_chunk_size = len(data_aligned)
n_chunk = math.ceil(len(data_aligned)/write_chunk_size)
bar = progress_bar.ProgressBar(n_chunk, hide=n_chunk == 1)
while data_aligned:
if len(data_aligned) < write_chunk_size:
write_chunk_size = len(data_aligned)
chunk = data_aligned[0:write_chunk_size]
self.logger.debug("Writing %d bytes to address 0x%06X", write_chunk_size, offset_aligned)
if memtype_string == MemoryNames.FUSES:
self.avr.nvm.write_fuse(offset_aligned, chunk)
elif memtype_string == MemoryNames.EEPROM:
self.avr.nvm.write_eeprom(offset_aligned, chunk)
else:
# Spence Konde, 5/8/2021:
# As far as I can tell, this is the only point where, we're writing a hex file, we know both the page size
# AND are in the path of blocksize parameter. So - if its 0 or not given we should "do the old behavior", then
# blocksize=2. The special value -1 tells us to have it write blocks equal to chunk/page size. Any other number
# will be used as blocksize. Negative numbers beyond -1 were replaced with zero way at the beginning, as they would
# result in crazy behavior and make everything fall over.
# megaTinyCore and DxCore will always pass -1 as blocksize unless we find something where that doesn't work.
#
# Also, we are now finally in the section of the code specific to serialupdi. Up until we get here, 0 is the default
# and if that's what we got, we omit it when making other calls, because there are almost certainly calls elsewhere
# that. Now that we are here, the default value is 2 (ie, one word at a time) but that won'ty be something we see often.
#
# It strikes me that here is *ALSO* where we know whether we are on the first, a middle, or the last page. Say we
# kept count of how many pages had been written already - if it was 0 and nChunk > 1, we would pass an argument that says
# This is the first page we are writing, do all that stuff we need to do at the start of a bulk write.
# if it was nChunk - 1, we would send a different value for that argumennt, saying it was the last one of a bulk write
# so it should do the stuff to end the bulk write mode. And otherwise, it gets a third value that gets treated as
# a signal to omit all of those. for the streamlined write protocol, which could improve performance by another 22-45%
# If you agree, we should do that.
# What we currently do is grossly inefficient, because (due to the penalty for small packets) we spend half of our time
# for every page: Setting the address pointer (only need to do this at the beginning - when reading second and subsequent pages
# the previous writes left the pointer at exactly the location we then set it to.). Setting NVM cmd to FLWR - only needs to be done
# at the start of a bulk write, assuming we also stop setting NVM command to NOOP after every page. Setting RSD - if we
# do all I'm talking about here, we can set it at start of bulk write. And we can juyst check for for NVM errors before
# the first and after the last page, not before and after every page. My models suggest this should improve performance
# by 22% at 115200 baud, and 44% and 345600 baud (which is 1.5x 230400 baud - and happens to be about the fastest you can
# do a bulk write that is consistent with the datasheet flash write time spec.
#
# See also my comment below in read() - these two places are where we can achieve the last noticable performance leaps.
# -Spence
bulk = 1
if n_chunk == 1:
#if omly one chunk, it is NOT a bulk write.
bulk = 0
elif len(data_aligned) <= write_chunk_size:
# We are on the last page of a bulk write
bulk = 2
if blocksize == 0:
self.avr.nvm.write_flash(offset_aligned, chunk, pagewrite_delay=pagewrite_delay)
else:
self.avr.nvm.write_flash(offset_aligned, chunk, blocksize=blocksize, bulkwrite = bulk, pagewrite_delay=pagewrite_delay)
offset_aligned += write_chunk_size
data_aligned = data_aligned[write_chunk_size:]
bar.step()
def read(self, memory_info, offset, numbytes, max_read_chunk=None):
"""
Read the memory in chunks
:param memory_info: dictionary for the memory as provided by the DeviceMemoryInfo class
:param offset: relative offset in the memory type
:param numbytes: number of bytes to read
:param max_read_chunk: <=256
:return: array of bytes read
"""
offset += memory_info[DeviceMemoryInfoKeys.ADDRESS]
# if reading from flash, we want to read words if it would reduce number of USB serial transactions.
# this function is called for everything though, so be careful not to use it for memories read one byte at a time, like fuses
data = []
if max_read_chunk is None:
read_chunk_size = 0x100
else:
read_chunk_size = max_read_chunk
use_word_access = False
memtype_string = memory_info[DeviceMemoryInfoKeys.NAME]
if memtype_string in (MemoryNames.FLASH):
if numbytes > 0x100 and max_read_chunk is None:
use_word_access = True
read_chunk_size = 0x200
# SACRIFICES SPEED FOR COMPATIBILITY - above line should happen whenever --limitreadsize=1 command line parameter is not passed, so we can only turn it on for specific tools -> programmer options that have this weird limitation. I couldn't propagate it through this mess!
n_chunk = math.ceil(numbytes/read_chunk_size)
bar = progress_bar.ProgressBar(n_chunk, hide=n_chunk == 1)
while numbytes:
if numbytes < read_chunk_size:
read_chunk_size = numbytes
self.logger.debug("Reading %d bytes from address 0x%06X", read_chunk_size, offset)
if use_word_access:
data += self.avr.read_data_words(offset, read_chunk_size>> 1)
else:
data += self.avr.read_data(offset, read_chunk_size)
offset += read_chunk_size
numbytes -= read_chunk_size
bar.step()
return data
def hold_in_reset(self):
"""
Hold device in reset
"""
# For UPDI parts it is sufficient to enter programming mode to hold the target in reset
# Since the start function is a prerequisite to all functions in this file it can be
# assumed that programming mode already has been entered
return
def release_from_reset(self):
"""
Release device from reset
"""
# Entering programming mode on UPDI parts will hold the device in reset. So to release
# the reset the programming mode must be left.
self.avr.leave_progmode()
def stop(self):
"""
Stop the debugging session
"""
if self.avr is not None:
self.avr.leave_progmode()

View File

@@ -0,0 +1,314 @@
"""
Python MCU programmer
"""
import copy
from logging import getLogger
from collections import namedtuple
# Device data
from .deviceinfo import deviceinfo
from . import utils
from .pymcuprog_errors import PymcuprogNotSupportedError, PymcuprogSessionConfigError
from .pymcuprog_errors import PymcuprogError
from .nvm import get_nvm_access_provider
from .deviceinfo.memorynames import MemoryNameAliases
from .deviceinfo.deviceinfokeys import DeviceInfoKeysPic, DeviceMemoryInfoKeys
DEFAULT_BULK_ERASE_ADDRESS_KEY = DeviceInfoKeysPic.DEFAULT_BULK_ERASE_ADDRESS
class Programmer:
"""
Main programmer class.
"""
def __init__(self, transport):
# Hook onto logger
self.logger = getLogger(__name__)
# Use transport passed in
self.transport = transport
# Clear device model and mem info objects
self.device_info = None
self.device_model = None
self.device_memory_info = None
self.options = {}
def set_options(self, options):
"""
Stores options
:param options: options to store
"""
self.options = options
def load_device(self, device_name):
"""
Loads the device from the device folder
:param device_name:
:raises: PymcuprogNotSupportedError if device is not supported
"""
# Try to instantiate device info. This will check if there is device support at all
try:
self.logger.info("Setting up programming session for '%s'", device_name)
self.device_info = deviceinfo.getdeviceinfo(device_name)
except ImportError as err:
raise PymcuprogNotSupportedError("Unable to find device info: {}".format(err))
# Now build a memory model for this device
self.device_memory_info = deviceinfo.DeviceMemoryInfo(self.device_info)
def setup_device(self, interface=None, packpath=None, clk=None):
"""
Sets up a programming session with a given device
:param device_name: device to program
:param interface: interface to use
:param packpath: path to packs to use (for PIC)
:param clk: clock frequency
:raises SerialException if unable to connect to serial port (if using serial port instead of physical debugger)
"""
# Device must be loaded first
if self.device_info is None:
raise PymcuprogError("Device must be loaded before setup!")
# Find a NVM provider that matches the device and transport
try:
self.device_model = get_nvm_access_provider(self.transport,
self.device_info,
interface=interface,
packpath=packpath,
frequency=clk,
options=self.options)
except ImportError:
raise PymcuprogSessionConfigError(
"Unable to setup stack using the given packpath: '{0:s}'".format(
packpath or "None"))
if self.device_model is None:
raise PymcuprogSessionConfigError("Unable to setup stack, check session config parameters")
def start(self, user_interaction_callback=None):
"""
Starts the programming session with the device model
:param user_interaction_callback: Callback to be called when user interaction is required,
for example when doing UPDI high-voltage activation with user target power toggle.
This function could ask the user to toggle power and halt execution waiting for the user
to respond (this is default behavior if the callback is None), or if the user is another
script it could toggle power automatically and then return.
"""
self.device_model.start(user_interaction_callback=user_interaction_callback)
def stop(self):
"""
Stops the programming session with the device model
"""
return self.device_model.stop()
def get_device_model(self):
"""
Exposes the device model in use to clients
"""
return self.device_model
def get_device_memory_info(self):
"""
Exposes the device memory model to clients
"""
return self.device_memory_info
# Device model API functions
def read_device_id(self):
"""
Read the device ID
:returns: Device ID raw bytes (Little endian)
"""
self.logger.info("Reading device ID...")
return self.device_model.read_device_id()
def erase(self, memory_name, address):
"""
Erase the device
:param memory_name: memory region to erase as defined in deviceinfo.memorynames
MemoryNameAliases.ALL will run the widest erase (e.g. chip erase on AVR or the widest bulk erase on PIC)
:param address: address to erase
"""
self.logger.info("Erase...")
if memory_name == MemoryNameAliases.ALL:
# Run default erase which is the widest erase
memory_info = None
if DEFAULT_BULK_ERASE_ADDRESS_KEY in self.device_info:
address = self.device_info[DEFAULT_BULK_ERASE_ADDRESS_KEY]
else:
address = None
else:
memory_info = self.device_memory_info.memory_info_by_name(memory_name)
self.device_model.erase(memory_info=memory_info, address=address)
def write_memory(self, data, memory_name, offset=0, blocksize=0, pagewrite_delay=0):
"""
Write memory on the device
:param data: data to write
:param memory_name: memory type to write
:param offset: offset/address within that region to write
:return: boolean status
:raises: ValueError if trying to write outside the specified memory
:raises: ValueError if the specified memory is not defined for the target device
:raises: PymcuprogNotSupportedError if memory can't be written
"""
self.logger.info("Write...")
# Just some sanity checking of inputs
if offset < 0:
raise ValueError("Write offset can't be negative, requested offset: {}".format(offset))
# Get information about the memory area
memory = self.device_memory_info.memory_info_by_name(memory_name)
size = memory[DeviceMemoryInfoKeys.SIZE]
if memory[DeviceMemoryInfoKeys.WRITE_SIZE] == 0:
raise PymcuprogNotSupportedError("{} memory can't be written".format(memory_name))
if offset + len(data) > size:
msg = "{} bytes of data at offset {} is outside the boundaries of '{}' with size {}".format(len(data),
offset,
memory_name,
size)
raise ValueError(msg)
# Write the data to NVM
self.logger.info("Writing %d bytes of data to %s...", len(data), memory[DeviceMemoryInfoKeys.NAME])
if blocksize == 0:
self.device_model.write(memory, offset, data, pagewrite_delay=pagewrite_delay)
else:
self.device_model.write(memory, offset, data, blocksize=blocksize, pagewrite_delay=pagewrite_delay)
self.logger.info("Write complete.")
return True
def verify_memory(self, data, memory_name, offset=0, max_read_chunk=None):
"""
Verify memory content
:param data: data to verify against
:param memory_name: memory type
:param offset: offset/address within that memory region
:return: boolean compare status
"""
# Get information about the memory area
memory = self.device_memory_info.memory_info_by_name(memory_name)
verify_mask = memory[DeviceMemoryInfoKeys.VERIFY_MASK]
# Read back and compare the data to verify
data_verify = self.read_memory(memory_name, offset, len(data), max_read_chunk=max_read_chunk)[0].data
self.logger.info("Verifying...")
try:
# Use the compare util, which throws ValueError on mismatch
utils.compare(data, data_verify, offset, verify_mask)
except ValueError as error:
self.logger.error("Verify failed: %s", str(error))
return False
return True
def read_memory(self, memory_name, offset, numbytes=0, max_read_chunk=None):
"""
Read device memory
:param memory_name: memory type to read as defined in deviceinfo.memorynames
MemoryNameAliases.ALL will read all memories defined in the device model for the configured
device (numbytes and offset will be ignored)
:param offset: offset/start address within the memory to start reading from
:param numbytes: number of bytes to read. 0 means read all memory locations for given memory
type (offset still applies)
:return: 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. Normally the list will contain one item,
but when memory_name parameter is MemoryNameAliases.ALL there will be one namedtuple
item per memory type read.
:raises: ValueError if trying to read outside the specified memory
:raises: ValueError if the specified memory is not defined for the target device
"""
# Just some sanity checking of inputs
if offset < 0:
raise ValueError("Read offset can't be negative, requested offset: {}".format(offset))
if numbytes < 0:
raise ValueError("Can't read negative number of bytes, requested numbytes: {}".format(numbytes))
memories_read = []
if memory_name == MemoryNameAliases.ALL:
memories = list(self.device_memory_info.mem_by_name.keys())
# When reading all memories offset is ignored
offset = 0
# ...and the same with numbytes
numbytes = 0
else:
memories = [memory_name]
for memory in memories:
# Get information about the memory area
meminfo = self.device_memory_info.memory_info_by_name(memory)
# For each memory type there will be one named tuple with raw data as a bytearray and a dictionary
# with information about the memory
memory_read_tuple = namedtuple("Memory", 'data memory_info')
memory_read_tuple.data = bytearray([])
memory_read_tuple.memory_info = meminfo
# Align the read to a page boundary
page_offset = offset % meminfo[DeviceMemoryInfoKeys.PAGE_SIZE]
offset_adjusted = offset - page_offset
numbytes_adjusted = numbytes
# If number of bytes is not given, default to read the complete memory starting at the given offset
if numbytes == 0:
numbytes_adjusted = meminfo[DeviceMemoryInfoKeys.SIZE] - offset_adjusted
else:
numbytes_adjusted = numbytes_adjusted + page_offset
# Read size correction
read_size_key = DeviceMemoryInfoKeys.READ_SIZE
if numbytes_adjusted % meminfo[read_size_key]:
extra = meminfo[read_size_key] - numbytes_adjusted % meminfo[read_size_key]
numbytes_adjusted += extra
else:
extra = 0
if offset_adjusted + numbytes_adjusted > meminfo[DeviceMemoryInfoKeys.SIZE]:
raise ValueError("{} bytes of data at offset {} is outside the boundaries of '{}' with size {}".format(
numbytes_adjusted, offset, meminfo[DeviceMemoryInfoKeys.NAME], meminfo[DeviceMemoryInfoKeys.SIZE]))
# Read the data
self.logger.info("Reading %d bytes from %s...", numbytes_adjusted, meminfo[DeviceMemoryInfoKeys.NAME])
data = self.device_model.read(meminfo, offset_adjusted, numbytes_adjusted, max_read_chunk=max_read_chunk)
# Strip the extra data that was read
memory_read_tuple.data = data[page_offset:numbytes_adjusted - extra]
# Append a copy of the memory namedtuple to avoid a reference being appended as the memory_read_tuple will
# change for each loop iteration. Note that when using a deepcopy the content of the memory_read_tuple will
# be copied too
memories_read.append(copy.deepcopy(memory_read_tuple))
return memories_read
def hold_in_reset(self):
"""
Hold the device in reset
"""
self.logger.info("Hold in reset")
self.device_model.hold_in_reset()
def release_from_reset(self):
"""
Release the device from reset (i.e. let the device run)
"""
self.logger.info("Release from reset")
self.device_model.release_from_reset()

View File

@@ -0,0 +1,58 @@
import sys
import time
class ProgressBar:
def __init__(self, n_steps, width=50, hide=False):
self.width = width
self.n_steps = n_steps
self.count_step = 0
self.count_char = 0
self.hide = hide
self.print_start()
def print_start(self):
if not self.hide:
sys.stdout.write("[%s]" % (" " * self.width))
sys.stdout.flush()
def print_end(self):
if not self.hide:
sys.stdout.write("\n")
sys.stdout.flush()
def update(self):
n1 = self.count_char
n2 = self.width - self.count_char
if not self.hide:
sys.stdout.write("\r[" + "=" * n1 + " " * n2 + "] {}/{}".format(self.count_step, self.n_steps))
sys.stdout.flush()
def step(self):
self.count_step += 1
count_char_new = self.width * self.count_step // self.n_steps
if count_char_new > self.count_char:
self.count_char = count_char_new
self.update()
if self.count_step == self.n_steps:
self.print_end()
def test():
n_steps = 100
print("Starting...")
b = ProgressBar(n_steps)
for i in range(n_steps):
time.sleep(0.01)
b.step()
print("done.")
if __name__ == "__main__":
test()

View File

@@ -0,0 +1,258 @@
"""
Python MCU programmer Command Line Interface utility
"""
# Python 3 compatibility for Python 2
from __future__ import print_function
# args, logging
import sys
import argparse
import os
import logging
from logging.config import dictConfig
import textwrap
import yaml
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path # python 2 backport
from appdirs import user_log_dir
from yaml.scanner import ScannerError
# pymcuprog main function
from . import pymcuprog_main
from .pymcuprog_main import WRITE_TO_HEX_MEMORIES
from .deviceinfo.memorynames import MemoryNames, MemoryNameAliases
def setup_logging(user_requested_level=logging.WARNING, default_path='logging.yaml',
env_key='MICROCHIP_PYTHONTOOLS_CONFIG'):
"""
Setup logging configuration for pymcuprog CLI
"""
# Logging config YAML file can be specified via environment variable
value = os.getenv(env_key, None)
if value:
path = value
else:
# Otherwise use the one shipped with this application
path = os.path.join(os.path.dirname(__file__), default_path)
# Load the YAML if possible
if os.path.exists(path):
try:
with open(path, 'rt') as file:
# Load logging configfile from yaml
configfile = yaml.safe_load(file)
# File logging goes to user log directory under Microchip/modulename
logdir = user_log_dir(__name__, "Microchip")
# Look through all handlers, and prepend log directory to redirect all file loggers
num_file_handlers = 0
for handler in configfile['handlers'].keys():
# A filename key
if 'filename' in configfile['handlers'][handler].keys():
configfile['handlers'][handler]['filename'] = os.path.join(
logdir, configfile['handlers'][handler]['filename'])
num_file_handlers += 1
# If file logging is enabled, it needs a folder
if num_file_handlers > 0:
# Create it if it does not exist
Path(logdir).mkdir(exist_ok=True, parents=True)
# Console logging takes granularity argument from CLI user
configfile['handlers']['console']['level'] = user_requested_level
# Root logger must be the most verbose of the ALL YAML configurations and the CLI user argument
most_verbose_logging = min(user_requested_level, getattr(logging, configfile['root']['level']))
for handler in configfile['handlers'].keys():
# A filename key
if 'filename' in configfile['handlers'][handler].keys():
level = getattr(logging, configfile['handlers'][handler]['level'])
most_verbose_logging = min(most_verbose_logging, level)
configfile['root']['level'] = most_verbose_logging
dictConfig(configfile)
return
except ScannerError:
# Error while parsing YAML
print("Error parsing logging config file '{}'".format(path))
except KeyError as keyerror:
# Error looking for custom fields in YAML
print("Key {} not found in logging config file".format(keyerror))
else:
# Config specified by environment variable not found
print("Unable to open logging config file '{}'".format(path))
# If all else fails, revert to basic logging at specified level for this application
print("Reverting to basic logging.")
logging.basicConfig(level=user_requested_level)
# Helper functions
def _parse_literal(literal):
"""
Literals can either be integers or float values. Default is Integer
"""
try:
return int(literal, 0)
except ValueError:
return float(literal)
def main():
"""
Entrypoint for installable CLI
Configures the CLI and parses the arguments
"""
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent('''\
Generic programmer of selected AVR, PIC and SAM devices
Basic actions:
- ping: reads the device ID or signature
- read: read NVM
- write: write NVM
- erase: erase NVM
'''),
epilog=textwrap.dedent('''\
Usage examples:
Ping a device on-board a kit:
- pymcuprog.py ping
Ping a device using Atmel-ICE
- pymcuprog.py -t atmelice -d atmega4809 -i updi ping
Read the some bytes of flash:
- pymcuprog.py read -m flash -o 0x80 -b 64
Erase an UPDI device:
- pymcuprog.py erase
Erase a locked UPDI device:
- pymcuprog.py ping --chip-erase-locked-device
Set target supply voltage on a kit:
- pymcuprog.py setsupplyvoltage -l 3.3
'''))
parser.add_argument("action",
help="action to perform",
# This makes the action argument optional
# only if -V/--version or -R/release_info argument is given
nargs="?" if "-V" in sys.argv or "--version" in sys.argv \
or "-R" in sys.argv or "--release-info" in sys.argv else None,
default="ping",
# nargs='?', # this makes ping the default, and -h the only way to get usage()
choices=['ping', 'erase', 'read', 'write', 'verify', 'getvoltage', 'getsupplyvoltage',
'reboot-debugger',
'setsupplyvoltage', 'getusbvoltage', 'reset'])
# Device to program
parser.add_argument("-d", "--device",
type=str,
help="device to program")
# Pack path
parser.add_argument("-p", "--packpath",
type=str,
help="path to pack")
# Tool to use
parser.add_argument("-t", "--tool",
type=str,
help="tool to connect to")
parser.add_argument("-s", "--serialnumber",
type=str,
help="USB serial number of the unit to use")
# Memtype
memtype_helpstring = "memory area to access: {}".format(MemoryNameAliases.ALL)
for memtype in MemoryNames.get_all():
memtype_helpstring += ", '{}'".format(memtype)
parser.add_argument("-m", "--memory",
type=str,
default=MemoryNameAliases.ALL,
help=memtype_helpstring)
parser.add_argument("-o", "--offset",
type=lambda x: int(x, 0),
default="0",
help="memory byte offset to access")
parser.add_argument("-b", "--bytes",
type=int,
default=0,
help="number of bytes to access")
parser.add_argument("-l", "--literal",
type=_parse_literal,
nargs='+',
help="literal values to write")
filename_helpstring_extra = "Note that when reading to hex file only "
filename_helpstring_extra += ", ".join(WRITE_TO_HEX_MEMORIES)
filename_helpstring_extra += " memories will be written to the hex file"
parser.add_argument("-f", "--filename",
type=str,
help="file to write / read. "
"{}".format(filename_helpstring_extra))
parser.add_argument("-c", "--clk",
type=str,
help="clock frequency in Hz (bps) for programming interface. "
"(eg: '-c 32768' or '-c 115k' or '-c 1M')")
parser.add_argument("-u", "--uart",
type=str,
help="UART to use for UPDI")
parser.add_argument("-i", "--interface",
type=str,
help="Interface to use")
parser.add_argument("-v", "--verbose",
default="warning", choices=['debug', 'info', 'warning', 'error', 'critical'],
help="Logging verbosity level")
parser.add_argument("-V", "--version",
help="Print pymcuprog version number and exit",
action="store_true")
parser.add_argument("-R", "--release-info", action="store_true",
help="Print pymcuprog release details and exit")
parser.add_argument("--verify",
help="verify after write from file",
action="store_true")
parser.add_argument("-x", "--timing",
help="add timing output",
action="store_true")
# Ex-options
parser.add_argument("-hv", "--high-voltage",
choices=['tool-toggle-power', 'user-toggle-power', 'simple-unsafe-pulse'],
help="UPDI high-voltage activation mode")
parser.add_argument("-ul", "--user-row-locked-device",
help="Writes the User Row on a locked device (UPDI devices only)",
action="store_true")
parser.add_argument("-cl", "--chip-erase-locked-device",
help="Execute a Chip Erase on a locked device (UPDI devices only)",
action="store_true")
# Parse args
arguments = parser.parse_args()
# Setup logging
setup_logging(user_requested_level=getattr(logging, arguments.verbose.upper()))
# Call main with args
return pymcuprog_main.pymcuprog(arguments)
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,79 @@
"""
Pymcuprog specific exceptions
"""
class PymcuprogError(Exception):
"""
Base class for all Pymcuprog specific exceptions
"""
def __init__(self, msg=None, code=0):
super(PymcuprogError, self).__init__(msg)
self.code = code
class PymcuprogToolConfigurationError(PymcuprogError):
"""
Signals that a tool was incorrectly configured
"""
def __init__(self, msg=None, code=0):
super(PymcuprogToolConfigurationError, self).__init__(msg)
self.code = code
class PymcuprogToolConnectionError(PymcuprogError):
"""
Signals that an attempted connect failed
"""
def __init__(self, msg=None, code=0):
super(PymcuprogToolConnectionError, self).__init__(msg)
self.code = code
class PymcuprogNotSupportedError(PymcuprogError):
"""
Signals that an attempted operation is not supported
"""
def __init__(self, msg=None, code=0):
super(PymcuprogNotSupportedError, self).__init__(msg)
self.code = code
class PymcuprogSessionError(PymcuprogError):
"""
Signals that a session is not active
"""
def __init__(self, msg=None, code=0):
super(PymcuprogSessionError, self).__init__(msg)
self.code = code
class PymcuprogSessionConfigError(PymcuprogError):
"""
Signals that a session is not configured correctly
"""
def __init__(self, msg=None, code=0):
super(PymcuprogSessionConfigError, self).__init__(msg)
self.code = code
class PymcuprogDeviceLockedError(PymcuprogError):
"""
Signals that the device is locked and a chip erase is required to unlock it
"""
def __init__(self, msg=None, code=0):
super(PymcuprogDeviceLockedError, self).__init__(msg)
self.code = code
class PymcuprogEraseError(PymcuprogError):
"""
Signals that an erase can't be executed
Either the erase is not possible or the erase can't be executed without side effects,
i.e. erasing more memories than requested
"""
def __init__(self, msg=None, code=0):
super(PymcuprogEraseError, self).__init__(msg)
self.code = code

View 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

View File

@@ -0,0 +1,268 @@
"""
Application layer for UPDI stack
"""
from logging import getLogger
from pymcuprog.pymcuprog_errors import PymcuprogError
from . import constants
from .link import UpdiDatalink16bit, UpdiDatalink24bit
from .nvm import NvmUpdi, NvmUpdiTinyMega, NvmUpdiAvrDx
from .readwrite import UpdiReadWrite
from .physical import UpdiPhysical
from .timeout import Timeout
def decode_sib(sib):
"""
Turns the SIB into something readable
:param sib: SIB data to decode
"""
sib_info = {}
logger = getLogger(__name__)
sib = sib.replace(b"\x00", b"")
try:
sib_string = sib.decode('ascii')
except UnicodeDecodeError:
return None
if len(sib_string) < 19:
return None
logger.info("SIB: '%s'", sib_string)
# Parse fixed width fields according to spec
family = sib[0:7].strip().decode()
logger.info("Device family ID: '%s'", family)
sib_info['family'] = family
nvm = sib[8:11].strip().decode()
logger.info("NVM interface: '%s'", nvm)
sib_info['NVM'] = nvm.split(':')[1]
ocd = sib[11:14].strip().decode()
logger.info("Debug interface: '%s'", ocd)
sib_info['OCD'] = ocd.split(':')[1]
osc = sib[15:19].strip().decode()
logger.info("PDI oscillator: '%s'", osc)
sib_info['OSC'] = osc
extra = sib[19:].strip().decode()
logger.info("Extra info: '%s'", extra)
sib_info['extra'] = extra
return sib_info
class UpdiApplication:
"""
Generic application layer for UPDI
"""
def __init__(self, serialport, baud, device=None):
self.logger = getLogger(__name__)
self.device = device
# Build the UPDI stack:
# Create a physical
baud_temp = min(baud, 115200)
self.phy = UpdiPhysical(serialport, baud_temp)
# Create a DL - use 16-bit until otherwise known
datalink = UpdiDatalink16bit()
# Set the physical for use in the datalink
datalink.set_physical(self.phy)
# Init (active) the datalink
datalink.init_datalink()
# set the actual baud
datalink.change_baud(baud)
# Create a read write access layer using this data link
self.readwrite = UpdiReadWrite(datalink)
# Create an NVM driver
self.nvm = NvmUpdi(self.readwrite, self.device)
def read_device_info(self):
"""
Reads out device information from various sources
"""
sib = self.readwrite.read_sib()
sib_info = decode_sib(sib)
if sib_info is None:
self.logger.warning("Cannot read SIB, hard reset...")
self.phy.send_double_break()
sib = self.readwrite.read_sib()
sib_info = decode_sib(sib)
if sib_info is None:
self.logger.error("Hard reset failed.")
raise RuntimeError("Failed to read device info.")
if sib_info['NVM'] == '2':
# This is a Dx-family member, and needs new DL and NVM
self.logger.info("Using 24-bit UPDI")
# Create new DL
datalink = UpdiDatalink24bit()
# Use the existing PHY
datalink.set_physical(self.phy)
# And re-init
datalink.init_datalink()
# Create a read write access layer using this data link
self.readwrite = UpdiReadWrite(datalink)
# Create new NVM driver
self.nvm = NvmUpdiAvrDx(self.readwrite, self.device)
else:
self.logger.info("Using 16-bit UPDI")
# DL is correctly configured already
# Create new NVM driver
self.nvm = NvmUpdiTinyMega(self.readwrite, self.device)
self.logger.info("PDI revision = 0x%02X", self.readwrite.read_cs(constants.UPDI_CS_STATUSA) >> 4)
if self.in_prog_mode():
if self.device is not None:
devid = self.read_data(self.device.sigrow_address, 3)
devrev = self.read_data(self.device.syscfg_address + 1, 1)
self.logger.info("Device ID from pyupdi = '%02X%02X%02X' rev '%s'", devid[0], devid[1], devid[2],
chr(ord('A') + devrev[0]))
def read_data(self, address, size):
"""
Reads a number of bytes of data from UPDI
:param address: address to write to
:param size: number of bytes to read
"""
return self.readwrite.read_data(address, size)
def read_data_words(self, address, words):
"""
Reads a number of words of data from UPDI
:param address: address to write to
:param words: number of words to read
"""
return self.readwrite.read_data_words(address, words)
def write_data_words(self, address, data):
"""
Writes a number of words to memory
:param address: address to write to
:param data: data to write
"""
return self.readwrite.write_data_words(address, data)
def write_data(self, address, data):
"""
Writes a number of bytes to memory
:param address: address to write to
:param data: data to write
"""
return self.write_data(address, data)
def in_prog_mode(self):
"""
Checks whether the NVM PROG flag is up
"""
if self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS) & (1 << constants.UPDI_ASI_SYS_STATUS_NVMPROG):
return True
return False
def wait_unlocked(self, timeout_ms):
"""
Waits for the device to be unlocked.
All devices boot up as locked until proven otherwise
:param timeout_ms: number of milliseconts to wait
"""
timeout = Timeout(timeout_ms)
while not timeout.expired():
if not self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS) & (
1 << constants.UPDI_ASI_SYS_STATUS_LOCKSTATUS):
return True
self.logger.error("Timeout waiting for device to unlock")
return False
def unlock(self):
"""
Unlock by chip erase
"""
# Put in the key
self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_CHIPERASE)
# Check key status
key_status = self.readwrite.read_cs(constants.UPDI_ASI_KEY_STATUS)
self.logger.debug("Key status = 0x%02X", key_status)
if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_CHIPERASE):
raise PymcuprogError("Key not accepted")
# Toggle reset
self.reset(apply_reset=True)
self.reset(apply_reset=False)
# And wait for unlock
if not self.wait_unlocked(100):
raise PymcuprogError("Failed to chip erase using key")
def enter_progmode(self):
"""
Enters into NVM programming mode
"""
# First check if NVM is already enabled
if self.in_prog_mode():
self.logger.info("Already in NVM programming mode")
return True
self.logger.info("Entering NVM programming mode")
# Put in the key
self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_NVM)
# Check key status
key_status = self.readwrite.read_cs(constants.UPDI_ASI_KEY_STATUS)
self.logger.debug("Key status = 0x%02X", key_status)
if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_NVMPROG):
self.logger.error("Key status = 0x%02X", key_status)
raise IOError("Key not accepted")
# Toggle reset
self.reset(apply_reset=True)
self.reset(apply_reset=False)
# And wait for unlock
if not self.wait_unlocked(100):
raise IOError("Failed to enter NVM programming mode: device is locked")
# Check for NVMPROG flag
if not self.in_prog_mode():
raise IOError("Failed to enter NVM programming mode")
self.logger.debug("Now in NVM programming mode")
return True
def leave_progmode(self):
"""
Disables UPDI which releases any keys enabled
"""
self.logger.info("Leaving NVM programming mode")
self.reset(apply_reset=True)
self.reset(apply_reset=False)
self.readwrite.write_cs(constants.UPDI_CS_CTRLB,
(1 << constants.UPDI_CTRLB_UPDIDIS_BIT) | (1 << constants.UPDI_CTRLB_CCDETDIS_BIT))
def reset(self, apply_reset):
"""
Applies or releases an UPDI reset condition
:param apply_reset: True to apply, False to release
"""
if apply_reset:
self.logger.info("Apply reset")
self.readwrite.write_cs(constants.UPDI_ASI_RESET_REQ, constants.UPDI_RESET_REQ_VALUE)
else:
self.logger.info("Release reset")
self.readwrite.write_cs(constants.UPDI_ASI_RESET_REQ, 0x00)

View File

@@ -0,0 +1,110 @@
"""
UPDI protocol constants
"""
# UPDI commands and control definitions
UPDI_BREAK = 0x00
UPDI_LDS = 0x00
UPDI_STS = 0x40
UPDI_LD = 0x20
UPDI_ST = 0x60
UPDI_LDCS = 0x80
UPDI_STCS = 0xC0
UPDI_REPEAT = 0xA0
UPDI_KEY = 0xE0
UPDI_PTR = 0x00
UPDI_PTR_INC = 0x04
UPDI_PTR_ADDRESS = 0x08
UPDI_ADDRESS_8 = 0x00
UPDI_ADDRESS_16 = 0x04
UPDI_ADDRESS_24 = 0x08
UPDI_DATA_8 = 0x00
UPDI_DATA_16 = 0x01
UPDI_DATA_24 = 0x02
UPDI_KEY_SIB = 0x04
UPDI_KEY_KEY = 0x00
UPDI_KEY_64 = 0x00
UPDI_KEY_128 = 0x01
UPDI_KEY_256 = 0x02
UPDI_SIB_8BYTES = UPDI_KEY_64
UPDI_SIB_16BYTES = UPDI_KEY_128
UPDI_SIB_32BYTES = UPDI_KEY_256
UPDI_REPEAT_BYTE = 0x00
UPDI_REPEAT_WORD = 0x01
UPDI_PHY_SYNC = 0x55
UPDI_PHY_ACK = 0x40
UPDI_MAX_REPEAT_SIZE = (0xFF+1) # Repeat counter of 1-byte, with off-by-one counting
# CS and ASI Register Address map
UPDI_CS_STATUSA = 0x00
UPDI_CS_STATUSB = 0x01
UPDI_CS_CTRLA = 0x02
UPDI_CS_CTRLB = 0x03
UPDI_ASI_KEY_STATUS = 0x07
UPDI_ASI_RESET_REQ = 0x08
UPDI_ASI_CTRLA = 0x09
UPDI_ASI_SYS_CTRLA = 0x0A
UPDI_ASI_SYS_STATUS = 0x0B
UPDI_ASI_CRC_STATUS = 0x0C
UPDI_CTRLA_IBDLY_BIT = 7
UPDI_CTRLB_CCDETDIS_BIT = 3
UPDI_CTRLB_UPDIDIS_BIT = 2
UPDI_KEY_NVM = b"NVMProg "
UPDI_KEY_CHIPERASE = b"NVMErase"
UPDI_ASI_STATUSA_REVID = 4
UPDI_ASI_STATUSB_PESIG = 0
UPDI_ASI_KEY_STATUS_CHIPERASE = 3
UPDI_ASI_KEY_STATUS_NVMPROG = 4
UPDI_ASI_KEY_STATUS_UROWWRITE = 5
UPDI_ASI_SYS_STATUS_RSTSYS = 5
UPDI_ASI_SYS_STATUS_INSLEEP = 4
UPDI_ASI_SYS_STATUS_NVMPROG = 3
UPDI_ASI_SYS_STATUS_UROWPROG = 2
UPDI_ASI_SYS_STATUS_LOCKSTATUS = 0
UPDI_RESET_REQ_VALUE = 0x59
# FLASH CONTROLLER
UPDI_NVMCTRL_CTRLA = 0x00
UPDI_NVMCTRL_CTRLB = 0x01
UPDI_NVMCTRL_STATUS = 0x02
UPDI_NVMCTRL_INTCTRL = 0x03
UPDI_NVMCTRL_INTFLAGS = 0x04
UPDI_NVMCTRL_DATAL = 0x06
UPDI_NVMCTRL_DATAH = 0x07
UPDI_NVMCTRL_ADDRL = 0x08
UPDI_NVMCTRL_ADDRH = 0x09
# NVMCTRL v0 CTRLA
UPDI_V0_NVMCTRL_CTRLA_NOP = 0x00
UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE = 0x01
UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE = 0x02
UPDI_V0_NVMCTRL_CTRLA_ERASE_WRITE_PAGE = 0x03
UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR = 0x04
UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE = 0x05
UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM = 0x06
UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE = 0x07
# NVMCTRL v1 CTRLA
UPDI_V1_NVMCTRL_CTRLA_NOCMD = 0x00
UPDI_V1_NVMCTRL_CTRLA_FLASH_WRITE = 0x02
UPDI_V1_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE = 0x13
UPDI_V1_NVMCTRL_CTRLA_CHIP_ERASE = 0x20
UPDI_NVM_STATUS_WRITE_ERROR = 2
UPDI_NVM_STATUS_EEPROM_BUSY = 1
UPDI_NVM_STATUS_FLASH_BUSY = 0

View File

@@ -0,0 +1,431 @@
"""
Link layer in UPDI protocol stack
"""
from logging import getLogger
from pymcuprog.pymcuprog_errors import PymcuprogError
from . import constants
class UpdiDatalink:
"""
UPDI data link class handles the UPDI data protocol within the device
"""
LDCS_RESPONSE_BYTES = 1
def __init__(self):
self.logger = getLogger(__name__)
self.updi_phy = None
def set_physical(self, physical):
"""
Inject a serial-port based physical layer for use by this DL
"""
self.updi_phy = physical
def _init_session_parameters(self):
"""
Set the inter-byte delay bit and disable collision detection
"""
self.stcs(constants.UPDI_CS_CTRLB, 1 << constants.UPDI_CTRLB_CCDETDIS_BIT)
self.stcs(constants.UPDI_CS_CTRLA, 0x06)
def init_datalink(self):
"""
Init DL layer
"""
self._init_session_parameters()
# Check
if not self._check_datalink():
# Send double break if all is not well, and re-check
self.updi_phy.send_double_break()
self._init_session_parameters()
if not self._check_datalink():
raise PymcuprogError("UPDI initialisation failed")
def change_baud(self, baud):
if self.updi_phy is not None:
self.stcs(constants.UPDI_CS_CTRLA, 0x06)
if baud <= 115200:
self.stcs(constants.UPDI_ASI_CTRLA, 0x03)
elif baud > 230400:
self.stcs(constants.UPDI_ASI_CTRLA, 0x01)
else:
self.stcs(constants.UPDI_ASI_CTRLA, 0x02)
self.updi_phy.change_baud(baud)
def _check_datalink(self):
"""
Check UPDI by loading CS STATUSA
"""
try:
if self.ldcs(constants.UPDI_CS_STATUSA) != 0:
self.logger.info("UPDI init OK")
return True
except PymcuprogError:
self.logger.warning("Check failed")
return False
self.logger.info("UPDI not OK - reinitialisation required")
return False
def ldcs(self, address):
"""
Load data from Control/Status space
:param address: address to load
"""
self.logger.debug("LDCS from 0x%02X", address)
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LDCS | (address & 0x0F)])
response = self.updi_phy.receive(self.LDCS_RESPONSE_BYTES)
numbytes_received = len(response)
if numbytes_received != self.LDCS_RESPONSE_BYTES:
raise PymcuprogError("Unexpected number of bytes in response: "
"{} byte(s) expected {} byte(s)".format(numbytes_received, self.LDCS_RESPONSE_BYTES))
return response[0]
def stcs(self, address, value):
"""
Store a value to Control/Status space
:param address: address to store to
:param value: value to write
"""
self.logger.debug("STCS to 0x%02X", address)
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_STCS | (address & 0x0F), value])
def ld_ptr_inc(self, size):
"""
Loads a number of bytes from the pointer location with pointer post-increment
:param size: number of bytes to load
:return: values read
"""
self.logger.debug("LD8 from ptr++")
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC |
constants.UPDI_DATA_8])
return self.updi_phy.receive(size)
def ld_ptr_inc16(self, words):
"""
Load a 16-bit word value from the pointer location with pointer post-increment.
For improved performance of serialupdi for Arduino, send the REP instruction in the same command as LD
:param words: number of words to load
:return: values read
"""
self.logger.debug("LD16 from ptr++")
# combine REP, words with ld *ptr++
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE,
(words - 1) & 0xFF, constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC |
constants.UPDI_DATA_16])
return self.updi_phy.receive(words << 1)
def st_ptr_inc(self, data):
"""
Store data to the pointer location with pointer post-increment
:param data: data to store
"""
self.logger.debug("ST8 to *ptr++")
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_8,
data[0]])
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("ACK error with st_ptr_inc")
num = 1
while num < len(data):
self.updi_phy.send([data[num]])
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("Error with st_ptr_inc")
num += 1
def st_ptr_inc16(self, data):
"""
Store a 16-bit word value to the pointer location with pointer post-increment
:param data: data to store
"""
self.logger.debug("ST16 to *ptr++")
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC |
constants.UPDI_DATA_16, data[0], data[1]])
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("ACK error with st_ptr_inc16")
num = 2
while num < len(data):
self.updi_phy.send([data[num], data[num + 1]])
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("Error with st_ptr_inc16")
num += 2
def st_ptr_inc16_RSD(self, data, blocksize):
"""
Store a 16-bit word value to the pointer location with pointer post-increment
:param data: data to store
:blocksize: max number of bytes being sent, None for all.
Warning: This does not strictly honor blocksize for values < 6
We always glob together the STCS(RSD) and REP commands.
But this should pose no problems for compatibility, because your serial adapter can't deal with 6b chunks,
none of pymcuprog would work!
"""
self.logger.debug("ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d", len(data), blocksize)
#for performance we glob everything together into one USB transfer....
repnumber= ((len(data) >> 1) -1)
data = [*data, *[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x06]]
if blocksize is None:
# Send whole thing at once stcs + repeat + st + (data + stcs)
blocksize = 3 + 3 + 2 + len(data)
num = 0
firstpacket = []
if blocksize < 10 :
# very small block size - we send pair of 2-byte commands first.
firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x0E],
*[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)]]
data = [*[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC |constants.UPDI_DATA_16], *data]
num = 0
else:
firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA , 0x0E],
*[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)],
*[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_16],
*data[:blocksize - 8]]
num = blocksize - 8
if len(firstpacket) == 64 and blocksize != 64:
firstpacket = firstpacket[:32]
num = 32-8
# workaround bug in D11C as serial adapter compiled with the USB implementation used in the mattairtech core;
# this chokes on any block of exactly (!!) 64 bytes. Nobody seems to understand the reason for this bizzare issue.
# The D11C as serial adapter is important because the fablab at MIT uses them heavilly, and wishes to upload Arduino sketches to tinyAVR 0/1/2-series
# and AVR-Dx-series parts with them. It was desirable from their perspective to have an more accessible firmware (like that Arduino one) as opposed
# to a pure-C one. Hence, a workaround is implemented to avoid triggering it. Thankfully, 64-byte blocks being written are not as natural as one might
# expect for a power-of-two, since we combine the data with the commands on either side.
# This workaround will only be invoked under unusual conditions, specifically if one of these is true:
# A. The last page of the hex file has a length of 53 bytes exactly OR
# B. The last page of the hex file has a length modulo the write chunk size, of 53 bytes exactly OR
# C. A write chunk is specified which is exactly 53 bytes shorter than the page OR
# D. The write chunk is specified as 153 when writing to a part with a page size of 512 OR
# E. Situations like D in the event that a future product permits REPEAT with 2 bytes of data to support pages larger than 512b.
# Conditions C-E will invoke it on the last chunk of every page; For E, here are 6 cursed numbers for 1024b pages with 2 byte repeats, and 2 for 2048b pages
# (in addition to pagelength - 52), however, there is no specific reason to expect such a part will be made. In any event, for those conditions, the
# performance impact is on the order of 1-4ms per page, the same as the the penalty for each chunk that a write is divided into, hence the speed penalty of
# using that write chunk size will be (1-4ms * ceil(page size/chunk size) + 1 instead of 1-4ms * ceil(page size/chunk size), which is always smaller than the
# penalty already being accepted for the write chunking.
# Conditions A and B will happen at most once per upload; thus the performance penalty would be acceptable even if they happened frequently. However
# avr-gcc rarely, if ever, generates odd size hex files. I was not able to get it to by tweaking the sketch I was compiling, so they will occur on
# considerably less than 1/64th of the time, if it is even possible for avr-gcc to generate such a file, which is unclear.
# In summary, the performance impact is small but noticable in remote corner cases (where chunk size is chosen randomly, or intentionally in an effort to provoke
# this bug, and is negligible (at worst 4 ms for 1 out of 32 uploads, assuming upload size modulo page size are evenly distributed and even ) in unlikely cases
# (requiring an odd write chunk size such that B could be provoked from an even-length hex file) as well as -possibly- case A if there is indeed a programming
# construct that results in a non-even length hex file. It is (barely) worth noting that for a hypothetical part that used a 2 byte REPEAT argument, this could
# be provoked with an even sketch size, and so would impose a penalty of less than 4ms on 1 out of 32 uploads.
# In light of the fact write chunking is intended as a workaround for an ill-mannered serial adapter in the first place (ex: HT42 workaround in DxCore), this
# performance impact is not a concern.
# If a write chunk size of 64 is specified, we will not activate this workaround.
# -Spence 6/29/21
self.updi_phy.send( firstpacket )
# if finite block size, this is used.
while num < len(data):
data_slice = data[num:num+blocksize]
if len(data_slice) == 64 and blocksize != 64:
data_slice=data[num:num+32] # workaround as above.
self.updi_phy.send(data_slice)
num += len(data_slice)
def repeat(self, repeats):
"""
Store a value to the repeat counter
:param repeats: number of repeats requested
"""
self.logger.debug("Repeat %d", repeats)
if (repeats - 1) > constants.UPDI_MAX_REPEAT_SIZE:
self.logger.error("Invalid repeat count of %d", repeats)
raise Exception("Invalid repeat count!")
repeats -= 1
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE,
repeats & 0xFF])
def read_sib(self):
"""
Read the SIB
"""
return self.updi_phy.sib()
def key(self, size, key):
"""
Write a key
:param size: size of key (0=64B, 1=128B, 2=256B)
:param key: key value
"""
self.logger.debug("Writing key")
if len(key) != 8 << size:
raise PymcuprogError("Invalid KEY length!")
self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_KEY | constants.UPDI_KEY_KEY | size])
self.updi_phy.send(list(reversed(list(key))))
def _st_data_phase(self, values):
"""
Performs data phase of transaction:
receive ACK
send data
:param values: bytearray of value(s) to send
"""
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("Error with st")
self.updi_phy.send(values)
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("Error with st")
class UpdiDatalink16bit(UpdiDatalink):
"""
UPDI data link layer in 16-bit version
This means that all addresses and pointers contain 2 bytes
"""
def __init__(self):
UpdiDatalink.__init__(self)
self.logger = getLogger(__name__)
# pylint: disable=invalid-name
def ld(self, address):
"""
Load a single byte direct from a 16-bit address
:param address: address to load from
:return: value read
"""
self.logger.info("LD from 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_16 | constants.UPDI_DATA_8,
address & 0xFF, (address >> 8) & 0xFF])
return self.updi_phy.receive(1)[0]
def ld16(self, address):
"""
Load a 16-bit word directly from a 16-bit address
:param address: address to load from
:return: values read
"""
self.logger.info("LD from 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_16 | constants.UPDI_DATA_16,
address & 0xFF, (address >> 8) & 0xFF])
return self.updi_phy.receive(2)
# pylint: disable=invalid-name
def st(self, address, value):
"""
Store a single byte value directly to a 16-bit address
:param address: address to write to
:param value: value to write
"""
self.logger.info("ST to 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_16 | constants.UPDI_DATA_8,
address & 0xFF, (address >> 8) & 0xFF])
return self._st_data_phase([value & 0xFF])
def st16(self, address, value):
"""
Store a 16-bit word value directly to a 16-bit address
:param address: address to write to
:param value: value to write
"""
self.logger.info("ST to 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_16 | constants.UPDI_DATA_16,
address & 0xFF, (address >> 8) & 0xFF])
return self._st_data_phase([value & 0xFF, (value >> 8) & 0xFF])
def st_ptr(self, address):
"""
Set the pointer location
:param address: address to write
"""
self.logger.info("ST to ptr")
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_ADDRESS | constants.UPDI_DATA_16,
address & 0xFF, (address >> 8) & 0xFF])
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("Error with st_ptr")
class UpdiDatalink24bit(UpdiDatalink):
"""
UPDI data link layer in 24-bit version
This means that all addresses and pointers contain 3 bytes
"""
def __init__(self):
UpdiDatalink.__init__(self)
self.logger = getLogger(__name__)
# pylint: disable=invalid-name
def ld(self, address):
"""
Load a single byte direct from a 24-bit address
:param address: address to load from
:return: value read
"""
self.logger.info("LD from 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_8,
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
return self.updi_phy.receive(1)[0]
def ld16(self, address):
"""
Load a 16-bit word directly from a 24-bit address
:param address: address to load from
:return: values read
"""
self.logger.info("LD from 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16,
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
return self.updi_phy.receive(2)
# pylint: disable=invalid-name
def st(self, address, value):
"""
Store a single byte value directly to a 24-bit address
:param address: address to write to
:param value: value to write
"""
self.logger.info("ST to 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_8,
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
return self._st_data_phase([value & 0xFF])
def st16(self, address, value):
"""
Store a 16-bit word value directly to a 24-bit address
:param address: address to write to
:param value: value to write
"""
self.logger.info("ST to 0x{0:06X}".format(address))
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16,
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
return self._st_data_phase([value & 0xFF, (value >> 8) & 0xFF])
def st_ptr(self, address):
"""
Set the pointer location
:param address: address to write
"""
self.logger.info("ST to ptr")
self.updi_phy.send(
[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_ADDRESS | constants.UPDI_DATA_24,
address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF])
response = self.updi_phy.receive(1)
if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK:
raise PymcuprogError("Error with st_ptr")

View File

@@ -0,0 +1,322 @@
"""
NVM implementations on various UPDI device families
"""
from logging import getLogger
from pymcuprog.pymcuprog_errors import PymcuprogError
from . import constants
from .timeout import Timeout
from time import sleep
class NvmUpdi(object):
"""
Base class for NVM
"""
def __init__(self, readwrite, device):
self.logger = getLogger(__name__)
self.readwrite = readwrite
self.device = device
def chip_erase(self):
"""
Does a chip erase using the NVM controller
"""
raise NotImplementedError("NVM stack not ready")
def write_flash(self, address, data):
"""
Writes data to flash
:param address: address to write to
:param data: data to write
"""
raise NotImplementedError("NVM stack not ready")
def write_eeprom(self, address, data):
"""
Write data to EEPROM
:param address: address to write to
:param data: data to write
"""
raise NotImplementedError("NVM stack not ready")
def write_fuse(self, address, data):
"""
Writes one fuse value
:param address: address to write to
:param data: data to write
"""
raise NotImplementedError("NVM stack not ready")
def wait_flash_ready(self):
"""
Waits for the NVM controller to be ready
"""
timeout = Timeout(10000) # 10 sec timeout, just to be sure
self.logger.debug("Wait flash ready")
while not timeout.expired():
status = self.readwrite.read_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_STATUS)
if status & (1 << constants.UPDI_NVM_STATUS_WRITE_ERROR):
self.logger.error("NVM error")
return False
if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) |
(1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)):
return True
self.logger.error("Wait flash ready timed out")
return False
def execute_nvm_command(self, command):
"""
Executes an NVM COMMAND on the NVM CTRL
:param command: command to execute
"""
self.logger.debug("NVMCMD %d executing", command)
return self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_CTRLA, command)
class NvmUpdiTinyMega(NvmUpdi):
"""
AKA Version 0 UPDI NVM
Present on, for example, tiny817 -> mega4809
"""
def __init__(self, readwrite, device):
NvmUpdi.__init__(self, readwrite, device)
self.logger = getLogger(__name__)
def chip_erase(self):
"""
Does a chip erase using the NVM controller
Note that on locked devices this is not possible
and the ERASE KEY has to be used instead, see the unlock method
"""
self.logger.info("Chip erase using NVM CTRL")
# Wait until NVM CTRL is ready to erase
if not self.wait_flash_ready():
raise IOError("Timeout waiting for flash ready before erase ")
# Erase
self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE)
# And wait for it
if not self.wait_flash_ready():
raise IOError("Timeout waiting for flash ready after erase")
return True
def write_flash(self, address, data, blocksize=2, bulkwrite=0, pagewrite_delay=0):
"""
Writes data to flash (v0)
:param address: address to write to
:param data: data to write
"""
return self.write_nvm(address, data, use_word_access=True, blocksize=blocksize, bulkwrite=bulkwrite, pagewrite_delay=pagewrite_delay)
def write_eeprom(self, address, data):
"""
Write data to EEPROM (v0)
:param address: address to write to
:param data: data to write
"""
return self.write_nvm(address, data, use_word_access=False,
nvmcommand=constants.UPDI_V0_NVMCTRL_CTRLA_ERASE_WRITE_PAGE)
def write_fuse(self, address, data):
"""
Writes one fuse value (v0)
:param address: address to write to
:param data: data to write
"""
# Check that NVM controller is ready
if not self.wait_flash_ready():
raise PymcuprogError("Timeout waiting for flash ready before page buffer clear ")
# Write address to NVMCTRL ADDR
self.logger.debug("Load NVM address")
self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_ADDRL, address & 0xFF)
self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_ADDRH, (address >> 8) & 0xFF)
# Write data
self.logger.debug("Load fuse data")
self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_DATAL, data[0] & 0xFF)
# Execute
self.logger.debug("Execute fuse write")
self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE)
if not self.wait_flash_ready():
raise PymcuprogError("Timeout waiting for flash ready before page buffer clear ")
def write_nvm(self, address, data, use_word_access, nvmcommand=constants.UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE,
blocksize=2, bulkwrite=0, pagewrite_delay=0):
"""
Writes a page of data to NVM (v0)
By default the PAGE_WRITE command is used, which
requires that the page is already erased.
By default word access is used (flash)
:param address: address to write to
:param data: data to write
:param use_word_access: write whole words?
:param nvmcommand: command to use for commit
:param bulkwrite: Passed down from nvmserialupdi 0 = normal or single write.
1 means it's part of writing the whole flash.
In that case we only st ptr if address = 0.
:param pagewrite_delay: (ms) delay before pagewrite
"""
# unless we are in a bulk (whole flash) write, in which case we skip almost everything.
if (bulkwrite == 0 ) or address == 0x8000 or address == 0x4000 or not use_word_access:
# Check that NVM controller is ready
# I will grudgingly check this at the very start. I am extremely skeptical about the usefulness of this test.
# If it's not ready, they'll get another error will they not? Every command like this costs about a half second
# on every upload when using serialupdi - at any bsaud rate, assuming 256 pages. It's all USB latency.
if not self.wait_flash_ready():
raise PymcuprogError("Timeout waiting for flash ready before page buffer clear ")
# Clear the page buffer
self.logger.debug("Clear page buffer")
self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR)
# Wait for NVM controller to be ready
if not self.wait_flash_ready():
raise PymcuprogError("Timeout waiting for flash ready after page buffer clear")
# Load the page buffer by writing directly to location
if use_word_access:
self.readwrite.write_data_words(address, data, blocksize)
else:
self.readwrite.write_data(address, data)
# Write the page to NVM, maybe erase first
self.logger.debug("Committing data")
self.execute_nvm_command(nvmcommand)
if pagewrite_delay > 0:
sleep(pagewrite_delay/1000.0)
# SACRIFICES SPEED FOR COMPATIBILITY - above line should execute only when --pagepause command line parameter is 1 or more (default 0), so we can adjust it externally
# it should sleep for that many milliseconds (the granularity of this is low enough enough that 0.001 vs 0.005 makes no difference in my testing)
# I couldn't propagate it through this mess, and I really tried, because it is a 2:1 performance hit on CH340 on some parts, which is brutal, but it breaks too many adapters to not have it
# this should only ever happen for tinyAVR/megaAVR, NEVER Dx-series parts.
if not bulkwrite == 1:
# do a final NVM status check only if not doing a bulk write, or after the last chunk (when bulkwrite = 2)
# not doing this every page made uploads about 15% faster
if not self.wait_flash_ready():
raise PymcuprogError("Timeout waiting for flash ready after page write ")
class NvmUpdiAvrDx(NvmUpdi):
"""
AKA Version 1 UPDI NVM
Present on, for example, AVR-DA and newer
"""
def __init__(self, readwrite, device):
NvmUpdi.__init__(self, readwrite, device)
self.logger = getLogger(__name__)
def chip_erase(self):
"""
Does a chip erase using the NVM controller
Note that on locked devices this it not possible
and the ERASE KEY has to be used instead
"""
self.logger.info("Chip erase using NVM CTRL")
# Wait until NVM CTRL is ready to erase
if not self.wait_flash_ready():
raise Exception("Timeout waiting for flash ready before erase ")
# Erase
self.execute_nvm_command(constants.UPDI_V1_NVMCTRL_CTRLA_CHIP_ERASE)
# And wait for it
if not self.wait_flash_ready():
raise Exception("Timeout waiting for flash ready after erase")
return True
def write_flash(self, address, data, blocksize=2, bulkwrite=0, pagewrite_delay=0):
"""
Writes data to flash (v1)
:param address: address to write to
:param data: data to write
:return:
"""
return self.write_nvm(address, data, use_word_access=True, blocksize=blocksize, bulkwrite=bulkwrite, pagewrite_delay=pagewrite_delay)
def write_eeprom(self, address, data):
"""
Writes data to NVM (EEPROM)
:param address: address to write to
:param data: data to write
"""
nvm_command = constants.UPDI_V1_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE
# Check that NVM controller is ready
if not self.wait_flash_ready():
raise Exception("Timeout waiting for NVM ready before command write")
# Write the command to the NVM controller
self.logger.info("NVM EEPROM erase/write command")
self.execute_nvm_command(nvm_command)
# Write the data
self.readwrite.write_data(address, data)
# Wait for NVM controller to be ready again
if not self.wait_flash_ready():
raise Exception("Timeout waiting for NVM ready after data write")
# Remove command from NVM controller
self.logger.info("Clear NVM command")
self.execute_nvm_command(constants.UPDI_V1_NVMCTRL_CTRLA_NOCMD)
def write_fuse(self, address, data):
"""
Writes one fuse value
V1 fuses are EEPROM-based
:param address: address to write to
:param data: data to write
"""
return self.write_eeprom(address, data)
def write_nvm(self, address, data, use_word_access, blocksize=2, bulkwrite=0, pagewrite_delay=0):
"""
Writes data to NVM (version 1)
This version of the NVM block has no page buffer, so words are written directly.
:param address: address to write to
:param data: data to write
:param use_word_access: write in whole words?
"""
nvm_command = constants.UPDI_V1_NVMCTRL_CTRLA_FLASH_WRITE
if bulkwrite == 0 or address == 0x800000:
# Check that NVM controller is ready
if not self.wait_flash_ready():
raise Exception("Timeout waiting for flash ready before page buffer clear ")
# Write the command to the NVM controller
self.logger.info("NVM write command")
self.execute_nvm_command(nvm_command)
# Write the data
if use_word_access:
self.readwrite.write_data_words(address, data, blocksize)
else:
self.readwrite.write_data(address, data)
# Wait for NVM controller to be ready again
if bulkwrite != 1:
if not self.wait_flash_ready():
raise Exception("Timeout waiting for flash ready after data write")
# Remove command from NVM controller
self.logger.info("Clear NVM command")
self.execute_nvm_command(constants.UPDI_V1_NVMCTRL_CTRLA_NOCMD)

View File

@@ -0,0 +1,150 @@
"""
Serial driver for UPDI stack
"""
import time
from logging import getLogger
import serial
from serial.serialutil import SerialException
from . import constants
class UpdiPhysical:
"""
PDI physical driver using a given serial port at a given baud
"""
def __init__(self, port, baud=115200):
"""
Initialise the serial port
"""
self.logger = getLogger(__name__)
# Inter-byte delay
self.ibdly = 0.0001
self.port = port
self.baud = baud
self.ser = None
self.initialise_serial(self.port, self.baud)
# send an initial break as handshake
self.send([constants.UPDI_BREAK])
def change_baud(self, newbaud):
self.ser.baudrate = newbaud
def initialise_serial(self, port, baud):
"""
Standard serial port initialisation
:param port: serial port to use
:param baud: baud rate
"""
self.logger.info("Opening port '%s' at '%d' baud", port, baud)
try:
self.ser = serial.Serial(None, baud, parity=serial.PARITY_EVEN, timeout=1, stopbits=serial.STOPBITS_TWO)
self.ser.port = port
self.ser.dtr = False
self.ser.rts = False
self.ser.open()
except SerialException:
self.logger.error("Unable to open serial port '%s'", port)
raise
def _loginfo(self, msg, data):
if data and isinstance(data[0], str):
i_data = [ord(x) for x in data]
else:
i_data = data
data_str = "[" + ", ".join([hex(x) for x in i_data]) + "]"
self.logger.debug("%s : %s", msg, data_str)
def send_double_break(self):
"""
Sends a double break to reset the UPDI port
BREAK is actually just a slower zero frame
A double break is guaranteed to push the UPDI state
machine into a known state, albeit rather brutally
"""
self.logger.info("Sending double break")
# Re-init at a lower baud
# At 300 bauds, the break character will pull the line low for 30ms
# Which is slightly above the recommended 24.6ms
self.ser.close()
temporary_serial = serial.Serial(None, 300, parity=serial.PARITY_EVEN, timeout=1,
stopbits=serial.STOPBITS_ONE)
temporary_serial.port = self.port
temporary_serial.dtr = False
temporary_serial.rts = False
temporary_serial.open()
# Send two break characters, with 1 stop bit in between
temporary_serial.write([constants.UPDI_BREAK])
# Wait for the double break end
temporary_serial.read(1)
time.sleep(0.1)
# Send two break characters, with 1 stop bit in between
temporary_serial.write([constants.UPDI_BREAK])
# Wait for the double break end
temporary_serial.read(1)
# Re-init at the real baud
temporary_serial.close()
self.initialise_serial(self.port, self.baud)
def send(self, command):
"""
Sends a char array to UPDI with NO inter-byte delay
Note that the byte will echo back
"""
self.logger.info("send %d bytes", len(command))
self._loginfo("data: ", command)
self.ser.write(command)
# it will echo back.
echo = self.ser.read(len(command))
def receive(self, size):
"""
Receives a frame of a known number of chars from UPDI
:param size: bytes to receive
"""
response = bytearray()
timeout = 1
# For each byte
while size and timeout:
# Read
character = self.ser.read()
# Anything in?
if character:
response.append(ord(character))
size -= 1
else:
timeout -= 1
self._loginfo("receive", response)
return response
def sib(self):
"""
System information block is just a string coming back from a SIB command
"""
self.send([
constants.UPDI_PHY_SYNC,
constants.UPDI_KEY | constants.UPDI_KEY_SIB | constants.UPDI_SIB_32BYTES])
return self.ser.readline()
def __del__(self):
if self.ser:
self.logger.info("Closing port '%s'", self.port)
self.ser.close()

View File

@@ -0,0 +1,160 @@
"""
Read/write access provider for UPDI
"""
from logging import getLogger
from pymcuprog.pymcuprog_errors import PymcuprogError
from . import constants
class UpdiReadWrite(object):
"""
Provides various forms of reads and writes for UPDI applications
Makes us of the datalink provided
"""
def __init__(self, datalink):
self.logger = getLogger(__name__)
self.datalink = datalink
def read_cs(self, address):
"""
Read from Control/Status space
:param address: address (index) to read
:return: value read
"""
return self.datalink.ldcs(address)
def write_cs(self, address, value):
"""
Write to Control/Status space
:param address: address (index) to write
:param value: 8-bit value to write
"""
return self.datalink.stcs(address, value)
def write_key(self, size, key):
"""
Write a KEY into UPDI
:param size: size of key to send
:param key: key value
"""
return self.datalink.key(size, key)
def read_sib(self):
"""
Read the SIB from UPDI
:return: SIB string (bytearray) read
"""
return self.datalink.read_sib()
def read_byte(self, address):
"""
Read a single byte from UPDI
:param address: address to read from
:return: value read
"""
return self.datalink.ld(address)
def write_byte(self, address, value):
"""
Writes a single byte to UPDI
:param address: address to write to
:param value: value to write
"""
return self.datalink.st(address, value)
def read_data(self, address, size):
"""
Reads a number of bytes of data from UPDI
:param address: address to write to
:param size: number of bytes to read
"""
self.logger.debug("Reading %d bytes from 0x%04X", size, address)
# Range check
if size > constants.UPDI_MAX_REPEAT_SIZE:
raise PymcuprogError("Cant read that many bytes in one go")
# Store the address
self.datalink.st_ptr(address)
# Fire up the repeat
if size > 1:
self.datalink.repeat(size)
# Do the read(s)
return self.datalink.ld_ptr_inc(size)
def read_data_words(self, address, words):
"""
Reads a number of words of data from UPDI
:param address: address to write to
:param words: number of words to read
"""
self.logger.debug("Reading %d words from 0x%04X", words, address)
# Range check
if words > constants.UPDI_MAX_REPEAT_SIZE:
raise PymcuprogError("Cant read that many words in one go")
# special case for single word - so we can optimize ld_ptr_inc16 for >1 word to improve performance.
if words == 1:
return self.datalink.ld16(self,address)
# Otherwise, store the address
self.datalink.st_ptr(address)
# For performance, managing repeat count is done in ld_ptr_inc16()
return self.datalink.ld_ptr_inc16(words)
def write_data_words(self, address, data, blocksize):
"""
Writes a number of words to memory
:param address: address to write to
:param data: data to write
:blocksize: max number of bytes being sent
"""
# Special-case of 1 word
if len(data) == 2:
value = data[0] + (data[1] << 8)
return self.datalink.st16(address, value)
# Range check
if len(data) > constants.UPDI_MAX_REPEAT_SIZE << 1:
raise PymcuprogError("Invalid length")
# Store the address
self.datalink.st_ptr(address)
# For performance, we want to do this with Response Signature Disable set, otherwise the USB Serial latency kills you
# Just setting RSD here then turning it off at the end - it helps a lot, but you run up against the USB latency. So
# now, EVERYTHING was moved into the st_ptr_inc16_RSD() function. Unless blocksize precludes it, the whole thing
# is sent to the serial adapter in a single transfer.
# the st_pty_inc16_RSD routine does the repeat and rsd enable/disable stu
return self.datalink.st_ptr_inc16_RSD(data, blocksize)
def write_data(self, address, data):
"""
Writes a number of bytes to memory
:param address: address to write to
:param data: data to write
"""
# Special case of 1 byte
if len(data) == 1:
return self.datalink.st(address, data[0])
# Special case of 2 byte
if len(data) == 2:
self.datalink.st(address, data[0])
return self.datalink.st(address + 1, data[1])
# Range check
if len(data) > constants.UPDI_MAX_REPEAT_SIZE:
raise PymcuprogError("Invalid length")
# Store the address
self.datalink.st_ptr(address)
# Fire up the repeat
self.datalink.repeat(len(data))
return self.datalink.st_ptr_inc(data)

View File

@@ -0,0 +1,25 @@
"""
Simple timer helper for UPDI stack
"""
import time
#pylint: disable=too-few-public-methods
class Timeout:
"""
Simple timeout helper in milliseconds.
"""
def __init__(self, timeout_ms):
"""
Start the expired counter instantly
:param timeout_ms: milliseconds to count
"""
self.timeout_ms = timeout_ms
self.start_time = time.time()
def expired(self):
"""
Check if the timeout has expired
"""
return time.time() - self.start_time > self.timeout_ms / 1000.0

View File

@@ -0,0 +1,36 @@
"""
This module includes wrapper classes for Tool connection parameters
"""
#pylint: disable=too-few-public-methods
class ToolConnection(object):
"""
Base class for ToolConnection classes used to wrap configuration parameters for tool connections
"""
#pylint: disable=too-few-public-methods
class ToolUsbHidConnection(ToolConnection):
"""
Helper class wrapping configuration parameters for a connection to a USB HID tool
"""
serialnumber = None
tool_name = None
def __init__(self, serialnumber=None, tool_name=None):
"""
:param tool_name: Tool name as given in USB Product string. Some shortnames are also supported
as defined in pyedbglib.hidtransport.toolinfo.py. Set to None if don't care
:param serialnumber: USB serial number string. Set to None if don't care
"""
self.serialnumber = serialnumber
self.tool_name = tool_name
#pylint: disable=too-few-public-methods
class ToolSerialConnection(ToolConnection):
"""
Helper class wrapping configuration parameters for a connection to a serial port
"""
serialport = None
def __init__(self, serialport="COM1"):
self.serialport = serialport

View File

@@ -0,0 +1,275 @@
"""
Utility functions for pymcuprog
"""
# Python 3 compatibility for Python 2
from __future__ import print_function
from pyedbglib.protocols.housekeepingprotocol import Jtagice3HousekeepingProtocol
from pyedbglib.protocols.jtagice3protocol import Jtagice3ResponseError
from pyedbglib.protocols.jtagice3protocol import Jtagice3Protocol
from .pymcuprog_errors import PymcuprogNotSupportedError
def read_tool_info(housekeeper):
"""
Interrogates tool (debugger) for useful info
:returns: Dictionary with various info about the connected debugger
"""
dap_info = housekeeper.dap_info()
# Add alias for serialnumber(==serial)
dap_info['serialnumber'] = dap_info['serial']
# Read FW versions
dap_info['firmware_major'] = housekeeper.get_byte(Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONTEXT_CONFIG,
Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONFIG_FWREV_MAJ)
dap_info['firmware_minor'] = housekeeper.get_byte(Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONTEXT_CONFIG,
Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONFIG_FWREV_MIN)
dap_info['build'] = housekeeper.get_le16(Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONTEXT_CONFIG,
Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONFIG_BUILD)
# Read HW revision
dap_info['hardware_rev'] = housekeeper.get_byte(Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONTEXT_CONFIG,
Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONFIG_HWREV)
# Some EDBG versions do NOT have the dap_info 'device' tag populated for non-ARM parts.
# Sneak in and collect the data from the EDBG config instead
if dap_info['product'][:4] == 'EDBG' and dap_info['device_name'] == '':
try:
# Vendor command
cmd = bytearray([0x83])
# Add cnt of '1' element:
cmd.append(1)
# Add tag of 'TARGET DEVICE NAME'
cmd.append(0x04)
# Add dummy 'param'
cmd.append(ord('?'))
# Add the offset
offset = 0
cmd.extend([offset & 0xFF, offset >> 8])
# Add the chunk size
numbytes = 32
cmd.extend([numbytes & 0xFF, numbytes >> 8])
# raw command routed via the HK interface
response = housekeeper.dap_command_response(cmd)
dap_info['device_name'] = response[6:6 + numbytes].split(b'\0')[0].decode()
except: #pylint: disable=bare-except
# resort to ''
pass
return dap_info
def print_tool_info(info):
"""
Print out various tool information
:param info: Dictionary with various tool info as returned from read_tool_info()
"""
print("Connected to {0:s} from {1:s} (serial number {2:s})".format(info['product'], info['vendor'],
info['serial']))
print("Debugger firmware version {0:d}.{1:d}.{2:d}".format(info['firmware_major'],
info['firmware_minor'],
info['build']))
print("Debugger hardware revision {0:d}".format(info['hardware_rev']))
def read_target_voltage(housekeeper):
"""
Read target voltage
:param housekeeper: instance of pyedbglib.protocols.housekeepingprotocol.Jtagice3HousekeepingProtocol
"""
return read_voltage_parameter(housekeeper, Jtagice3HousekeepingProtocol.HOUSEKEEPING_ANALOG_VTREF)
def read_supply_voltage_setpoint(housekeeper):
"""
Read supply setpoint
:param housekeeper: instance of pyedbglib.protocols.housekeepingprotocol.Jtagice3HousekeepingProtocol
"""
return read_voltage_parameter(housekeeper, Jtagice3HousekeepingProtocol.HOUSEKEEPING_TSUP_VOLTAGE)
def read_usb_voltage(housekeeper):
"""
Read USB voltage
:param housekeeper: instance of pyedbglib.protocols.housekeepingprotocol.Jtagice3HousekeepingProtocol
"""
return read_voltage_parameter(housekeeper, Jtagice3HousekeepingProtocol.HOUSEKEEPING_ANALOG_VUSB)
def read_voltage_parameter(housekeeper, offset):
"""
Generic read voltage from tool parameter
:param housekeeper: Instance of pyedbglib.protocols.housekeepingprotocol.Jtagice3HousekeepingProtocol
:param offset: Tool parameter offset to read
"""
housekeeper.start_session()
voltage = housekeeper.get_le16(Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONTEXT_ANALOG, offset)
voltage = voltage / 1000.0
housekeeper.end_session()
return voltage
def set_supply_voltage_setpoint(housekeeper, voltage):
"""
Set supply setpoint
:param housekeeper: Instance of pyedbglib.protocols.housekeepingprotocol.Jtagice3HousekeepingProtocol
:param voltage: New setpoint for target supply
"""
try:
housekeeper.get_le16(Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONTEXT_ANALOG,
Jtagice3HousekeepingProtocol.HOUSEKEEPING_TSUP_VOLTAGE)
except Jtagice3ResponseError:
raise PymcuprogNotSupportedError("Connected debugger/board does not have supply voltage capability.")
setpoint_mv = int(voltage*1000)
try:
housekeeper.set_le16(Jtagice3HousekeepingProtocol.HOUSEKEEPING_CONTEXT_ANALOG,
Jtagice3HousekeepingProtocol.HOUSEKEEPING_TSUP_VOLTAGE, setpoint_mv)
# Unfortunately pyedbglib only throws a generic Exception in this case so no specific Exceptions can be caught
# See DSG-1494
#pylint: disable=broad-except
except Exception as error:
if "failure code 0x{:02x}".format(Jtagice3Protocol.SETGET_FAILURE_INVALID_VALUE) in str(error).lower():
raise ValueError("Specified voltage out of range!")
# Voltage was within range but something else went wrong. Just forward the exception.
raise
def compare(data0, data1, offset, verify_mask=None):
"""
Compares the two byte arrays
:param data0: first array for compare
:param data1: second array for compare
:param offset: address offset in the memory area, for printing
:param verify_mask: compare mask (for varying instruction width)
:return:
"""
if verify_mask is None:
verify_mask = [0xFF]
# Check first that lengths match
if len(data0) != len(data1):
raise ValueError("Length mismatch on verify, expect 0x{:04X} but got 0x{:04X}".format(len(data0),len(data1)))
mask_len = len(verify_mask)
for i in range(0, len(data0), mask_len):
for dat in range(0, mask_len):
if (data0[i+dat] & verify_mask[dat]) != (data1[i+dat] & verify_mask[dat]):
raise ValueError("Verify mismatch starting at location 0x{:06X}: 0x{:02X} vs 0x{:02X}".
format(i+dat+offset, data0[i+dat] & verify_mask[dat], data1[i+dat] & verify_mask[dat]))
def showdata(data, address=0, page_size=None, line_wrap=16):
"""
Show (print) the data
:param data: an array/list of data to show
:param address: byte address to data
:param page_size: page size in bytes
:param line_wrap: how many bytes to print per line
"""
# Cannot print more per line than the page size
if page_size is not None:
if line_wrap > page_size:
line_wrap = page_size
print("-"*(line_wrap*3+9))
# Page alignment
rows = 0
if page_size is not None:
page = address % page_size
rows = int(page / line_wrap)
for row in range(rows):
print("0x{0:06X}: ".format(address-page+row*line_wrap), end='')
print("xx "*line_wrap, end='')
print("")
# Calculate offset from aligned data
div = address % line_wrap
print("0x{0:06X}: ".format(address-div), end='')
# Add some empty bytes
print("xx "*div, end='')
# keep track of page wraps
wrap = False
for i, value in enumerate(data, div+1):
print("{0:02X} ".format(value), end='')
if page_size is not None:
if (i+(rows*line_wrap)) % page_size == 0 and i != len(data)+div:
print("")
wrap = True
if i % line_wrap == 0 and i != len(data)+div or wrap:
print("")
print("0x{0:06X}: ".format(address-div + i), end='')
wrap = False
# Figure out how many extra empty data positions to print
extra = line_wrap - div - (len(data) % line_wrap)
if extra % line_wrap == 0:
extra = 0
print("xx "*extra, end='')
print("")
print("-"*(line_wrap*3+9))
def pagealign(data, address, page_size, data_size=1):
"""
Aligns data to the start of a page
"""
# Pre-pad the data if it does not start at the start of a page
offset = address % page_size
for _ in range(offset):
data.insert(0, 0xFF)
# In case of other data sizes, post-pad the data
while len(data) % data_size:
data.append(0xFF)
return data, address-offset
def pad_to_size(memory_block, chunk_size, pad_value):
"""
Pads a chunk of memory
"""
while len(memory_block) % chunk_size > 0:
memory_block.append(pad_value)
def enum(**enums):
"""
Emulates an Enum type
Needed for Python 2.7 compatibility as Python did not get built-in support for enums until version 3.4
"""
return type('Enum', (), enums)
def verify_flash_from_bin(bin_filename, backend, offset=0, max_read_chunk=None):
"""
Verify the contents of flash against a bin-file
:param filename: Name/path of bin-file to verify
:param backend: Reference the Backend class of pymcuprog
:param offset: Memory offset to start verify from
:returns: Boolean value indicating success or failure of the operation
"""
bin_file = open(bin_filename, 'rb')
bin_data = bytearray()
for line in bin_file.readlines():
bin_data.extend(line)
verify_status = backend.verify_memory(bin_data, 'flash', offset, max_read_chunk=max_read_chunk)
if verify_status is False:
return False
return True

View File

@@ -0,0 +1,4 @@
""" This file was generated when pymcuprog was built """
VERSION = '3.6.4.86'
COMMIT_ID = '6224623cfc85c5e7cb6b1df8419b72c338c223f9'
BUILD_DATE = '2020-11-09 14:49:15 +0000'