Our application makes use of energy harvesting, so we try to overlap operations in our firmware when possible, e.g. using the LDMA to service a peripheral while the CPU is executing code. So, knowing this, can you tell us if it is possible to run code from the boot loader flash on EFM32TG11 at the same time as we are programming the main flash array?
What a great question! This seems like a reasonable assumption because the boot loader flash array on Series 1 devices resides at address 0x0FE10000 while the main flash array sits at 0x0. Given the two different base addresses, these would seem to be physically distinct flash arrays that would have their own dedicated charge pumps for generation of the high voltages used during program and erase operations.
Alas, in this case, different addresses do not mean physically separate arrays with separate charge pumps. Running code that programs or erases the flash from the boot loader array is subject to the same CPU stall that occurs when from the main flash array on devices that have single flash instances (more on this later). Let's investigate this further. Is there a visually satisfying way that shows us what's actually happening with the CPU when the flash high voltage is applied? Of course there is! We can toggle a GPIO pin.
First, though, we need to consider how to actually get code into the boot loader space. To do that, we'll follow the steps outlined in the Knowledge Base article "Using a Linker Script in GCC to Locate Functions at Specific Memory Regions" and have Simplicity Studio use a custom linker file with some additions. To start, we'll add the BLFLASH region of memory. Note that the first 4 KB of the boot loader flash is excluded specifically to keep the EFM32TG11 factory boot loader intact.
MEMORY
{
FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x20000 /* 128k */
BLFLASH (rx) : ORIGIN = 0x0FE11000, LENGTH = 0x4800 /* 14k */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x8000 /* 32k */
}
The .blspace section is added next for functions designated to reside in the BLFLASH region of memory:
.blspace :
{
. = ALIGN(4);
__blspace_start__ = .;
*(.blspace*)
__blspace_end__ = .;
} > BLFLASH
Lastly, a check is added to make sure functions placed in the .blspace section do not overflow the allotted space:
/* Check if BLFLASH usage exceeds FLASH size */
ASSERT( LENGTH(BLFLASH) >= (__blspace_end__ - __blspace_start__), "BLFLASH memory overflowed!")
We'll use the following simple program to directly issue a flash page erase (no use of emlib in order to keep the code compact and as close to assembly as possible). Notice the commented out function declarations. We'll make use of these later to place the erasepage() function in main flash or RAM in order to see how it behaves.
#include "em_device.h"
void erasepage(void);
int main(void)
{
// Disable MSC prefetch
MSC->READCTRL &= ~_MSC_READCTRL_PREFETCH_MASK;
// Disable the MSC cache
MSC->READCTRL |= MSC_READCTRL_IFCDIS;
// Configure PC2 as an output
CMU->HFBUSCLKEN0 |= CMU_HFBUSCLKEN0_GPIO;
GPIO->P[2].MODEL = GPIO_P_MODEL_MODE2_PUSHPULL;
while (1)
{
erasepage();
}
}
#define BLSPACE __attribute__((__section__(".blspace")))
void BLSPACE erasepage(void)
//void __attribute__ ((section(".ram"))) erasepage(void)
//void erasepage(void)
{
// Enable writes
MSC->WRITECTRL |= MSC_WRITECTRL_WREN;
// Address to erase
MSC->ADDRB = 0x8000;
// Load internal write address register from MSC_ADDRB
MSC->WRITECMD = MSC_WRITECMD_LADDRIM;
// Turn on PC2
GPIO->P[2].DOUT = 0x4;
// Enable high voltage to start erase operation
MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE;
// Flush pipeline after starting erase
__NOP();
__ISB();
// Turn off PC2
GPIO->P[2].DOUT = 0x0;
// Make sure erase is complete
while (MSC->STATUS & MSC_STATUS_BUSY);
// Halt here
__BKPT(0);
}
So, with the code downloaded to the Tiny Gecko 11 device and the erasepage() function residing at 0x0FE11000 in the boot loader flash, here's what we see on the scope:

That 26.3 ms pulse is nicely in line with the typical page erase time specified in the datasheet, too:

Naturally, we should see what happens when the erasepage() function is placed in the main array. To do so, we can remove the BLSPACE attribute from the erasepage() function declaration or simply comment out the declaration entirely and uncomment the third declaration which excludes the BLSPACE attribute. Here's what we see now:

Looks good! This matches right up with the result seen when running from the boot loader array, which indicates that, in both cases, the CPU is stalled while the high voltage is applied to the flash during the erase operation. Of course, we need to see the alternative, which is what happens when erasepage() runs from RAM. To see, this comment out the current declaration that places the code in flash and uncomment out the declaration with the .ram section attribute. Running this code, we see:

The short duration of this GPIO pulse when running erasepage() from RAM clearly indicates that the CPU is still executing code while the high voltage is applied. Series 1 devices default to operation at 19 MHz, so this amounts to just a few instructions as shown below in the debugger disassembly:

