ticktrace
v0.1 · RP2350 · Cortex-M33

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.

278
T1 tests
27
Drivers verified
49
Examples
blinky.Sarm-none-eabi-as · cortex-m33
@ 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.

Pure assembly

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.

Atomic register aliases

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.

Four-tier test pyramid

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.

AAPCS everywhere

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.

Dual-core, scheduler, trace

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.

ticktrace Studio

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.

getting startedbash
# 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.

xosc + PLL
150 MHz boot
CLOCKS
tree + tick
GPIO + PADS
48 pins
TIMER0/1
1 MHz alarms
SysTick
proc clock
NVIC
install + enable
DMA
16 channels
PWM
12 slices
UART (PL011)
IRQ + DMA
I²C
master + slave
SPI (PL022)
full duplex
USB device
CDC-ACM
SHA-256
hardware
ADC
8 ch + temp
TRNG
CryptoCell EHR
PIO
hand-encoded
multicore
SIO FIFO
spinlocks
32 hardware
interpolators
INTERP0/1
QMI / XIP
clkdiv tune
OTP read
CHIPID + RAND
bootrom
1200-baud BOOTSEL
watchdog
enable + feed
DWT / ITM / ETM
cycle trace

// where to start

“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.”
from the introduction