samd21 fuse settings causing programming failure

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I have been experimenting with the samd21 I have noticed that on occasion openocd combined with the at91samd commands would cause the samd21 to go into a state where I could no longer program with my atmel-ice.  Further experimentation with a samd21 xplained board where I was writing a short program to modify the fuses resulted in a similar problem where I was no longer able to program the board.  Looking closely at the fuse settings and you have the following for a couple of the reserved fuse settings (24:17 and 41):

 

Voltage Regulator Internal BOD (BOD12) configuration. These bits are written in production and must not be changed.

 

Through my experimentation I have found the fuses to be set to 0xFFFFFFFF and I have had to set them back to default configuration (such as 0xD8E0C7FF and 0xFFFFFC5D).  Now I am wondering if blinding setting them without masking those reserved bits is causing my issues?

 

Anyone have some insight on these reserved bits, the damage that can be done by changing them, and methods for being able to program boards again?  I have tried programming with atmel studio after they become unresponsive to programming via openocd and I still can not program.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to update, it appears that if something happens while programming the samd21 fuses (power issue or similar) and the correct values don't get set, it is possible for the fuse values to end up as 0xFFFFFFFF which causes all kinds of headaches.  I still don't fully understand why this happens but I have updated code that attempts to protect lock and reserved bits from being changed:

#define USE_DEFAULT 0

#if USE_DEFAULT

#define USER_WORD_0_DEFAULT (0xD8E0C7FFLU)
#define USER_WORD_1_DEFAULT (0xFFFFFC5DLU)

#define USER_WORD_0 USER_WORD_0_DEFAULT
#define USER_WORD_1 USER_WORD_1_DEFAULT

#else

/* Revision A(2017) Table 10-4 P. 45 */
#define BOOTPROT			(0x07LU << 0)
#define RESERVEDA			(0x01LU << 3)
#define EEPROM				(0x07LU << 4)
#define RESERVEDB			(0x01LU << 7)
#define BOD33_LEVEL			(0x07LU << 8)
#define BOD33_ENABLE		(0x01LU << 14)
#define BOD33_ACTION		(0x01LU << 15)
#define RESERVEDC			(0x70LU << 17)
#define WDT_ENABLE			(0x00LU << 25)
#define WDT_ALWAYS_ON		(0x00LU << 26)
#define WDT_PERIOD			(0x0BLU << 27)
#define WDT_WINDOW_LBIT		(0x01LU << 31)
#define WDT_WINDOW_HBITS	(0x02LU << 0)
#define WDT_EWOFFSET		(0x0BLU << 3)
#define WDT_WEN				(0x00LU << 7)
#define BOD33_HYS			(0x00LU << 8)
#define RESERVEDD			(0x00LU << 9)
#define RESERVEDE			(0xFCLU << 10)
#define LOCK				(0xFFFFLU << 16)

#define USER_WORD_0			(WDT_WINDOW_LBIT | WDT_PERIOD | WDT_ALWAYS_ON | WDT_ENABLE | RESERVEDC | BOD33_ACTION | BOD33_ENABLE | BOD33_LEVEL | RESERVEDB | EEPROM | RESERVEDA | BOOTPROT)

#define USER_WORD_1			(LOCK | RESERVEDE | RESERVEDD | BOD33_HYS | WDT_WEN | WDT_EWOFFSET | WDT_WINDOW_HBITS)

#endif

/* MASK Lock and Reserve Bits */
#define USER_MASK_0 0x01FE0088
#define USER_MASK_1 0xFFFFFE00

#define NVM_COMMAND_ERASE_AUX_ROW NVMCTRL_CTRLA_CMD_EAR
#define NVM_COMMAND_WRITE_AUX_ROW NVMCTRL_CTRLA_CMD_WAP
#define NVM_COMMAND_PAGE_BUFFER_CLEAR NVMCTRL_CTRLA_CMD_PBC

volatile uint32_t cnt;
volatile uint8_t test;

