Toggle Navigation
Hatchery
Eggs
updi_test
application.py
Users
Badges
Login
Register
MCH2022 badge?
go to mch2022.badge.team
application.py
raw
Content
""" Application layer for UPDI stack """ import updi_test.constants as constants from updi_test.link import UpdiDatalink from updi_test.timeout import Timeout class UpdiApplication(object): """ Generic application layer for UPDI """ def __init__(self, comport, baud, device=None): self.datalink = UpdiDatalink(comport, baud) self.device = device def device_info(self): """ Reads out device information from various sources """ sib = self.datalink.read_sib() print("SIB read out as: {}".format(sib)) print("Family ID = {}".format(sib[0:7])) print("NVM revision = {}".format(sib[10])) print("OCD revision = {}".format(sib[13])) print("PDI OSC = {}MHz".format(sib[15])) print("PDI revision = 0x{:X}".format(self.datalink.ldcs(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) print("Device ID = {0:X}{1:X}{2:X} rev {3:}".format(devid[0], devid[1], devid[2], chr(ord('A') + devrev[0]))) def in_prog_mode(self): """ Checks whether the NVM PROG flag is up """ if self.datalink.ldcs(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 """ timeout = Timeout(timeout_ms) while not timeout.expired(): if not self.datalink.ldcs(constants.UPDI_ASI_SYS_STATUS) & (1 << constants.UPDI_ASI_SYS_STATUS_LOCKSTATUS): return True print("Timeout waiting for device to unlock") return False def unlock(self): """ Unlock and erase """ # Put in the key self.datalink.key(constants.UPDI_KEY_64, constants.UPDI_KEY_CHIPERASE) # Check key status key_status = self.datalink.ldcs(constants.UPDI_ASI_KEY_STATUS) print("Key status = 0x{0:02X}".format(key_status)) if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_CHIPERASE): raise Exception("Key not accepted") # Insert NVMProg key as well # In case of CRC being enabled, the device must be left in programming mode after the erase # to allow the CRC to be disabled (or flash reprogrammed) self._progmode_key() # Toggle reset self.reset(apply_reset=True) self.reset(apply_reset=False) # And wait for unlock if not self.wait_unlocked(100): raise Exception("Failed to chip erase using key") def _progmode_key(self): """ Inserts the NVMProg key and checks that its accepted """ # First check if NVM is already enabled if self.in_prog_mode(): print("Already in NVM programming mode") return print("Entering NVM programming mode") # Put in the key self.datalink.key(constants.UPDI_KEY_64, constants.UPDI_KEY_NVM) # Check key status key_status = self.datalink.ldcs(constants.UPDI_ASI_KEY_STATUS) print("Key status = 0x{0:02X}".format(key_status)) if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_NVMPROG): raise Exception("Key not accepted") def enter_progmode(self): """ Enters into NVM programming mode """ # Enter NVMProg key self._progmode_key() # Toggle reset self.reset(apply_reset=True) self.reset(apply_reset=False) # And wait for unlock if not self.wait_unlocked(100): raise Exception("Failed to enter NVM programming mode: device is locked") # Check for NVMPROG flag if not self.in_prog_mode(): raise Exception("Failed to enter NVM programming mode") print("Now in NVM programming mode") return True def leave_progmode(self): """ Disables UPDI which releases any keys enabled """ print("Leaving NVM programming mode") self.reset(apply_reset=True) self.reset(apply_reset=False) self.datalink.stcs(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 """ if apply_reset: print("Apply reset") self.datalink.stcs(constants.UPDI_ASI_RESET_REQ, constants.UPDI_RESET_REQ_VALUE) else: print("Release reset") self.datalink.stcs(constants.UPDI_ASI_RESET_REQ, 0x00) def wait_flash_ready(self): """ Waits for the NVM controller to be ready """ timeout = Timeout(10000) # 10 sec timeout, just to be sure print("Wait flash ready") while not timeout.expired(): status = self.datalink.ld(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_STATUS) if status & (1 << constants.UPDI_NVM_STATUS_WRITE_ERROR): print("NVM error") return False if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) | (1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)): return True print("Wait flash ready timed out") return False def execute_nvm_command(self, command): """ Executes an NVM COMMAND on the NVM CTRL """ print("NVMCMD {:d} executing".format(command)) return self.datalink.st(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_CTRLA, command) 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 """ print("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_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_data_words(self, address, data): """ Writes a number of words to memory """ # 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) << 1: raise Exception("Invalid length") # Store the address self.datalink.st_ptr(address) # Fire up the repeat self.datalink.repeat(len(data) >> 1) return self.datalink.st_ptr_inc16(data) def write_data(self, address, data): """ Writes a number of bytes to memory """ # Special case of 1 byte if len(data) == 1: return self.datalink.st(address, data[0]) # Special case of 2 byte elif 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 + 1): raise Exception("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) def write_nvm(self, address, data, nvm_command=constants.UPDI_NVMCTRL_CTRLA_WRITE_PAGE, use_word_access=True): """ Writes a page of data to NVM. By default the PAGE_WRITE command is used, which requires that the page is already erased. By default word access is used (flash) """ # Check that NVM controller is ready if not self.wait_flash_ready(): raise Exception("Timeout waiting for flash ready before page buffer clear ") # Clear the page buffer print("Clear page buffer") self.execute_nvm_command(constants.UPDI_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) # Waif for NVM controller to be ready if not self.wait_flash_ready(): raise Exception("Timeout waiting for flash ready after page buffer clear") # Load the page buffer by writing directly to location if use_word_access: self.write_data_words(address, data) else: self.write_data(address, data) # Write the page to NVM, maybe erase first print("Committing page") self.execute_nvm_command(nvm_command) # Waif for NVM controller to be ready again if not self.wait_flash_ready(): raise Exception("Timeout waiting for flash ready after page write ") def read_data(self, address, size): """ Reads a number of bytes of data from UPDI """ print("Reading {0:d} bytes from 0x{1:04X}".format(size, address)) # Range check if size > constants.UPDI_MAX_REPEAT_SIZE + 1: raise Exception("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 """ print("Reading {0:d} words from 0x{1:04X}".format(words, address)) # Range check if words > (constants.UPDI_MAX_REPEAT_SIZE >> 1) + 1: raise Exception("Cant read that many words in one go") # Store the address self.datalink.st_ptr(address) # Fire up the repeat if words > 1: self.datalink.repeat(words) # Do the read return self.datalink.ld_ptr_inc16(words)