github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_timer.go (about) 1 //go:build rp2040 2 3 package machine 4 5 import ( 6 "device/arm" 7 "device/rp" 8 "runtime/interrupt" 9 "runtime/volatile" 10 "unsafe" 11 ) 12 13 const numTimers = 4 14 15 // Alarm0 is reserved for sleeping by tinygo runtime code for RP2040. 16 // Alarm0 is also IRQ0 17 const sleepAlarm = 0 18 const sleepAlarmIRQ = 0 19 20 // The minimum sleep duration in μs (ticks) 21 const minSleep = 10 22 23 type timerType struct { 24 timeHW volatile.Register32 25 timeLW volatile.Register32 26 timeHR volatile.Register32 27 timeLR volatile.Register32 28 alarm [numTimers]volatile.Register32 29 armed volatile.Register32 30 timeRawH volatile.Register32 31 timeRawL volatile.Register32 32 dbgPause volatile.Register32 33 pause volatile.Register32 34 intR volatile.Register32 35 intE volatile.Register32 36 intF volatile.Register32 37 intS volatile.Register32 38 } 39 40 var timer = (*timerType)(unsafe.Pointer(rp.TIMER)) 41 42 // TimeElapsed returns time elapsed since power up, in microseconds. 43 func (tmr *timerType) timeElapsed() (us uint64) { 44 // Need to make sure that the upper 32 bits of the timer 45 // don't change, so read that first 46 hi := tmr.timeRawH.Get() 47 var lo, nextHi uint32 48 for { 49 // Read the lower 32 bits 50 lo = tmr.timeRawL.Get() 51 // Now read the upper 32 bits again and 52 // check that it hasn't incremented. If it has, loop around 53 // and read the lower 32 bits again to get an accurate value 54 nextHi = tmr.timeRawH.Get() 55 if hi == nextHi { 56 break 57 } 58 hi = nextHi 59 } 60 return uint64(hi)<<32 | uint64(lo) 61 } 62 63 // lightSleep will put the processor into a sleep state a short period 64 // (up to approx 72mins per RP2040 datasheet, 4.6.3. Alarms). 65 // 66 // This function is a 'light' sleep and will return early if another 67 // interrupt or event triggers. This is intentional since the 68 // primary use-case is for use by the TinyGo scheduler which will 69 // re-sleep if needed. 70 func (tmr *timerType) lightSleep(us uint64) { 71 // minSleep is a way to avoid race conditions for short 72 // sleeps by ensuring there is enough time to setup the 73 // alarm before sleeping. For very short sleeps, this 74 // effectively becomes a 'busy loop'. 75 if us < minSleep { 76 return 77 } 78 79 // Interrupt handler is essentially a no-op, we're just relying 80 // on the side-effect of waking the CPU from "wfe" 81 intr := interrupt.New(sleepAlarmIRQ, func(interrupt.Interrupt) { 82 // Clear the IRQ 83 timer.intR.Set(1 << sleepAlarm) 84 }) 85 86 // Reset interrupt flag 87 tmr.intR.Set(1 << sleepAlarm) 88 89 // Enable interrupt 90 tmr.intE.SetBits(1 << sleepAlarm) 91 intr.Enable() 92 93 // Only the low 32 bits of time can be used for alarms 94 target := uint64(tmr.timeRawL.Get()) + us 95 tmr.alarm[sleepAlarm].Set(uint32(target)) 96 97 // Wait for sleep (or any other) interrupt 98 arm.Asm("wfe") 99 100 // Disarm timer 101 tmr.armed.Set(1 << sleepAlarm) 102 103 // Disable interrupt 104 intr.Disable() 105 }