rp2040: replace sleep 'busy loop' with timer alarm
Этот коммит содержится в:
родитель
fa673f0827
коммит
8f40b107d9
3 изменённых файлов: 72 добавлений и 0 удалений
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче