aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/machine/machine_rp2_timer.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/machine/machine_rp2_timer.go')
-rw-r--r--src/machine/machine_rp2_timer.go103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/machine/machine_rp2_timer.go b/src/machine/machine_rp2_timer.go
new file mode 100644
index 000000000..a78ed70bb
--- /dev/null
+++ b/src/machine/machine_rp2_timer.go
@@ -0,0 +1,103 @@
+//go:build rp2040 || rp2350
+
+package machine
+
+import (
+ "device/arm"
+ "runtime/interrupt"
+ "runtime/volatile"
+)
+
+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
+ timeHR volatile.Register32
+ timeLR volatile.Register32
+ alarm [numTimers]volatile.Register32
+ armed volatile.Register32
+ timeRawH volatile.Register32
+ timeRawL volatile.Register32
+ dbgPause volatile.Register32
+ pause volatile.Register32
+ locked [rp2350ExtraReg]volatile.Register32
+ source [rp2350ExtraReg]volatile.Register32
+ intR volatile.Register32
+ intE volatile.Register32
+ intF volatile.Register32
+ intS volatile.Register32
+}
+
+// TimeElapsed returns time elapsed since power up, in microseconds.
+func (tmr *timerType) timeElapsed() (us uint64) {
+ // Need to make sure that the upper 32 bits of the timer
+ // don't change, so read that first
+ hi := tmr.timeRawH.Get()
+ var lo, nextHi uint32
+ for {
+ // Read the lower 32 bits
+ lo = tmr.timeRawL.Get()
+ // Now read the upper 32 bits again and
+ // check that it hasn't incremented. If it has, loop around
+ // and read the lower 32 bits again to get an accurate value
+ nextHi = tmr.timeRawH.Get()
+ if hi == nextHi {
+ break
+ }
+ hi = nextHi
+ }
+ 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()
+}