Peta Alamat Fisik Periferal Processor BCM2835

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.

KategoriPeriferalOffsetAlamat Fisik (Hex)Fungsi Utama di OS
WaktuSystem Timer0x30000x20003000Menghitung waktu (uptime) & delay presisi.
KontrolInterrupt Controller0xB0000x2000B000Mengelola interupsi hardware (IRQ/FIQ).
Mailbox0xB8800x2000B880Komunikasi surat-menyurat dengan GPU (untuk Layar/Video).
Power Manager0x1000000x20100000Reset sistem (Reboot) & Watchdog.
I/OGPIO0x2000000x20200000Mengontrol Pin Header (LED, Button).
SerialUART0 (PL011)0x2010000x20201000Serial Console stabil (Komunikasi ke PC).
Mini UART0x2150000x20215000Serial cadangan (bergantung clock GPU).
BusSPI00x2040000x20204000Komunikasi ke sensor/display SPI.
BSC0 (I2C)0x2050000x20205000I2C Bus 0.
BSC1 (I2C)0x8040000x20804000I2C Bus 1.
MemoriEMMC0x3000000x20300000Kontroler 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 (+0x0C s/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).