In this lesson, you will learn how to architect and implement flash memory drivers that allow your running application to modify its own program storage.
The Challenge of Self-Modification
In standard embedded development, your code lives in flash, and the CPU reads instructions from that flash. When you perform In-Application Programming (IAP), you are asking the CPU to write to the very memory bank it is currently executing from. This presents a critical conflict: most modern microcontrollers cannot simultaneously read instructions from a flash sector and perform a write/erase operation on that same sector.
Relocating Logic to RAM
To overcome this, you must decouple your flash-writing logic from the persistent storage. Since you cannot execute the flash driver from the flash being modified, you must copy the driver code into RAM before invocation.
- Section Placement: Use linker script attributes to place your flash driver functions into a specific section (e.g.,
.ramfunc). - Copy to RAM: Just as you handle data initialization in the startup sequence, you must ensure your boot process copies these specific functions from the flash image into a pre-allocated RAM buffer.
- Execution: When the application needs to update a sector, it calls the function pointer pointing to the RAM location of your driver.
// Example of placing a driver function in RAM
__attribute__((section(".ramfunc")))
void write_flash_sector(uint32_t address, uint8_t *data) {
// 1. Disable interrupts to ensure atomic operation
// 2. Unlock flash controller registers
// 3. Perform write operation
// 4. Lock flash controller
// 5. Re-enable interrupts
}
Safety Considerations
While the driver runs in RAM, your application remains "stalled" in terms of flash access. Any interrupt that occurs during this time must not attempt to fetch code from the flash sector being modified; otherwise, the CPU will fetch corrupted data, leading to an immediate hard fault. Therefore, it is mandatory to disable global interrupts before initiating the flash write and re-enable them only after verification is complete. Always implement a CRC or checksum verification step immediately after the write to ensure data integrity before exiting the RAM routine.
Key Takeaway: To modify flash memory while running, you must relocate your driver code into RAM to avoid instruction fetch conflicts during the erasure and programming cycles.