How to write OTP within XMOS code?

Technical questions regarding the XTC tools and programming with XMOS.
tim
Junior Member
Posts: 6
Joined: Sat Mar 06, 2021 1:22 am

How to write OTP within XMOS code?

Post by tim »

Basically I am designing a product based on the xCORE-200 eXplorerKIT (XK-EVK-XE216), and I need to be able to write to the OTP from the program itself.

Here are my successes so far:
  • I can write to OTP from my computer using the xburn utility (but I need to be able to write from the code itself)
  • I can read from OTP using the lib_otpinfo library, version 2.1.0
  • Using logic from lib_otpinfo, I can read OTP arbitrarily from the otp_ports_t
Unfortunately, lib_otpinfo seems to be read-only. There is a lib_otp included in XTC v15, but I can't use it since my project must use XTC v14 for unrelated reasons. It's annoying because lib_otp has functions called "otp_program" and "otp_program_no_repair" that should be able to do what I need.

Is there any way to access the source files for otp.xc etc.? I couldn't find lib_otp on github. I checked XTC v15 but it only includes the compiled versions of these files:
  • libsrc/otp.xc
  • libsrc/otp_access.xc
  • libsrc/otp_model.xc
  • libsrc/otp_soft_redundancy.xc
  • libsrc/otp_stats.xc
  • libsrc/otp_validate_architecture.xc
Alternatively, if anyone knows how to write to the OTP, please let me know. I couldn't find anything online and the lib_otpinfo doesn't provide any OTP_CTRL enum values for writing the OTP
User avatar
Daz
Junior Member
Posts: 5
Joined: Wed Jul 24, 2024 12:05 pm

Post by Daz »

It looks like the headers are missing in the Tools 14 release, otherwise it should be usable. Including these in your project (along with -lotp) should work. These are the Tools 14 versions of the relevant headers.

otp.h:

Code: Select all

/**
 *  \file otp.h
 *
 * This API provides functions to program and read the OTP (One Time
 * Programmable) memory of an xCORE. Functions are provided to program and read
 * the OTP memory using both single-ended and differential modes. Functions are
 * also provided that allow automatic repair of failed programming locations
 * using the OTP redundancy registers.  Access is also provided to program and
 * read the OTP special registers.
 */

#ifndef _OTP_H_
#define _OTP_H_

#include <xccompat.h>

#ifdef __otp_conf_h_exists__
#include "otp_conf.h"
#endif

////////////////////////////////////////////////////////////////////////////////

/**
 * OTP Statistics gathering is disabled by default.  #define OTP_STATS 1 in
 * user defined otp_conf.h to enable
 */
#ifndef OTP_STATS
#define OTP_STATS 0
#endif //OTP_STATS

/**
 * Maximum size of OTP memory
 */
#define OTP_SIZE 0x800

/**
 * Standard initializer for an otp_ports_t structure. Use as follows:
 * on stdcore[0]: OTPPorts otp_ports = OTP_PORTS_INITIALIZER;
 */
#define OTP_PORTS_INITIALIZER \
{ \
  XS1_PORT_32B, \
  XS1_PORT_16C, \
  XS1_PORT_16D \
}

////////////////////////////////////////////////////////////////////////////////

/**
 * Structure contains ports used to access OTP memory.
 */
#if (__XC__)
typedef struct OTPPorts
{
  port data;
  out port addr;
  out port ctrl;
} OTPPorts;
#endif //__XC__

/**
 * If OTP_STATS is set then the OTP library will record statistics
 * during OTP memory programming. These statistics can then be used to
 * determine the success/failure status from the programming event. Stats can
 * the be retrieved using function OTPStats_GetStats.
 */
#if (OTP_STATS)
typedef struct OTPStats
{
  unsigned programmed_words;        ///< Number of words that were successfully programmed
  unsigned programmed_bits;         ///< Number of bits that were successfully programmed using the short programming pulse
  unsigned soaked_bits;             ///< Number of bits that were successfully programmed using the long programming pulse
  unsigned leaky_bits;              ///< Number of bits that are already in the programmed state before programming commenced but should not be programmed
  unsigned failed_to_program_words; ///< Number of words that failed to program
  unsigned redundant_sectors_used;  ///< Number of redundant sectors used (L1 family only)
} OTPStats;
#endif //OTP_STATS


////////////////////////////////////////////////////////////////////////////////

#if (__XC__)

/**
 * This function programs a block of OTP memory of defined size at address
 * using default single_ended mode. This function will attempt to repair any
 * failures using the redundancy registers on the L1 family of processors.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] data : const unsigned[] - an array of data to be written.
 * \param[in] size : unsigned - the amount of data to be written.
 * \returns 1 if successful; 0 if failed.
 */
int otp_program(OTPPorts &ports,
                unsigned address,
                const unsigned data[],
                unsigned size);

/**
 * This function programs a block of OTP memory of defined size at address
 * using enhanced differential mode. This function will attempt to repair any
 * failures using the redundancy registers on the L1 family of processors.
 * Differential mode programming is not available on the G4 family of
 * processors and will simply return 0 (failed).
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] data : const unsigned[] - an array of data to be written.
 * \param[in] size : unsigned - the amount of data to be written.
 * \returns 1 if successful; 0 if failed.
 */
int otp_program_differential(OTPPorts &ports,
                              unsigned address,
                              const unsigned data[],
                              unsigned size);

/**
 * This function programs a block of OTP memory of defined size at address
 * using default single_ended mode. No attempt to repair unprogrammed/faulty
 * bits will take place using this function however a failure map is returned
 * providing an indication of otp memory that was not successfully programmed.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] data : const unsigned[] - an array of data to be written.
 * \param[in] size : unsigned - the amount of data to be written.
 * \param[out] failmap : unsigned [] - location of bits that failed to program
 * successfully.
 * \returns 1 if successful; 0 if failed.
 */
int otp_program_no_repair(OTPPorts &ports,
                          unsigned address,
                          const unsigned data[],
                          unsigned size,
                          char failmap[]);

/**
 * This function programs a block of OTP memory of defined size at address
 * using enhanced differential mode. No attempt to repair unprogrammed/faulty
 * bits will take place using this function however a failure map is returned
 * providing an indication of otp memory that was not successfully programmed.
 * Differential mode programming is not available on the G4 family of
 * processors and will simply return 0 (failed).
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] data : const unsigned[] - an array of data to be written.
 * \param[in] size : unsigned - the amount of data to be written.
 * \param[out] failmap : unsigned [] - location of bits that failed to program
 * successfully.
 * \returns 1 if successful; 0 if failed.
 */
int otp_program_differential_no_repair(OTPPorts &ports,
                                        unsigned address,
                                        const unsigned data[],
                                        unsigned size,
                                        char failmap[]);

/**
 * OTP_ProgramSpecialRegister programs a word to the OTP special register.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] t : timer - a timer handle.
 * \param[in] data : unsigned - the data to be written.
 * \returns 1 if successful; 0 if failed.
 */
int otp_program_special_register(OTPPorts &ports, unsigned data);

/**
 * otp_program_secure_config_register programs a word to the OTP secure config 
 * register.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] t : timer - a timer handle.
 * \param[in] data : unsigned - the data to be written.
 * \returns 1 if successful; 0 if failed.
 */
int otp_program_secure_config_register(OTPPorts &ports, unsigned data);

//Program a single word without repairing any errors
int otpprogram_writeword_norepair(OTPPorts &ports,
                                  unsigned address,
                                  unsigned data);

/**
 * OTP_Read reads a block of OTP data of defined size from address using
 * default single_ended mode.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to read from.
 * \param[out] data : unsigned[] - an array of words that the data being
 *  read can be stored in.
 * \param[in] size : unsigned - the amount of data to be read.
 */
void otp_read(OTPPorts &ports,
              unsigned address,
              unsigned data[],
              unsigned size);

/**
 * OTP_ReadDifferential reads a block of OTP data of defined size from address
 * using enhanced differential mode.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to read from.
 * \param[out] data : unsigned[] - an array of words that the data being read
 *  can be stored in.
 * \param[in] size : unsigned - the amount of data to be read.
 */
void otp_read_differential(OTPPorts &ports,
                            unsigned address,
                            unsigned data[],
                            unsigned size);

