Skip to content

Commit d1eca84

Browse files
committed
fix(rp2350): add software spinlocks
1 parent 109e076 commit d1eca84

File tree

3 files changed

+91
-30
lines changed

3 files changed

+91
-30
lines changed

src/runtime/runtime_rp2.go

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -298,36 +298,6 @@ var (
298298
futexLock = spinLock{id: 23}
299299
)
300300

301-
func resetSpinLocks() {
302-
for i := uint8(0); i < numSpinlocks; i++ {
303-
l := &spinLock{id: i}
304-
l.spinlock().Set(0)
305-
}
306-
}
307-
308-
// A hardware spinlock, one of the 32 spinlocks defined in the SIO peripheral.
309-
type spinLock struct {
310-
id uint8
311-
}
312-
313-
// Return the spinlock register: rp.SIO.SPINLOCKx
314-
func (l *spinLock) spinlock() *volatile.Register32 {
315-
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&rp.SIO.SPINLOCK0), l.id*4))
316-
}
317-
318-
func (l *spinLock) Lock() {
319-
// Wait for the lock to be available.
320-
spinlock := l.spinlock()
321-
for spinlock.Get() == 0 {
322-
arm.Asm("wfe")
323-
}
324-
}
325-
326-
func (l *spinLock) Unlock() {
327-
l.spinlock().Set(0)
328-
arm.Asm("sev")
329-
}
330-
331301
// Wait until a signal is received, indicating that it can resume from the
332302
// spinloop.
333303
func spinLoopWait() {

src/runtime/runtime_rp2040.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,43 @@
33
package runtime
44

55
import (
6+
"device/arm"
67
"device/rp"
8+
"runtime/volatile"
9+
"unsafe"
710
)
811

912
const (
1013
sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_PROC0
1114
sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_PROC1
1215
)
16+
17+
func resetSpinLocks() {
18+
for i := uint8(0); i < numSpinlocks; i++ {
19+
l := &spinLock{id: i}
20+
l.spinlock().Set(0)
21+
}
22+
}
23+
24+
// A hardware spinlock, one of the 32 spinlocks defined in the SIO peripheral.
25+
type spinLock struct {
26+
id uint8
27+
}
28+
29+
// Return the spinlock register: rp.SIO.SPINLOCKx
30+
func (l *spinLock) spinlock() *volatile.Register32 {
31+
return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&rp.SIO.SPINLOCK0), l.id*4))
32+
}
33+
34+
func (l *spinLock) Lock() {
35+
// Wait for the lock to be available.
36+
spinlock := l.spinlock()
37+
for spinlock.Get() == 0 {
38+
arm.Asm("wfe")
39+
}
40+
}
41+
42+
func (l *spinLock) Unlock() {
43+
l.spinlock().Set(0)
44+
arm.Asm("sev")
45+
}

src/runtime/runtime_rp2350.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package runtime
44

55
import (
6+
"device/arm"
67
"device/rp"
78
)
89

@@ -14,3 +15,60 @@ const (
1415
sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_FIFO
1516
sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_FIFO
1617
)
18+
19+
// Due to hardware errata RP2350-E2 spinlocks are emulated in software as in pico-sdk.
20+
type spinLock struct {
21+
// lock field must be first field so its address is the same as the struct address.
22+
lock uint8
23+
id uint8
24+
uint16 // Padding to prevent false sharing
25+
}
26+
27+
func (l *spinLock) Lock() {
28+
// Original reference:
29+
// https://github.com/raspberrypi/pico-sdk/blob/2.2.0/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h#L112
30+
31+
// r0 is automatically filled with the pointer value "l" here.
32+
// We create a variable to allow access the lock byte and avoid a memory
33+
// fault when accessing l.lock in assembly.
34+
lock := &l.lock
35+
_ = lock
36+
37+
// Set a loop start point
38+
arm.Asm("1:")
39+
// Exclusively load the state variable (l.lock) and put its value in r2.
40+
arm.Asm("ldaexb r2, [r0]")
41+
// Store the "locked" state value (1) into r1 to keep things moving.
42+
arm.Asm("movs r1, #1")
43+
// Check if the lock was already taken (r2 != 0).
44+
arm.Asm("cmp r2, #0")
45+
// Jump back to "1:" if the lock is already held
46+
arm.Asm("bne 1b")
47+
48+
// Attempt to store '1' into the lock address.
49+
// The return code (0 for success, 1 for failure) is placed in r2.
50+
arm.Asm("strexb r2, r1, [r0]")
51+
// Check if the result was successful (r2 == 0).
52+
arm.Asm("cmp r2, #0")
53+
// Jump back to "1:" if the lock was not acquired.
54+
arm.Asm("bne 1b")
55+
56+
// Memory barrier to ensure everyone knows we're holding the lock now.
57+
arm.Asm("dmb")
58+
}
59+
60+
func (l *spinLock) Unlock() {
61+
// Original reference:
62+
// https://github.com/raspberrypi/pico-sdk/blob/2.2.0/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h#L197
63+
64+
// r0 is automatically filled with the pointer value l here.
65+
// We create a variable to allow access the lock byte and avoid a memory
66+
// fault when accessing l.lock in assembly.
67+
lock := &l.lock
68+
_ = lock
69+
// Fill r1 with 0 and store it to the lock address in r0. stlb requires a
70+
// register as a source so we can't use a literal 0 directly.
71+
arm.Asm("movs r1, #0")
72+
// Release the pseudo-spinlock by writing 0 to and releasing l.lock.
73+
arm.Asm("stlb r1, [r0]")
74+
}

0 commit comments

Comments
 (0)