Everything Is Memory

Learning Tock from the ground up · Chapter 1

‹ Contents

Everything Is Memory

A microcontroller gives software exactly one move: put a number at a numbered location. Everything the hardware does, it does because of that. This chapter follows one such move all the way down to a wire changing voltage — and shows why it forces an operating system like Tock to exist.

What you'll be able to do at the end

  1. Read something like 0xD0000018 and say which piece of hardware answers to it, and why that one.
  2. Meet a datasheet listing that gives distances rather than finished numbers, and work out the one you would actually use.
  3. Say which of a processor's thirty-two output wires a given number moves, and write the number that moves one of them.

Plan on forty minutes, not twenty. There is nothing to install and no hardware needed, and nothing here is a race.

In the book: nothing on this. It names memory-mapped I/O in doc/design and development/peripheral without stopping to say what one is.

The nine words, and where each one is

These get used constantly from here on. None is hard, and you do not have to remember them — each is repeated in context when it matters.

What is worth having now is a place for each one, because they name things in three different places.

Every word the chapter uses, including these, is collected in the glossary at the end.

Figure 1 The nine words, and where each one is

Click a word. The picture shows you where that thing actually is.

Out on the board GPIO: 26 of these one pin 3.3 V 0 V In between, as numbers 0xD0000018 a whole address one byte: a row of eight one bit: a single square Inside the chip core store a peripheral one register, 32 bits wide

Three of them name something you could point at on the board in front of you.

Three name part of a number, which is the only thing that ever travels between the other two.

Three name something sealed inside the chip, which you will never see.

One numbered connection between the chip and the outside world. On a Pico 2 these come out as numbered holes along the board's two long edges.
Electrical pressure, measured in volts. On this board the two that matter are 3.3 V and 0 V.
General-purpose input/output. A pin the chip has no fixed plan for, so you decide what it means.
A single 0 or 1. The smallest thing a computer holds.
Eight bits kept together. The unit that locations are counted in.
A number that names one location. Like a house number along a very long street.
A lump of dedicated hardware beside the processor with one job: drive pins, count time, send bytes down a wire.
A fixed-width slot of hardware, 32 bits wide here, that lives at an address and that some part of the chip is watching.
What a processor does when it puts a number at an address. One machine instruction.
Notice that nothing in the right-hand column is reachable. You cannot touch a register or watch a store happen. The whole of the rest of this chapter is about reaching that column using only the middle one.

Start with the metal

Start with what is physically in front of you, because it is smaller and plainer than people expect.

The chip is a black square about 7 mm on a side, soldered flat, with nothing sticking out of it. What you actually touch is the board it sits on.

Figure 2 The board on your desk, hole by hole

Click any hole — or tab to the board and use the arrow keys. Find the one your light will go in.

hole 1 carries GP0
USB RP2350 7 mm 10 mm on the real board 21 × 51 mm, forty holes at 2.54 mm pitch GP0 1 GP1 2 GND 3 GP2 4 GP3 5 GP4 6 GP5 7 GND 8 GP6 9 GP7 10 GP8 11 GP9 12 GND 13 GP10 14 GP11 15 GP12 16 GP13 17 GND 18 GP14 19 GP15 20 VBUS 40 VSYS 39 GND 38 3V3_EN 37 3V3(OUT) 36 ADC_VREF 35 GP28 34 AGND 33 GP27 32 GP26 31 RUN 30 GP22 29 GND 28 GP21 27 GP20 26 GP19 25 GP18 24 GND 23 GP17 22 GP16 21

26 holes carry a GPIO you may drive. Any of these will light a small lamp. This is the ordinary case.

2 of those are already spoken for. Tock sends its own console output through GPIO 0 and GPIO 1, so driving either one cuts off the kernel while you watch.

8 holes are 0 volts. Seven are plain ground; the eighth is a quieter ground kept for the four pins that measure voltages. Your lamp’s far end goes to one of the seven plain ones.

5 holes are power, not signal. Some feed the board and some are fed by it. None of them answers to a store.

