Generic OTP/eFuse Programming

From SEGGER Knowledge Base
Jump to navigation Jump to search

This article describes how OTP (one-time programmable) memory or eFuses can be programmed with J-Flash when the device does not natively support OTP as a flash bank. The approaches described here do not require a custom flash loader.

OTP memory and eFuses are typically used for boot configuration, security settings (e.g. secure boot key hashes, debug access restrictions) or device-specific data such as MAC addresses. They are usually programmed via an OTP controller, using a sequence of register accesses which is described in the reference manual of the device. This sequence can be executed automatically as part of flash programming in two ways:

Further options are described in the section Alternatives.

Native OTP support

For some devices, J-Link supports the OTP area natively as an additional flash bank. Before implementing one of the approaches described in this article, check whether this is the case for the device in use: the KB article of the device family lists the flash banks supported by J-Link in the section Flash Banks (see e.g. ST STM32N6).

If the OTP area is supported as a flash bank, it is programmed like any other flash memory: the OTP data is placed at the OTP address in the data file. No additional steps are required.

For some devices, OTP programming and other provisioning features (e.g. option bytes, product lifecycle) are supported by the Device Provisioner (DevPro). The supported devices and features are listed in the section Supported Devices and Features of the Device Provisioner article.

Before you start

Programming OTP memory cannot be undone. A wrong value, a wrong address or a wrong programming sequence can permanently damage the device or make it inaccessible for the debugger.

  • The OTP programming sequence is device-specific. It is described in the reference manual of the device. If the description is incomplete, please contact the device vendor.
  • Make sure that the programming conditions required by the device are met, e.g. supply voltage and clock settings. OTP controllers often require timing values which depend on the current clock frequency.
  • Test the sequence on sample devices first, preferably with an OTP word which does not affect the boot or debug behavior (e.g. a general purpose / user OTP word).
  • OTP bits which restrict debug access or change the boot behavior should be programmed as the very last step. After the next reset, the debugger may no longer be able to connect to the device.
  • Read the current OTP value before programming and skip the programming if all bits are already set. This makes it safe to run the sequence again, e.g. when a programming cycle is repeated.

J-Flash Init/Exit steps

J-Flash projects can contain a list of Init steps and Exit steps. They are configured in the J-Flash project settings, tab MCU.

  • Init steps are executed at the beginning of production programming (Target -> Production Programming), before the flash memory is erased and programmed.
  • Exit steps are executed after the flash memory has been programmed (and verified) successfully.

For OTP programming, Exit steps are usually the better choice: the OTP is only programmed if flash programming was successful.

The following actions are typically used to implement an OTP programming sequence:

Action Usage
Write 32bit Write OTP controller registers (e.g. unlock code, address, data).
Read 32bit Read a register into the internal variable.
Var AND / Var OR / Var XOR Mask, modify and compare the internal variable.
Var Write 32bit Write the internal variable to an address (read-modify-write).
Var BEQ / Var BNE Jump to the given step index if the internal variable is equal / not equal to 0.
Delay Wait for the end of the programming operation.
Report Error Report an error with an error code.
Comment Structure the step list.

Common patterns:

Read-modify-write (e.g. enable a clock or a programming supply):

Read 32bit       [Address]
Var OR           [Bits to set]
Var Write 32bit  [Address]

Skip if already programmed (the internal variable is 0 if all requested bits are already set):

Read 32bit       [Address of the current OTP value]
Var AND          [Value]
Var XOR          [Value]
Var BEQ          [Index of the first step after the programming sequence]

Error check:

Read 32bit       [Status register]
Var AND          [Busy and error bits]
Var BEQ          [Index of the step after Report Error]
Report Error     [Error code]


Note:
Init/Exit steps do not support calculations or loops with timeout. Register values such as clock-dependent timing parameters must be calculated in advance for a known clock frequency, and waiting is done with a fixed delay. Jump targets are step indices and must be updated when steps are inserted or removed.

