1# This file describes eFuses controller for ESP32-C2 chip
2#
3# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
4#
5# SPDX-License-Identifier: GPL-2.0-or-later
6
7from bitstring import BitStream
8
9import reedsolo
10
11from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters
12from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError
13
14
15class EmulateEfuseController(EmulateEfuseControllerBase):
16    """The class for virtual efuse operation. Using for HOST_TEST."""
17
18    CHIP_NAME = "ESP32-C2"
19    mem = None
20    debug = False
21    Blocks = EfuseDefineBlocks
22    Fields = EfuseDefineFields
23    REGS = EfuseDefineRegisters
24
25    def __init__(self, efuse_file=None, debug=False):
26        super(EmulateEfuseController, self).__init__(efuse_file, debug)
27        self.write_reg(self.REGS.EFUSE_STATUS_REG, 1)
28
29    """ esptool method start >>"""
30
31    def get_major_chip_version(self):
32        return 1
33
34    def get_minor_chip_version(self):
35        return 0
36
37    def get_crystal_freq(self):
38        return 40  # MHz
39
40    def get_security_info(self):
41        return {
42            "flags": 0,
43            "flash_crypt_cnt": 0,
44            "key_purposes": 0,
45            "chip_id": 0,
46            "api_version": 0,
47        }
48
49    """ << esptool method end """
50
51    def handle_writing_event(self, addr, value):
52        if addr == self.REGS.EFUSE_CMD_REG:
53            if value & self.REGS.EFUSE_PGM_CMD:
54                self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF)
55                self.clean_blocks_wr_regs()
56                self.check_rd_protection_area()
57                self.write_reg(addr, 0)
58                self.write_reg(self.REGS.EFUSE_STATUS_REG, 1)
59            elif value == self.REGS.EFUSE_READ_CMD:
60                self.write_reg(addr, 0)
61                self.write_reg(self.REGS.EFUSE_STATUS_REG, 1)
62                self.save_to_file()
63
64    def get_bitlen_of_block(self, blk, wr=False):
65        if blk.id == 0:
66            if wr:
67                return 32 * 8
68            else:
69                return 32 * blk.len
70        else:
71            if wr:
72                rs_coding = 32 * 3
73                return 32 * 8 + rs_coding
74            else:
75                return 32 * blk.len
76
77    def handle_coding_scheme(self, blk, data):
78        if blk.id != 0:
79            # CODING_SCHEME RS applied only for all blocks except BLK0.
80            coded_bytes = 12
81            data.pos = coded_bytes * 8
82            plain_data = data.readlist("32*uint:8")[::-1]
83            # takes 32 bytes
84            # apply RS encoding
85            rs = reedsolo.RSCodec(coded_bytes)
86            # 32 byte of data + 12 bytes RS
87            calc_encoded_data = list(rs.encode([x for x in plain_data]))
88            data.pos = 0
89            if calc_encoded_data != data.readlist("44*uint:8")[::-1]:
90                raise FatalError("Error in coding scheme data")
91            data = data[coded_bytes * 8 :]
92        if blk.len < 8:
93            data = data[(8 - blk.len) * 32 :]
94        return data
95
96    def check_rd_protection_area(self):
97        # checks fields which have the read protection bits.
98        # if the read protection bit is set then we need to reset this field to 0.
99
100        def get_read_disable_mask(blk):
101            mask = 0
102            if isinstance(blk.read_disable_bit, list):
103                for i in blk.read_disable_bit:
104                    mask |= 1 << i
105            else:
106                mask = 1 << blk.read_disable_bit
107            return mask
108
109        read_disable_bit = self.read_field("RD_DIS", bitstring=False)
110        for b in self.Blocks.BLOCKS:
111            blk = self.Blocks.get(b)
112            block = self.read_block(blk.id)
113            if (
114                blk.read_disable_bit is not None
115                and read_disable_bit & get_read_disable_mask(blk)
116            ):
117                if isinstance(blk.read_disable_bit, list):
118                    if read_disable_bit & (1 << blk.read_disable_bit[0]):
119                        block.set(
120                            0, [i for i in range(blk.len * 32 // 2, blk.len * 32)]
121                        )
122                    if read_disable_bit & (1 << blk.read_disable_bit[1]):
123                        block.set(0, [i for i in range(0, blk.len * 32 // 2)])
124                else:
125                    block.set(0)
126            else:
127                for e in self.Fields.EFUSES:
128                    field = self.Fields.get(e)
129                    if (
130                        blk.id == field.block
131                        and field.read_disable_bit is not None
132                        and read_disable_bit & get_read_disable_mask(field)
133                    ):
134                        raw_data = self.read_field(field.name)
135                        raw_data.set(0)
136                        block.pos = block.length - (
137                            field.word * 32 + field.pos + raw_data.length
138                        )
139                        block.overwrite(BitStream(raw_data.length))
140            self.overwrite_mem_from_block(blk, block)
141