Using Named Address Spaces in Embedded C to Keep Lookup Tables in Flash
Learn how Embedded C’s named address spaces let you place constant tables directly in flash, saving RAM and removing runtime copies—complete with a GCC‑AVR example, portability tips, and verification steps.
29 Sept 2026, 16:29 UTC

Problem: RAM‑constrained microcontrollers need large constant data
Many 8‑bit MCUs have only a few hundred bytes of RAM but can store kilobytes of flash. When a firmware module requires a sizable read‑only table—such as a sine wave, font glyphs, or calibration constants—copying that table into RAM at startup wastes precious memory and adds startup latency.
Thesis: Named address spaces let the compiler place const objects directly in flash
Embedded C extensions (supported by GCC, IAR, Keil, etc.) introduce qualifiers like __flash, __mem, or __eeprom. A variable declared with such a qualifier resides in the named memory region for its entire lifetime, and ordinary C syntax (table[i]) generates the appropriate load instructions. No runtime memcpy is needed.
Declaring and accessing flash variables with GCC for AVR
The following example targets an ATmega328P using avr‑gcc. The qualifier __flash places the object in the .progmem section, which the linker maps to flash.
#include
#include
/* 256‑entry sine table, one byte per entry */
const uint8_t sine_table[256] __flash = {
/* values generated offline, omitted for brevity */
};
uint8_t get_sine(uint8_t index)
{
/* Direct read from flash; the compiler emits an LPM instruction */
return sine_table[index];
}
int main(void)
{
/* Example use: output sine value to PORTB */
while (1) {
for (uint8_t i = 0; i < 256; ++i) {
PORTB = get_sine(i);
_delay_ms(10);
}
}
}
Because sine_table lives in flash, the SRAM footprint of the program is reduced by 256 bytes.
Trade‑offs and limitations
- Portability: The qualifier name is compiler‑specific. IAR uses
__flashas well, but Keil uses__rom. To keep source portable, wrap the qualifier in a macro:
#if defined(__GNUC__)
#define FLASH_ATTR __flash
#elif defined(__ICCAVR__)
#define FLASH_ATTR __flash
#elif defined(__ARMCC_VERSION)
#define FLASH_ATTR __rom
#else
#error "Unsupported toolchain"
#endif
const uint8_t sine_table[256] FLASH_ATTR = { ... };
Practical verification steps
- Compile with map file generation:
avr-gcc -mmcu=atmega328p -Wl,-Map=output.map -Os sine.c -o sine.elf - Open
output.mapand look for a line similar to.* .progmem.* sine_tableconfirming the symbol resides in the flash section. - Run the binary in a simulator (e.g.,
simavr) or on hardware and verify thatget_sine(0)returns the expected first table value without any memcpy. - Check the disassembly (
avr-objdump -d sine.elf) for anlpminstruction when accessingsine_table.
Actionable closing
If your project needs to keep large read‑only data in flash, adopt named address spaces behind a portable macro, verify placement via the map file, and avoid using the qualifier for any object that may be written at runtime. This approach reduces RAM usage, eliminates startup copy overhead, and keeps the code readable with standard C syntax.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.