1 hole resets the chip. Pull it to 0 volts and the RP2350 stops and starts again.

Four of the chip’s thirty GPIOs never reach a hole at all. The board kept them, and what it kept them for is the difference between the two boards:

Pico 2 GPIO 23 on-board power-save control GPIO 24 senses whether USB power is present GPIO 25 the little green light GPIO 29 measures the input voltage
Pico 2 W GPIO 23 switches the radio on GPIO 24 carries data to the radio GPIO 25 selects the radio GPIO 29 clocks the radio

On the wireless board the light is not on the RP2350 at all. It hangs off the radio chip, and the only way to reach it is to ask the radio.

Drawn to scale from the Pico 2 datasheet, §3 and its own pinout figure. Both boards are 21 × 51 mm and both carry the same forty signals in the same forty places; only the four that never reach a hole differ. The proportions are exact — the chip really is that small beside the board — but the drawing is not life-size, because its size follows your screen.

Each of those holes is a pin, and when the chip drives one, it can do exactly two things. That is the whole vocabulary. Every blinking light, every spinning motor, every message down a wire is built from that one choice, repeated quickly.

Figure 3 The only two ways the chip can drive a pin

Turn it on and off. Watch which parts of the picture change, and which do not.

RP2350 the chip GPIO 25 3.3 V light resistor 0 V — ground

High. The pin is connected to 3.3 volts. Current runs from the pin, through the lamp, to ground, and the lamp glows.

Low. The pin is connected to 0 volts. Both ends of the lamp are now at the same voltage, so no current runs and the lamp is dark.

what did that *0xD0000018 = 0x02000000
That bottom line is the entire subject of this chapter, and by the end of it you will be able to read every character of it. Notice that turning the pin off changes the address, not the number — the same 0x02000000 goes to a different place.

If you have used an Arduino you have done this by writing digitalWrite(25, HIGH). It reads like an order given to a device: pin, turn on.

But nothing in the chip receives orders. There is no device listening. So how does a line of code change a voltage?

The answer is short, and it is why this chapter exists.

One instruction puts one number at one address. That is the entire mechanism.

Everything else is bookkeeping around that. The interesting part is how the chip turns a number and an address into 3.3 volts on one particular pin, and it does that by answering three questions in a row.

Each question has a different answer, and mixing them up is the main reason this subject feels slippery. So they get one section each.

1 Which block of hardware? 2 Which control in that block? 3 Which pin, of the thirty?

Question 1 — the first hex digit picks the hardware

The same word means two different things, depending on the machine.

on a desktop
an address names a slot of memory. Store a number, read it back, get it. That is where variables live.
on this chip
only part of the range is memory. The rest is wired to hardware, and storing a number there operates it.

Which stretch is which was burned into the silicon once, by the chip designers. So the chip's first question is: who does this address belong to?

It answers it by looking at a single digit.

That digit is written in a counting system you may not have met: sixteens rather than tens, called hexadecimal, flagged by the 0x in front.

Sixteens need sixteen digits, so the usual ten run on into six letters.

00 11 22 33 44 55 66 77 88 99 A10 B11 C12 D13 E14 F15

One hex digit is exactly four bits, always. So an eight-digit address is exactly 32 bits, and each digit owns a fixed slice of them. Take one apart.

Figure 4 Every digit is four bits

Pick a digit. Then flip its four bits below and watch that digit change.

0x

The four bits of the digit you picked, the one at bits 31:2827:2423:2019:1615:1211:87:43:0. Each one is worth what it says, and the digit is whatever they add up to.

bits that are on 8 + 4 + 1 adding up to 13 written as one digit D
the whole address 0xD0000018 the same number in ordinary tens 3,489,660,952
Watch the first digit while you edit any of the other seven. It never moves. That separation is what lets the chip route a store by reading four bits, before the rest of the address means anything at all. Now look at what the ordinary-tens number does while you work: it changes completely and tells you nothing.

That is not a simplification invented for teaching. It is what the hardware does, and the datasheet says so in one line:

Rough address decode is first performed on bits 31:28 of the address:

