Graeme Winter

Accessing hardware registers on SAMD51

Some interesting hardware in this one, not least a hardware TRNG - which could be useful for something, I guess. Accessing involves probing a “ready” status bit before reading the data value. Doing this in Python would be insanely slow, so assembly. In this case, writing the data to the SD card but that turns out to be punishingly slow by comparison.

Code

from machine import Pin, SPI, mem32
from uctypes import addressof
import sdcard
import os

sd = sdcard.SDCard(
    SPI(2, mosi=Pin("SD_MOSI"), miso=Pin("SD_MISO"), sck=Pin("SD_SCK")), Pin("SD_CS")
)
os.mount(sd, "/sd")

# set up the RNG
MCLK_BASE = 0x40000800
APBCMASK = MCLK_BASE | 0x1C

mem32[APBCMASK] = mem32[APBCMASK] | 1 << 10

# TRNG -> BASE = 0x42002800
# TRNG enable -> BASE -> bit 1 needs to be toggled

TRNG_BASE = 0x42002800
mem32[0x42002800] = mem32[0x42002800] | 1 << 1


# r0: pointer to data buffer
# r1: count of how many random numbers to collect
#
# internally:
# r2: points at TRNG_BASE address
# r3: scratch register
#
# be wary of PC - this is a pipelined system
@micropython.asm_thumb
def rng(r0, r1):
    align(4)
    mov(r2, pc)
    b(start)
    data(4, 0x42002800)
    align(2)
    label(start)
    ldr(r2, [r2, 0])
    label(tick)
    label(wait)
    ldrb(r3, [r2, 0x0A])
    cmp(r3, 1)
    bne(wait)
    ldr(r3, [r2, 0x20])
    str(r3, [r0, 0])
    add(r0, r0, 4)
    sub(r1, r1, 1)
    cmp(r1, 0)
    bgt(tick)


# storage for 1k uint32_t - assume that this will always be 4-byte aligned?
data = bytearray(1024 * 4)
addr = addressof(data)

with open("/sd/random.dat", "wb") as f:
    for j in range(1024):
        rng(addr, 1024)
        f.write(data, 1024 * 4)

os.umount("/sd")

1<<20 random 32 bit ints took around a second to compute with the basic formalism, but writing to disk took more than 30s. That could well be partly my cheap SD card.

On the whole, a nice little piece of code & good to work out a neat way to do the busy-wait loop for the ready flag.