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 e in self.Fields.EFUSES: 113 field = self.Fields.get(e) 114 if field.name == name: 115 self.read_block(field.block) 116 block = self.read_block(field.block) 117 if field.type.startswith("bool"): 118 field_len = 1 119 else: 120 field_len = int(re.search(r"\d+", field.type).group()) 121 if field.type.startswith("bytes"): 122 field_len *= 8 123 block.pos = block.length - (field.word * 32 + field.pos + field_len) 124 if bitstring: 125 return block.read(field_len) 126 else: 127 return block.read(field.type) 128 return None 129 130 def get_bitlen_of_block(self, blk, wr=False): 131 return 32 * blk.len 132 133 def read_block(self, idx, wr_regs=False): 134 block = None 135 for b in self.Blocks.BLOCKS: 136 blk = self.Blocks.get(b) 137 if blk.id == idx: 138 blk_len_bits = self.get_bitlen_of_block(blk, wr=wr_regs) 139 addr = blk.wr_addr if wr_regs else blk.rd_addr 140 self.mem.pos = self.mem.length - ( 141 (addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + blk_len_bits 142 ) 143 block = self.mem.read(blk_len_bits) 144 break 145 return block 146 147 def update_block(self, blk, wr_data): 148 wr_data = self.read_block(blk.id) | wr_data 149 self.overwrite_mem_from_block(blk, wr_data) 150 151 def overwrite_mem_from_block(self, blk, wr_data): 152 self.mem.pos = self.mem.length - ( 153 (blk.rd_addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + wr_data.len 154 ) 155 self.mem.overwrite(wr_data) 156 157 def check_wr_protection_area(self, num_blk, wr_data): 158 # checks fields which have the write protection bit. 159 # if the write protection bit is set, we need to protect that area from changes. 160 write_disable_bit = self.read_field("WR_DIS", bitstring=False) 161 mask_wr_data = BitStream(len(wr_data)) 162 mask_wr_data.set(0) 163 blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk]) 164 if blk.write_disable_bit is not None and write_disable_bit & ( 165 1 << blk.write_disable_bit 166 ): 167 mask_wr_data.set(1) 168 else: 169 for e in self.Fields.EFUSES: 170 field = self.Fields.get(e) 171 if blk.id == field.block and field.block == num_blk: 172 if field.write_disable_bit is not None and write_disable_bit & ( 173 1 << field.write_disable_bit 174 ): 175 data = self.read_field(field.name) 176 data.set(1) 177 mask_wr_data.pos = mask_wr_data.length - ( 178 field.word * 32 + field.pos + data.len 179 ) 180 mask_wr_data.overwrite(data) 181 mask_wr_data.invert() 182 return wr_data & mask_wr_data 183 184 def check_rd_protection_area(self): 185 # checks fields which have the read protection bits. 186 # if the read protection bit is set then we need to reset this field to 0. 187 read_disable_bit = self.read_field("RD_DIS", bitstring=False) 188 for b in self.Blocks.BLOCKS: 189 blk = self.Blocks.get(b) 190 block = self.read_block(blk.id) 191 if blk.read_disable_bit is not None and read_disable_bit & ( 192 1 << blk.read_disable_bit 193 ): 194 block.set(0) 195 else: 196 for e in self.Fields.EFUSES: 197 field = self.Fields.get(e) 198 if ( 199 blk.id == field.block 200 and field.read_disable_bit is not None 201 and read_disable_bit & (1 << field.read_disable_bit) 202 ): 203 raw_data = self.read_field(field.name) 204 raw_data.set(0) 205 block.pos = block.length - ( 206 field.word * 32 + field.pos + raw_data.length 207 ) 208 block.overwrite(BitStream(raw_data.length)) 209 self.overwrite_mem_from_block(blk, block) 210 211 def clean_mem(self): 212 self.mem.set(0) 213 if self.efuse_file: 214 with open(self.efuse_file, "wb") as f: 215 self.mem.tofile(f) 216 217 218class FatalError(RuntimeError): 219 """ 220 Wrapper class for runtime errors that aren't caused by internal bugs 221 """ 222 223 def __init__(self, message): 224 RuntimeError.__init__(self, message) 225 226 @staticmethod 227 def WithResult(message, result): 228 return FatalError(result) 229