Understanding how to burn eFuses on i.MX8 and i.MX8x families

6 minute read

On the i.MX8 and i.MX8x families the OTP (One Time Programmable) memory is part of the security subsystem and is controlled by the SCU (System Controller Unit) and SECO (Security Controller) only.

This blog post provides a quick overview on the new architecture and explains how users can read and write eFuses using the NXP L4.14.62_1.0.0 BSP beta release.

Understanding the new architecture

The fuses arrays

The OTP memory is controlled by the OCOTP CTRL (On-Chip One Time Programmable Controller) block, differently from the i.MX6 and i.MX7 families on i.MX8 and i.MX8x the OCOTP CTRL organizes the OTP memory in fuse arrays instead of fuse banks and words.

Each fuse module has a certain size and an associated supplementary array of 16 Words, this determines the number of words available on each device.

The i.MX8QXP has one fuse module of 16K, this module consists of a 16 words supplementary array and a 512 words (16x1024 bits) main array, a total of 528 words.

Fuse Row Index Array Number of words
0 - 15 16k Supplementary Array 16
16 - 271 16k Main array 256
272 - 543 Not being used 0
544 - 799 16K Main array 256

ECC and Redundancy bits

The eFuses in i.MX8 and i.MX8x families includes the ECC and Redundancy features which are used to verify if the fuse has been correctly programmed.

On each eFuse programming an ECC is calculated in a word by word basis, this means that all words covered by this feature can be only programmed once, individual bits cannot be programmed independently.

For words that have to be programmed each efuse at a time (e.g. Monotonic Array) the redundancy feature is used instead, the mechanism is based on a bit by bit basis allowing the word to be programmed multiple times.

The table below lists the words supporting the redundancy feature on i.MX8QXP, hence the words can be programmed more than one time:

Fuse Row Index Array Fuse
0 - 15 16k Supplementary Array All Words
544 - 663 16K Main array Monotonic Array
664 - 669 16K Main array SCU SW Version

Details regarding redundancy and ECC features can be found in section “Bank redundancy vs ECC section” in processors reference manual.

The SCFW API functions

As mentioned in the introduction only SECO and SCU can program eFuses, the System Controller Firmware (SCFW) provides API functions which are used to interact with OCOTP CTRL block. The read and write API functions are listed below:

/* This function writes a given fuse word index */
sc_misc_otp_fuse_write ( sc_ipc_t ipc, uint32_t word, uint32_t val )

/* This function reads a given fuse word index */
sc_misc_otp_fuse_read ( sc_ipc_t ipc, uint32_t word, uint32_t *val )

/* This function securely writes a group of fuse words */
sc_err_t sc_misc_seco_fuse_write ( sc_ipc_t ipc, sc_faddr_t addr )

The user should call the SCFW API functions in order to read and write in the OTP memory, the Fuse Row Index should be provided as the word parameter.

The list of programmable eFuses and their respective indexes are available in fusemap section on the processors Reference Manual.

  • sc_misc_otp_fuse_write(): This API function is used to program eFuses using SCU, and can be only called by the owner of the SC_R_SYSTEM resource. Details about ownership can be seen in SCFW API Reference guide available in SCFW porting kit.

  • sc_misc_otp_fuse_read(): This API function is used to read eFuses using SCU, and can be called by all execution environments.

  • sc_err_t sc_misc_seco_fuse_write(): A group of fuses can be programmed by SECO, the address provided in the function must be a pointer into secure RAM containing a signed fuse command message block.

Please refer to the System Controller Unit (SCU) Introduction blog post for additional details.

The current BSP implementation

Programing eFuses using U-Boot

Starting in L4.14.62_1.0.0 Beta release the U-Boot fuse command is capable of reading and writing the efuses using the SCFW API functions, the process is similar as in i.MX6 and 7 families. The U-Boot source code is already programmed in such a way that all API calls are transparent to the user.

=> fuse
fuse - Fuse sub-system

Usage:
fuse read <bank> <word> [<cnt>] - read 1 or 'cnt' fuse words,
    starting at 'word'
