-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Fix runtime on rp2350/2040 for multicore scheduler #5286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
df199cc
293bb3f
44879bb
a254b27
fe5c6e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||
| //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 +12,7 @@ import ( | |||||||
| _ "machine/usb/cdc" | ||||||||
| "runtime/interrupt" | ||||||||
| "runtime/volatile" | ||||||||
| "sync/atomic" | ||||||||
| "unsafe" | ||||||||
| ) | ||||||||
|
|
||||||||
|
|
@@ -116,6 +119,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,9 +157,16 @@ 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() | ||||||||
|
|
@@ -169,15 +192,26 @@ 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) | ||||||||
| 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() | ||||||||
|
|
@@ -291,41 +325,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() | ||||||||
| } | ||||||||
|
Comment on lines
329
to
352
|
||||||||
|
|
||||||||
| } | ||||||||
|
|
||||||||
| 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) | ||||||||
|
||||||||
| l.Uint32.Store(0) | |
| l.Uint32.Store(0) | |
| arm.Asm("sev") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non necessary the scheduler is taking care about waking up the core when needed. Tested with multiple g routines running at the same time and calls to GC.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,3 +10,6 @@ const ( | |
| sioIrqFifoProc0 = rp.IRQ_SIO_IRQ_PROC0 | ||
| sioIrqFifoProc1 = rp.IRQ_SIO_IRQ_PROC1 | ||
| ) | ||
|
|
||
| func initRP() { | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
multicore_fifo_pop_blocking() here can deadlock boot if the FIFO is empty (which is a valid state after reset/power-on). If the intent is to clear stale FIFO data before restarting core1, use multicore_fifo_drain() (non-blocking) or gate the pop behind a FIFO_ST.VLD check to avoid blocking forever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FIFO will be empty but CORE1 will write to it as soon as it starts. The logic is to reset the CORE and wait for it to tell us it is ready, so we block while waiting for the message coming from CORE1 into the FIFO