SAME70 General-purpose NVM Operations

This is a short note on how to deal with SAME70 GPNVM bits. Technically, it is easy and straightforward. The problem is that in different parts of the datasheet there are controversial process descriptions.

I found it slightly confusing:

The “Set GPNVM Bit” sequence is the following:

  1. Execute the ‘Set GPNVM Bit’ command by writing EEFC_FCR.FCMD with the SGPB command and EEFC_FCR.FARG with the number of GPNVM bits to be set.
  2. When the GPNVM bit is set, the bit EEFC_FSR.FRDY rises. If an interrupt was enabled by setting the bit EEFC_FMR.FRDY, the interrupt line of the interrupt controller is activated.
  3. The result of the SGPB command can be checked by running a ‘Get GPNVM Bit’ (GGPB) command.

Respective EEFC_FCR register description does not help either:

SGPB, CGPB GPNVM commands FARG defines the GPNVM number to be programmed

GPNVM bits are the following:

same70-gpnvm-map

My assumption was that to set up/clear something in the GPNVM I need to supply a value that will be written with SGPB. For example:

# J-Link commands
# 0x400E0C04 - EEFC FCR register address for SAME70
w4 0x400E0C04 0x5A00020B
# 0x5A - KEY for executing Flash command
# 0002 - Supplied value - to set the value 0x02 which should be 0b0'0000'0010
# 0x0B - Set GPNVM bit command

The actual behavior is different though - instead of writing the 0x02 to the GPNVM, the command above sets up the second bit. So, to summarize - if you want to set bits 7 and 8 in the GPNVM, you have to execute two separate commands:

# J-Link commands
# 0x400E0C04 - EEFC FCR register address for SAME70
# clear bit 1
w4 0x400E0C04 0x5A00010C
# set bit 7
w4 0x400E0C04 0x5A00070B
# set bit 8
w4 0x400E0C04 0x5A00080B

The same approach applies if you want to set/clear GPNVM bits from a firmware. Below is the snippet in C:

#define EEFC_FCR_FCMD_SGPB 0x0BUL
#define EEFC_FCR_FCMD_CGPB 0x0CUL
#define EEFC_FCR_FKEY_PASSWD (0x5AUL << 24UL)
#define EEFC_FCR_FARG(x) (((x) & 0xFFFF) << 8UL)

// set bit 8
EFC_REGS->EEFC_FCR = 
   (EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_SGPB | EEFC_FCR_FARG(8));

while ((EFC_REGS->EEFC_FSR & EEFC_FSR_FRDY_Msk) == 0)
{
}

// clear bit 7 
EFC_REGS->EEFC_FCR = 
   (EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FCMD_CGPB | EEFC_FCR_FARG(7));

while ((EFC_REGS->EEFC_FSR & EEFC_FSR_FRDY_Msk) == 0)
{
}