Bits 31 to 28 are the top four bits, which is the first hex digit. Before anything else happens, the chip reads that digit and hands the whole store to whichever block owns it. The remaining digits are that block's business.

Sixteen values a first hex digit can take, and each one routes a store to some block of the chip. How many of the sixteen name real hardware?

Nothing catches the rest. Nine of the sixteen belong to nobody, and the datasheet is blunt about them: Unmapped address ranges raise a bus error when accessed.

Closer, and still generous. Seven is not half of sixteen, and the seven are not evenly spread either — two of them are next door to each other and one sits on its own at the far end.

Right, and the nine that are left are the interesting part. This space is mostly empty, with a few occupied stripes, which is the opposite of how a laptop's memory feels. A store into one of the nine is a fault rather than a value going quietly nowhere.

The digit means everything. It is the whole routing decision, made on four bits before any of the other twenty-eight are looked at.

Figure 5 One digit decides who answers

Click a first digit. See who claims the address, and who does not.

address 0xD0000018

ROM answers. Built-in code, burned in at the factory. This is where both processors start looking when power arrives.

Flash answers. Your program. The kernel and the applications both live here, and the chip runs code straight out of it.

SRAM answers. The only general read/write memory. Variables, stacks and heaps. Holds whatever was there at power-on until something writes it.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Peripherals answers. The big shared group: clocks, resets, serial ports, timers, and the blocks that configure the pins electrically.

Peripherals answers. A second peripheral group on a faster internal connection, used by the blocks that move a lot of data.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

SIO answers. The block that owns the output state of every pin. This is the one an address that moves a light has to reach.

Processor's own registers answers. Controls belonging to the processor itself rather than to the chip around it.

Nobody answers. Nothing is wired to this range. The store raises a fault rather than quietly vanishing.

Notice how many digits belong to nobody. The datasheet is blunt about those: "Unmapped address ranges raise a bus error when accessed." This space is mostly empty, with a few occupied stripes. That is the opposite of how a laptop's memory feels.

So the D in 0xD0000018 is not decoration. It is the routing decision, and it sends the store to the block named SIO.

SIO stands for single-cycle input/output. It is the peripheral that owns the output state of every pin, which is why an address that changes a light starts with D and not something else.

The RP2350 has 520 kB of SRAM. How does the kernel know how much of it there is to hand out?

Nothing asks. There is no register that reports how much memory is fitted, and no discovery step anywhere in the boot sequence — the number has to be written down by somebody.

Right, and it was wrong for the whole life of this port. The script declared 264 kB, a number inherited unchanged from the RP2040 Pico board where 264 kB was correct. Half the chip's memory was never handed out to anything. Nothing prevented the kernel touching it; the port simply did not know it was there.

The code that describes the chip holds the addresses of peripherals, not the size of memory. Where a board's memory starts and ends is the board's business, and boards differ.

That gives the bottom of the free space, not the top. Something still has to say where the memory stops, and nothing on the chip will tell you.

Figure 6 The whole address space of your board

Click any stripe to see what is really there, and where that fact was checked.

Code etched into the chip at manufacture and unchangeable afterwards. The datasheet puts it plainly: located at address zero, which is the starting point for both Arm processors when the device is reset. Chapter 3 is largely about what this code does before yours runs.

Two things share this space: a 28-byte header declaring the image runnable, and a 256-byte loader that sets up flash so code can run straight out of it. Chapter 3 opens them up.

The kernel itself. Code, so it never changes at runtime. The header in the boot block points here, which is how the chip finds it.

Your compiled applications, one after another. Note that they sit in a different part of flash from the kernel; that separation is where isolation starts.

The general read/write memory: kernel data and every application's memory come out of this one block. Look hard at the size, because for the whole life of this port so far it was wrong. The RP2350 has 520 kB, but Tock's linker script for this board declared 264 kB — a number inherited unchanged from the RP2040 Pico board, where 264 kB was correct. Half the chip's memory was never handed out to anything: nothing prevented the kernel touching it, the port simply did not know it was there. It was corrected to 520 kB on 20 August 2026, three days after the commit this chapter is pinned to. That makes this stripe the one place on the page reporting a tree newer than the pin. A linker script is a claim about the hardware, and claims can be wrong.

