Skip to content

Instantly share code, notes, and snippets.

@cmaglie
Last active December 6, 2020 09:18
Show Gist options
  • Save cmaglie/c411621b8d49494faeb7869ad59aa6b6 to your computer and use it in GitHub Desktop.
Save cmaglie/c411621b8d49494faeb7869ad59aa6b6 to your computer and use it in GitHub Desktop.
// This sketch should fix a "bricked" Arduino Zero board due to a bug in "Burn bootloader" command.
// For more information see: https://github.com/arduino/ArduinoCore-samd/issues/137
// **IMPORTANT**: After running the sketch, you must power cycle the board.
// This code is freely inspired from Atmel Application Note:
// http://atmel.force.com/support/articles/en_US/FAQ/SAMD20-SAMD21-Programming-the-fuses-from-application-code
void setup() {
Serial.begin(115200);
uint32_t old_fusebits[2];
read_fuse_bits(old_fusebits);
Serial.println("Current NVM User Row value is:");
Serial.println(old_fusebits[0], HEX);
Serial.println(old_fusebits[1], HEX);
if (old_fusebits[0] == 0xFFFFFFFA && old_fusebits[1] == 0xFFFFFFFF) {
Serial.println("-> Fixing...");
fix_fuses();
Serial.print("DONE");
}
}
void loop() {
}
static void read_fuse_bits(uint32_t *data)
{
/* Make sure the module is ready */
/* Wait for NVM command to complete */
while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY));
/* Read the fuse settings in the user row, 64 bit */
data[0] = *((uint32_t *)NVMCTRL_AUX0_ADDRESS);
data[1] = *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1);
}
void fix_fuses() {
#define NVM_COMMAND_ERASE_AUX_ROW 0x05
#define NVM_COMMAND_PAGE_BUFFER_CLEAR 0x44
#define NVM_COMMAND_WRITE_AUX_ROW 0x06
uint32_t new_fusebits[2];
new_fusebits[0] = 0xD8E0C7FF; // Default values
new_fusebits[1] = 0xFFFFFC5D; //
/* Auxiliary space cannot be accessed if the security bit is set */
if (NVMCTRL->STATUS.reg & NVMCTRL_STATUS_SB) {
return;
}
/* Disable Cache */
uint32_t 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;
*((uint32_t *)NVMCTRL_AUX0_ADDRESS) = new_fusebits[0];
*(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1) = new_fusebits[1];
/* Write the user page */
NVMCTRL->CTRLA.reg = NVM_COMMAND_WRITE_AUX_ROW | NVMCTRL_CTRLA_CMDEX_KEY;
/* Restore the settings */
NVMCTRL->CTRLB.reg = temp;
}
@electron1979
Copy link

Also, try a 1k resistor from 3V3 pin to RESET pin...

@limpkin
Copy link

limpkin commented Dec 6, 2020

Just to make it clear to people viewing this page: auxiliary space can indeed be accessed, fuses can't be reprogrammed when security bit is set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment