Menjinakkan firmware yang bermasalah: sesi GDB via OpenOCD dengan breakpoint dan watchpoint, semihosting dan RTT untuk log real-time, membaca fault handlers HardFault lewat register stack, serta praktik sesi debugging end-to-end.

Setelah di episode 8 kita memahami memori dan linker, sekarang saatnya menghadapi kenyataan pahit dunia firmware: kode yang kita tulis jarang benar pada percobaan pertama. Yang membedakan engineer junior dari senior bukan "tidak pernah error", melainkan kecepatan menemukan akar masalah. Episode ini memberi kalian senjata lengkap: GDB, semihosting, RTT, dan fault handler.
Mengapa ini penting? Karena di firmware tidak ada strace, tidak ada debugger JS di browser, dan error sering muncul sebagai perangkat yang hang diam-diam di lapangan. Satu-satunya jalan adalah membaca apa yang terjadi di dalam chip — lewat debug probe dan trace.
OpenOCD menjembatani debugger (ST-Link) ke GDB. Alur kerja klasik:
openocd -f interface/stlink.cfg -f target/stm32f1x.cfgarm-none-eabi-gdb firmware.elf
(gdb) target extended-remote localhost:3333
(gdb) load
(gdb) monitor reset haltSetelah terhubung, kalian bisa breakpoint, step, membaca/menulis register dan memori, bahkan mengganti variabel saat program berjalan.
(gdb) break main # breakpoint di main
(gdb) break gpio_write if led==1 # breakpoint kondisional
(gdb) watch my_var # watchpoint: berhenti saat berubah
(gdb) info registers r0 r1 pc # baca register
(gdb) x/8xw 0x20000000 # examine 8 word dari RAM
(gdb) x/4b &packet # lihat byte demi byte
(gdb) print frame->len # evaluasi ekspresi
(gdb) bt # backtrace panggilan
(gdb) continueWatchpoint adalah kekuatan firmware: berhenti tepat saat variabel berubah — sangat berguna untuk melacak "siapa yang mengubah nilai saya". Examine memory (x) membuktikan apakah buffer terisi seperti dugaan kalian.
Semihosting mengizinkan kode di target memanggil layanan host (misal printf) melalui debug probe — tanpa UART sama sekali. Aktifkan dari OpenOCD:
(gdb) monitor arm semihosting enable#include <stdio.h>
int main(void) {
printf("hello dari target! %d\n", 42);
for (;;);
}Karena printf berat, semihosting dipakai hanya saat debug, bukan produksi. Ia juga memperlambat eksekusi nyata (setiap printf lewat debugger).
SEGGER RTT (Real-Time Transfer) menulis log ke buffer RAM yang dibaca host tanpa menghentikan target — jauh lebih cepat dari semihosting dan tanpa menunggu UART. Konsepnya: firmware menulis ke ring buffer; J-Link / ST-Link (via plugin) membacanya.
#include "SEGGER_RTT.h"
SEGGER_RTT_printf(0, "sensor=%d\n", adc_val);Untuk tracing perilaku real-time (urutan event, timing antar task), RTT adalah pilihan utama — log bisa ratusan ribu baris/detik tanpa mengganggu timing sistem.
Saat CPU menemui error (instruksi ilegal, akses memori salah, division by zero), ia melompat ke HardFault_Handler. Kode produksi harus mengumpulkan bukti sebelum hang:
void HardFault_Handler(void) {
uint32_t pc = __get_PC(); // program counter saat fault
uint32_t lr = __get_LR(); // link register
uint32_t cfsr = SCB->CFSR; // Configurable Fault Status
uint32_t hfsr = SCB->HFSR; // HardFault status
uint32_t msp = __get_MSP(); // stack pointer
fault_pc = pc; fault_lr = lr; fault_cfsr = cfsr;
// simpan ke struktur global, set flag, lalu reboot loop
while (1) {} // atau: panggil fungsionalitas dump/reboot
}CFSR bit 0 (IACCVIOL) : instruksi akses memori salah
CFSR bit 1 (DACCVIOL) : data akses memori salah
CFSR bit 8 (DIVBYZERO): pembagian nol
CFSR bit 25 (UNDEFINSTR): instruksi tidak dikenalDengan mencatat PC pada saat fault, kalian bisa addr2line — ubah alamat menjadi baris kode sumber:
arm-none-eabi-addr2line -e firmware.elf -f 0x08001a2cTip
Pasang dump fault sejak hari pertama, bukan saat sudah produksi: simpan PC, LR, CFSR, dan stack context ke struct global, lalu kirim lewat UART/RTT. Firmware yang "mati diam-diam" tanpa jejak adalah mimpi buruk — fault handler yang baik mengubah mimpi buruk menjadi satu perintah addr2line.
Alur yang akan kalian ulang puluhan kali:
1. Reproduksi: apa yang terjadi? (hang / reset loop / nilai salah)
2. Breakpoint di titik masuk (misal main, ISR)
3. Watchpoint di variabel yang mencurigakan
4. Step + periksa register/memori; bandingkan dengan datasheet
5. Jika fault: catat PC, jalankan addr2line, periksa CFSR
6. Perbaiki, rebuild, flash, verifikasi dengan RTT/logIngat: reproduksi yang konsisten adalah 50% debugging. Firmware yang hang kadang hanya terjadi saat kondisi tertentu — buat trigger yang andal sebelum menyalahkan kode.
-O2 mengaburkan line info — saat debugging, build -O0 -g; baru -O2 saat release.Inti yang harus dibawa pulang:
addr2line.Di episode 10 selanjutnya kita merapikan kode menjadi komponen: Device Drivers — perbedaan HAL vs bare-metal, arsitektur driver yang baik, lapisan abstraksi, dan praktik menulis driver sensor yang bisa dipakai ulang. Dari debugger, kita kembali ke meja desain!