Graeme Winter

Simple register toggling

One of the things I really liked in the rp2040 is almost every (or actually every?) register was aliased to another copy which could be toggled, rather than going through a read / modify / write cycle. Reading through the SAMD51 data sheet just now in the PORT controller chapter, found a section on toggle registers for the output ports -> win.

LED on the board is on pin D13, which is port B (offset 0x80) - poking 0x2 at OUTTGL (+0x1c) will toggle an already configured pin, so:

from machine import Pin
import time
led = Pin("D13")

@micropython.asm_thumb
def fast_blink(r0):
    mov(r1, 2)
    # move a big value into r2, say 1<<20
    data(2, 0b11110_1_0_0010_0_1111, 0b0_001_0010_10000000)
    label(start)
    str(r1, [r0, 0])
    sub(r2, r2, 1)
    cmp(r2, 0)
    bgt(start)

def nop(arg):
    pass

BASE=0x41008000+0x80+0x1C
t0 = time.ticks_us()
nop(BASE)
t1 = time.ticks_us()
t_nop = t1 - t0
t0 = time.ticks_us()
fast_blink(BASE)
t1 = time.ticks_us()
t_blink = t1 - t0

print(f"Extra time: {t_blink - t_nop:d}µs")

Set up the D13 pin using the usual µPython then 0x100000 (1048576) times toggle the LED on and off. This turns out to be surprisingly quick - executing it suggests an overhead of ~ 52500µs, which makes for around 0.05µs / sample. This time looks like about 6 clock ticks… which is about right? str is two ticks, sub, cmp one and then bgt takes two ticks if taken, one otherwise.

500,000 blinks in 50ms - it’ll do, fast-wise.