Decides how fast the chip runs. Get this wrong early in boot and nothing else works.

Holds other blocks switched off. Most of the chip is dead until something clears its bit here.

Decides what each pin is for. A pin can be plain output, or a serial line, or several other things; this picks which.

The electrical side of each pin: how hard it drives, and whether it is gently pulled high or low when nothing else is driving it.

The crystal oscillator: a real quartz crystal on the board, and the most accurate timing reference the chip has. It is not the only one. Two less accurate oscillators sit inside the chip, and at reset the processors are running from one of those.

Multiplies the crystal's frequency up to the speed the processors run at, as high as 150 MHz.

A second multiplier, fixed at 48 MHz for USB, so USB timing survives retuning the main clock.

The serial port Tock's console uses. Kernel messages left the chip one byte at a time through a register in here.

A second, identical serial port. Same layout, different base — the pattern from Figure 12.

A counter that never stops, ticking once a microsecond. Reading it gives the current time, which has nothing to do with anything you wrote.

Divides a clock down to the slower rates the timers and the watchdog need.

The pin-driving block, and the one this chapter is about. The datasheet calls it Core-local Peripherals and says each processor gets its own connection to it. gpio_out_set sits 0x018 past this base, which is where 0xD0000018 comes from.

Controls belonging to the processor itself rather than to the chip around it. What sits here is the memory protection unit that later chapters use to fence applications in, the processor's own timer, and the controller that decides what it stops for. This is the odd entry in the list. The datasheet notes these registers are “internal to the processors, not accessed through the system bus”, so a store here is not routed by the fabric at all. Each processor sees a different set behind the same address.

Notice the first digit of every address in that list, then compare it with Figure 5. Nothing here is arbitrary: the stripes start where they do because that is the digit the decode reads.

Question 1 is now settled, and it settles a lot. But reading a stripe off a map is not the same as knowing what is behind it.

The map tells you who answers. It says nothing about what answering means, and that turns out to be where people go wrong. First, though, the address has more to say.

1 Which block of hardware? 2 Which control in that block? 3 Which pin, of the thirty?

Question 2 — the distance past the base picks the control

SIO is not a single switch. It is a panel of them, and each switch on that panel is a register with its own address.

Registers are laid out the same way on every peripheral of this chip, and on nearly every chip you will meet. The block gets a base address — SIO's is 0xD0000000 — and each register sits a small fixed distance beyond it. That distance is the register's offset.

So the address we started with splits into two decisions that have already been made.

0xD0000018 D which block SIO drives the pins 018 which control gpio_out_set 24 past the base Not in the address at all: which of the thirty pins to act on. An address can only say where the store lands. Which pin moves is carried by the number you store there, and that is question 3.
Two of the three questions are settled by the address. The third is settled by the value.

A warning about the word "register." It has two unrelated meanings and this chapter needs both.

peripheral register
hardware sitting at an address — what we just described
processor register
one of a handful of tiny scratch slots inside the processor itself, called r0, r1 and so on, holding whatever it is working on this instant

Same word, different things. Where it matters below, the text says which one.

Here is SIO's output section: the offsets and their order exactly as Tock's chip crate declares them, with the address arithmetic worked alongside.

Figure 7 Base plus offset, one register at a time

Click a register. Watch its address get built out of two halves.

  0xD0000000 where SIO starts, the base address + 0x018 how far past it, the offset = 0xD0000018 gpio_out gpio_hi_out gpio_out_set gpio_hi_out_set gpio_out_clr gpio_hi_out_clr gpio_out_xor gpio_hi_out_xor

Four of these eight are for the high half: GPIO 32 to 47, plus the pins that talk to the flash chip and the USB socket. Each one sits directly after its twin. Take them away and watch what the offsets do.

