7. DA14531 OTP Memory

The DA14531 contains 32kB of One-Time Programmable (OTP) memory which is used to store various configuration settings and can also be used for the storage of the user’s application. In addition, the OTP memory can also be used for the storage of application data.

The following section details how to use the driver provided within SDK6 to read/write data to the DA14531 OTP memory at runtime.

Note

You must have created a modified version of the empty_peripheral_template example project, as described in the Initial Project chapter, before proceeding!

7.1. OTP Memory Layout

The OTP memory is organized as 8K x 32-bit words. Each word is protected by a 6-bit Error Correction Code (ECC) that gets set when data is written to the word. This means that each word can only be written to once, for example you can’t write data to the lower 8-bits of the word and then in another operation attempt to write data to the rest of the word.

Before using the OTP memory for storage of application data it is important to remember that certain areas are reserved and therefore application data should not be written to these locations.

Table 2 OTP Memory Layout

OTP Address

Size

Description

0x07F8 0000

Variable

If the application code is stored in OTP, then it is located here.

0x07F8 7ED0

60 Words

Configuration script.

0x07F8 7FC0

16 Words

Application image header.

See the DA14531 Datasheet for a complete description of the OTP memory.

7.2. Using the OTP Memory Driver

The following section provides step-by-step instructions showing how to update the modified empty_peripheral_template example project you created in the Initial Project chapter to read and write data to the OTP memory using the driver provided with SDK6.

7.2.1. Adding the OTP Memory Driver

The OTP memory driver is implemented in the files hw_otpc_531.c and this is already present in the empty_peripheral_template example project. However, in order to use the OTP driver functions we need to add the appropriate include file into the user_empty_peripheral_template.c file:

#include "hw_otpc.h"

7.2.2. Reading from OTP Memory

Now add the function shown below into the user_empty_peripheral_template.c file. This will be used to read a number of 32-bit words from OTP memory. Note that the offset parameter refers to the word offset within the OTP i.e. an offset of zero refers to the first 32-bit word in OTP which is located at physical address 0x07F80000.

/**
 ****************************************************************************************
 * @brief Read 32-bit word(s) from the OTP.
 * @param[in]  offset    Word offset within OTP memory from which data is to be read.
 *                       An offset of zero means the first address in OTP (0x07F80000).
 * @param[out] data      Pointer to buffer into which word(s) will be read.
 * @param[in]  nbr_words Number of 32-bit words to read.
 * @return On failure -1, otherwise the number of words read.
 ****************************************************************************************
 */
int otp_read(uint32_t offset, uint32_t * data, uint32_t nbr_words)
{
    int ret = -1;

    /* Check data to be read is within OTP region */
    if ((MEMORY_OTP_BASE + ((offset + nbr_words) * sizeof(uint32_t))) <= MEMORY_OTP_END) {
        uint32_t addr = MEMORY_OTP_BASE + (offset * sizeof(uint32_t));
        ret = nbr_words;

        hw_otpc_init();
        hw_otpc_enter_mode(HW_OTPC_MODE_READ);

        while (nbr_words--) {
            *data++ = GetWord32(addr);
            addr += sizeof(uint32_t);
        }
        hw_otpc_close();
    }
    return ret;
}

7.2.3. Writing to OTP Memory

Next add the function shown below into the user_empty_peripheral_template.c file. This will be used to write a number of 32-bit words to OTP memory.

/**
 ****************************************************************************************
 * @brief Write 32-bit words to OTP.
 * @param[in] offset    Word offset within OTP memory to which data is to be written,
 *                      An offset of zero means the first address in OTP (0x07F80000).
 * @param[in] data      Pointer to data to be written.
 * @param[in] nbr_words Number of 32-bit words to write.
 * @return    On failure -1, otherwise the number of words written.
 ****************************************************************************************
 */
int otp_write(uint32_t offset, uint32_t * const data, uint32_t nbr_words)
{
    int ret = -1;

    /* Check data to be written is within OTP region */
    if ((MEMORY_OTP_BASE + ((offset + nbr_words) * sizeof(uint32_t))) <= MEMORY_OTP_END) {
        hw_otpc_init();
        hw_otpc_prog(data, offset, nbr_words);
        hw_otpc_close();
        ret = nbr_words;
    }
    return ret;
}

7.2.4. Putting it all Together

Once the above additions have been made, we can test the read and write functions. To test the write function, we will write 4 words of data to unused area of OTP memory just before the configuration script (0x07F87C00 to 0x07F87C10). To do this add the following code to the user_on_set_dev_config_complete function contained within user_empty_peripheral_template.c:

/* We want to write starting at physical address 0x07F87C00 which
   is an offset of ((0x07F87C00 - 0x07F80000) / 4) = 0x1F00) */
int status;
uint32_t offset = 0x1F00;
uint32_t buffer[4] = { 0x01234567, 0xBAB5CAFE, 0xDEADBEEF, 0x76543210 };

status = otp_write(offset, buffer, 4);
arch_printf("\n\rotp_write - %d", status);

When you build the project and run on your target you should see the following message being output via the serial debug port:

_images/otp_write.png

Finally, we can use the read function to read back the data we just wrote to OTP. To do this add the following code to the user_on_set_dev_config_complete function just after the OTP write code you added above:

status = otp_read(offset, buffer, 4);
arch_printf("\n\rotp_read - %d", status);

if (status >= 0) {
    for(int i = 0; i < 4; i++) {
        arch_printf("\n\r0x%08X 0x%08X", offset + i, buffer[i]);
    }
}

When you build the project and run on your target you should see the following message being output via the serial debug port:

_images/otp_read.png