Episode ini membawa C ke perangkat keras: embedded dan bare-metal programming, startup code, linker script, dan memory map, real-time constraints dengan perilaku deterministik, serta debugging embedded memakai JTAG dan simulator.

C adalah bahasa utama dunia embedded: dari mikrokontroler di kendaraan, peralatan medis, hingga perangkat IoT. Episode 21 membawa kalian ke bare-metal programming — menulis kode yang berjalan langsung di atas perangkat keras tanpa sistem operasi.
Tanpa OS, kalian bertanggung jawab atas semuanya: apa yang terjadi sebelum main dipanggil, di mana kode diletakkan di memori, dan bagaimana mengatur waktu yang presisi. Inilah tugas startup code dan linker script.
Kalian juga akan belajar menangani constraint real-time dan debugging memakai JTAG dan simulator — cara kerja nyata para pengembang firmware.
Pada sistem bare-metal, perangkat keras dikendalikan lewat register yang dipetakan ke alamat memori tertentu. Kalian mengaksesnya lewat pointer dan volatile agar compiler tidak mengoptimasi pembacaan:
#include <stdint.h>
#define GPIO_BASE 0x40021000UL
#define GPIO_MODER (*(volatile uint32_t *)(GPIO_BASE + 0x00))
void nyalakan_led(void) {
GPIO_MODER |= (1U << 20);
}
int main(void) {
nyalakan_led();
for (;;) {
}
return 0;
}
EOF
gcc -std=c17 -Wall -Wextra -pedantic main.c -o mainGPIO_MODER adalah dereference pointer ke alamat register, dengan volatile memaksa pembacaan dan penulisan nyata setiap kali. for (;;){} adalah loop utama yang berjalan tanpa batas — pola standar firmware yang tidak pernah kembali dari main.
Resource di mikrokontroler sangat terbatas: kilobyte RAM, megabyte flash. Hindari heap dan library besar. Setiap byte dan siklus CPU berharga. Kompilasi dengan optimasi ukuran untuk embedded:
arm-none-eabi-gcc -mcpu=cortex-m4 -Os -ffreestanding main.c -o main.elf-mcpu=cortex-m4 menargetkan inti ARM tertentu, -Os mengoptimasi ukuran binary, dan -ffreestanding memberi tahu compiler bahwa tidak ada runtime host — tidak ada main yang dipanggil OS.
Saat perangkat menyala, kode startup bekerja lebih dulu: mengatur stack pointer, menyalin data terinisialisasi dari flash ke RAM, mengosongkan BSS, lalu memanggil main. Semua ini diatur oleh startup file yang biasanya diberikan vendor. Memahami alurnya menjelaskan mengapa variabel global bernilai benar saat main mulai.
Linker script menentukan di mana tiap bagian kode diletakkan di memori:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.text : { *(.text*) } > FLASH
.data : { *(.data*) } > RAM
.bss : { *(.bss*) } > RAM
}Script MEMORY di atas mendeklarasikan dua wilayah: FLASH untuk kode read-only dan RAM untuk data. Blok SECTIONS menempatkan .text ke FLASH dan .data serta .bss ke RAM. Alamat ini sesuai memory map mikrokontroler dan harus cocok dengan lembar data.
Alat seperti arm-none-eabi-objdump dan arm-none-eabi-size menampilkan hasil layout:
arm-none-eabi-size main.elf
arm-none-eabi-objdump -h main.elfarm-none-eabi-size main.elf menunjukkan ukuran text, data, dan bss. objdump -h mencetak bagian dan alamatnya. Membandingkan angka ini dengan kapasitas FLASH dan RAM adalah pemeriksaan kesehatan wajib sebelum mem-flash perangkat.
Sistem real-time memiliki deadline: hasil harus siap sebelum batas waktu, atau sistem dianggap gagal. Konsekuensinya, alokasi heap yang lama eksekusinya tak menentu dan lock yang bisa membeku tidak boleh ada di jalur kritis.
Latensi harus bisa diprediksi. Artinya: tidak ada malloc di loop waktu-kritis, tidak ada fungsi yang berjalan tanpa batas waktu, dan interrupt diatur prioritasnya. Interrupt bertingkat memastikan tugas paling penting selalu menang. Pengujian memakai timer hardware mengukur latensi nyata dan membuktikan bahwa deadline terpenuhi.
JTAG adalah antarmuka debugging hardware yang memungkinkan menghentikan CPU, membaca memori, dan mengatur breakpoint langsung di chip. OpenOCD menjembatani probe ke gdb:
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg
gdb-multiarch main.elfPerintah openocd -f interface/stlink.cfg memulai server debugging untuk probe ST-Link dan target STM32. Di gdb, target remote localhost:3333 menghubungkan ke chip, lalu breakpoint dan watchpoint berfungsi seperti di host — tetapi berjalan di perangkat keras nyata.
Simulator mengeksekusi instruksi target di mesin kalian tanpa hardware. QEMU dalam mode semihosting menyediakan lingkungan pembangunan cepat untuk logika yang tidak bergantung perangkat keras. Kombinasi simulator untuk pengembangan cepat dan JTAG untuk validasi akhir adalah alur kerja standar tim firmware.
Warning
Debugging embedded di perangkat keras itu lambat dan mahal. Uji logika sebanyak mungkin di host atau simulator terlebih dahulu, baru turunkan ke JTAG. Setiap iterasi di chip nyata memakan waktu jauh lebih lama.
Inti yang harus dibawa pulang:
Di episode 22 selanjutnya kita akan membahas observability dan production support — logging, tracing, dan monitoring untuk aplikasi C, crash dump analysis dengan core files dan post-mortem debugging, rollback strategy dan release management, hingga documentation, code review, dan maintenance.