class EfuseBlockBase(EfuseProtectBase): def __init__(self, parent, param, skip_read=False): self.parent = parent self.name = param.name self.alias = param.alias self.id = param.id self.rd_addr = param.rd_addr self.wr_addr = param.wr_addr self.write_disable_bit = param.write_disable_bit self.read_disable_bit = param.read_disable_bit self.len = param.len self.key_purpose_name = param.key_purpose bit_block_len = self.get_block_len() * 8 self.bitarray = BitString(bit_block_len) self.bitarray.set(0) self.wr_bitarray = BitString(bit_block_len) self.wr_bitarray.set(0) if not skip_read: self.read() def get_block_len(self): coding_scheme = self.get_coding_scheme() if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: return self.len * 4 elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: return (self.len * 3 // 4) * 4 elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: return self.len * 4 else: raise esptool.FatalError("Coding scheme (%d) not supported" % (coding_scheme)) def get_coding_scheme(self): if self.id == 0: return self.parent.REGS.CODING_SCHEME_NONE else: return self.parent.coding_scheme def get_raw(self, from_read=True): if from_read: return self.bitarray.bytes else: return self.wr_bitarray.bytes def get(self, from_read=True): self.get_bitstring(from_read=from_read) def get_bitstring(self, from_read=True): if from_read: return self.bitarray else: return self.wr_bitarray def convert_to_bitstring(self, new_data): if isinstance(new_data, BitArray): return new_data else: return BitArray(bytes=new_data, length=len(new_data) * 8) def get_words(self): def get_offsets(self): return [ x + self.rd_addr for x in range(0, self.get_block_len(), 4) ] return [self.parent.read_reg(offs) for offs in get_offsets(self)] def read(self): words = self.get_words() data = BitArray() for word in reversed(words): data.append("uint:32=%d" % word) self.bitarray.overwrite(data, pos=0) self.print_block(self.bitarray, "read_regs") def print_block(self, bit_string, comment, debug=False): if self.parent.debug or debug: bit_string.pos = 0 print( "%-15s (%-16s) [%-2d] %s:" % (self.name, " ".join(self.alias)[:16], self.id, comment), " ".join([ "%08x" % word for word in bit_string.readlist('%d*uint:32' % (bit_string.len / 32))[::-1] ])) def check_wr_data(self): wr_data = self.wr_bitarray if wr_data.all(False): # nothing to burn if self.parent.debug: print("[{:02}] {:20} nothing to burn".format( self.id, self.name)) return False if len(wr_data.bytes) != len(self.bitarray.bytes): raise esptool.FatalError( "Data does not fit: the block%d size is %d bytes, data is %d bytes" % (self.id, len(self.bitarray.bytes), len(wr_data.bytes))) self.check_wr_rd_protect() if self.get_bitstring().all(False): print("[{:02}] {:20} is empty, will burn the new value".format( self.id, self.name)) else: # the written block in chip is not empty if self.get_bitstring() == wr_data: print( "[{:02}] {:20} is already written the same value, continue with EMPTY_BLOCK" .format(self.id, self.name)) wr_data.set(0) else: print("[{:02}] {:20} is not empty".format(self.id, self.name)) print("\t(written ):", self.get_bitstring()) print("\t(to write):", wr_data) mask = self.get_bitstring() & wr_data if mask == wr_data: print( "\tAll wr_data bits are set in the written block, continue with EMPTY_BLOCK." ) wr_data.set(0) else: coding_scheme = self.get_coding_scheme() if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: print("\t(coding scheme = NONE)") elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: print("\t(coding scheme = RS)") error_msg = "\tBurn into %s is forbidden (RS coding scheme does not allow this)." % ( self.name) self.parent.print_error_msg(error_msg) elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: print("\t(coding scheme = 3/4)") data_can_not_be_burn = False for i in range(0, self.get_bitstring().len, 6 * 8): rd_chunk = self.get_bitstring()[i:i + 6 * 8:] wr_chunk = wr_data[i:i + 6 * 8:] if rd_chunk.any(True): if wr_chunk.any(True): print( "\twritten chunk [%d] and wr_chunk are not empty. " % (i // (6 * 8)), end="") if rd_chunk == wr_chunk: print( "wr_chunk == rd_chunk. Countinue with empty chunk." ) wr_data[i:i + 6 * 8:].set(0) else: print( "wr_chunk != rd_chunk. Can not burn." ) print("\twritten ", rd_chunk) print("\tto write", wr_chunk) data_can_not_be_burn = True if data_can_not_be_burn: error_msg = "\tBurn into %s is forbidden (3/4 coding scheme does not allow this)." % ( self.name) self.parent.print_error_msg(error_msg) else: raise esptool.FatalError( "The coding scheme ({}) is not supported".format( coding_scheme)) def save(self, new_data): # new_data will be checked by check_wr_data() during burn_all() # new_data (bytes) = [0][1][2] ... [N] (original data) # in string format = [0] [1] [2] ... [N] (util.hexify(data, " ")) # in hex format = 0x[N]....[2][1][0] (from bitstring print(data)) # in reg format = [3][2][1][0] ... [N][][][] (as it will be in the device) # in bitstring = [N] ... [2][1][0] (to get a correct bitstring need to reverse new_data) # *[x] - means a byte. data = BitString(bytes=new_data[::-1], length=len(new_data) * 8) if self.parent.debug: print("\twritten : {} ->\n\tto write: {}".format( self.get_bitstring(), data)) self.wr_bitarray.overwrite(data, pos=0) def burn_words(self, words): self.parent.efuse_controller_setup() if self.parent.debug: print("Write data to BLOCK%d" % (self.id)) write_reg_addr = self.wr_addr for word in words: # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data # 32 bytes to EFUSE_PGM_DATA[0..7]_REG # 12 bytes to EFUSE_CHECK_VALUE[0..2]_REG. These regs are next after EFUSE_PGM_DATA_REG # for esp32: # each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG for writing data if self.parent.debug: print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word)) self.parent.write_reg(write_reg_addr, word) write_reg_addr += 4 warnings_before = self.parent.get_coding_scheme_warnings() warnings_after = self.parent.write_efuses(self.id) if warnings_after & ~warnings_before != 0: print( "WARNING: Burning efuse block added coding scheme warnings 0x%x -> 0x%x. Encoding bug?" % (warnings_before, warnings_after)) def burn(self): if self.wr_bitarray.all(False): # nothing to burn return before_burn_bitarray = self.bitarray[:] assert before_burn_bitarray is not self.bitarray self.print_block(self.wr_bitarray, "to_write") words = self.apply_coding_scheme() self.burn_words(words) self.read() if not self.is_readable(): print( "{} ({}) is read-protected. Read back the burn value is not possible." .format(self.name, self.alias)) if self.bitarray.all(False): print("Read all '0'") else: # Should never be happened raise esptool.FatalError( "The {} is read-protected but not all '0' ({})".format( self.name, self.bitarray.hex)) else: if self.wr_bitarray == self.bitarray: print("BURN BLOCK%-2d - OK (write block == read block)" % self.id) elif self.wr_bitarray & self.bitarray == self.wr_bitarray and self.bitarray & before_burn_bitarray == before_burn_bitarray: print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id) else: self.print_block(self.wr_bitarray, "Expected") self.print_block(self.bitarray, "Real ") raise esptool.FatalError( "Burn {} ({}) was not successful".format( self.name, self.alias)) self.wr_bitarray.set(0)