/**
 * OTP_ReadSpecialRegister reads a word from the OTP special register.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[out] data : unsigned - the word read from the special register.
 */
void otp_read_special_register(OTPPorts &ports, unsigned &data);

/**
 * otp_read_secure_config_register reads a word from the OTP secure config 
 * register.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[out] data : unsigned - the word read from the special register.
 */
void otp_read_secure_config_register(OTPPorts &ports, unsigned &data);

#endif //__XC__

////////////////////////////////////////////////////////////////////////////////

#if (OTP_STATS)

/**
 * This function retrieves the statistics from the last programming event.
 * Requires the #define OTP_STATS to be set to 1 in user defined otp_conf.h
 * to activate statistic gathering.
 *
 * \param[out] stats : OTPStats - structure that will contain the statitic
 * information
 */
void otpstats_get_stats(REFERENCE_PARAM(OTPStats, stats));

/**
 * This function allows two OTPStats structs to be merged together.
 * Requires the #define OTP_STATS to be set to 1 in user defined otp_conf.h
 * to activate statistic gathering.
 *
 * \param[in, out] a : OTPStats - structure contains the original stats and
 *  param b will be merged with them
 * \param[in] b : OTPStats - structure contains the stats that are to be merged
 * with param a
 */
void otpstats_merge_stats(REFERENCE_PARAM(OTPStats, a),
                          REFERENCE_PARAM(const OTPStats, b));

/**
 * This function outputs the statistics from the given OTPStats struct.
 * Requires the #define OTP_STATS to be set to 1 in user defined otp_conf.h
 * to activate statistic gathering.
 *
 * \param[in] stats : OTPStats - structure containing the stats to be printed.
 *
 */
void otpstats_print_stats(REFERENCE_PARAM(const OTPStats, stats));

#endif //OTP_STATS

////////////////////////////////////////////////////////////////////////////////

#endif //_OTP_H_

////////////////////////////////////////////////////////////////////////////////
otp_soft_redundancy.h:

Code: Select all

/**
 *  \file otp_soft_redundancy.h
 *
 * \brief Interface to the One Time Programming (OTP) library utilising
 * Software Redundancy Repair.
 * \brief Include this file in your application project to use OTP module with
 * Software Redundancy Repair.
 */

#ifndef _otp_soft_redundancy_h_
#define _otp_soft_redundancy_h_

#include "otp.h"

////////////////////////////////////////////////////////////////////////////////

/**
 * OTPSoftRedundancy_Program programs a block of OTP memory of defined size at
 * address using default single_ended mode. This function will attempt to
 * repair any failures using Hardware Redundancy on the L1 family of processors.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] payload : const unsigned[] - an array of data to be written.
 * \param[in] payloadSize : unsigned - the amount of data to be written.
 * \param[in] maxSize : unsigned - the maximum amount of memory to be used for
 *  software repair.
 * \param[out] size : unsigned - the actual amount of memory used for software
 *  repair.
 * \returns 1 if successful; 0 if failed.
 */
int otpsoftredundancy_program(OTPPorts &ports,
                              unsigned address,
                              const unsigned payload[],
                              unsigned payloadSize,
                              unsigned maxSize,
                              unsigned &size);

/**
 * OTPSoftRedundancy_Program programs a block of OTP memory of defined size at
 * address using enhanced differential mode. This function will attempt to
 * repair any failures using Hardware Redundancy on the L1 family of processors.
 * Differential mode programming is not available on the G4 family of
 * processors and will simply return 0 (failed).
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] payload : const unsigned[] - an array of data to be written.
 * \param[in] payloadSize : unsigned - the amount of data to be written.
 * \param[in] maxSize : unsigned - the maximum amount of memory to be used for
 * software repair.
 * \param[out] size : unsigned - the actual amount of memory used for software
 * repair.
 * \returns 1 if successful; 0 if failed.
 */
int otpsoftredundancy_program_differential(OTPPorts &ports,
                                            unsigned address,
                                            const unsigned payload[],
                                            unsigned payloadSize,
                                            unsigned maxSize,
                                            unsigned &size);

