From cc93f3324b230df91b56d135d37ef702f28d569d Mon Sep 17 00:00:00 2001 From: Graziano Previato Date: Sun, 22 Nov 2020 15:13:29 +0100 Subject: [PATCH 1/2] Included -x option for Hx file with Fuses defined --- updi/pyupdi.py | 74 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 7 deletions(-) mode change 100644 => 100755 updi/pyupdi.py diff --git a/updi/pyupdi.py b/updi/pyupdi.py old mode 100644 new mode 100755 index 63ed86b..952404b --- a/updi/pyupdi.py +++ b/updi/pyupdi.py @@ -7,6 +7,7 @@ import re import logging +from io import StringIO from device.device import Device from updi.nvm import UpdiNvmProgrammer """ @@ -72,6 +73,8 @@ def _main(): help="Reset") parser.add_argument("-i", "--info", action="store_true", help="Info") + parser.add_argument("-x", "--integratedHex", action="store_true", + help="Intel HEX file that include fuses definition.") parser.add_argument("-fs", "--fuses", action="append", nargs="*", help="Fuse to set (syntax: fuse_nr:0xvalue)") parser.add_argument("-fr", "--readfuses", action="store_true", @@ -113,14 +116,76 @@ def _main(): nvm.leave_progmode() +def _GetFusesFromHexLine(lFuses): + FusesString = [] + NumOfBytes = int(lFuses[1:3], 16) + FFuses = lFuses[9:-2] + Crc = lFuses[:-2] + if len(FFuses) == NumOfBytes * 2: + # ok, seem good + i = 0 + while i < NumOfBytes: + bPos = i*2 + fFuse = '%d:0x%s' % (i, FFuses[bPos:bPos+2]) + FusesString.append(fFuse) + i = i+1 + return FusesString + + +def _SplitHexFile(filein): + FileHex = StringIO() + FusesString = [] + fhex = open(filein, 'r').read().split('\n') + GetFuses = 0 + for lhex in fhex: + # search records of tyoe 0x04 + # records are at position7,8 + recordId = lhex[7:9] + if recordId == '04': + # found an address record. Get the address: + AddressBase = lhex[9:13] + if AddressBase == '0082': + # found the fuses string + GetFuses = 1 + elif GetFuses == 1: + FusesString = _GetFusesFromHexLine(lhex) + GetFuses = 0 + else: + FileHex.write('%s\n' % lhex) + FileHex.seek(0) + + return FileHex, FusesString + + def _process(nvm, args): if args.erase: try: nvm.chip_erase() except: return False - if args.fuses is not None: - for fslist in args.fuses: + FileHex = None + FusesString = None + if args.integratedHex: + if args.flash is not None: + filename = args.flash + FileHex, FusesString = _SplitHexFile(filename) + FusesString = [FusesString] + else: + print("Needs to specify FileIn with flag -f") + sys.exit(1) + + if args.flash is not None: + if FileHex is None: + FileHex = args.flash + ret = _flash_file(nvm, FileHex) + if args.integratedHex is None: + return ret + + if FusesString is None: + FusesString = args.fuses + if FusesString is not None: + print(FusesString) + for fslist in FusesString: for fsarg in fslist: if not re.match("^[0-9]+:0x[0-9a-fA-F]+$", fsarg): print("Bad fuses format {}. Expected fuse_nr:0xvalue".format(fsarg)) @@ -130,11 +195,6 @@ def _process(nvm, args): value = int(lst[1], 16) if not _set_fuse(nvm, fusenum, value): return False - if args.flash is not None: - return _flash_file(nvm, args.flash) - if args.readfuses: - if not _read_fuses(nvm): - return False return True From 863f4f34c6024bb97ea170f98a2287c4fce002b7 Mon Sep 17 00:00:00 2001 From: Graziano Previato Date: Mon, 30 Nov 2020 15:43:48 +0100 Subject: [PATCH 2/2] - Added flag -l for lock bits Lockbits can be inserted as parameters or in the hex files with 04 record 0x83 Reprogramming can be done. 1) Try to program the device; 2) Reset the power and then reprogram it. --- device/device.py | 2 ++ updi/application.py | 55 +++++++++++++++++++++++++++-- updi/constants.py | 1 + updi/nvm.py | 27 ++++++++++++++ updi/pyupdi.py | 85 +++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 160 insertions(+), 10 deletions(-) mode change 100644 => 100755 device/device.py mode change 100644 => 100755 updi/application.py mode change 100644 => 100755 updi/constants.py mode change 100644 => 100755 updi/nvm.py diff --git a/device/device.py b/device/device.py old mode 100644 new mode 100755 index d5fbebf..9b5ad31 --- a/device/device.py +++ b/device/device.py @@ -25,6 +25,7 @@ DEFAULT_SIGROW_ADDRESS = 0x1100 DEFAULT_FUSES_ADDRESS = 0x1280 DEFAULT_USERROW_ADDRESS = 0x1300 +DEFAULT_LOCKBIT_ADDRESS = 0x128a class Device(object): # pylint: disable=too-few-public-methods """ @@ -37,6 +38,7 @@ def __init__(self, device_name): self.nvmctrl_address = DEFAULT_NVMCTRL_ADDRESS self.sigrow_address = DEFAULT_SIGROW_ADDRESS self.fuses_address = DEFAULT_FUSES_ADDRESS + self.lockbit_address = DEFAULT_LOCKBIT_ADDRESS self.userrow_address = DEFAULT_USERROW_ADDRESS if device_name in DEVICE_AVR_D_SERIES: diff --git a/updi/application.py b/updi/application.py old mode 100644 new mode 100755 index 64a17d9..ec1add9 --- a/updi/application.py +++ b/updi/application.py @@ -6,6 +6,7 @@ import updi.constants as constants from updi.link import UpdiDatalink from updi.timeout import Timeout +import time class UpdiApplication(object): @@ -22,6 +23,7 @@ def __init__(self, comport, baud, device=None): self.write_nvm = self.write_nvm_v0 self.chip_erase = self.chip_erase_v0 self.write_fuse = self.write_fuse_v0 + self.write_locks = self.write_locks_v0 def device_info(self): """ @@ -44,6 +46,7 @@ def device_info(self): self.write_nvm = self.write_nvm_v1 self.chip_erase = self.chip_erase_v1 self.write_fuse = self.write_fuse_v1 + self.write_locks = self.write_locks_v1 self.datalink.set_24bit_updi(True) ocd = sib[11:14].strip() @@ -101,10 +104,14 @@ def unlock(self): self.datalink.key(constants.UPDI_KEY_64, constants.UPDI_KEY_CHIPERASE) # Check key status + print("Getting Key Status") + self.logger.info("Getting Key Status") key_status = self.datalink.ldcs(constants.UPDI_ASI_KEY_STATUS) + print("Key status = 0x{0:02X}".format(key_status)) self.logger.info("Key status = 0x{0:02X}".format(key_status)) - if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_CHIPERASE): + + if not (key_status & (1 << constants.UPDI_ASI_KEY_STATUS_CHIPERASE)): raise Exception("Key not accepted") # Insert NVMProg key as well @@ -153,12 +160,17 @@ def enter_progmode(self): self.reset(apply_reset=False) # Wait for NVMPROG flag + print("Wait 60 secs for NVMPROG") + Now = time.time() + Timeout = 0 while True: - self.logger.info("Wait for NVMPROG") + self.logger.info("Wait 60 secs for NVMPROG") sys_status = self.datalink.ldcs(constants.UPDI_ASI_SYS_STATUS) if sys_status & (1 << constants.UPDI_ASI_SYS_STATUS_NVMPROG): break - + if time.time() > (Now + 60): + Timeout = 1 + break # TODO - add timeout if not self.in_prog_mode(): @@ -435,6 +447,43 @@ def write_fuse_v1(self, fusenum, value): """ return self.write_eeprom_v1(fusenum, value) + def write_locks_v0(self, locknum, value): + """ + Writes one fuse value + """ + # Must be in prog mode + if not self.in_prog_mode(): + raise Exception("Enter progmode first!") + + if not self.wait_flash_ready(): + raise Exception("Flash not ready for fuse setting") + + lockbit_data = value + lockbit_address = self.device.lockbit_address + locknum + + address = self.device.nvmctrl_address + constants.UPDI_NVMCTRL_ADDRL + data = [lockbit_address & 0xff] + self.write_data(address, data) + + address = self.device.nvmctrl_address + constants.UPDI_NVMCTRL_ADDRH + data = [lockbit_address >> 8] + self.write_data(address, data) + + address = self.device.nvmctrl_address + constants.UPDI_NVMCTRL_DATAL + self.write_data(address, lockbit_data) + + address = self.device.nvmctrl_address + constants.UPDI_NVMCTRL_CTRLA + data = [constants.UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE] + + self.write_data(address, data) + + def write_locks_v1(self, locknum, value): + """ + Writes one fuse value + DA fuses are EEPROM-based + """ + return self.write_eeprom_v1(locknum, value) + def read_data(self, address, size): """ Reads a number of bytes of data from UPDI diff --git a/updi/constants.py b/updi/constants.py old mode 100644 new mode 100755 index 2761b1c..b86cf00 --- a/updi/constants.py +++ b/updi/constants.py @@ -61,6 +61,7 @@ UPDI_KEY_NVM = b"NVMProg " UPDI_KEY_CHIPERASE = b"NVMErase" +#UPDI_KEY_CHIPERASE = 0x4E564D4572617365 UPDI_ASI_STATUSA_REVID = 4 UPDI_ASI_STATUSB_PESIG = 0 diff --git a/updi/nvm.py b/updi/nvm.py old mode 100644 new mode 100755 index 6476e6b..5a07560 --- a/updi/nvm.py +++ b/updi/nvm.py @@ -133,6 +133,33 @@ def write_fuse(self, fusenum, value): return self.application.write_fuse(fusenum, fuse_data) + def read_locks(self, locsnum): + """ + Reads one fuse value + """ + # Must be in prog mode + if not self.progmode: + raise Exception("Enter progmode first!") + + address = self.device.fuses_address + locsnum + data = self.application.datalink.ld(address) + return data + + def write_locks(self, locksnum, value): + """ + Writes one fuse value + """ + # Must be in prog mode + if not self.progmode: + raise Exception("Enter progmode first!") + + if not self.application.wait_flash_ready(): + raise Exception("Flash not ready for fuse setting") + + locks_data = [value] + + return self.application.write_locks(locksnum, locks_data) + def pad_data(self, data, blocksize, character=0xFF): """ Pads data so that there are full pages diff --git a/updi/pyupdi.py b/updi/pyupdi.py index 952404b..370947b 100755 --- a/updi/pyupdi.py +++ b/updi/pyupdi.py @@ -56,6 +56,7 @@ def _main(): + ExecuteLeave = 1 if sys.version_info[0] < 3: print("WARNING: for best results use Python3") @@ -75,6 +76,8 @@ def _main(): help="Info") parser.add_argument("-x", "--integratedHex", action="store_true", help="Intel HEX file that include fuses definition.") + parser.add_argument("-l", "--lockbits", action="append", nargs="*", + help="LockBits to set (syntax: lockbyte_nr:0xvalue)") parser.add_argument("-fs", "--fuses", action="append", nargs="*", help="Fuse to set (syntax: fuse_nr:0xvalue)") parser.add_argument("-fr", "--readfuses", action="store_true", @@ -109,11 +112,30 @@ def _main(): print("Device info: {0:s}".format(str(nvm.get_device_info()))) - if not _process(nvm, args): + if _process(nvm, args) == 0: print("Error during processing") + else: + ExecuteLeave = 0 # Reset only needs this. - nvm.leave_progmode() + if ExecuteLeave: + nvm.leave_progmode() + + +def _GetLockBitFromHexLine(lLock): + LockString = [] + NumOfBytes = int(lLock[1:3], 16) + FLocks = lLock[9:-2] + Crc = lLock[:-2] + if len(FLocks) == NumOfBytes * 2: + # ok, seem good + i = 0 + while i < NumOfBytes: + bPos = i*2 + lLock = '%d:0x%s' % (i, FLocks[bPos:bPos+2]) + LockString.append(lLock) + i = i+1 + return LockString def _GetFusesFromHexLine(lFuses): @@ -135,8 +157,10 @@ def _GetFusesFromHexLine(lFuses): def _SplitHexFile(filein): FileHex = StringIO() FusesString = [] + LockBitString = [] fhex = open(filein, 'r').read().split('\n') GetFuses = 0 + GetLockBit = 0 for lhex in fhex: # search records of tyoe 0x04 # records are at position7,8 @@ -147,14 +171,20 @@ def _SplitHexFile(filein): if AddressBase == '0082': # found the fuses string GetFuses = 1 + elif AddressBase == '0083': + # found lockbit string + GetLockBit = 1 elif GetFuses == 1: FusesString = _GetFusesFromHexLine(lhex) GetFuses = 0 + elif GetLockBit == 1: + LockBitString = _GetLockBitFromHexLine(lhex) + GetLockBit = 0 else: FileHex.write('%s\n' % lhex) FileHex.seek(0) - return FileHex, FusesString + return FileHex, FusesString, LockBitString def _process(nvm, args): @@ -165,11 +195,13 @@ def _process(nvm, args): return False FileHex = None FusesString = None + LockString = None if args.integratedHex: if args.flash is not None: filename = args.flash - FileHex, FusesString = _SplitHexFile(filename) + FileHex, FusesString, LockString = _SplitHexFile(filename) FusesString = [FusesString] + LockString = [LockString] else: print("Needs to specify FileIn with flag -f") sys.exit(1) @@ -179,7 +211,10 @@ def _process(nvm, args): FileHex = args.flash ret = _flash_file(nvm, FileHex) if args.integratedHex is None: - return ret + if ret == True: + return 1 + else: + return 0 if FusesString is None: FusesString = args.fuses @@ -194,8 +229,24 @@ def _process(nvm, args): fusenum = int(lst[0]) value = int(lst[1], 16) if not _set_fuse(nvm, fusenum, value): - return False - return True + return 0 + if LockString is None: + LockString = args.lockbits + if LockString is not None: + print(LockString) + for fslist in LockString: + for fsarg in fslist: + if not re.match("^[0-9]+:0x[0-9a-fA-F]+$", fsarg): + print("Bad fuses format {}. Expected fuse_nr:0xvalue".format(fsarg)) + continue + lst = fsarg.split(":0x") + fusenum = int(lst[0]) + value = int(lst[1], 16) + if not _set_locks(nvm, fusenum, value): + return 0 + else: + return 2 + return 1 def _flash_file(nvm, filename): @@ -229,6 +280,26 @@ def _set_fuse(nvm, fusenum, value): print("Fuse {0} set to 0x{1:02X} successfully".format(fusenum, value)) return ret +def _set_locks(nvm, locksnum, value): + ret = 0 + actual_val = nvm.read_locks(locksnum) + print("LockBits {0}: Actual Val: 0x{1:02X}".format(locksnum, actual_val)) + nvm.write_locks(locksnum, value) + # now exit program mode + # check if the lock is programmed exiting and entring programm mode + nvm.leave_progmode() + try: + nvm.enter_progmode() + except: + print("Device is locked.") + ret = 1 +# ret = actual_val == value +# if not ret: +# print("Verify error for LockBits {0}, expected 0x{1:02X} read 0x{2:02X}".format(locksnum, value, actual_val)) +# else: +# print("LockBits {0} set to 0x{1:02X} successfully".format(locksnum, value)) + return ret + def _read_fuses(nvm): print("Fuse:Value")