// lesson: sio-gpio

GPIO Through the SIO

The SIO block at 0xd0000000 is how RP2040 code drives pins. Its GPIO registers sit at these offsets (datasheet ยง2.3.1.7):

offset register what it does
0x010 GPIO_OUT output level of each pin
0x014 GPIO_OUT_SET write mask: set those OUT bits
0x018 GPIO_OUT_CLR write mask: clear those OUT bits
0x01c GPIO_OUT_XOR write mask: flip those OUT bits
0x020 GPIO_OE output enable for each pin
0x024 GPIO_OE_SET write mask: set those OE bits
0x028 GPIO_OE_CLR write mask: clear those OE bits
0x02c GPIO_OE_XOR write mask: flip those OE bits

Why the SET/CLR/XOR aliases when last lesson's read-modify-write already works? Because RMW is three operations, and between your read and your write an interrupt handler โ€” or the RP2040's second core; it's a dual-core chip, and both cores can drive GPIO through the same SIO block โ€” may also touch GPIO_OUT. Do a plain RMW and that other write can land in the gap between your read and your write, and gets silently overwritten. Writing a mask to GPIO_OUT_SET is one store; the hardware does the modify atomically. This pattern is all over the RP2040, so drivers barely ever RMW the SIO.

Instead of casting raw offsets one at a time, real drivers describe a whole register block as a struct whose field layout mirrors the datasheet, then point it at the base address:

struct pico_sio {
	volatile uint32_t cpuid;        /* 0x000 */
	volatile uint32_t gpio_in;      /* 0x004 */
	volatile uint32_t gpio_hi_in;   /* 0x008 */
	volatile uint32_t _pad;         /* 0x00c (reserved) */
	volatile uint32_t gpio_out;     /* 0x010 */
	volatile uint32_t gpio_out_set; /* 0x014 */
	volatile uint32_t gpio_out_clr; /* 0x018 */
	volatile uint32_t gpio_out_xor; /* 0x01c */
	volatile uint32_t gpio_oe;      /* 0x020 */
	volatile uint32_t gpio_oe_set;  /* 0x024 */
	volatile uint32_t gpio_oe_clr;  /* 0x028 */
	volatile uint32_t gpio_oe_xor;  /* 0x02c */
};

#define SIO ((volatile struct pico_sio *)0xd0000000u)

The padding field is load-bearing: every field must land on its datasheet offset, so reserved holes get explicit placeholders. (In a real project the struct lives in a header; here each file carries its own copy because challenge files are self-contained.)

The tests play the part of the hardware: they hand you a struct pico_sio in ordinary memory and then inspect which alias register your code wrote to, and with what mask.

โ€บ Drive a Pin Atomically

15 pts

Implement four pin helpers using only the SET/CLR/XOR alias registers โ€” the tests verify that gpio_out and gpio_oe themselves are never written directly.

Log in to submit a solution and earn points.