offsetin tensnamewhat a 1 bit doeswhich pins
Offsets are written to a fixed width here, so 0x018 and 0x18 are the same number — a leading zero pads the column and changes nothing. Later figures print it both ways, because the padding is only there to line the column up. The offsets and the order are chips/rp2350/src/gpio.rs, lines 88–108, with none dropped; the last two columns are written for this page rather than quoted. Tock's driver for this chip neither reads nor writes any of the four gpio_hi_ registers.

Two patterns there are arithmetic you can check rather than trivia you have to trust. Neighbours land 4 apart because a register is 4 bytes and addresses count bytes, so every offset is a multiple of 4. Do not expect every step to be 4, though: a block can leave gaps, and this file leaves several further down. Take the twins away and the four that remain are 8 apart, the same reason applied twice.

Those higher GPIO numbers make a point the code cannot. Two packages carry this same silicon, and the datasheet spells out the consequence:

GPIOs 0 through 29 are available in all package variants. GPIOs 30 through 47 are available only in QFN-80 (RP2350B) package.

Change the package. The three facts under the same either way hold for both.

RP2350A — the one on your deskQFN-60 — sixty pins on a square 7 × 7 mm. Thirty GPIOs, numbered 0 to 29, and four analogue inputs. Bits 30 and 31 of gpio_out are real bits in a real register with no pin behind them, and so is every GPIO bit of the four gpio_hi_ registers.

RP2350BQFN-80 — eighty pins on a square 10 × 10 mm. Forty-eight GPIOs, numbered 0 to 47, and eight analogue inputs. Bits 30 and 31 drive pins here, and the four gpio_hi_ registers drive GPIO 32 to 47 — the same registers, finally with something behind them.

the same either way SIO answers at 0xD0000000. gpio_out_set sits 0x018 past it. Tock's crate for this chip defines GPIO0 through GPIO29 and stops, whichever package the silicon came in.

Hold on to that. It comes back in question 3, where it explains something that otherwise looks like a mistake.

1 Which block of hardware? 2 Which control in that block? 3 Which pin, of the thirty?

Question 3 — one bit per pin picks which one

Before any explanation, go and play with this. Drag the slider, watch which square lights and what the code line underneath says, and see whether you can work out the rule before it is stated.

Figure 8 Build the number that moves one pin

Drag to choose a pin. Try 25, then 0, then 30.

25
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
31
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
Written as code 1 << 25
The same number in hex 0x02000000
And in ordinary tens 33554432
The whole operation *0xD0000018 = 0x02000000
Bit 25 drives GPIO 25 on this chip.
Notice that exactly one square is ever lit, and that the lit square moves one place left each time the pin number goes up by one. The top row is bits 31–16 and the bottom row bits 15–0, which is the arrangement datasheets use.

Here is the rule you were looking for. A register is 32 bits wide, and SIO spends those bits one per pin: bit 0 is GPIO 0, bit 25 is GPIO 25. A group of pins driven by one register like this is called a bank.

To act on pin 25 you need a number with bit 25 set and every other bit clear. Written in code, that is 1 << 25: take the number 1, and shift it 25 places to the left. That is the whole reason Tock's source says 1 << self.pin.

So the three questions are answered by three different mechanisms, and now they can sit in one line together.

*0xD0000018 = 0x02000000 one store: an address on the left, a value on the right D 1. the block SIO 018 2. the control gpio_out_set 0x02000000 3. the pin bit 25 is set Result: SIO turns bit 25 on inside its output register, and the pad driver connects GPIO 25 to 3.3 volts. on
The same store, read three ways. Each selection narrows the target: from the whole chip, to one panel, to one pin.

That is the anatomy. What it does not show is the order things happen in, and the question this chapter opened with was a question about order: how does a line of code change a voltage?

So here is the same store again, one moment at a time.

Figure 9 One store, from instruction to light

Drag the handle, or use the arrow keys, to move through the eight moments.

