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