J-Link script functions

A J-Link script file can implement the functions HandleBeforeFlashProg() and HandleAfterFlashProg(). If present, the J-Link DLL calls them right before and right after flash programming (see also J-Link flash programming). The OTP programming sequence can be implemented in one of these functions:

int HandleBeforeFlashProg(void);
int HandleAfterFlashProg(void);

A return value >= 0 means O.K., a return value < 0 means error.

For OTP programming, HandleAfterFlashProg() is usually the better choice, for the same reason as Exit steps: the OTP is programmed after the flash memory.

Compared to Init/Exit steps, a J-Link script file:

  • is a text file which is easy to read, edit and compare,
  • supports loops, e.g. polling a busy flag with a timeout (JLINK_GetTime()),
  • supports calculations, e.g. timing values derived from the current clock configuration,
  • can output messages to the log (JLINK_SYS_Report()).

To use the script file with J-Flash, select it in the J-Flash project settings, tab MCU, option Use J-Link script file.


Note:
The functions are called for every flash programming performed by the J-Link DLL with this script file, e.g. also for a download from a debugger or from J-Link Commander. Use the script file in the production setup only, or make sure that the function skips the programming if the OTP is already programmed.

The following template shows the basic structure. The device-specific programming sequence has to be added according to the reference manual of the device.

Template code
/*********************************************************************
*
*       Constants (similar to defines)
*
**********************************************************************
*/
__constant U32 _OTP_STATUS      = 0x00000000;  // Device-specific: OTP controller status register
__constant U32 _OTP_STATUS_BUSY = 0x00000001;  // Device-specific: busy flag
__constant U32 _OTP_STATUS_ERR  = 0x00000002;  // Device-specific: error flag
__constant U32 _OTP_WORD        = 0x00000000;  // Device-specific: address to read the current OTP value
__constant U32 _OTP_VAL         = 0x00000000;  // Bits to program

/*********************************************************************
*
*       _WaitIdle()
*
*  Function description
*    Waits until the OTP controller is idle.
*
*  Return value
*    >= 0  O.K.
*    <  0  Timeout
*/
int _WaitIdle(int TimeoutMs) {
  int r;
  int t0;
  int t;
  U32 v;

  r  = -1;
  t  = 0;
  t0 = JLINK_GetTime();
  do {
    v = JLINK_MEM_ReadU32(_OTP_STATUS);
    if ((v & _OTP_STATUS_BUSY) == 0) {
      r = 0;
    }
    t = JLINK_GetTime() - t0;
  } while ((r < 0) && (t < TimeoutMs));
  return r;
}

/*********************************************************************
*
*       HandleAfterFlashProg()
*
*  Function description
*    Called by the J-Link DLL right after flash programming.
*    Programs the OTP word if not all bits are programmed yet.
*
*  Return value
*    >= 0  O.K.
*    <  0  Error
*/
int HandleAfterFlashProg(void) {
  int r;
  U32 v;

  r = 0;
  v = JLINK_MEM_ReadU32(_OTP_WORD);
  if ((v & _OTP_VAL) != _OTP_VAL) {           // Not all bits programmed yet?
    r = _WaitIdle(20);
    if (r >= 0) {
      //
      // Implement programming sequence here.
      // Device-specific: clocks, timing, unlock, address, data.
      // See reference manual of the device.
      //
      r = _WaitIdle(100);                     // Wait for end of programming
    }
    if (r >= 0) {
      v = JLINK_MEM_ReadU32(_OTP_STATUS);
      if ((v & _OTP_STATUS_ERR) != 0) {
        r = -1;
      }
    }
    if (r < 0) {
      JLINK_SYS_Report("OTP: Programming failed.");
    }
  }
  return r;
}

Alternatives

Custom flash loader