1 of 8
1The processor — Runs one instruction: str r1, [r0, #0x18]. It adds the offset 0x18 to the base sitting in r0, and gets the address 0xD0000018.
2The processor — Puts that address, and the 32-bit value 0x02000000, onto the bus that connects it to the rest of the chip.
3The bus fabric — Looks at the top four bits of the address and sees 0xD. That single digit is the whole routing decision.
4Every other block — Ignores it. Flash, memory, the timers, the serial ports — none of them owns 0xD, so none of them responds. SIO alone answers.
5Inside SIO — The offset 0x018 names gpio_out_set, whose entire job is one operation: OR the arriving value into gpio_out.
6Inside SIO — Bit 25 of gpio_out changes from 0 to 1. The other 31 bits are untouched, because the value carried no other bits.
7The pad — GPIO 25's driver follows that bit and connects the pin to the chip's I/O supply rail, which is 3.3 volts on a Pico 2.
8The world — Current flows out of the pin, through the light, to ground. It glows. Nothing else on the chip noticed.
Every step is on screen at once on purpose: the whole shape of the journey is the thing worth seeing, and the highlight is only where you are in it. Steps 3 and 4 are the part almost every explanation skips, and they are the reason an address can do anything at all.

One honest qualification. That trace assumes the pin was already set up. Two things had to be arranged first.

the wiring
something had to point pin 25 at SIO rather than at one of the other blocks that can drive it. The datasheet is direct: To drive a pin with the SIO's GPIO registers, the GPIO multiplexer for this pin must first be configured to select the SIO GPIO function.
the direction
something had to switch the pin from listening to driving.

Both are stores to other registers, in other blocks, obeying exactly the three questions above — which is why they are not a special case worth learning separately. Tock does them when it starts. So "one instruction changes the pin" is true of changing the pin, and the setup that made it possible is its own story.

Worth doing once by hand, because the arithmetic is the idea. Below is the calculation fully worked for pin 25, then the same calculation with pieces missing.

Figure 10 Work one out yourself

Work down the three cards. They get emptier as you go.

Worked for you — pin 25
base 0xD0000000 + offset 0x018 = 0xD0000018
value = 1 << 25 = 0x02000000

Bit 25 is the twenty-sixth bit, counting from bit 0 at the right. Shifting 1 left by 25 places lands it there and leaves everything else at zero.

Half done — pin 3, same register
base 0xD0000000 + offset 0x018 =
value = 1 << 3 =

The address does not depend on which pin you want. Only the value does.

All yours — turn pin 14 off
address =
value =

Turning a pin off is a different register, not a different value. Figure 7 has it.

Answers check themselves as you type, and hex is accepted with or without the 0x. Nothing is scored and nothing is recorded.

Now the payoff from question 2. Drag Figure 8 up to pin 30 or 31 and the square goes dashed, because Tock's crate for this chip defines GPIO0 through GPIO29 and stops.

Those two bits are perfectly real. The store would succeed and drive nothing, because this package brings out no such pin. On the 80-pin part they drive GPIO 30 and GPIO 31 exactly as you would expect.

So the lesson is sharper than "the register has spare bits."

How wide a register is and how many pins exist are two separate facts. Only one of them is visible from the code.

What is pin 25 actually wired to?

Everything above is a fact about the chip. What a pin is wired to is a fact about the board, and those are different documents written by different people.

On a plain Pico 2, the board's datasheet lists GPIO 25 as "Connected to user LED", which is why the store we have been building lights something. On the wireless boards it is wired to the radio instead. Raspberry Pi's own header for the Pico 2 W says so:

#define CYW43_DEFAULT_PIN_WL_REG_ON   23u
#define CYW43_DEFAULT_PIN_WL_DATA_OUT 24u
#define CYW43_DEFAULT_PIN_WL_CLOCK    29u
#define CYW43_DEFAULT_PIN_WL_CS       25u
#define CYW43_WL_GPIO_LED_PIN          0

pico-sdk, src/boards/include/boards/pico2_w.h — five lines picked out of that file and grouped together. The values are exact; the order and the surrounding #ifndef guards are not.

So on a Pico 2 W the store still works perfectly. The voltage on that pin still changes. It just moves the radio's chip-select line rather than a light, and the onboard light is pin 0 of the radio — which the chip can only reach by talking to the radio first.

If you want to see this with your own eyes on a W: pick any GPIO from 2–22 or 26–28. The board takes 23, 24, 25 and 29, and Tock itself takes 0 and 1 for the console, so driving either of those would cut off the kernel's own output. Put a light and a resistor between it and a ground pin. The resistor is what sets the current, and 330 ohms is the conventional choice. That lands right about at the pin's 4 mA default drive strength, and anything up to a kilohm still lights an LED. Then everything in this chapter applies unchanged, with your pin number in place of 25.

This is the first real lesson about layering, and it is worth more than the LED. The chip says "pin 25". The board says what pin 25 reaches. Confusing the two is how people lose an afternoon to a light that was never there.

Check yourself

Reading this again will not make it stick. Neither will highlighting it — that is one of the few study habits the research is actually rude about.

What does work is producing the answer before you look at one. Three questions, three choices each: commit to one and the reasoning opens underneath it. Getting one wrong is useful information, not a problem — it is the whole reason to answer before reading.

1. An address begins with 0x4. What do you already know about it?

Right. Not quite. That it belongs to the peripherals on the APB side of the chip — the serial ports, the timers, the pin-configuration blocks. The first hex digit is the whole routing decision, so it is the only part of an address you can interpret without looking anything up.

2. Why is a register listing written as offsets rather than finished addresses?

Right. Not quite. Because the offsets are the part that is shared. UART0 and UART1 are the same hardware built twice, so 0x000 is the data register on both; only the base differs. Writing offsets lets one driver serve both, and it matches the processor's addressing mode, which takes a base in a register plus a small fixed offset.

3. A 32-bit register drives a bank of pins. How many pins does the chip have?

Right. Not quite. Unknown from that fact alone — and this is the trap. The part on a Pico 2 has 30, so bits 30 and 31 drive nothing. The 80-pin version of the same silicon has 48, and those bits drive real pins there. Register width and pin count are two independent facts.

Every word, collected

address
A number naming one location. The processor puts it on wires and the chip decides who answers.
bank
A group of pins driven together by one register, one bit each.
base address
The address a peripheral's block of registers starts at. SIO's is 0xD0000000.
bit
A single 0 or 1.
bus
the wiring inside the chip that carries an address to whichever block claims it, and raises an error when none does
byte
Eight bits. The unit addresses count in, which is why 32-bit registers sit 4 apart.
crate
A Rust library, compiled as one unit. Tock's chip support, its kernel and its drivers are separate ones.
flash
Memory that keeps what it holds when the power is off. Your program lives here.
instruction
One step the processor can perform, encoded as a number. This chapter follows exactly one of them.
kernel
The program that owns the machine and decides what everything else is allowed to do. Tock is one.
processor
The part of the chip that fetches instructions and carries them out. This chip has two.
GPIO
General-purpose input/output: a pin with no fixed job, so you decide what it means.
hexadecimal
Counting in sixteens, flagged by 0x. One hex digit is exactly four bits.
offset
How far past a base address one register sits.
peripheral
Dedicated hardware beside the processor with one job, operated by storing numbers at its addresses.
pin
One numbered connection between the chip and the outside world.
register
A fixed-width slot of hardware at an address. Also, confusingly, a scratch slot inside the processor.
SIO
Single-cycle input/output, at 0xD0000000. The peripheral owning the output state of every pin.
SRAM
the chip's general read/write memory: the one region where an ordinary variable can live
store
The instruction that puts a number at an address.
voltage
Electrical pressure. Here it is 3.3 V or 0 V, and nothing in between.

Everything above, checked against source

Every claim on this page comes from one of two documents: the Tock tree at commit 83bad9388, and the RP2350 and Pico 2 datasheets. Nothing here was written from memory. One number has moved in the tree since that commit — the Pico 2's declared RAM size — and Figure 6 reports the correction rather than the pin.

  • Address decode on bits 31:28, the segment table, and unmapped ranges raising a bus error — RP2350 datasheet §2.2, Table 8
  • SIO base 0xD0000000, IO_BANK0 0x40028000, PADS 0x40038000chips/rp2350/src/gpio.rs:1164–1169
  • The eight output registers at offsets 0x0100x02C, including the gpio_hi_ siblings — chips/rp2350/src/gpio.rs:88–108, "Output value for GPIO32...47, QSPI IOs and USB pins"
  • Pins defined as GPIO0GPIO29chips/rp2350/src/gpio.rs:1245–1250
  • 30 GPIOs on QFN-60 (RP2350A) versus 48 on QFN-80 (RP2350B), their four and eight analogue inputs, the 7 × 7 mm and 10 × 10 mm packages, and GPIOs 30–47 being QFN-80 only — RP2350 datasheet Table 1, the sentence introducing it, and the note under the pin-function table
  • A pin's high level being the IOVDD rail, and all GPIOs sharing it — RP2350 datasheet §6.1.1. That rail is 3.3 V on a Pico 2: "3V3 is the main 3.3 V supply to RP2350 and its I/O" — Pico 2 datasheet
  • Default drive strength of 4 mA — RP2350 datasheet, PADS_BANK0: GPIOx, field DRIVE, reset 0x1
  • GPIO 25 as the user LED, and 23, 24, 25 and 29 being used by the board — Pico 2 datasheet, "A few RP2350 GPIO pins are used for internal board functions"
  • GPIO 25 as the radio chip-select on wireless boards, and the LED being pin 0 of the radio — Pico 2 W datasheet §3.1: "GPIO25 — OP wireless SPI CS", "GPIO23 — OP wireless power on signal", "GPIO24 — OP/IP wireless SPI data/IRQ", "GPIO29 — OP/IP wireless SPI CLK/ADC mode (ADC3) to measure VSYS/3", and "WL_GPIO0 — OP connected to user LED". Raspberry Pi's pico-sdk/src/boards/include/boards/pico2_w.h agrees. Tock has no Pico 2 W board yet, so no claim here rests on the kernel tree
  • Figure 2's forty holes, and the seven the datasheet groups apart from the GPIO and ground pins (VBUS 40, VSYS 39, 3V3_EN 37, 3V3 36, ADC_VREF 35, AGND 33, RUN 30) — Pico 2 datasheet §3.1 and its pinout figure. The figure counts AGND with the grounds instead, which is what it is electrically, so it shows eight of those and five power pins. The Pico 2 W datasheet lists the same seven at the same numbers, which is why one drawing serves both
  • The board being 51 × 21 × 1 mm with its forty pins on a 2.54 mm pitch, which is what makes Figure 2 drawable to scale — Pico 2 datasheet §3: "a single sided 51×21 mm 1 mm thick PCB", "the 40 main user pins on a 2.54 mm (0.1″) pitch grid"
  • Flash, kernel, application and RAM origins — boards/raspberry_pi_pico_2/layout.ld. Its RAM length read 264K at the pinned commit; it was corrected to 520K by PR 5086, merged 20 August 2026
  • Boot block contents: a 28-byte METADATA_BLOCK and a 256-byte FLASH_BOOTLOADERboards/raspberry_pi_pico_2/src/flash_bootloader.rs

The datasheets are linked from Raspberry Pi's silicon documentation page. They are licensed no-derivatives, so this page quotes them briefly with attribution rather than reproducing them.

One earlier draft of this chapter said SIO was reached by a route separate from the peripheral fabric. That was wrong. The datasheet says it is accessed via the AHB Fabric, with a dedicated bus interface per core. The mistake came from recalling the RP2040, where SIO does sit on a single-cycle port off the processor. Different chip, different answer, and it is recorded here rather than quietly fixed.

Text, diagrams and interactive figures © Jon Hillesheim 2026, licensed CC BY-SA 4.0 — share and adapt freely with credit, under the same license. Tock source excerpts quoted above remain under their own Apache-2.0 OR MIT license and are not relicensed here.

Every line reference below links to that commit on the fork it was read from, at the lines it names.