Browse Source

rp2040: replace sleep 'busy loop' with timer alarm

pull/2853/head
Kenneth Bell 3 years ago
committed by Ron Evans
parent
commit
8f40b107d9
  1. 5
      src/machine/machine_rp2040.go
  2. 54
      src/machine/machine_rp2040_timer.go
  3. 13
      src/runtime/runtime_rp2040.go

5
src/machine/machine_rp2040.go

@ -86,6 +86,11 @@ func ticks() uint64 {
return timer.timeElapsed()
}
//go:linkname lightSleep runtime.machineLightSleep
func lightSleep(ticks uint64) {
timer.lightSleep(ticks)
}
// UART pins
const (
UART_TX_PIN = UART0_TX_PIN

54
src/machine/machine_rp2040_timer.go

@ -4,13 +4,23 @@
package machine
import (
"device/arm"
"device/rp"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
const numTimers = 4
// Alarm0 is reserved for sleeping by tinygo runtime code for RP2040.
// Alarm0 is also IRQ0
const sleepAlarm = 0
const sleepAlarmIRQ = 0
// The minimum sleep duration in μs (ticks)
const minSleep = 10
type timerType struct {
timeHW volatile.Register32
timeLW volatile.Register32
@ -50,3 +60,47 @@ func (tmr *timerType) timeElapsed() (us uint64) {
}
return uint64(hi)<<32 | uint64(lo)
}
// lightSleep will put the processor into a sleep state a short period
// (up to approx 72mins per RP2040 datasheet, 4.6.3. Alarms).
//
// This function is a 'light' sleep and will return early if another
// interrupt or event triggers. This is intentional since the
// primary use-case is for use by the TinyGo scheduler which will
// re-sleep if needed.
func (tmr *timerType) lightSleep(us uint64) {
// minSleep is a way to avoid race conditions for short
// sleeps by ensuring there is enough time to setup the
// alarm before sleeping. For very short sleeps, this
// effectively becomes a 'busy loop'.
if us < minSleep {
return
}
// Interrupt handler is essentially a no-op, we're just relying
// on the side-effect of waking the CPU from "wfe"
intr := interrupt.New(sleepAlarmIRQ, func(interrupt.Interrupt) {
// Clear the IRQ
timer.intR.Set(1 << sleepAlarm)
})
// Reset interrupt flag
tmr.intR.Set(1 << sleepAlarm)
// Enable interrupt
tmr.intE.SetBits(1 << sleepAlarm)
intr.Enable()
// Only the low 32 bits of time can be used for alarms
target := uint64(tmr.timeRawL.Get()) + us
tmr.alarm[sleepAlarm].Set(uint32(target))
// Wait for sleep (or any other) interrupt
arm.Asm("wfe")
// Disarm timer
tmr.armed.Set(1 << sleepAlarm)
// Disable interrupt
intr.Disable()
}

13
src/runtime/runtime_rp2040.go

@ -12,6 +12,9 @@ import (
// machineTicks is provided by package machine.
func machineTicks() uint64
// machineLightSleep is provided by package machine.
func machineLightSleep(uint64)
type timeUnit uint64
// ticks returns the number of ticks (microseconds) elapsed since power up.
@ -32,6 +35,16 @@ func sleepTicks(d timeUnit) {
if d == 0 {
return
}
if hasScheduler {
// With scheduler, sleepTicks may return early if an interrupt or
// event fires - so scheduler can schedule any go routines now
// eligible to run
machineLightSleep(uint64(d))
return
}
// Busy loop
sleepUntil := ticks() + d
for ticks() < sleepUntil {
}

Loading…
Cancel
Save