Kompilasi oleh : Reza Ervani bin Asmanu
Dalam episode sebelumnya, kita berhasil membuat “Hello World” versi hardware: Mengedipkan LED. Namun, sebuah Sistem Operasi tidak bisa hanya berkedip selamanya.
Agar OS kita (yang kita bangun dari nol/Bare Metal) bisa berkomunikasi dengan dunia luar—seperti mengirim pesan teks ke PC, mendeteksi input keyboard, atau menghitung waktu dengan presisi—kita harus tahu di mana letak “tombol-tombol” pengendalinya.
Berikut adalah dokumentasi lengkap Peta Alamat Fisik (Physical Address Map) untuk prosesor BCM2835 (Raspberry Pi 1, Zero, dan Compute Module 1).
1. Konsep Dasar: Jangan Terkecoh “Alamat Hantu”
Jika Anda membaca datasheet resmi Broadcom, Anda akan sering melihat alamat yang diawali dengan 0x7E.... Hati-hati! Itu adalah alamat dari sudut pandang GPU (VideoCore).
Sebagai programmer CPU (ARM), kita menggunakan pemetaan alamat fisik yang dimulai dari 0x20....
- Bus Address (GPU):
0x7E000000 - Physical Address (ARM):
0x20000000
Jadi, rumus kuncinya adalah:
#define PBASE 0x20000000
2. Peta Besar Periferal (The Big Map)
Semua alamat di bawah ini adalah offset (jarak) dari PBASE.
| Kategori | Periferal | Offset | Alamat Fisik (Hex) | Fungsi Utama di OS |
| Waktu | System Timer | 0x3000 | 0x20003000 | Menghitung waktu (uptime) & delay presisi. |
| Kontrol | Interrupt Controller | 0xB000 | 0x2000B000 | Mengelola interupsi hardware (IRQ/FIQ). |
| Mailbox | 0xB880 | 0x2000B880 | Komunikasi surat-menyurat dengan GPU (untuk Layar/Video). | |
| Power Manager | 0x100000 | 0x20100000 | Reset sistem (Reboot) & Watchdog. | |
| I/O | GPIO | 0x200000 | 0x20200000 | Mengontrol Pin Header (LED, Button). |
| Serial | UART0 (PL011) | 0x201000 | 0x20201000 | Serial Console stabil (Komunikasi ke PC). |
| Mini UART | 0x215000 | 0x20215000 | Serial cadangan (bergantung clock GPU). | |
| Bus | SPI0 | 0x204000 | 0x20204000 | Komunikasi ke sensor/display SPI. |
| BSC0 (I2C) | 0x205000 | 0x20205000 | I2C Bus 0. | |
| BSC1 (I2C) | 0x804000 | 0x20804000 | I2C Bus 1. | |
| Memori | EMMC | 0x300000 | 0x20300000 | Kontroler SD Card. |
3. Detail Periferal Paling Vital
Untuk melangkah dari “Blink LED” menuju “Simple OS”, kita butuh tiga komponen berikut segera:
A. System Timer (0x20003000)
Saat ini kita menggunakan nop (loop kosong) untuk delay. Cara ini buruk karena membuat CPU panas dan waktunya tidak akurat. System Timer BCM2835 berjalan di frekuensi 1 MHz (1 juta tik per detik).
- Counter Low (
+0x04): Mengambil 32-bit bagian bawah waktu sistem. - Counter High (
+0x08): Mengambil 32-bit bagian atas. - Compare 0-3 (
+0x0Cs/d+0x18): Digunakan untuk membuat “Alarm” (Interupsi Timer). Catatan: Compare 1 dan 3 biasanya sudah dipakai oleh GPU, kita aman menggunakan Compare 0 atau 2.
B. Interrupt Controller (0x2000B000)
Agar OS menjadi responsif (tidak hang saat menunggu input), kita harus menggunakan Interupsi.
- IRQ Basic Pending (
+0x200): Tempat pertama mengecek “Siapa yang minta perhatian CPU?” (Timer? UART?). - Enable IRQs 1 (
+0x210): Sakelar untuk mengizinkan interupsi nomor 0-31. - Enable IRQs 2 (
+0x214): Sakelar untuk interupsi nomor 32-63.
C. UART0 / PL011 (0x20201000)
Ini adalah “Mulut” dan “Telinga” OS kita. Lewat UART, OS bisa mengirim teks debug ke komputer kita (misal: “Kernel Loaded Successfully”).
- DR (Data Register) (
+0x00): Tulis ke sini untuk kirim karakter, baca dari sini untuk terima karakter. - FR (Flag Register) (
+0x18): Cek bit TXFF (Transmit Full) sebelum mengirim, atau RXFE (Receive Empty) sebelum membaca.
4. Implementasi Kode C (Header File)
Agar kode program kita bersih dan profesional, jangan menabur angka heksadesimal di file main.c. Buatlah file header bernama mmio.h atau peripherals.h.
#ifndef PERIPHERALS_H
#define PERIPHERALS_H
#include <stdint.h>
/* Base Address untuk Raspberry Pi 1 */
#define PBASE 0x20000000
/* Makro untuk menghitung alamat fisik */
/* Peripheral = Base + Offset */
#define MMIO_BASE(offset) (PBASE + offset)
/* Definisi Alamat Periferal Utama */
#define SYS_TIMER_BASE MMIO_BASE(0x003000)
#define INTERRUPT_BASE MMIO_BASE(0x00B000)
#define MAILBOX_BASE MMIO_BASE(0x00B880)
#define GPIO_BASE MMIO_BASE(0x200000)
#define UART0_BASE MMIO_BASE(0x201000) /* PL011 */
#define EMMC_BASE MMIO_BASE(0x300000)
/* Contoh Akses Register (System Timer) */
typedef struct {
volatile uint32_t CS; /* Control/Status */
volatile uint32_t CLO; /* Counter Low */
volatile uint32_t CHI; /* Counter High */
volatile uint32_t C0; /* Compare 0 */
volatile uint32_t C1; /* Compare 1 */
volatile uint32_t C2; /* Compare 2 */
volatile uint32_t C3; /* Compare 3 */
} sys_timer_t;
#define SYS_TIMER ((sys_timer_t *)SYS_TIMER_BASE)
#endif
Dengan struktur data sys_timer_t di atas, membaca waktu sistem di kode C menjadi sangat elegan:
uint32_t waktu_sekarang = SYS_TIMER->CLO;
Ini jauh lebih mudah dibaca daripada *(volatile uint32_t*)(0x20003004).