Knowing all of this, there is a remaining question: What happens on dual bank devices like EFM32PG12, which explicitly have read-while-write (RWW) support via the MSC_WRITECTRL_RWWEN bit. Let's run the same code with erasepage() in the boot loader flash but with the erase target addresses being 0x40000 and 0xC0000, which, on Pearl Gecko 12, are in the bottom and top flash banks, respectively. We'll also need to modify the write enabling sequence to permit RWW operation as follows:
// Enable writes
MSC->WRITECTRL |= (MSC_WRITECTRL_WREN | MSC_WRITECTRL_RWWEN);
In the side-by-side scope captures below, the page erase operation takes the datasheet typical 27 ms when the target address is 0x40000 but only 1.8 µs when the target address is 0xC0000. What this shows is that the boot loader flash is part of the same physical flash block as the bottom flash bank (0x0 to 0x7FFFF) and thus shares the same high voltage circuitry. The top flash bank (0x80000 to 0xFFFFF) is physically distinct from the boot loader array, which is why code can continue executing even when the high voltage is applied for erasing (or programming).

|

|
Users of Giant Gecko Series 1 should take note that this behavior is reversed. On these devices, the boot loader array is associated with the upper flash bank, so RWW is possible when the target of flash operations is in the lower flash bank. Of course, as the reference manual denotes, RWW is always possible when code is running in one of the physical flash arrays and the target of flash operations is the other (e.g. lower bank and upper bank or vice versa).
Read-While-Write and the Boot Loader Flash on EFM32 and EFR32 Series 1
Our application makes use of energy harvesting, so we try to overlap operations in our firmware when possible, e.g. using the LDMA to service a peripheral while the CPU is executing code. So, knowing this, can you tell us if it is possible to run code from the boot loader flash on EFM32TG11 at the same time as we are programming the main flash array?
What a great question! This seems like a reasonable assumption because the boot loader flash array on Series 1 devices resides at address 0x0FE10000 while the main flash array sits at 0x0. Given the two different base addresses, these would seem to be physically distinct flash arrays that would have their own dedicated charge pumps for generation of the high voltages used during program and erase operations.
Alas, in this case, different addresses do not mean physically separate arrays with separate charge pumps. Running code that programs or erases the flash from the boot loader array is subject to the same CPU stall that occurs when from the main flash array on devices that have single flash instances (more on this later). Let's investigate this further. Is there a visually satisfying way that shows us what's actually happening with the CPU when the flash high voltage is applied? Of course there is! We can toggle a GPIO pin.
First, though, we need to consider how to actually get code into the boot loader space. To do that, we'll follow the steps outlined in the Knowledge Base article "Using a Linker Script in GCC to Locate Functions at Specific Memory Regions" and have Simplicity Studio use a custom linker file with some additions. To start, we'll add the BLFLASH region of memory. Note that the first 4 KB of the boot loader flash is excluded specifically to keep the EFM32TG11 factory boot loader intact.
The .blspace section is added next for functions designated to reside in the BLFLASH region of memory:
Lastly, a check is added to make sure functions placed in the .blspace section do not overflow the allotted space:
We'll use the following simple program to directly issue a flash page erase (no use of emlib in order to keep the code compact and as close to assembly as possible). Notice the commented out function declarations. We'll make use of these later to place the erasepage() function in main flash or RAM in order to see how it behaves.
So, with the code downloaded to the Tiny Gecko 11 device and the erasepage() function residing at 0x0FE11000 in the boot loader flash, here's what we see on the scope:
That 26.3 ms pulse is nicely in line with the typical page erase time specified in the datasheet, too:
Naturally, we should see what happens when the erasepage() function is placed in the main array. To do so, we can remove the BLSPACE attribute from the erasepage() function declaration or simply comment out the declaration entirely and uncomment the third declaration which excludes the BLSPACE attribute. Here's what we see now:
Looks good! This matches right up with the result seen when running from the boot loader array, which indicates that, in both cases, the CPU is stalled while the high voltage is applied to the flash during the erase operation. Of course, we need to see the alternative, which is what happens when erasepage() runs from RAM. To see, this comment out the current declaration that places the code in flash and uncomment out the declaration with the .ram section attribute. Running this code, we see:
The short duration of this GPIO pulse when running erasepage() from RAM clearly indicates that the CPU is still executing code while the high voltage is applied. Series 1 devices default to operation at 19 MHz, so this amounts to just a few instructions as shown below in the debugger disassembly:
Knowing all of this, there is a remaining question: What happens on dual bank devices like EFM32PG12, which explicitly have read-while-write (RWW) support via the MSC_WRITECTRL_RWWEN bit. Let's run the same code with erasepage() in the boot loader flash but with the erase target addresses being 0x40000 and 0xC0000, which, on Pearl Gecko 12, are in the bottom and top flash banks, respectively. We'll also need to modify the write enabling sequence to permit RWW operation as follows:
In the side-by-side scope captures below, the page erase operation takes the datasheet typical 27 ms when the target address is 0x40000 but only 1.8 µs when the target address is 0xC0000. What this shows is that the boot loader flash is part of the same physical flash block as the bottom flash bank (0x0 to 0x7FFFF) and thus shares the same high voltage circuitry. The top flash bank (0x80000 to 0xFFFFF) is physically distinct from the boot loader array, which is why code can continue executing even when the high voltage is applied for erasing (or programming).
Users of Giant Gecko Series 1 should take note that this behavior is reversed. On these devices, the boot loader array is associated with the upper flash bank, so RWW is possible when the target of flash operations is in the lower flash bank. Of course, as the reference manual denotes, RWW is always possible when code is running in one of the physical flash arrays and the target of flash operations is the other (e.g. lower bank and upper bank or vice versa).