1# This file describes eFuses controller for ESP32 chip 2# 3# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD 4# 5# SPDX-License-Identifier: GPL-2.0-or-later 6 7import re 8 9from bitstring import BitStream 10 11 12class EmulateEfuseControllerBase(object): 13 """The class for virtual efuse operations. Using for HOST_TEST.""" 14 15 CHIP_NAME = "" 16 mem = None 17 debug = False 18 Blocks = None 19 Fields = None 20 REGS = None 21 22 def __init__(self, efuse_file=None, debug=False): 23 self.debug = debug 24 self.efuse_file = efuse_file 25 if self.efuse_file: 26 try: 27 self.mem = BitStream( 28 bytes=open(self.efuse_file, "rb").read(), 29 length=self.REGS.EFUSE_MEM_SIZE * 8, 30 ) 31 except (ValueError, FileNotFoundError): 32 # the file is empty or does not fit the length. 33 self.mem = BitStream(length=self.REGS.EFUSE_MEM_SIZE * 8) 34 self.mem.set(0) 35 self.mem.tofile(open(self.efuse_file, "a+b")) 36 else: 37 # efuse_file is not provided 38 # it means we do not want to keep the result of efuse operations 39 self.mem = BitStream(self.REGS.EFUSE_MEM_SIZE * 8) 40 self.mem.set(0) 41 42 """ esptool method start >> """ 43 44 def get_chip_description(self): 45 major_rev = self.get_major_chip_version() 46 minor_rev = self.get_minor_chip_version() 47 return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" 48 49 def get_chip_revision(self): 50 return self.get_major_chip_version() * 100 + self.get_minor_chip_version() 51 52 def read_efuse(self, n, block=0): 53 """Read the nth word of the ESP3x EFUSE region.""" 54 blk = self.Blocks.get(self.Blocks.BLOCKS[block]) 55 return self.read_reg(blk.rd_addr + (4 * n)) 56 57 def read_reg(self, addr): 58 self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) 59 return self.mem.read("uint:32") 60 61 def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): 62 self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) 63 self.mem.overwrite("uint:32={}".format(value & mask)) 64 self.handle_writing_event(addr, value) 65 66 def update_reg(self, addr, mask, new_val): 67 position = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) 68 self.mem.pos = position 69 cur_val = self.mem.read("uint:32") 70 self.mem.pos = position 71 self.mem.overwrite("uint:32={}".format(cur_val | (new_val & mask))) 72 73 def write_efuse(self, n, value, block=0): 74 """Write the nth word of the ESP3x EFUSE region.""" 75 blk = self.Blocks.get(self.Blocks.BLOCKS[block]) 76 self.write_reg(blk.wr_addr + (4 * n), value) 77 78 """ << esptool method end """ 79 80 def handle_writing_event(self, addr, value): 81 self.save_to_file() 82 83 def save_to_file(self): 84 if self.efuse_file: 85 with open(self.efuse_file, "wb") as f: 86 self.mem.tofile(f) 87 88 def handle_coding_scheme(self, blk, data): 89 return data 90 91 def copy_blocks_wr_regs_to_rd_regs(self, updated_block=None): 92 for b in reversed(self.Blocks.BLOCKS): 93 blk = self.Blocks.get(b) 94 if updated_block is not None: 95 if blk.id != updated_block: 96 continue 97 data = self.read_block(blk.id, wr_regs=True) 98 if self.debug: 99 print(blk.name, data.hex) 100 plain_data = self.handle_coding_scheme(blk, data) 101 plain_data = self.check_wr_protection_area(blk.id, plain_data) 102 self.update_block(blk, plain_data) 103 104 def clean_blocks_wr_regs(self): 105 for b in self.Blocks.BLOCKS: 106 blk = self.Blocks.get(b) 107 for offset in range(0, blk.len * 4, 4): 108 wr_addr = blk.wr_addr + offset 109 self.write_reg(wr_addr, 0) 110 111 def read_field(self, name, bitstring=True): 112 for field in self.Fields.EFUSES: 113 if field.name == name: 114 self.read_block(field.block) 115 block = self.read_block(field.block) 116 if field.type.startswith("bool"): 117 field_len = 1 118 else: 119 field_len = int(re.search(r"\d+", field.type).group()) 120 if field.type.startswith("bytes"): 121 field_len *= 8 122 block.pos = block.length - (field.word * 32 + field.pos + field_len) 123 if bitstring: 124 return block.read(field_len) 125 else: 126 return block.read(field.type) 127 return None 128 129 def get_bitlen_of_block(self, blk, wr=False): 130 return 32 * blk.len 131 132 def read_block(self, idx, wr_regs=False): 133 block = None 134 for b in self.Blocks.BLOCKS: 135 blk = self.Blocks.get(b) 136 if blk.id == idx: 137 blk_len_bits = self.get_bitlen_of_block(blk, wr=wr_regs) 138 addr = blk.wr_addr if wr_regs else blk.rd_addr 139 self.mem.pos = self.mem.length - ( 140 (addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + blk_len_bits 141 ) 142 block = self.mem.read(blk_len_bits) 143 break 144 return block 145 146 def update_block(self, blk, wr_data): 147 wr_data = self.read_block(blk.id) | wr_data 148 self.overwrite_mem_from_block(blk, wr_data) 149 150 def overwrite_mem_from_block(self, blk, wr_data): 151 self.mem.pos = self.mem.length - ( 152 (blk.rd_addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + wr_data.len 153 ) 154 self.mem.overwrite(wr_data) 155 156 def check_wr_protection_area(self, num_blk, wr_data): 157 # checks fields which have the write protection bit. 158 # if the write protection bit is set, we need to protect that area from changes. 159 write_disable_bit = self.read_field("WR_DIS", bitstring=False) 160 mask_wr_data = BitStream(len(wr_data)) 161 mask_wr_data.set(0) 162 blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk]) 163 if blk.write_disable_bit is not None and write_disable_bit & ( 164 1 << blk.write_disable_bit 165 ): 166 mask_wr_data.set(1) 167 else: 168 for field in self.Fields.EFUSES: 169 if blk.id == field.block and field.block == num_blk: 170 if field.write_disable_bit is not None and write_disable_bit & ( 171 1 << field.write_disable_bit 172 ): 173 data = self.read_field(field.name) 174 data.set(1) 175 mask_wr_data.pos = mask_wr_data.length - ( 176 field.word * 32 + field.pos + data.len 177 ) 178 mask_wr_data.overwrite(data) 179 mask_wr_data.invert() 180 return wr_data & mask_wr_data 181 182 def check_rd_protection_area(self): 183 # checks fields which have the read protection bits. 184 # if the read protection bit is set then we need to reset this field to 0. 185 read_disable_bit = self.read_field("RD_DIS", bitstring=False) 186 for b in self.Blocks.BLOCKS: 187 blk = self.Blocks.get(b) 188 block = self.read_block(blk.id) 189 if blk.read_disable_bit is not None and read_disable_bit & ( 190 1 << blk.read_disable_bit 191 ): 192 block.set(0) 193 else: 194 for field in self.Fields.EFUSES: 195 if ( 196 blk.id == field.block 197 and field.read_disable_bit is not None 198 and read_disable_bit & (1 << field.read_disable_bit) 199 ): 200 raw_data = self.read_field(field.name) 201 raw_data.set(0) 202 block.pos = block.length - ( 203 field.word * 32 + field.pos + raw_data.length 204 ) 205 block.overwrite(BitStream(raw_data.length)) 206 self.overwrite_mem_from_block(blk, block) 207 208 def clean_mem(self): 209 self.mem.set(0) 210 if self.efuse_file: 211 with open(self.efuse_file, "wb") as f: 212 self.mem.tofile(f) 213 214 215class FatalError(RuntimeError): 216 """ 217 Wrapper class for runtime errors that aren't caused by internal bugs 218 """ 219 220 def __init__(self, message): 221 RuntimeError.__init__(self, message) 222 223 @staticmethod 224 def WithResult(message, result): 225 return FatalError(result) 226