Detecting Raspberry Pi Board

From OSDev Wiki
Jump to navigation Jump to search

There are several Raspberry Pi boards, all shipped with an ARM SoC, but each has different memory addresses.

Detecting the board

In order to detect which board you have, you have to read the MIDR_ELx system register. The manufacturer is encoded in bits [31:24], and the model in the so called PartNum in the bits [15:4]. PartNum is implementation defined, meaning each manufacturer are free to assign values as they like.

Board AArch64 PartNum MMIO base address
Rpi1 / Rpi Zero No 0xB76 0x20000000
Rpi2 No 0xC07 0x3F000000
Rpi3 Yes 0xD03 0x3F000000
Rpi4 Yes 0xD08 0xFE000000


You can run kernels compiled for AArch32 mode on all boards, and they are loaded into memory at 0x8000. The RaspberryPi 3 and upwards also supports AArch64, loaded into 0x80000, and uses a different Assembly to read the same system register. Regardless to mode and load address, MMIO base address is tied to the board.

#include <stdint.h>

void _start()
{
    uint32_t reg;
    uint32_t *mmio_base;
    char *board;

    /* read the system register */
#if __AARCH64__
    asm volatile ("mrs %x0, midr_el1" : "=r" (reg));
#else
    asm volatile ("mrc p15,0,%0,c0,c0,0" : "=r" (reg));
#endif

    /* get the PartNum, detect board and MMIO base address */
    switch ((reg >> 4) & 0xFFF) {
        case 0xB76: board = "Rpi1"; mmio_base = 0x20000000; break;
        case 0xC07: board = "Rpi2"; mmio_base = 0x3F000000; break;
        case 0xD03: board = "Rpi3"; mmio_base = 0x3F000000; break;
        case 0xD08: board = "Rpi4"; mmio_base = 0xFE000000; break;
        default:    board = "????"; mmio_base = 0x20000000; break;
    }
}

Once you know the base address, you can add the peripheral's address to it to get its IO registers. For example, BCM System Timer can be accessed at base address + 0x3000, and the UART0 is at base address + 0x201000.

See also

  1. ARM Overview
  2. Raspberry Pi
  3. Raspberry Pi Bare Bones
  4. ARM Developer, Main ID Register