Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 0 additions & 30 deletions src/runtime/runtime_rp2.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,36 +298,6 @@ var (
futexLock = spinLock{id: 23}
)

func resetSpinLocks() {
for i := uint8(0); i < numSpinlocks; i++ {
l := &spinLock{id: i}
l.spinlock().Set(0)
}
}

// A hardware spinlock, one of the 32 spinlocks defined in the SIO peripheral.
type spinLock struct {
id uint8
}

// Return the spinlock register: rp.SIO.SPINLOCKx
func (l *spinLock) spinlock() *volatile.Register32 {
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&rp.SIO.SPINLOCK0), l.id*4))
}

func (l *spinLock) Lock() {
// Wait for the lock to be available.
spinlock := l.spinlock()
for spinlock.Get() == 0 {
arm.Asm("wfe")
}
}

func (l *spinLock) Unlock() {
l.spinlock().Set(0)
arm.Asm("sev")
}

// Wait until a signal is received, indicating that it can resume from the
// spinloop.
func spinLoopWait() {
Expand Down
33 changes: 33 additions & 0 deletions src/runtime/runtime_rp2040.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,43 @@
package runtime

import (
"device/arm"
"device/rp"
"runtime/volatile"
"unsafe"
)

const (
sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_PROC0
sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_PROC1
)

func resetSpinLocks() {
for i := uint8(0); i < numSpinlocks; i++ {
l := &spinLock{id: i}
l.spinlock().Set(0)
}
}

// A hardware spinlock, one of the 32 spinlocks defined in the SIO peripheral.
type spinLock struct {
id uint8
}

// Return the spinlock register: rp.SIO.SPINLOCKx
func (l *spinLock) spinlock() *volatile.Register32 {
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&rp.SIO.SPINLOCK0), l.id*4))
}

func (l *spinLock) Lock() {
// Wait for the lock to be available.
spinlock := l.spinlock()
for spinlock.Get() == 0 {
arm.Asm("wfe")
}
}

func (l *spinLock) Unlock() {
l.spinlock().Set(0)
arm.Asm("sev")
}
56 changes: 56 additions & 0 deletions src/runtime/runtime_rp2350.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,40 @@

package runtime

// #include <stdint.h>
//
// static void spinlock_lock(void *lock) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was wrong with the sync/atomic operations? Custom assembly for just one target (rp2350) seems to me like a maintenance headache for little gain.

Copy link
Contributor Author

@mikesmitty mikesmitty Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget, but I'll take a second look at them. One reason was the size of them, but I think there was another reason also. In hindsight, the size difference is fairly small all things considered

// uint32_t _tmp0, _tmp1;
// __asm volatile ( \
// "1:\n" \
// "ldaexb %1, [%2]\n" \
// "movs %0, #1\n" /* fill dependency slot */ \
// "cmp %1, #0\n" \
// /* Immediately retry if lock is seen to be taken */ \
// "bne 1b\n" \
// /* Attempt to claim */ \
// "strexb %1, %0, [%2]\n" \
// "cmp %1, #0\n" \
// /* Claim failed due to intervening write, so retry */ \
// "bne 1b\n" \
// : "=&r" (_tmp0), "=&r" (_tmp1) : "r" (lock) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized this is incomplete. You also need to clobber flags and memory at least.

// ); \
// __asm volatile ("dmb" : : : "memory");
// }
//
// static void spinlock_unlock(void *lock) {
// /* Release-ordered store is available: use instead of separate fence */ \
// uint32_t zero = 0; \
// __asm volatile ( \
// "stlb %0, [%1]\n" \
// : : "r" (zero), "r" (lock) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. It modifies memory, though I don't think it updates any flags.

// ); \
// }
import "C"

import (
"device/rp"
"unsafe"
)

const (
Expand All @@ -14,3 +46,27 @@ const (
sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_FIFO
sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_FIFO
)

// Software spinlocks don't persist across soft resets so this is a no-op.
func resetSpinLocks() {}

type spinLock struct {
state uint8
id uint8
_ [2]uint8
}

func (l *spinLock) Lock() {
// Try to replace 0 with 1. Once we succeed, the lock has been acquired.
C.spinlock_lock(unsafe.Pointer(&l.state))
}

func (l *spinLock) Unlock() {
// Safety check: the spinlock should have been locked.
if schedulerAsserts && l.state != 1 {
runtimePanic("unlock of unlocked spinlock")
}

// Unlock the lock. Simply write 0, because we already know it is locked.
C.spinlock_unlock(unsafe.Pointer(&l.state))
}
Loading