With the SEGGER Flash Loader (SFL), which is part of the J-Link Device Support Kit, the OTP area can be added as an additional flash bank. The OTP data is then part of the data file and is programmed like any other flash memory, also with other J-Link based tools (e.g. a debugger or J-Link Commander). This requires more effort than the approaches described above, and the flash loader has to take the OTP specifics into account (e.g. OTP cannot be erased). In return, it is the most integrated solution, e.g. when the OTP values differ from device to device.

Device Provisioner custom operation

The Device Provisioner (DevPro) can execute custom operations which are implemented as functions in a J-Link script file. The OTP programming sequence can be implemented in such a function (e.g. based on the template above) and executed from the command line, e.g. as a separate step of a production script. Parameters such as the OTP value can be passed with the command line option -SetConfigVal and read in the script file with JLINK_GetConfigValue(). This allows programming device-individual values, e.g. MAC addresses. For examples, see the section Demo of the Device Provisioner article.

DevPro.exe -if SWD -speed 4000 -operation ProgramOTP -SetConfigVal Value=[Value] -ScriptFile ProgramOTP.JLinkScript

J-Link Commander command file

For a quick test in the lab or for programming single devices, the OTP programming sequence can also be executed with a J-Link Command File, using the commands w4 (write 32-bit), mem32 (read 32-bit) and sleep. Command files are executed line by line without conditions, so the programming cannot be skipped automatically and errors are not detected automatically. The output of the mem32 commands has to be checked manually. For production, use one of the approaches described above.

Example command file
JLink.exe -device [Device] -if SWD -speed 4000 -autoconnect 1 -CommandFile C:\OTP.jlink

Contents of OTP.jlink:
r                                     // Reset and halt
h
mem32 [Address of the current OTP value] 1   // Value before programming
//
// Implement programming sequence here.
// Device-specific: clocks, timing, unlock, address, data.
// See reference manual of the device.
//
w4 [Data register] [Value]            // Example: write data, starts programming
sleep 10                              // Wait for end of programming
mem32 [Status register] 1             // Check busy and error flags
mem32 [Address of the current OTP value] 1   // Value after programming
q

Example: NXP i.MX RT1024

The examples program one eFuse word of the NXP i.MX RT1024 via the OCOTP controller, as described in the i.MX RT1024 reference manual (chapter On-Chip OTP Controller (OCOTP_CTRL)):

  1. Enable the OCOTP clock and the eFuse programming supply (CCM_CGPR, bit EFUSE_PROG_SUPPLY_GATE).
  2. Set the OCOTP timing registers (OCOTP_TIMING, OCOTP_TIMING2) for the current ipg_clk frequency.
  3. Wait until the OCOTP controller is idle and clear the error flag.
  4. Skip the programming if all requested bits are already set.
  5. Write the unlock code and the word address to OCOTP_CTRL, then write the value to OCOTP_DATA. This starts the programming.
  6. Wait until the OCOTP controller is no longer busy and check the error flag.
  7. Reload the shadow registers and verify the programmed value.
  8. Disable the eFuse programming supply again.

Example files:

  • MIMXRT1024_eFuse.jflash: J-Flash project with the sequence implemented as Exit steps. The Exit steps start with a reset, so the clock setting of the boot ROM (ipg_clk = 132 MHz) is active. The timing values are calculated in advance for this frequency. The project checks the clock configuration before programming and reports an error if it does not match.
  • NXP_iMXRT1024_eFuse.JLinkScript: J-Link script file implementing HandleAfterFlashProg(). The script calculates ipg_clk from the current clock configuration, calculates the timing values and polls the OCOTP controller with timeouts.

In both examples, the eFuse word (GP1) and the value are placeholders. With the value 0x00000000, nothing is programmed.


Note:
The formula for the programming time (tPGM) in the i.MX RT1024 reference manual differs from the one used by the NXP MCUXpresso SDK. The example files use timing values which meet the requirement of both formulas. See the comments in the example files for details.

See also