1# This file describes the common eFuses structures for chips
2#
3# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
4#
5# SPDX-License-Identifier: GPL-2.0-or-later
6
7import binascii
8import re
9import sys
10
11from bitstring import BitArray, BitStream, CreationError
12
13import esptool
14
15from . import util
16
17
18class CheckArgValue(object):
19    def __init__(self, efuses, name):
20        self.efuses = efuses
21        self.name = name
22
23    def __call__(self, new_value_str):
24        def check_arg_value(efuse, new_value):
25            if efuse.efuse_type.startswith("bool"):
26                new_value = 1 if new_value is None else int(new_value, 0)
27                if new_value != 1:
28                    raise esptool.FatalError(
29                        "New value is not accepted for efuse '{}' "
30                        "(will always burn 0->1), given value={}".format(
31                            efuse.name, new_value
32                        )
33                    )
34            elif efuse.efuse_type.startswith(("int", "uint")):
35                if efuse.efuse_class == "bitcount":
36                    if new_value is None:
37                        # find the first unset bit and set it
38                        old_value = efuse.get_raw()
39                        new_value = old_value
40                        bit = 1
41                        while new_value == old_value:
42                            new_value = bit | old_value
43                            bit <<= 1
44                    else:
45                        new_value = int(new_value, 0)
46                else:
47                    if new_value is None:
48                        raise esptool.FatalError(
49                            "New value required for efuse '{}' (given None)".format(
50                                efuse.name
51                            )
52                        )
53                    new_value = int(new_value, 0)
54                    if new_value == 0:
55                        raise esptool.FatalError(
56                            "New value should not be 0 for '{}' "
57                            "(given value= {})".format(efuse.name, new_value)
58                        )
59            elif efuse.efuse_type.startswith("bytes"):
60                if new_value is None:
61                    raise esptool.FatalError(
62                        "New value required for efuse '{}' "
63                        "(given None)".format(efuse.name)
64                    )
65                if len(new_value) * 8 != efuse.bitarray.len:
66                    raise esptool.FatalError(
67                        "The length of efuse '{}' ({} bits) "
68                        "(given len of the new value= {} bits)".format(
69                            efuse.name, efuse.bitarray.len, len(new_value) * 8
70                        )
71                    )
72            else:
73                raise esptool.FatalError(
74                    "The '{}' type for the '{}' efuse is not supported yet.".format(
75                        efuse.efuse_type, efuse.name
76                    )
77                )
78            return new_value
79
80        efuse = self.efuses[self.name]
81        new_value = efuse.check_format(new_value_str)
82        return check_arg_value(efuse, new_value)
83
84
85class EfuseProtectBase(object):
86    # This class is used by EfuseBlockBase and EfuseFieldBase
87
88    def get_read_disable_mask(self):
89        mask = 0
90        if isinstance(self.read_disable_bit, list):
91            for i in self.read_disable_bit:
92                mask |= 1 << i
93        else:
94            mask = 1 << self.read_disable_bit
95        return mask
96
97    def is_readable(self):
98        """Return true if the efuse is readable by software"""
99        num_bit = self.read_disable_bit
100        if num_bit is None:
101            return True  # read cannot be disabled
102        return (self.parent["RD_DIS"].get() & (self.get_read_disable_mask())) == 0
103
104    def disable_read(self):
105        num_bit = self.read_disable_bit
106        if num_bit is None:
107            raise esptool.FatalError("This efuse cannot be read-disabled")
108        if not self.parent["RD_DIS"].is_writeable():
109            raise esptool.FatalError(
110                "This efuse cannot be read-disabled due the to RD_DIS field is "
111                "already write-disabled"
112            )
113        self.parent["RD_DIS"].save(self.get_read_disable_mask())
114
115    def is_writeable(self):
116        num_bit = self.write_disable_bit
117        if num_bit is None:
118            return True  # write cannot be disabled
119        return (self.parent["WR_DIS"].get() & (1 << num_bit)) == 0
120
121    def disable_write(self):
122        num_bit = self.write_disable_bit
123        if not self.parent["WR_DIS"].is_writeable():
124            raise esptool.FatalError(
125                "This efuse cannot be write-disabled due to the WR_DIS field is "
126                "already write-disabled"
127            )
128        self.parent["WR_DIS"].save(1 << num_bit)
129
130    def check_wr_rd_protect(self):
131        if not self.is_readable():
132            error_msg = "\t{} is read-protected.".format(self.name)
133            "The written value can not be read, the efuse/block looks as all 0.\n"
134            error_msg += "\tBurn in this case may damage an already written value."
135            self.parent.print_error_msg(error_msg)
136        if not self.is_writeable():
137            error_msg = "\t{} is write-protected. Burn is not possible.".format(
138                self.name
139            )
140            self.parent.print_error_msg(error_msg)
141
142
143class EfuseBlockBase(EfuseProtectBase):
144    def __init__(self, parent, param, skip_read=False):
145        self.parent = parent
146        self.name = param.name
147        self.alias = param.alias
148        self.id = param.id
149        self.rd_addr = param.rd_addr
150        self.wr_addr = param.wr_addr
151        self.write_disable_bit = param.write_disable_bit
152        self.read_disable_bit = param.read_disable_bit
153        self.len = param.len
154        self.key_purpose_name = param.key_purpose
155        bit_block_len = self.get_block_len() * 8
156        self.bitarray = BitStream(bit_block_len)
157        self.bitarray.set(0)
158        self.wr_bitarray = BitStream(bit_block_len)
159        self.wr_bitarray.set(0)
160        self.fail = False
161        self.num_errors = 0
162        if self.id == 0:
163            self.err_bitarray = BitStream(bit_block_len)
164            self.err_bitarray.set(0)
165        else:
166            self.err_bitarray = None
167
168        if not skip_read:
169            self.read()
170
171    def get_block_len(self):
172        coding_scheme = self.get_coding_scheme()
173        if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE:
174            return self.len * 4
175        elif coding_scheme == self.parent.REGS.CODING_SCHEME_34:
176            return (self.len * 3 // 4) * 4
177        elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS:
178            return self.len * 4
179        else:
180            raise esptool.FatalError(
181                "Coding scheme (%d) not supported" % (coding_scheme)
182            )
183
184    def get_coding_scheme(self):
185        if self.id == 0:
186            return self.parent.REGS.CODING_SCHEME_NONE
187        else:
188            return self.parent.coding_scheme
189
190    def get_raw(self, from_read=True):
191        if from_read:
192            return self.bitarray.bytes
193        else:
194            return self.wr_bitarray.bytes
195
196    def get(self, from_read=True):
197        self.get_bitstring(from_read=from_read)
198
199    def get_bitstring(self, from_read=True):
200        if from_read:
201            return self.bitarray
202        else:
203            return self.wr_bitarray
204
205    def convert_to_bitstring(self, new_data):
206        if isinstance(new_data, BitArray):
207            return new_data
208        else:
209            return BitArray(bytes=new_data, length=len(new_data) * 8)
210
211    def get_words(self):
212        def get_offsets(self):
213            return [x + self.rd_addr for x in range(0, self.get_block_len(), 4)]
214
215        return [self.parent.read_reg(offs) for offs in get_offsets(self)]
216
217    def read(self):
218        words = self.get_words()
219        data = BitArray()
220        for word in reversed(words):
221            data.append("uint:32=%d" % word)
222        self.bitarray.overwrite(data, pos=0)
223        self.print_block(self.bitarray, "read_regs")
224
225    def print_block(self, bit_string, comment, debug=False):
226        if self.parent.debug or debug:
227            bit_string.pos = 0
228            print(
229                "%-15s (%-16s) [%-2d] %s:"
230                % (self.name, " ".join(self.alias)[:16], self.id, comment),
231                " ".join(
232                    [
233                        "%08x" % word
234                        for word in bit_string.readlist(
235                            "%d*uint:32" % (bit_string.len / 32)
236                        )[::-1]
237                    ]
238                ),
239            )
240
241    def check_wr_data(self):
242        wr_data = self.wr_bitarray
243        if wr_data.all(False):
244            # nothing to burn
245            if self.parent.debug:
246                print("[{:02}] {:20} nothing to burn".format(self.id, self.name))
247            return False
248        if len(wr_data.bytes) != len(self.bitarray.bytes):
249            raise esptool.FatalError(
250                "Data does not fit: the block%d size is %d bytes, data is %d bytes"
251                % (self.id, len(self.bitarray.bytes), len(wr_data.bytes))
252            )
253        self.check_wr_rd_protect()
254
255        if self.get_bitstring().all(False):
256            print(
257                "[{:02}] {:20} is empty, will burn the new value".format(
258                    self.id, self.name
259                )
260            )
261        else:
262            # the written block in chip is not empty
263            if self.get_bitstring() == wr_data:
264                print(
265                    "[{:02}] {:20} is already written the same value, "
266                    "continue with EMPTY_BLOCK".format(self.id, self.name)
267                )
268                wr_data.set(0)
269            else:
270                print("[{:02}] {:20} is not empty".format(self.id, self.name))
271                print("\t(written ):", self.get_bitstring())
272                print("\t(to write):", wr_data)
273                mask = self.get_bitstring() & wr_data
274                if mask == wr_data:
275                    print(
276                        "\tAll wr_data bits are set in the written block, "
277                        "continue with EMPTY_BLOCK."
278                    )
279                    wr_data.set(0)
280                else:
281                    coding_scheme = self.get_coding_scheme()
282                    if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE:
283                        print("\t(coding scheme = NONE)")
284                    elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS:
285                        print("\t(coding scheme = RS)")
286                        error_msg = (
287                            "\tBurn into %s is forbidden "
288                            "(RS coding scheme does not allow this)." % (self.name)
289                        )
290                        self.parent.print_error_msg(error_msg)
291                    elif coding_scheme == self.parent.REGS.CODING_SCHEME_34:
292                        print("\t(coding scheme = 3/4)")
293                        data_can_not_be_burn = False
294                        for i in range(0, self.get_bitstring().len, 6 * 8):
295                            rd_chunk = self.get_bitstring()[i : i + 6 * 8 :]
296                            wr_chunk = wr_data[i : i + 6 * 8 :]
297                            if rd_chunk.any(True):
298                                if wr_chunk.any(True):
299                                    print(
300                                        "\twritten chunk [%d] and wr_chunk "
301                                        "are not empty. " % (i // (6 * 8)),
302                                        end="",
303                                    )
304                                    if rd_chunk == wr_chunk:
305                                        print(
306                                            "wr_chunk == rd_chunk. "
307                                            "Countinue with empty chunk."
308                                        )
309                                        wr_data[i : i + 6 * 8 :].set(0)
310                                    else:
311                                        print("wr_chunk != rd_chunk. Can not burn.")
312                                        print("\twritten ", rd_chunk)
313                                        print("\tto write", wr_chunk)
314                                        data_can_not_be_burn = True
315                        if data_can_not_be_burn:
316                            error_msg = (
317                                "\tBurn into %s is forbidden "
318                                "(3/4 coding scheme does not allow this)." % (self.name)
319                            )
320                            self.parent.print_error_msg(error_msg)
321                    else:
322                        raise esptool.FatalError(
323                            "The coding scheme ({}) is not supported".format(
324                                coding_scheme
325                            )
326                        )
327
328    def save(self, new_data):
329        # new_data will be checked by check_wr_data() during burn_all()
330        # new_data (bytes)  = [0][1][2] ... [N]            (original data)
331        # in string format  = [0] [1] [2] ... [N]          (util.hexify(data, " "))
332        # in hex format     = 0x[N]....[2][1][0]           (from bitstring print(data))
333        # in reg format     = [3][2][1][0] ... [N][][][]   (as it will be in the device)
334        # in bitstring      = [N] ... [2][1][0]            (to get a correct bitstring
335        #                                                   need to reverse new_data)
336        # *[x] - means a byte.
337        data = BitStream(bytes=new_data[::-1], length=len(new_data) * 8)
338        if self.parent.debug:
339            print(
340                "\twritten : {} ->\n\tto write: {}".format(self.get_bitstring(), data)
341            )
342        self.wr_bitarray.overwrite(self.wr_bitarray | data, pos=0)
343
344    def burn_words(self, words):
345        for burns in range(3):
346            self.parent.efuse_controller_setup()
347            if self.parent.debug:
348                print("Write data to BLOCK%d" % (self.id))
349            write_reg_addr = self.wr_addr
350            for word in words:
351                # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data
352                #   32 bytes to EFUSE_PGM_DATA[0..7]_REG
353                #   12 bytes to EFUSE_CHECK_VALUE[0..2]_REG. These regs are next after
354                #   EFUSE_PGM_DATA_REG
355                # for esp32:
356                #   each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG
357                #   for writing data
358                if self.parent.debug:
359                    print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word))
360                self.parent.write_reg(write_reg_addr, word)
361                write_reg_addr += 4
362
363            self.parent.write_efuses(self.id)
364            for _ in range(5):
365                self.parent.efuse_read()
366                self.parent.get_coding_scheme_warnings(silent=True)
367                if self.fail or self.num_errors:
368                    print(
369                        "Error in BLOCK%d, re-burn it again (#%d), to fix it. "
370                        "fail_bit=%d, num_errors=%d"
371                        % (self.id, burns, self.fail, self.num_errors)
372                    )
373                    break
374            if not self.fail and self.num_errors == 0:
375                break
376
377    def burn(self):
378        if self.wr_bitarray.all(False):
379            # nothing to burn
380            return
381        before_burn_bitarray = self.bitarray[:]
382        assert before_burn_bitarray is not self.bitarray
383        self.print_block(self.wr_bitarray, "to_write")
384        words = self.apply_coding_scheme()
385        self.burn_words(words)
386        self.read()
387        if not self.is_readable():
388            print(
389                "{} ({}) is read-protected. "
390                "Read back the burn value is not possible.".format(
391                    self.name, self.alias
392                )
393            )
394            if self.bitarray.all(False):
395                print("Read all '0'")
396            else:
397                # Should never happen
398                raise esptool.FatalError(
399                    "The {} is read-protected but not all '0' ({})".format(
400                        self.name, self.bitarray.hex
401                    )
402                )
403        else:
404            if self.wr_bitarray == self.bitarray:
405                print("BURN BLOCK%-2d - OK (write block == read block)" % self.id)
406            elif (
407                self.wr_bitarray & self.bitarray == self.wr_bitarray
408                and self.bitarray & before_burn_bitarray == before_burn_bitarray
409            ):
410                print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id)
411            else:
412                # Happens only when an efuse is written and read-protected
413                # in one command
414                self.print_block(self.wr_bitarray, "Expected")
415                self.print_block(self.bitarray, "Real    ")
416                # Read-protected BLK0 values are reported back as zeros,
417                # raise error only for other blocks
418                if self.id != 0:
419                    raise esptool.FatalError(
420                        "Burn {} ({}) was not successful".format(self.name, self.alias)
421                    )
422        self.wr_bitarray.set(0)
423
424
425class EspEfusesBase(object):
426    """
427    Wrapper object to manage the efuse fields in a connected ESP bootloader
428    """
429
430    _esp = None
431    blocks = []
432    efuses = []
433    coding_scheme = None
434    force_write_always = None
435    batch_mode_cnt = 0
436
437    def __iter__(self):
438        return self.efuses.__iter__()
439
440    def get_crystal_freq(self):
441        return self._esp.get_crystal_freq()
442
443    def read_efuse(self, n):
444        """Read the nth word of the ESP3x EFUSE region."""
445        return self._esp.read_efuse(n)
446
447    def read_reg(self, addr):
448        return self._esp.read_reg(addr)
449
450    def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0):
451        return self._esp.write_reg(addr, value, mask, delay_us, delay_after_us)
452
453    def update_reg(self, addr, mask, new_val):
454        return self._esp.update_reg(addr, mask, new_val)
455
456    def efuse_controller_setup(self):
457        pass
458
459    def reconnect_chip(self, esp):
460        print("Re-connecting...")
461        baudrate = esp._port.baudrate
462        port = esp._port.port
463        esp._port.close()
464        return esptool.cmds.detect_chip(port, baudrate)
465
466    def get_index_block_by_name(self, name):
467        for block in self.blocks:
468            if block.name == name or name in block.alias:
469                return block.id
470        return None
471
472    def read_blocks(self):
473        for block in self.blocks:
474            block.read()
475
476    def update_efuses(self):
477        for efuse in self.efuses:
478            efuse.update(self.blocks[efuse.block].bitarray)
479
480    def burn_all(self, check_batch_mode=False):
481        if check_batch_mode:
482            if self.batch_mode_cnt != 0:
483                print(
484                    "\nBatch mode is enabled, "
485                    "the burn will be done at the end of the command."
486                )
487                return False
488        print("\nCheck all blocks for burn...")
489        print("idx, BLOCK_NAME,          Conclusion")
490        have_wr_data_for_burn = False
491        for block in self.blocks:
492            block.check_wr_data()
493            if not have_wr_data_for_burn and block.get_bitstring(from_read=False).any(
494                True
495            ):
496                have_wr_data_for_burn = True
497        if not have_wr_data_for_burn:
498            print("Nothing to burn, see messages above.")
499            return
500        EspEfusesBase.confirm("", self.do_not_confirm)
501
502        # Burn from BLKn -> BLK0. Because BLK0 can set rd or/and wr protection bits.
503        for block in reversed(self.blocks):
504            old_fail = block.fail
505            old_num_errors = block.num_errors
506            block.burn()
507            if (block.fail and old_fail != block.fail) or (
508                block.num_errors and block.num_errors > old_num_errors
509            ):
510                raise esptool.FatalError("Error(s) were detected in eFuses")
511        print("Reading updated efuses...")
512        self.read_coding_scheme()
513        self.read_blocks()
514        self.update_efuses()
515        return True
516
517    @staticmethod
518    def confirm(action, do_not_confirm):
519        print(
520            "%s%s\nThis is an irreversible operation!"
521            % (action, "" if action.endswith("\n") else ". ")
522        )
523        if not do_not_confirm:
524            print("Type 'BURN' (all capitals) to continue.")
525            # required for Pythons which disable line buffering, ie mingw in mintty
526            sys.stdout.flush()
527            yes = input()
528            if yes != "BURN":
529                print("Aborting.")
530                sys.exit(0)
531
532    def print_error_msg(self, error_msg):
533        if self.force_write_always is not None:
534            if not self.force_write_always:
535                error_msg += "(use '--force-write-always' option to ignore it)"
536        if self.force_write_always:
537            print(error_msg, "Skipped because '--force-write-always' option.")
538        else:
539            raise esptool.FatalError(error_msg)
540
541
542class EfuseFieldBase(EfuseProtectBase):
543    def __init__(self, parent, param):
544        self.category = param.category
545        self.parent = parent
546        self.block = param.block
547        self.word = param.word
548        self.pos = param.pos
549        self.write_disable_bit = param.write_disable_bit
550        self.read_disable_bit = param.read_disable_bit
551        self.name = param.name
552        self.efuse_class = param.class_type
553        self.efuse_type = param.type
554        self.description = param.description
555        self.dict_value = param.dictionary
556        self.fail = False
557        self.num_errors = 0
558        if self.efuse_type.startswith("bool"):
559            field_len = 1
560        else:
561            field_len = int(re.search(r"\d+", self.efuse_type).group())
562            if self.efuse_type.startswith("bytes"):
563                field_len *= 8
564        self.bitarray = BitStream(field_len)
565        self.bit_len = field_len
566        self.bitarray.set(0)
567        self.update(self.parent.blocks[self.block].bitarray)
568
569    def check_format(self, new_value_str):
570        if new_value_str is None:
571            return new_value_str
572        else:
573            if self.efuse_type.startswith("bytes"):
574                if new_value_str.startswith("0x"):
575                    # cmd line: 0x0102030405060708 .... 112233ff      (hex)
576                    # regs: 112233ff ... 05060708 01020304
577                    # BLK: ff 33 22 11 ... 08 07 06 05 04 03 02 01
578                    return binascii.unhexlify(new_value_str[2:])[::-1]
579                else:
580                    # cmd line: 0102030405060708 .... 112233ff        (string)
581                    # regs: 04030201 08070605 ... ff332211
582                    # BLK: 01 02 03 04 05 06 07 08 ... 11 22 33 ff
583                    return binascii.unhexlify(new_value_str)
584            else:
585                return new_value_str
586
587    def convert_to_bitstring(self, new_value):
588        if isinstance(new_value, BitArray):
589            return new_value
590        else:
591            if self.efuse_type.startswith("bytes"):
592                # new_value (bytes) = [0][1][2] ... [N]
593                #                                                        (original data)
594                # in string format  = [0] [1] [2] ... [N]
595                #                                               (util.hexify(data, " "))
596                # in hex format     = 0x[N]....[2][1][0]
597                #                                           (from bitstring print(data))
598                # in reg format     = [3][2][1][0] ... [N][][][]
599                #                                          (as it will be in the device)
600                # in bitstring      = [N] ... [2][1][0]
601                #                 (to get a correct bitstring need to reverse new_value)
602                # *[x] - means a byte.
603                return BitArray(bytes=new_value[::-1], length=len(new_value) * 8)
604            else:
605                try:
606                    return BitArray(self.efuse_type + "={}".format(new_value))
607                except CreationError as err:
608                    print(
609                        "New value '{}' is not suitable for {} ({})".format(
610                            new_value, self.name, self.efuse_type
611                        )
612                    )
613                    raise esptool.FatalError(err)
614
615    def check_new_value(self, bitarray_new_value):
616        bitarray_old_value = self.get_bitstring() | self.get_bitstring(from_read=False)
617        if bitarray_new_value.len != bitarray_old_value.len:
618            raise esptool.FatalError(
619                "For {} efuse, the length of the new value is wrong, "
620                "expected {} bits, was {} bits.".format(
621                    self.name, bitarray_old_value.len, bitarray_new_value.len
622                )
623            )
624        if bitarray_new_value == bitarray_old_value:
625            error_msg = "\tThe same value for {} ".format(self.name)
626            error_msg += "is already burned. Do not change the efuse."
627            print(error_msg)
628            bitarray_new_value.set(0)
629        elif bitarray_new_value == self.get_bitstring(from_read=False):
630            error_msg = "\tThe same value for {} ".format(self.name)
631            error_msg += "is already prepared for the burn operation."
632            print(error_msg)
633            bitarray_new_value.set(0)
634        else:
635            if self.name not in ["WR_DIS", "RD_DIS"]:
636                # WR_DIS, RD_DIS fields can have already set bits.
637                # Do not neeed to check below condition for them.
638                if bitarray_new_value | bitarray_old_value != bitarray_new_value:
639                    error_msg = "\tNew value contains some bits that cannot be cleared "
640                    error_msg += "(value will be {})".format(
641                        bitarray_old_value | bitarray_new_value
642                    )
643                    self.parent.print_error_msg(error_msg)
644            self.check_wr_rd_protect()
645
646    def save_to_block(self, bitarray_field):
647        block = self.parent.blocks[self.block]
648        wr_bitarray_temp = block.wr_bitarray.copy()
649        position = wr_bitarray_temp.length - (
650            self.word * 32 + self.pos + bitarray_field.len
651        )
652        wr_bitarray_temp.overwrite(bitarray_field, pos=position)
653        block.wr_bitarray |= wr_bitarray_temp
654
655    def save(self, new_value):
656        bitarray_field = self.convert_to_bitstring(new_value)
657        self.check_new_value(bitarray_field)
658        self.save_to_block(bitarray_field)
659
660    def update(self, bit_array_block):
661        if self.word is None or self.pos is None:
662            self.bitarray.overwrite(self.convert_to_bitstring(self.get()), pos=0)
663            return
664        field_len = self.bitarray.len
665        bit_array_block.pos = bit_array_block.length - (
666            self.word * 32 + self.pos + field_len
667        )
668        self.bitarray.overwrite(bit_array_block.read(field_len), pos=0)
669        err_bitarray = self.parent.blocks[self.block].err_bitarray
670        if err_bitarray is not None:
671            err_bitarray.pos = err_bitarray.length - (
672                self.word * 32 + self.pos + field_len
673            )
674            self.fail = not err_bitarray.read(field_len).all(False)
675        else:
676            self.fail = self.parent.blocks[self.block].fail
677            self.num_errors = self.parent.blocks[self.block].num_errors
678
679    def get_raw(self, from_read=True):
680        """Return the raw (unformatted) numeric value of the efuse bits
681
682        Returns a simple integer or (for some subclasses) a bitstring.
683        type: int or bool -> int
684        type: bytes -> bytearray
685        """
686        return self.get_bitstring(from_read).read(self.efuse_type)
687
688    def get(self, from_read=True):
689        """Get a formatted version of the efuse value, suitable for display
690        type: int or bool -> int
691        type: bytes -> string  "01 02 03 04 05 06 07 08 ... ".
692        Byte order [0] ... [N]. dump regs: 0x04030201 0x08070605 ...
693        """
694        if self.efuse_type.startswith("bytes"):
695            return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ")
696        else:
697            return self.get_raw(from_read)
698
699    def get_meaning(self, from_read=True):
700        """Get the meaning of efuse from dict if possible, suitable for display"""
701        if self.dict_value:
702            try:
703                return self.dict_value[self.get_raw(from_read)]
704            except KeyError:
705                pass
706        return self.get(from_read)
707
708    def get_bitstring(self, from_read=True):
709        if from_read:
710            self.bitarray.pos = 0
711            return self.bitarray
712        else:
713            field_len = self.bitarray.len
714            block = self.parent.blocks[self.block]
715            block.wr_bitarray.pos = block.wr_bitarray.length - (
716                self.word * 32 + self.pos + field_len
717            )
718            return block.wr_bitarray.read(self.bitarray.len)
719
720    def burn(self, new_value):
721        # Burn a efuse. Added for compatibility reason.
722        self.save(new_value)
723        self.parent.burn_all()
724