Menjadi penguasa memori firmware: memahami pembagian flash dan RAM ke section .text, .rodata, .data, dan .bss, membaca dan mengubah linker script MEMORY/SECTIONS, penempatan stack dan heap, serta menganalisis file .map untuk mendeteksi pemborosan memori.

Setelah di episode 7 firmware kita berjalan multitasking dengan FreeRTOS, pada episode ini kita turun ke lapisan yang paling menentukan kestabilan: memori. RTOS yang benar pun akan ambruk jika stack task menimpa data, atau heap penuh tanpa diketahui.
Mengapa topik ini penting? Karena di firmware, memori adalah sumber daya yang tidak bisa di-query di runtime — kalian harus tahu sejak build time di mana program, data, stack, dan heap berada. Kesalahan di sini muncul sebagai reboot loop di lapangan yang sangat sulit direproduksi. Linker script dan file .map adalah peta lengkapnya.
Program firmware terbagi menjadi beberapa section yang disimpan ke dua jenis memori:
| Section | Isi | Lokasi | Contoh |
|---|---|---|---|
.text | Kode program | Flash | Fungsi, ISR |
.rodata | Data konstan | Flash | String, lookup table |
.data | Variabel global terinisialisasi | RAM (disalin dari flash saat boot) | int counter = 5; |
.bss | Variabel global nol/uninitialized | RAM (dizero-kan saat boot) | int buffer[1024]; |
.stack | Stack CPU | RAM (atas) | Konteks fungsi & ISR |
.heap | Alokasi dinamis | RAM | malloc / pvPortMalloc |
Kunci pemahaman: .data dan .bss tinggal di RAM, tapi nilainya harus disiapkan saat boot — itulah tugas startup code (episode 2): menyalin .data dari flash dan men-zero-kan .bss.
Linker script memberitahu linker ke mana tiap section ditaruh. Untuk STM32F103 (64 KB flash, 20 KB RAM) biasanya terlihat seperti ini:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}Dan penempatan section di bagian SECTIONS:
SECTIONS
{
.isr_vector : { KEEP(*(.isr_vector)) } > FLASH
.text : { *(.text*) } > FLASH
.rodata : { *(.rodata*) } > FLASH
_sidata = LOADADDR(.data);
.data :
{
_sdata = .;
*(.data*);
_edata = .;
} > RAM AT > FLASH
.bss :
{
_sbss = .;
*(.bss*);
_ebss = .;
} > RAM
_estack = ORIGIN(RAM) + LENGTH(RAM);
}Simbol seperti _sdata, _sidata, _estack dipakai oleh startup code untuk menyalin dan nol-kan section. Mengubah LENGTH RAM/FLASH tanpa tahu sebab-akibatnya adalah cara tercepat merusak firmware — selalu sesuaikan dengan angka di datasheet chip.
Note
Istilah yang wajib dipahami: AT > FLASH artinya data awalnya "dilahirkan" di flash (di sinilah _sidata menunjuk), lalu dijalankan dari RAM (> RAM). Tanpa perbedaan load address vs runtime address ini, variabel terinisialisasi tidak akan pernah berisi nilai yang benar.
Stack: dipakai setiap pemanggilan fungsi — variabel lokal, konteks ISR, dan konteks task RTOS. Cepat, otomatis, tapi terbatas. Overflow (stack tumbuh menimpa data) = korupsi memori diam-diam. Pada Cortex-M, stack tumbuh ke bawah dari _estack (puncak RAM) — lebih rendah alamat = tumbuh mendekati data.
Heap: dipakai malloc/pvPortMalloc (FreeRTOS). Di firmware, alokasi dinamis harus dibatasi — fragmentasi dan alokasi gagal di tengah operasi bisa membahayakan sistem real-time. Banyak project produksi memakai alokasi statis penuh (semua buffer dideklarasikan di compile time).
File .map adalah laporan lengkap linker — alat detektif pemborosan memori:
arm-none-eabi-gcc -Wl,-Map=firmware.map ...
arm-none-eabi-size firmware.elf text data bss dec hex filename
18344 388 1416 20148 4eb4 firmware.elfBacaan: text = flash untuk kode+rodata, data = bagian .data (di flash + RAM), bss = RAM untuk variabel. Bandingkan dengan LENGTH di linker script — kalau text mendekati flash, atau data+bss mendekati RAM, kalian tahu sisa ruang sebelum mengoptimasi.
Latihan nyata: sesuaikan linker script dengan chip yang kalian pakai. Untuk chip 256 KB flash / 64 KB RAM:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}Langkah verifikasi setelah mengubah linker script:
1. Build; cek tidak ada error "region FLASH overflowed"
2. baca output `arm-none-eabi-size` - bandingkan dengan LENGTH
3. flash + boot; jika langsung HardFault -> cek _estack & vector table
4. (lanjut) analisis detail dengan `arm-none-eabi-nm -n firmware.elf`Untuk FreeRTOS, ingat episode 7: setiap task butuh stack sendiri dari heap. Ukur kebutuhan dengan uxTaskGetStackHighWaterMark saat debug.
Warning
Aturan firmware produksi: minimalkan alokasi dinamis. Alokasikan buffer sekali di setup (atau statis), jangan malloc di loop. FreeRTOS punya xTaskCreateStatic untuk task statis, dan heap_4/heap_5 menyatukan fragmentasi — tapi desain terbaik adalah tidak memerlukan alokasi dinamis sama sekali.
.bss — RAM penuh sebelum runtime; gunakan size untuk audit.malloc di loop — fragmentasi dan crash acak.__attribute__((packed)) hanya untuk register; misalignment = fault + performa buruk.Inti yang harus dibawa pulang:
.text/.rodata (flash) dan .data/.bss (RAM), disiapkan oleh startup code saat boot.MEMORY + SECTIONS) menentukan penempatan; simbol _sdata, _sidata, _estack dipakai startup code..map dan arm-none-eabi-size.Di episode 9 selanjutnya kita menghadapi momen paling penting seorang firmware engineer: Debugging & Tracing — GDB, semihosting, RTT, dan fault handlers untuk membedah firmware yang tidak berperilaku seperti harapan. Karena di dunia nyata, firmware hampir selalu salah pada percobaan pertama!