/**
 * OTPSoftRedundancy_Read programs a block of OTP memory of defined size at
 * address using default single_ended mode.
 *
 * This function will attempt to repair any failures using Hardware Redundancy
 * on the L1 family of processors.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] data : const unsigned[] - an array of data to be written.
 * \param[in] size : unsigned - the amount of data to be written.
 * \returns 1 if successful; 0 if failed.
 */
int otpsoftredundancy_read(OTPPorts &ports,
                            unsigned address,
                            unsigned data[],
                            unsigned size);

/**
 * OTPSoftRedundancy_ReadDifferential programs a block of OTP memory of defined
 * size at address using default single_ended mode.
 *
 * This function will attempt to repair any failures using Hardware Redundancy
 * on the L1 family of processors.
 *
 * \param[in] ports : OTPPorts - the OTPPorts structure.
 * \param[in] address : unsigned - the location in OTP memory to write to.
 * \param[in] data : const unsigned[] - an array of data to be written.
 * \param[in] size : unsigned - the amount of data to be written.
 * \returns 1 if successful; 0 if failed.
 */
int otpsoftredundancy_read_differential(OTPPorts &ports,
                                        unsigned address,
                                        unsigned data[],
                                        unsigned size);

////////////////////////////////////////////////////////////////////////////////

#endif // _otp_soft_redundancy_h_

////////////////////////////////////////////////////////////////////////////////
Could I ask - what are the "unrelated reasons" that prohibit moving to Tools 15? We are trying to improve the migration experience for xCORE-200 projects.
XMOS Tools
tim
Junior Member
Posts: 6
Joined: Sat Mar 06, 2021 1:22 am

Post by tim »

Hi Daz, thanks for providing this otp.h file! Indeed when including this file along with the -lotp compiler flag, I am able to read and write from the OTP on xCORE-200.

I've noticed that when I read from an empty OTP (nothing burned yet), all of the even words are 0x00000000 and all of the odd words are 0xffffffff . Do you know why this is? I'm used to seeing empty OTPs filled with all "1" bits (0xffffffff) which can only be changed to "0" bits.

Regarding migration to XTC v15, I don't know of any technical reasons that prevent migration. However, the project is partly government-owned and I have to stay compatible with previously written documents and procedures that are based on XTC v14.
User avatar
Daz
Junior Member
Posts: 5
Joined: Wed Jul 24, 2024 12:05 pm

Post by Daz »

tim wrote: Mon Mar 24, 2025 8:37 pm Hi Daz, thanks for providing this otp.h file! Indeed when including this file along with the -lotp compiler flag, I am able to read and write from the OTP on xCORE-200.
Great to hear!
tim wrote: Mon Mar 24, 2025 8:37 pm I've noticed that when I read from an empty OTP (nothing burned yet), all of the even words are 0x00000000 and all of the odd words are 0xffffffff . Do you know why this is? I'm used to seeing empty OTPs filled with all "1" bits (0xffffffff) which can only be changed to "0" bits.
That is an artifact of the differential read mode, where 2 physical bits store one logical bit. One bit stores the intended logical value, the other bit stores its complement. Address bit 0 is used to access each differential cell, so what you are seeing in the "first" cell is 0 (blank value), and the "second" cell ~0 (its complement).
tim wrote: Mon Mar 24, 2025 8:37 pm Regarding migration to XTC v15, I don't know of any technical reasons that prevent migration. However, the project is partly government-owned and I have to stay compatible with previously written documents and procedures that are based on XTC v14.
Cool - thanks for clarifying!
XMOS Tools
tim
Junior Member
Posts: 6
Joined: Sat Mar 06, 2021 1:22 am

Post by tim »

Has anyone experienced OTP access timing warnings when compiling the library?
Are these warnings implying that the timing is OK as long as the cores are running faster than 300 MHz?

xta: ../libsrc/otp_access.xc:14: warning: route(0)     Pass with 3 unknowns, Num Paths: 1, Slack: 40.0 ns, Required: 100.0 ns, Worst: 60.0 ns, Min Core Frequency: 300 MHz
xta: ../libsrc/otp_access.xc:19: warning: route(1)     Pass with 2 unknowns, Num Paths: 1, Slack: 40.0 ns, Required: 70.0 ns, Worst: 30.0 ns, Min Core Frequency: 214 MHz