Add Support for Flash Erase / Write operations with BLE Enabled

Nordic's Soft Device is not tolerant to applications making direct access to
the non-volatile memory controller (NVMC) hardware. This patch adds a code
path to replace native NVMC calls with equivalent Soft Device calls when Soft
Device is enabled.

This patch is necessary to resolve issues related to lock ups when using
either MicroBitStorage or MicroBitFileSystem when BLE is enabled.
This commit is contained in:
Joe Finney 2017-03-01 02:48:45 +00:00
parent 96549fb657
commit 73d51d8731
4 changed files with 113 additions and 125 deletions

View file

@ -9,19 +9,6 @@ class MicroBitFlash
{
private:
/**
* Write to flash memory, assuming that a write is valid
* (using need_erase).
*
* @param page_address address of memory to write to.
* Must be word aligned.
* @param buffer address to write from, must be word-aligned.
* @param len number of uint32_t words to write.
*/
void flash_burn(uint32_t* page_address, uint32_t* buffer, int len);
/**
* Check if an erase is required to write to a region in flash memory.
* This is determined if, for any byte:
@ -66,6 +53,18 @@ class MicroBitFlash
* @param page_address address of first word of page.
*/
void erase_page(uint32_t* page_address);
/**
* Write to flash memory, assuming that a write is valid
* (using need_erase).
*
* @param page_address address of memory to write to.
* Must be word aligned.
* @param buffer address to write from, must be word-aligned.
* @param len number of uint32_t words to write.
*/
void flash_burn(uint32_t* page_address, uint32_t* buffer, int len);
};
#endif

View file

@ -124,19 +124,6 @@ class MicroBitStorage
*/
MicroBitStorage();
/**
* Writes the given number of bytes to the address specified.
*
* @param buffer the data to write.
*
* @param address the location in memory to write to.
*
* @param length the number of bytes to write.
*
* @note currently not implemented.
*/
int writeBytes(uint8_t *buffer, uint32_t address, int length);
/**
* Method for erasing a page in flash.
*

View file

@ -1,5 +1,6 @@
#include "MicroBitConfig.h"
#include "MicroBitFlash.h"
#include "MicroBitDevice.h"
#include "ErrorNo.h"
#include "mbed.h" // NVIC
@ -7,6 +8,28 @@
#define WORD_ADDR(x) (((uint32_t)x) & 0xFFFFFFFC)
/*
* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
* If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
* The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
* as a compatability option, but does not support the options used...
*/
#if !defined(__arm)
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include "nrf_soc.h"
/*
* Return to our predefined compiler settings.
*/
#if !defined(__arm)
#pragma GCC diagnostic pop
#endif
/**
* Default Constructor
*/
@ -44,17 +67,41 @@ int MicroBitFlash::need_erase(uint8_t* source, uint8_t* flash_addr, int len)
*/
void MicroBitFlash::erase_page(uint32_t* pg_addr)
{
// Turn on flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
if (ble_running())
{
// Schedule SoftDevice to erase the page for us, and wait for it to complete.
// This happens ASYNCHRONOUSLY when SD is enabled (and synchronously if disabled!!)
if (sd_flash_page_erase(((uint32_t)pg_addr)/PAGE_SIZE) == NRF_SUCCESS)
{
uint32_t *p = pg_addr;
int i = 0;
// Erase page:
NRF_NVMC->ERASEPAGE = (uint32_t)pg_addr;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
while (i < PAGE_SIZE / 4)
{
if (*p != 0xffffffff)
{
p = pg_addr;
i=0;
}
// Turn off flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
p++; i++;
}
}
}
else
{
// Turn on flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
// Erase page:
NRF_NVMC->ERASEPAGE = (uint32_t)pg_addr;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
// Turn off flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
}
}
/**
@ -67,21 +114,45 @@ void MicroBitFlash::erase_page(uint32_t* pg_addr)
* @param len number of uint32_t words to write.
*/
void MicroBitFlash::flash_burn(uint32_t* addr, uint32_t* buffer, int size)
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
for(int i=0;i<size;i++)
{
if (ble_running())
{
*(addr+i) = *(buffer+i);
// Schedule SoftDevice to write this memory for us, and wait for it to complete.
// This happens ASYNCHRONOUSLY when SD is enabled (and synchronously if disabled!!)
sd_flash_write(addr, buffer, size);
uint32_t *p = addr;
uint32_t *p2 = buffer;
int i = 0;
while (i < size)
{
if (*p != *p2)
{
p = addr;
p2 = buffer;
i=0;
}
p++; p2++; i++;
}
}
else
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
for(int i=0;i<size;i++)
{
*(addr+i) = *(buffer+i);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
/**

View file

@ -30,8 +30,10 @@ DEALINGS IN THE SOFTWARE.
#include "MicroBitConfig.h"
#include "MicroBitStorage.h"
#include "MicroBitFlash.h"
#include "MicroBitCompat.h"
/**
* Default constructor.
*
@ -44,26 +46,6 @@ MicroBitStorage::MicroBitStorage()
size();
}
/**
* Writes the given number of bytes to the address specified.
*
* @param buffer the data to write.
*
* @param address the location in memory to write to.
*
* @param length the number of bytes to write.
*
* @note currently not implemented.
*/
int MicroBitStorage::writeBytes(uint8_t *buffer, uint32_t address, int length)
{
(void) buffer;
(void) address;
(void) length;
return MICROBIT_OK;
}
/**
* Method for erasing a page in flash.
*
@ -71,20 +53,8 @@ int MicroBitStorage::writeBytes(uint8_t *buffer, uint32_t address, int length)
*/
void MicroBitStorage::flashPageErase(uint32_t * page_address)
{
// Turn on flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
// Erase page:
NRF_NVMC->ERASEPAGE = (uint32_t)page_address;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
// Turn off flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
MicroBitFlash flash;
flash.erase_page(page_address);
}
/**
@ -98,20 +68,8 @@ void MicroBitStorage::flashPageErase(uint32_t * page_address)
*/
void MicroBitStorage::flashCopy(uint32_t* from, uint32_t* to, int sizeInWords)
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
for(int i = 0; i < sizeInWords; i++)
{
*(to + i) = *(from + i);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
MicroBitFlash flash;
flash.flash_burn(to, from, sizeInWords);
}
/**
@ -123,19 +81,7 @@ void MicroBitStorage::flashCopy(uint32_t* from, uint32_t* to, int sizeInWords)
*/
void MicroBitStorage::flashWordWrite(uint32_t * address, uint32_t value)
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
*address = value;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
flashCopy(&value, address, 1);
}
/**
@ -148,19 +94,10 @@ void MicroBitStorage::scratchKeyValueStore(KeyValueStore store)
//calculate our various offsets
uint32_t *s = (uint32_t *) &store;
uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
//KeyValueStore is word aligned.
int wordsToWrite = sizeof(KeyValueStore) / 4;
//write the given KeyValueStore
for (int i = 0; i < wordsToWrite; i++)
{
flashWordWrite(scratchPointer, *s);
scratchPointer++;
s++;
}
flashCopy(s, scratchPointer, wordsToWrite);
}
/**
@ -191,13 +128,7 @@ void MicroBitStorage::scratchKeyValuePair(KeyValuePair pair, uint32_t* flashPoin
//KeyValuePair is word aligned...
int wordsToWrite = sizeof(KeyValuePair) / 4;
//write
for (int i = 0; i < wordsToWrite; i++)
{
flashWordWrite(scratchPointer, *p);
scratchPointer++;
p++;
}
flashCopy(p, scratchPointer, wordsToWrite);
}
/**