import re
from bitstring import BitStream
class EmulateEfuseControllerBase(object):
"""The class for virtual efuse operations. Using for HOST_TEST."""
CHIP_NAME = ""
mem = None
debug = False
Blocks = None
Fields = None
REGS = None
def __init__(self, efuse_file=None, debug=False):
self.debug = debug
self.efuse_file = efuse_file
if self.efuse_file:
try:
self.mem = BitStream(
bytes=open(self.efuse_file, "rb").read(),
length=self.REGS.EFUSE_MEM_SIZE * 8,
)
except (ValueError, FileNotFoundError):
self.mem = BitStream(length=self.REGS.EFUSE_MEM_SIZE * 8)
self.mem.set(0)
self.mem.tofile(open(self.efuse_file, "a+b"))
else:
self.mem = BitStream(self.REGS.EFUSE_MEM_SIZE * 8)
self.mem.set(0)
""" esptool method start >> """
def get_chip_description(self):
major_rev = self.get_major_chip_version()
minor_rev = self.get_minor_chip_version()
return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})"
def get_chip_revision(self):
return self.get_major_chip_version() * 100 + self.get_minor_chip_version()
def read_efuse(self, n, block=0):
"""Read the nth word of the ESP3x EFUSE region."""
blk = self.Blocks.get(self.Blocks.BLOCKS[block])
return self.read_reg(blk.rd_addr + (4 * n))
def read_reg(self, addr):
self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32)
return self.mem.read("uint:32")
def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0):
self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32)
self.mem.overwrite("uint:32={}".format(value & mask))
self.handle_writing_event(addr, value)
def update_reg(self, addr, mask, new_val):
position = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32)
self.mem.pos = position
cur_val = self.mem.read("uint:32")
self.mem.pos = position
self.mem.overwrite("uint:32={}".format(cur_val | (new_val & mask)))
def write_efuse(self, n, value, block=0):
"""Write the nth word of the ESP3x EFUSE region."""
blk = self.Blocks.get(self.Blocks.BLOCKS[block])
self.write_reg(blk.wr_addr + (4 * n), value)
""" << esptool method end """
def handle_writing_event(self, addr, value):
self.save_to_file()
def save_to_file(self):
if self.efuse_file:
with open(self.efuse_file, "wb") as f:
self.mem.tofile(f)
def handle_coding_scheme(self, blk, data):
return data
def copy_blocks_wr_regs_to_rd_regs(self, updated_block=None):
for b in reversed(self.Blocks.BLOCKS):
blk = self.Blocks.get(b)
if updated_block is not None:
if blk.id != updated_block:
continue
data = self.read_block(blk.id, wr_regs=True)
if self.debug:
print(blk.name, data.hex)
plain_data = self.handle_coding_scheme(blk, data)
plain_data = self.check_wr_protection_area(blk.id, plain_data)
self.update_block(blk, plain_data)
def clean_blocks_wr_regs(self):
for b in self.Blocks.BLOCKS:
blk = self.Blocks.get(b)
for offset in range(0, blk.len * 4, 4):
wr_addr = blk.wr_addr + offset
self.write_reg(wr_addr, 0)
def read_field(self, name, bitstring=True):
for field in self.Fields.EFUSES:
if field.name == name:
self.read_block(field.block)
block = self.read_block(field.block)
if field.type.startswith("bool"):
field_len = 1
else:
field_len = int(re.search(r"\d+", field.type).group())
if field.type.startswith("bytes"):
field_len *= 8
block.pos = block.length - (field.word * 32 + field.pos + field_len)
if bitstring:
return block.read(field_len)
else:
return block.read(field.type)
return None
def get_bitlen_of_block(self, blk, wr=False):
return 32 * blk.len
def read_block(self, idx, wr_regs=False):
block = None
for b in self.Blocks.BLOCKS:
blk = self.Blocks.get(b)
if blk.id == idx:
blk_len_bits = self.get_bitlen_of_block(blk, wr=wr_regs)
addr = blk.wr_addr if wr_regs else blk.rd_addr
self.mem.pos = self.mem.length - (
(addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + blk_len_bits
)
block = self.mem.read(blk_len_bits)
break
return block
def update_block(self, blk, wr_data):
wr_data = self.read_block(blk.id) | wr_data
self.overwrite_mem_from_block(blk, wr_data)
def overwrite_mem_from_block(self, blk, wr_data):
self.mem.pos = self.mem.length - (
(blk.rd_addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + wr_data.len
)
self.mem.overwrite(wr_data)
def check_wr_protection_area(self, num_blk, wr_data):
write_disable_bit = self.read_field("WR_DIS", bitstring=False)
mask_wr_data = BitStream(len(wr_data))
mask_wr_data.set(0)
blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk])
if blk.write_disable_bit is not None and write_disable_bit & (
1 << blk.write_disable_bit
):
mask_wr_data.set(1)
else:
for field in self.Fields.EFUSES:
if blk.id == field.block and field.block == num_blk:
if field.write_disable_bit is not None and write_disable_bit & (
1 << field.write_disable_bit
):
data = self.read_field(field.name)
data.set(1)
mask_wr_data.pos = mask_wr_data.length - (
field.word * 32 + field.pos + data.len
)
mask_wr_data.overwrite(data)
mask_wr_data.invert()
return wr_data & mask_wr_data
def check_rd_protection_area(self):
read_disable_bit = self.read_field("RD_DIS", bitstring=False)
for b in self.Blocks.BLOCKS:
blk = self.Blocks.get(b)
block = self.read_block(blk.id)
if blk.read_disable_bit is not None and read_disable_bit & (
1 << blk.read_disable_bit
):
block.set(0)
else:
for field in self.Fields.EFUSES:
if (
blk.id == field.block
and field.read_disable_bit is not None
and read_disable_bit & (1 << field.read_disable_bit)
):
raw_data = self.read_field(field.name)
raw_data.set(0)
block.pos = block.length - (
field.word * 32 + field.pos + raw_data.length
)
block.overwrite(BitStream(raw_data.length))
self.overwrite_mem_from_block(blk, block)
def clean_mem(self):
self.mem.set(0)
if self.efuse_file:
with open(self.efuse_file, "wb") as f:
self.mem.tofile(f)
class FatalError(RuntimeError):
"""
Wrapper class for runtime errors that aren't caused by internal bugs
"""
def __init__(self, message):
RuntimeError.__init__(self, message)
@staticmethod
def WithResult(message, result):
return FatalError(result)