Every cycle
matters.
ticktrace is a complete bare-metal SDK for the Raspberry Pi RP2350, written entirely in ARM Thumb-2 assembly. No C compiler. No HAL. Just arm-none-eabi-as + arm-none-eabi-ldand a 1.2 KB blinky.
@ blink GP25 forever; the entire program.
.syntax unified
.cpu cortex-m33
.thumb
.equ GPIO25_OUT_XOR, 0xD0000028
.equ DELAY, 12500000
.global main
main:
bl xosc_init
bl pll_sys_150_mhz
bl clocks_init
bl gpio_init_led
1: ldr r0, =GPIO25_OUT_XOR
movs r1, #(1 << 25)
str r1, [r0] @ 2-cycle atomic toggle
ldr r2, =DELAY
2: subs r2, #1
bne 2b
b 1b
// principles
A whole SDK that fits in your head.
The firmware is arm-none-eabi-as + arm-none-eabi-ld. Python is host-side only: a UF2 packer and the test harness. Every byte of the binary corresponds to a line you can read.
Every peripheral write uses the +0x2000 SET / +0x3000 CLR / +0x1000 XOR aliases. One STR, two cycles, no scratch, no ISR race. SIO_GPIO_OUT_XOR turns an LED toggle into a single 2-cycle store.
T1 Unicorn (driver register sequences), T2 QEMU (Cortex-M33 ISA), T3 Renode (peripheral interaction), T4 real Pico 2 (ground truth). 278 unit tests green; 27 drivers verified on silicon.
All drivers are plain Thumb-2 functions that follow AAPCS so they compose. Call them from asm, from C via the optional bridge, or from no_std Rust via the rp-asm-sys crate.
SIO FIFO handshake brings up core 1. A QV-style scheduler with 5-cycle task_post and 0 stack-per-task. CoreSight DWT / ITM / TPIU / ETM wired up for on-chip cycle counting and printf.
A Go sibling with a TOML catalog, a rpasm CLI, and a Gio GUI. Picotool flashing, BOOTSEL detection, examples filter, project save/load; and it's byte-identical to make.
// install
From clone to blink in 60 seconds.
You need a Linux box, binutils-arm-none-eabi, and Python 3. The Makefile takes care of the rest, including a virtualenv for Unicorn-based tests.
# 1. Toolchain (Debian/Ubuntu)
sudo apt install binutils-arm-none-eabi python3 python3-venv
# 2. Clone & build
git clone https://github.com/ticktrace-sdk/rp-asm.git
cd ticktrace
make pydeps # one-shot: create .venv + install deps
make # build/blinky.uf2 + every examples/*.S
make test # T1 (Unicorn) + T2 (QEMU)
# 3. Flash
# - Hold BOOTSEL on the Pico 2 while plugging in USB
# - Drag build/blinky_flash.uf2 onto the RPI-RP2 mass-storage volume
# - Open a serial terminal: 115200 8N1 on UART0 TX (GP0 / pin 1)
// what's inside
Every RP2350 peripheral, driven.
// where to start
A 14-chapter walk from "what is a register" to multicore. No assembly experience required, only that you've written a for-loop.
One concise page per peripheral. Register-level recipes, the gotchas we hit on silicon, and links to a working demo for each.
Each example is self-contained and builds to its own <4 KB UF2. Read them like patterns; every demo is annotated.
“There is a particular pleasure in seeing a 728-byte binary bring up a clock tree, configure a UART, and start blinking an LED — and understanding every byte.”