static void program_fuse_bits(void) {
	uint32_t temp = 0;

	/* Auxiliary space cannot be accessed if the security bit is set */
	if (NVMCTRL->STATUS.reg & NVMCTRL_STATUS_SB) {
		printf("Security Bit set\n");
		return;
	}

	uint32_t userWord0 = *((uint32_t *)NVMCTRL_AUX0_ADDRESS);
	uint32_t userWord1 = *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1);

	printf("Initial fuse settings: USER_WORD_0:%08lX and USER_WORD_1:%08lX\n", userWord0, userWord1);

	/* if the fuses are already at the default value then return */
	if ((userWord0 == USER_WORD_0) && (userWord1== USER_WORD_1)) {
		printf("No Change necessary\n");
		return;
	}

	/* Disable Cache */
	temp = NVMCTRL->CTRLB.reg;

	NVMCTRL->CTRLB.reg = temp | NVMCTRL_CTRLB_CACHEDIS;

	/* Clear error flags */
	NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;

	/* Set address, command will be issued elsewhere */
	NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;

	/* Erase the user page */
	NVMCTRL->CTRLA.reg = NVM_COMMAND_ERASE_AUX_ROW | NVMCTRL_CTRLA_CMDEX_KEY;

	/* Wait for NVM command to complete */
	while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY));

	/* Clear error flags */
	NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;

	/* Set address, command will be issued elsewhere */
	NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;

	/* Erase the page buffer before buffering new data */
	NVMCTRL->CTRLA.reg = NVM_COMMAND_PAGE_BUFFER_CLEAR | NVMCTRL_CTRLA_CMDEX_KEY;

	/* Wait for NVM command to complete */
	while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY));

	/* Clear error flags */
	NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK;

	/* Set address, command will be issued elsewhere */
	NVMCTRL->ADDR.reg = NVMCTRL_AUX0_ADDRESS / 2;

	userWord0 &= USER_MASK_0;
	userWord1 &= USER_MASK_1;

	userWord0 |= USER_WORD_0;
	userWord1 |= USER_WORD_1;

	printf("Fuse settings will be: USER_WORD_0:%08lX and USER_WORD_1:%08lX\n", userWord0, userWord1);

	/* write the fuses values to the memory buffer */
	*((uint32_t *)NVMCTRL_AUX0_ADDRESS) = userWord0;
	*(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1) = userWord1;

	/* Write the user page */
	NVMCTRL->CTRLA.reg = NVM_COMMAND_WRITE_AUX_ROW | NVMCTRL_CTRLA_CMDEX_KEY;

	/* Restore the settings */
	NVMCTRL->CTRLB.reg = temp;

	printf("New fuse settings are: USER_WORD_0:%08lX and USER_WORD_1:%08lX\n",
		   *((uint32_t *)NVMCTRL_AUX0_ADDRESS), *((uint32_t *)NVMCTRL_AUX0_ADDRESS + 1));
}

int main(void) {
	printf("Programming Fuse bits\n");
	program_fuse_bits();
	printf("Done and ready to exit\n");
	while (1) { }
}

 

Last Edited: Wed. May 17, 2017 - 08:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello, can you fix this problem? I have the same and I dont know how to resolve it.

 

Thank you!

nios

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sometimes I can use Atmel Studio and reset fuses to DEFAULT_VALUES and recover the board.  Other times I can use openocd and add the at91samd chip-erase:

 

#
# Atmel SAMD21 Xplained Pro evaluation kit.
#

source [find interface/cmsis-dap.cfg]

# chip name
set CHIPNAME at91samd21g18

source [find target/at91samdXX.cfg]

init
targets
at91samd chip-erase
reset
shutdown

Then try and program the fuses using a program similar to one I posted previously.  However, I have had a couple boards that I have not been able to recover.  Discussions with Atmel support didn't give me any good insight.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Wannabe, Thank you for replay... I can reset fuses to DEFAULT_VALUES from Atmel Studio and after desconnect the jlink and connect again the program works... I reset the fuses to default values using the datasheet from samd21e.

 

I have a samd21e in my own board and  the problem was when i wanted set the eeprom memory space. Now im going to set up NVMCTRL_EEPROM_SIZE from fuses in Tool -> devices programming and hope that this works... 

 

Thank you ...

 

nios

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've had similar experience with unexpected behavior relating to these fuse bits in the NVM user row.

 

My situation is this: I want to set the user row at the time of programming, so define a const struct at fixed location overlayed onto the user row.  I've done the linker script and attribute leg work and examining the user row address at run time and I see what I expect, most of the fuse bits seem to behave as advertised but there are exceptions. 

 

The WDT enable fuse seems to have no effect, all other WDT related fuses work fine, just not enable. 

 

The bootprot fuses also seemed to cause me issues.  I set these to 5 to protect my 1k bootloader and found the debugger would fail verify even when set to erase the entire chip.  After a little more prodding the chip was dead, chiperase command exits successfully but aside from that the atmel-ice says it cant connect to the debug interface.  Hitting chiperase at the first sign of trouble has recovered another board but I haven't played much with bootprot since.

 

I had the same thought as photonthunder - do the reserve bits do anything?  Do they need to be a specific value?

 

Nick

Last Edited: Wed. Aug 16, 2017 - 06:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would like to add that I still see this issue on occasion where the samd21 can no longer be programmed.  Atmel (now Microchip) has been great at responding to my requests for assistance and sent me a file to fix it.  In essence it just sends a chip erase many times as you reset the board and then you can program again.  I have been able to recover almost all boards using this method.  I am still curious the cause and think it has something to do with clocks and standby sleep.