rp2040: replace sleep 'busy loop' with timer alarm

Этот коммит содержится в:
Kenneth Bell 2022-05-11 20:39:41 +01:00 коммит произвёл Ron Evans
родитель fa673f0827
коммит 8f40b107d9
3 изменённых файлов: 72 добавлений и 0 удалений

Просмотреть файл

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

Просмотреть файл

@ -4,13 +4,23 @@
package machine package machine
import ( import (
"device/arm"
"device/rp" "device/rp"
"runtime/interrupt"
"runtime/volatile" "runtime/volatile"
"unsafe" "unsafe"
) )
const numTimers = 4 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 { type timerType struct {
timeHW volatile.Register32 timeHW volatile.Register32
timeLW volatile.Register32 timeLW volatile.Register32
@ -50,3 +60,47 @@ func (tmr *timerType) timeElapsed() (us uint64) {
} }
return uint64(hi)<<32 | uint64(lo) 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()
}

Просмотреть файл

@ -12,6 +12,9 @@ import (
// machineTicks is provided by package machine. // machineTicks is provided by package machine.
func machineTicks() uint64 func machineTicks() uint64
// machineLightSleep is provided by package machine.
func machineLightSleep(uint64)
type timeUnit uint64 type timeUnit uint64
// ticks returns the number of ticks (microseconds) elapsed since power up. // ticks returns the number of ticks (microseconds) elapsed since power up.
@ -32,6 +35,16 @@ func sleepTicks(d timeUnit) {
if d == 0 { if d == 0 {
return 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 sleepUntil := ticks() + d
for ticks() < sleepUntil { for ticks() < sleepUntil {
} }