Skip to content

Tutorial: Getting to Hello World

Lance Goodridge edited this page Mar 10, 2017 · 1 revision

Writing an Operating System from scratch is difficult, especially when it is on real Hardware. On real Hardware, there are very little debugging tools and it is hard to know when something goes wrong. Therefore, the most important thing is that a programmer realizes the fastest path to a Hello World program. A Hello World program at its simplest provides output to the programmer. The fastest this program is created the faster a person can debug their broken programs.

The Operating System is responsible for all Inputs/Outputs (IO) of the system. This means that we (as the OS writers) are responsible for getting pixels written to the screen, getting characters written out to a serial port, and getting sound to arrive out of a speaker. It is difficult to write hardware drivers and it is more difficult to debug hardware drivers. This becomes even more difficult when the hardware drivers are responsible for providing the IO that the programmer uses to debug. The simplest IO device on the Rbpi2 are the two LEDs attached to the board. We will start by programming these in order to get the simplest Hello World program working.

Memory Mapped IO (MMIO)

In a 32 byte addressing system, memory goes from 0x00000000 to 0xFFFFFFFF. Most of these addresses are available for an OS to store and retrieve values, but some addresses are hardwired such that they are attached to another device. We call these MMIO addresses because they interact with Input and Output of the system and they are mapped to an address in main memory. Reading and Writing to MMIO can have variously different effects such as turning on the screen, reading the battery level, sending a packet out of the Ethernet port, etc.

All of the IOs on a System on a Chip (SoC) are known as peripherals. What each MMIO address does is located in a peripheral document and this is released for individual SoCs by the company that created the chip. The Raspberry Pi and Raspberry Pi 2 Peripherals were released by the company Broadcom and are available for anyone to look at.

The LED registers

The Raspberry Pi 2 has the following information

PERIPHERAL_BASE = 0x3F000000
GPBASE          = 0x  200000
GPFSEL3         = 0x      0C
GPFSEL4         = 0x      10
GPSET1          = 0x      20
GPCLR1          = 0x      2C
ACT_LED         = 47
PWR_LED         = 35

To Access the General Purpose Set Register 1 (GPSET1) you must calculate PERIPHERAL_BASE + GPBASE + GPSEL1 = 0x3F200020

Setting the GPIO Functions

The GPFSEL registers are defined by the peripheral document (p90) to "define the operations of the 54 General Purpose IO pins". These General Purpose IO (GPIO) pins are the pins located on the side of the Raspberry Pi. For the Pi2 the ACT led (Green) is tied to the 47th GPIO pin and the PWR led (Red) is tied to the 35th pin. The function of the GPIO pin needs to be acting as an output, because we are outputting power to the led. We can see from the docs that each GPIO pin has a 3 bit value for the function and that 0b001 is output.

The code below sets gpio 47 (ACT) to function as an output. The 21 bit offset was discovered from the docs on page 90.

fn = GET32(GPFSEL4);
// Clear out all 3 bits
fn &= ~(0b111<<21);
// Set the gpio function to output
fn |=  (0b001<<21);
PUT32(GPSEL4, fn);

Setting the GPIO Values

Now that the Pi is configured to use the GPIO pins as outputs, we can use the lights!

  • The Pi sends a high voltage when a 1 is written to the GP Set register.
  • The Pi sends a low voltage when a 1 is written to the GP Clear Register.

The Clear and Set registers are configured so that each bit corresponds to that numbered GPIO pin. Each register is 32 bits long, so to write to 35 and 47 we will need to write to GPCLR1 and GPSET1 (register 1 contains GPIOs 33-64).

// turn both LEDs on
PUT32(GPSET1, 1<<(ACT_LED - 32));
PUT32(GPCSET1, 1<<(PWR_LED - 32));

// turn both LEDs off
PUT32(GPCLR1, 1<<(ACT_LED - 32));
PUT32(GPCLR1, 1<<(PWR_LED - 32));

Clone this wiki locally