fuse sense <bank> <word> [<cnt>] - sense 1 or 'cnt' fuse words,
    starting at 'word'
fuse prog [-y] <bank> <word> <hexval> [<hexval>...] - program 1 or
    several fuse words, starting at 'word' (PERMANENT)
fuse override <bank> <word> <hexval> [<hexval>...] - override 1 or
    several fuse words, starting at 'word'
=>

As mentioned in this post, on i.MX8 and i.MX8x families, the fuses are organized in fuse arrays instead of fuse banks and words, in this case, the bank parameter should be set to zero and the word should match the Fuse row Index in the processors fusemap.

The command line below can be used as an example to program the MAC1_ADDR[31:00] fuses in i.MX8QXP:

=> fuse prog 0 708 0xa295fc11
Programming bank 0 word 0x000002c4 to 0xa295fc11...
Warning: Programming fuses is an irreversible operation!
        This may brick your system.
        Use this command only if you are sure of what you are doing!

Really perform this fuse programming? <y/N>
y

u-boot adds another confirmation layer for Fuses protected by ECC, and will ask you twice if you are really sure you want to burn the fuses. Below an example, setting boot mode fuse to eMMC boot.

=> fuse prog 0 18 0x00000042
Programming bank 0 word 0x00000012 to 0x00000042...
Warning: Programming fuses is an irreversible operation!
        This may brick your system.
        Use this command only if you are sure of what you are doing!

Really perform this fuse programming? <y/N>
y
Warning: Words in this index range have ECC protection
and can only be programmed once per word. Individual bit
operations will be rejected after the first one.


 Really program this word? <y/N>
y

In order to bypass the second confirmation (useful for uuu scripts, see below), one has to first set the force_prog_ecc variable to y (or 1):

=> setenv force_prog_ecc y
=> fuse prog 0 18 0x00000042
Programming bank 0 word 0x00000012 to 0x00000042...
Warning: Programming fuses is an irreversible operation!
        This may brick your system.
        Use this command only if you are sure of what you are doing!

Really perform this fuse programming? <y/N>
y
=>

Programing eFuses using UUU (Universal Update Utility)

The UUU (Universal Update Utility) is a SDP protocol flash programming tool, additional details can be found on the blog post How to use UUU to flash the iMX boards.

The uuu.auto file contains a set of commands used for properly flashing the targeted device, users can add U-Boot commands to program a set of eFuses depending on their specific use case. The command lines below added in i.MX8QXP uuu.auto file can be used as an example on how to program the MAC1_ADDR[47:00] fuses in i.MX8QXP:

 SDPS: boot -f imx-boot-imx8qxpmek-sd.bin-flash
+FB: ucmd fuse prog -y 0 708 0xa295fc11
+FB: ucmd fuse prog -y 0 709 0x000017b4
 FB: ucmd setenv fastboot_dev mmc
 FB: ucmd setenv mmcdev ${emmc_dev}
 FB: ucmd mmc dev ${emmc_dev}

or for ECC-protected fuses:

 SDPS: boot -f imx-boot-imx8qxpmek-sd.bin-flash
+FB: ucmd setenv force_ecc_prog y
+FB: ucmd fuse prog -y 0 18 0x00000042
 FB: ucmd setenv fastboot_dev mmc
 FB: ucmd setenv mmcdev ${emmc_dev}
 FB: ucmd mmc dev ${emmc_dev}

Understanding the current BSP implementation

The sc_misc_otp_fuse_write() API function can be only called by the owner of the SC_R_SYSTEM resource, in the current BSP implementation this resource is owned by the ARM Trusted Firmware running on EL3 Secure exception level.

The U-Boot is running in EL2 Non-Secure exception level and cannot call the sc_misc_otp_fuse_write() API function directly, the U-Boot is using the SIP services and calling the SCFW API in ATF.

The diagram below provides a high-level overview of the process for writing a fuse:


For additional details regarding Secure Monitor Call (SMC) and Exception Levels (EL), please refer to the ARMv8 Architecture Reference Manual.