From df199ccdfffc8c36031882ba5afe609619b9fd57 Mon Sep 17 00:00:00 2001 From: Jean-Marie Verdun Date: Wed, 8 Apr 2026 10:57:10 -0700 Subject: [PATCH 1/5] Fix runtime on rp2350/2040 for multicore scheduler - Add interrupt handler core detection to properly call the garbage collector - Switch to software spinlock as to avoid hardware bugs on rp2350 - reset second core before running it (in some cases it stay stuck) Signed-off-by: Jean-Marie Verdun --- src/runtime/runtime_rp2.go | 105 ++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/src/runtime/runtime_rp2.go b/src/runtime/runtime_rp2.go index 1cd23d6dcb..01b69d379f 100644 --- a/src/runtime/runtime_rp2.go +++ b/src/runtime/runtime_rp2.go @@ -1,5 +1,6 @@ //go:build rp2040 || rp2350 - +// Used to track contribution from various authors automatically +// SPDX-FileCopyrightText: Copyright Hewlett Packard Enterprise Development LP package runtime import ( @@ -10,6 +11,7 @@ import ( _ "machine/usb/cdc" "runtime/interrupt" "runtime/volatile" + "sync/atomic" "unsafe" ) @@ -116,6 +118,19 @@ func currentCPU() uint32 { // Start the secondary cores for this chip. // On the RP2040/RP2350, there is only one other core to start. func startSecondaryCores() { + + // Reset Core1 + arm.DisableIRQ(uint32(sioIrqFifoProc0)) + rp.PSM.SetFRCE_OFF_PROC1(1) + // Now we need to restart the core + for rp.PSM.GetFRCE_OFF_PROC1() == 0 { + // Sleep a little bit + sleepTicks(100) + } + rp.PSM.SetFRCE_OFF_PROC1(0) + multicore_fifo_pop_blocking() + sleepTicks(1000) + // Start the second core of the RP2040/RP2350. // See sections 2.8.2 and 5.3 in the datasheets for RP2040 and RP2350 respectively. seq := 0 @@ -141,11 +156,18 @@ func startSecondaryCores() { // We can only do this after we don't need the FIFO anymore for starting the // second core. intr := interrupt.New(sioIrqFifoProc0, func(intr interrupt.Interrupt) { - switch rp.SIO.FIFO_RD.Get() { - case 1: - gcInterruptHandler(0) - } - }) + status := rp.SIO.GetFIFO_ST_VLD() + if status != 0 { + switch rp.SIO.FIFO_RD.Get() { + case 1: + if currentCPU() == 1 { + gcInterruptHandler(1) + } else { + gcInterruptHandler(0) + } + } + } + }) intr.Enable() intr.SetPriority(0xff) } @@ -169,17 +191,28 @@ var stack1TopSymbol [0]uint32 func runCore1() { // Clear sticky bit that seems to have been set while starting this core. rp.SIO.FIFO_ST.Set(rp.SIO_FIFO_ST_ROE) + rp.SIO.FIFO_ST.Set(rp.SIO_FIFO_ST_WOF) // Enable the FIFO interrupt, mainly used for the stop-the-world phase of // the GC. // Use the lowest possible priority (highest priority value), so that other // interrupts can still happen while the GC is running. - intr := interrupt.New(sioIrqFifoProc1, func(intr interrupt.Interrupt) { - switch rp.SIO.FIFO_RD.Get() { - case 1: - gcInterruptHandler(1) - } - }) + intr := interrupt.New(sioIrqFifoProc1, func(intr interrupt.Interrupt) { + status := rp.SIO.GetFIFO_ST_VLD() + // ok we have just one interrupt vector ... sor + // we shall be acting based on CPU number calling in + + if status != 0 { + switch rp.SIO.FIFO_RD.Get() { + case 1: + if currentCPU() == 1 { + gcInterruptHandler(1) + } else { + gcInterruptHandler(0) + } + } + } + }) intr.Enable() intr.SetPriority(0xff) @@ -291,41 +324,39 @@ func coreStackTop(core uint32) uintptr { } // These spinlocks are needed by the runtime. -var ( - printLock = spinLock{id: 20} - schedulerLock = spinLock{id: 21} - atomicsLock = spinLock{id: 22} - 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 + atomic.Uint32 } -// 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)) +var ( + schedulerLock spinLock + futexLock spinLock + atomicsLock spinLock + printLock spinLock +) + +func resetSpinLocks() { + schedulerLock.Store(0) + futexLock.Store(0) + atomicsLock.Store(0) + printLock.Store(0) } func (l *spinLock) Lock() { // Wait for the lock to be available. - spinlock := l.spinlock() - for spinlock.Get() == 0 { - arm.Asm("wfe") - } + + for !l.Uint32.CompareAndSwap(0, 1) { + spinLoopWait() + } + } func (l *spinLock) Unlock() { - l.spinlock().Set(0) - arm.Asm("sev") + if schedulerAsserts && l.Uint32.Load() != 1 { + runtimePanic("unlock of unlocked spinlock") + } + l.Uint32.Store(0) } // Wait until a signal is received, indicating that it can resume from the @@ -361,6 +392,8 @@ func init() { machineInit() machine.InitSerial() + + rp.PPB.SetACTLR_EXTEXCLALL(1) } func prerun() { From 293bb3f1d7826f415bc7493cb2c4229ea12025e3 Mon Sep 17 00:00:00 2001 From: Jean-Marie Verdun Date: Wed, 8 Apr 2026 11:31:53 -0700 Subject: [PATCH 2/5] Format the file to pass validation test Signed-off-by: Jean-Marie Verdun --- src/runtime/runtime_rp2.go | 102 +++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/src/runtime/runtime_rp2.go b/src/runtime/runtime_rp2.go index 01b69d379f..aa390ed819 100644 --- a/src/runtime/runtime_rp2.go +++ b/src/runtime/runtime_rp2.go @@ -1,6 +1,8 @@ //go:build rp2040 || rp2350 + // Used to track contribution from various authors automatically // SPDX-FileCopyrightText: Copyright Hewlett Packard Enterprise Development LP +// package runtime import ( @@ -121,15 +123,15 @@ func startSecondaryCores() { // Reset Core1 arm.DisableIRQ(uint32(sioIrqFifoProc0)) - rp.PSM.SetFRCE_OFF_PROC1(1) - // Now we need to restart the core - for rp.PSM.GetFRCE_OFF_PROC1() == 0 { - // Sleep a little bit - sleepTicks(100) - } - rp.PSM.SetFRCE_OFF_PROC1(0) + rp.PSM.SetFRCE_OFF_PROC1(1) + // Now we need to restart the core + for rp.PSM.GetFRCE_OFF_PROC1() == 0 { + // Sleep a little bit + sleepTicks(100) + } + rp.PSM.SetFRCE_OFF_PROC1(0) multicore_fifo_pop_blocking() - sleepTicks(1000) + sleepTicks(1000) // Start the second core of the RP2040/RP2350. // See sections 2.8.2 and 5.3 in the datasheets for RP2040 and RP2350 respectively. @@ -156,18 +158,18 @@ func startSecondaryCores() { // We can only do this after we don't need the FIFO anymore for starting the // second core. intr := interrupt.New(sioIrqFifoProc0, func(intr interrupt.Interrupt) { - status := rp.SIO.GetFIFO_ST_VLD() - if status != 0 { - switch rp.SIO.FIFO_RD.Get() { - case 1: - if currentCPU() == 1 { - gcInterruptHandler(1) - } else { - gcInterruptHandler(0) - } - } - } - }) + status := rp.SIO.GetFIFO_ST_VLD() + if status != 0 { + switch rp.SIO.FIFO_RD.Get() { + case 1: + if currentCPU() == 1 { + gcInterruptHandler(1) + } else { + gcInterruptHandler(0) + } + } + } + }) intr.Enable() intr.SetPriority(0xff) } @@ -197,22 +199,22 @@ func runCore1() { // the GC. // Use the lowest possible priority (highest priority value), so that other // interrupts can still happen while the GC is running. - intr := interrupt.New(sioIrqFifoProc1, func(intr interrupt.Interrupt) { - status := rp.SIO.GetFIFO_ST_VLD() - // ok we have just one interrupt vector ... sor - // we shall be acting based on CPU number calling in - - if status != 0 { - switch rp.SIO.FIFO_RD.Get() { - case 1: - if currentCPU() == 1 { - gcInterruptHandler(1) - } else { - gcInterruptHandler(0) - } - } - } - }) + intr := interrupt.New(sioIrqFifoProc1, func(intr interrupt.Interrupt) { + status := rp.SIO.GetFIFO_ST_VLD() + // ok we have just one interrupt vector ... sor + // we shall be acting based on CPU number calling in + + if status != 0 { + switch rp.SIO.FIFO_RD.Get() { + case 1: + if currentCPU() == 1 { + gcInterruptHandler(1) + } else { + gcInterruptHandler(0) + } + } + } + }) intr.Enable() intr.SetPriority(0xff) @@ -326,37 +328,37 @@ func coreStackTop(core uint32) uintptr { // These spinlocks are needed by the runtime. type spinLock struct { - atomic.Uint32 + atomic.Uint32 } var ( - schedulerLock spinLock - futexLock spinLock - atomicsLock spinLock - printLock spinLock + schedulerLock spinLock + futexLock spinLock + atomicsLock spinLock + printLock spinLock ) func resetSpinLocks() { schedulerLock.Store(0) - futexLock.Store(0) - atomicsLock.Store(0) - printLock.Store(0) + futexLock.Store(0) + atomicsLock.Store(0) + printLock.Store(0) } func (l *spinLock) Lock() { // Wait for the lock to be available. for !l.Uint32.CompareAndSwap(0, 1) { - spinLoopWait() - } + spinLoopWait() + } } func (l *spinLock) Unlock() { - if schedulerAsserts && l.Uint32.Load() != 1 { - runtimePanic("unlock of unlocked spinlock") - } - l.Uint32.Store(0) + if schedulerAsserts && l.Uint32.Load() != 1 { + runtimePanic("unlock of unlocked spinlock") + } + l.Uint32.Store(0) } // Wait until a signal is received, indicating that it can resume from the From 44879bb71a911b5f78f2f35245eb55655111b902 Mon Sep 17 00:00:00 2001 From: Jean-Marie Verdun Date: Wed, 8 Apr 2026 12:39:11 -0700 Subject: [PATCH 3/5] Run golint to format file Signed-off-by: Jean-Marie Verdun --- src/runtime/runtime_rp2.go | 1 - 1 file changed, 1 deletion(-) diff --git a/src/runtime/runtime_rp2.go b/src/runtime/runtime_rp2.go index aa390ed819..4c201a3663 100644 --- a/src/runtime/runtime_rp2.go +++ b/src/runtime/runtime_rp2.go @@ -2,7 +2,6 @@ // Used to track contribution from various authors automatically // SPDX-FileCopyrightText: Copyright Hewlett Packard Enterprise Development LP -// package runtime import ( From a254b27c9793c4c8f35fb3d1285b5834496a6e26 Mon Sep 17 00:00:00 2001 From: Jean-Marie Verdun Date: Wed, 8 Apr 2026 13:24:55 -0700 Subject: [PATCH 4/5] Remove call to SetACTLR_EXTEXCLALL as it is CPU specific Signed-off-by: Jean-Marie Verdun --- src/runtime/runtime_rp2.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/runtime/runtime_rp2.go b/src/runtime/runtime_rp2.go index 4c201a3663..1cb2b2405a 100644 --- a/src/runtime/runtime_rp2.go +++ b/src/runtime/runtime_rp2.go @@ -393,8 +393,6 @@ func init() { machineInit() machine.InitSerial() - - rp.PPB.SetACTLR_EXTEXCLALL(1) } func prerun() { From fe5c6e2b60fcf341a848c1c62edbbf6571d36b96 Mon Sep 17 00:00:00 2001 From: Jean-Marie Verdun Date: Wed, 8 Apr 2026 14:23:26 -0700 Subject: [PATCH 5/5] Create a specific init function for each chip architecture when dealing with RP2xxx as to enable EXTEXCALL on rp2350 Could be used for other specific initialization Signed-off-by: Jean-Marie Verdun --- src/runtime/runtime_rp2.go | 2 ++ src/runtime/runtime_rp2040.go | 3 +++ src/runtime/runtime_rp2350.go | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/src/runtime/runtime_rp2.go b/src/runtime/runtime_rp2.go index 1cb2b2405a..0c085ac4ab 100644 --- a/src/runtime/runtime_rp2.go +++ b/src/runtime/runtime_rp2.go @@ -393,6 +393,8 @@ func init() { machineInit() machine.InitSerial() + + initRP() } func prerun() { diff --git a/src/runtime/runtime_rp2040.go b/src/runtime/runtime_rp2040.go index 2ca3605e03..dbac61651e 100644 --- a/src/runtime/runtime_rp2040.go +++ b/src/runtime/runtime_rp2040.go @@ -10,3 +10,6 @@ const ( sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_PROC0 sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_PROC1 ) + +func initRP() { +} diff --git a/src/runtime/runtime_rp2350.go b/src/runtime/runtime_rp2350.go index 91af23212e..3a07d0bf05 100644 --- a/src/runtime/runtime_rp2350.go +++ b/src/runtime/runtime_rp2350.go @@ -14,3 +14,7 @@ const ( sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_FIFO sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_FIFO ) + +func initRP() { + rp.PPB.SetACTLR_EXTEXCLALL(1) +}