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  }