STM32F405: flash write causes processor to halt
I am working with an STM32F405 and am attempting to use a page of flash to store ~1K of persistent data that will be seldom written and often read (device serial number and calibration data).
I have been trying to reference sample code and a few other questions in this forum, but am still having some trouble. What I have so far:
In my .ld, I have created a new section for my user data in sector 11 of flash (addr 0x080E 0000 - 0x080F FFFF):
/* Memories definition */
MEMORY
{
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 896K
DATA (rwx) : ORIGIN = 0x80E0000, LENGTH = 128K
}
/* Sections */
SECTIONS
{
.user_data :
{
. = ALIGN(4);
KEEP(*(.user_data))
. = ALIGN(4);
} > DATA
...In my application, I have a variable mapped to this region that I plan on using for reading my data that I'm making sure is initialized to all 0xFF on initial program write:
__attribute__((__section__(".user_data"))) const uint8_t userConfig[1024*128] = {[0 ... (1024*128-1)] = 0xFF};In my application, I have tried erasing sector 11 with HAL_FLASHEx_Erase:
static void Write_Flash(uint32_t address, uint8_t toWrite)
{
uint32_t SectorError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;
HAL_StatusTypeDef halStat;
halStat = HAL_FLASH_Unlock();
if (halStat != HAL_OK){
while(1);
}
__disable_irq();
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
EraseInitStruct.Sector = FLASH_SECTOR_11;
EraseInitStruct.NbSectors = 1;
halStat = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError);
if (halStat != HAL_OK) {
while(1);
}
HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (uint32_t)address, toWrite);
HAL_FLASH_Lock();
__enable_irq();
return;
}And when that did not work, I tried instead using FLASH_Erase_Sector
void Erase_Flash(void)
{
__disable_irq();
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR );
FLASH_Erase_Sector(FLASH_SECTOR_11, VOLTAGE_RANGE_3);
FLASH->CR |= FLASH_CR_LOCK;
__enable_irq();
}For both of these functions, I am seeing the same results: when running with a debugger, my code stops executing as soon as __enable_irq() is invoked. What I mean by this is if I set a break point for the instruction after __enable_irq() is invoked, the debugger never reaches that break point. However, if I single step my code with the debugger, that code still executes. If I pause my code via debugger while waiting for the break point to be reached, my code will be inside an interrupt vector (for example, my systick interrupt or one of my UART handlers). When this happens, I can set a break point later on in the ISR, and it will be reached via free run. I can single step through the remaining ISR code and validate that upon exit, it returns to the line of code immediately after __enable_irq() was invoked, and again I'm able to single step through code. However, if I free run the code, it again never reaches the next immediate instruction, and pausing via debugger will show the code in the same state described above (it will be in an ISR, but the call stack of the debugger shows the code never got past the instruction it was at when I hit free run).
I thought this might be an issue related to the address bus stalling when a flash erase occurs, which would cause the next instruction of my application not to be executed, but my understanding is that as soon as the BSY bit of the FLASH_SR is clear, the erase should be complete and my processor should be able to fetch its next instruction out of sector 0. I validated with the debugger that the BSY bit is indeed clear, and that my program is executing from flash sector 0 (as opposed to trying to run from the same sector I'm erasing).
To test this a bit more, I modified my .ld to have a section in RAM for code to execute from:
/* Initialized data sections into "RAM" Ram type memory */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.code_for_ferase) /* RAM section for running flash erase fxns */
*(.RamFunc) /* .RamFunc sections */
*(.RamFunc*) /* .RamFunc* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASHAnd then changed my flash erase function to use this:
#define KEY1 0x45670123
#define KEY2 0xCDEF89AB
__attribute__ ((long_call, section (".code_for_ferase"))) static void Erase_Flash(void)
{
// make sure nothing's doing a write right now
while (FLASH->SR & FLASH_FLAG_BSY);
//unlock the control register
WRITE_REG(FLASH->KEYR, KEY1);
WRITE_REG(FLASH->KEYR, KEY2);
// set the erase size, 32 bit based on running @ 3.3V
CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);
FLASH->CR |= FLASH_PSIZE_WORD;
// set the sector to erase, sector 11
CLEAR_BIT(FLASH->CR, FLASH_CR_SNB);
FLASH->CR |= FLASH_CR_SER | (11 << FLASH_CR_SNB_Pos);
// start the operation
FLASH->CR |= FLASH_CR_STRT;
// wait for completion
while (FLASH->SR & FLASH_FLAG_BSY);
}I also tried modifying my above flash_erase functions (the ones that use FLASH_Erase_Sector and HAL_FLASHEx_Erase) to similarly run from RAM (including all the functions those in turn call). In all of those cases, I saw better results- the BSY bit cleared, and I was able to set break points and free run as expected, but as soon as I exited a function that was running in RAM and returned to running out of FLASH, I saw the same bad behavior of my next code fetch not occurring as expected when free running.
At this point, I'm at a loss for what to do- I'm sure I've just missed something small somewhere, but I don't know what. Any help on this would be greatly appreciated.
