This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Flash protection nrf52840 SDK: 14.0.0

I am trying to implement a bootloader with rewrite protection on nrf52840 with SDK: 14.0.0. But it seems that there is no flash write protection registers in nrf52840. So is there a way to implement a flash rewrite protection?

Thanks.

  • Hi,

    The BPROT peripheral that is found in the nRF52832, has been replaced with the ACL peripheral on the nRF52840. ACL works similar to the BPROT(erase/write protection), but also adds support for protection against reading the flash.

    You can find more information about the ACL here.

  • Hi,

    Thanks for the link! I tried this code:

    #define BOOTLOADER_START    *(uint32_t *) (NRF_UICR_BOOTLOADER_START_ADDRESS)
    #define BOOTLOADER_LENGTH   0x0000D000
    #define ALC_WR_PROTECT      0x00000002
    NRF_ACL->ACL[0].ADDR = BOOTLOADER_START;
    NRF_ACL->ACL[0].PERM = ALC_WR_PROTECT;
    NRF_ACL->ACL[0].SIZE = BOOTLOADER_LENGTH;
    nrf_nvmc_page_erase(BOOTLOADER_START);
    

    And after that first bootloader page (BOOTLOADER_START) is erased and all blank with 0xFF. Did I miss something?

  • The UICR register does not behave as normal flash. After it have been written, you have to reset the chip for it to take effect. Erasing UICR is not done as with normal flash, UICR can be erased using the ERASEUICR register, or in a operation where the whole flash is erased(ERASEALL register).

    See this page for more information.

  • Sigurd,

    Thanks for your help, but I think you misunderstood me. I use UICR register only to get the start address of bootloader: #define BOOTLOADER_START *(uint32_t *) (NRF_UICR_BOOTLOADER_START_ADDRESS)

    My question was about ACL. I tried to setup a write protection on first bootloader flash page but had no success.

    NRF_ACL->ACL[0].ADDR = BOOTLOADER_START; // Write address to ACL address register (0xF0000) NRF_ACL->ACL[0].PERM = ALC_WR_PROTECT; // Write protection bits to ACL address register (0x02) NRF_ACL->ACL[0].SIZE = BOOTLOADER_LENGTH; // block length to protect.

    And after that I try to erase flash page at address 0xF0000 nrf_nvmc_page_erase(BOOTLOADER_START);

    And it is erased so it looks like ACL register values are ignored.

  • Are you testing this in an active debug session? I think the current implementation of ACL is that erase/write is allowed while debugging.

    Here is the code I used to test this. LED3 will light if the data is successfully deleted, and re-written. If tested in non-debug mode session, the LED3 and LED4 will not light, indicating that the device hardfaults because there is an attempt to erase/write to protected flash.

    #include <stdbool.h>
    #include <stdint.h>
    
    #include "boards.h"
    #include "nordic_common.h"
    #include "nrf.h"
    #include "nrf_nvmc.h"
    
    #define LED_PIN1 13 // Pin P0.13
    #define LED_PIN2 14 // Pin P0.14
    #define LED_PIN3 15 // Pin P0.15
    #define LED_PIN4 16 // Pin P0.16
    
    #define PAGE_SIZE 4096
    
    #define REGION1_START 0xF0000  //0xF0000 
    #define REGION1_SIZE PAGE_SIZE*10 //PAGE_SIZE*10
    
    
    
    
    
    void led_off(uint8_t led)
    {
        NRF_GPIO->OUTSET = (1UL << led);
    }
    
    void led_on(uint8_t led)
    {
        NRF_GPIO->OUTCLR = (1UL << led);
    }
    
    void configure_leds(void)
    {
        uint8_t led_pins[4] = {LED_PIN1, LED_PIN2, LED_PIN3, LED_PIN4};
        for (int i = 0; i < sizeof(led_pins); i++)
        {
            // setup led gpio in output mode
            led_off(led_pins[i]);
            NRF_GPIO->PIN_CNF[led_pins[i]] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
        }
    }
    
    int test_writeProtect(void)
    {
        uint32_t errcode = 0;
    
        // Start by erasing region 1
        nrf_nvmc_page_erase(REGION1_START);
    
        //Writing to region 1
        nrf_nvmc_write_word(REGION1_START, 0xABBA1234);
    
        // Read before ACL region is configured
        uint32_t orig_data = 0;
        orig_data          = *((uint32_t *)REGION1_START);
    
        // Disable write/erase for region 1 with ACL
        NRF_ACL->ACL[0].ADDR = REGION1_START;
        NRF_ACL->ACL[0].SIZE = REGION1_SIZE;
        NRF_ACL->ACL[0].PERM = ACL_ACL_PERM_WRITE_Disable << ACL_ACL_PERM_WRITE_Pos;
    
        
        //Try to erase region 1
        nrf_nvmc_page_erase(REGION1_START);
    
        // Write to region 1
        nrf_nvmc_write_word(REGION1_START, 0xABBADEAD);
    
        // Try to read from region using CPU
        uint32_t new_data = 0;
        new_data          = *((uint32_t *)REGION1_START);
    
        if (new_data == 0xABBADEAD) // data successfully deleted, and re-written
        { 
            errcode = 1;
            led_on(LED_PIN3);
        }
        return errcode;
    }
    
    int main(void)
    {
        configure_leds();
        led_on(LED_PIN1); // Start
    
        test_writeProtect();
        
        led_on(LED_PIN4);
    
        while (true)
        {
            // Do nothing.
        }
    }
    /** @} */
    
Related