github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_stm32_timers.go (about)

     1  //go:build stm32
     2  
     3  package runtime
     4  
     5  // This file implements a common implementation of implementing 'ticks' and
     6  // 'sleep' for STM32 devices. The implementation uses a single timer.  The
     7  // timer's PWM frequency (controlled by PSC and ARR) are configured for
     8  // periodic interrupts at 100Hz (TICK_INTR_PERIOD_NS).  The PWM counter
     9  // register is used for fine-grained resolution (down to ~150ns) with an
    10  // Output Comparator used for fine-grained sleeps.
    11  
    12  import (
    13  	"device/stm32"
    14  	"machine"
    15  	"runtime/interrupt"
    16  	"runtime/volatile"
    17  )
    18  
    19  type timerInfo struct {
    20  	EnableRegister *volatile.Register32
    21  	EnableFlag     uint32
    22  	Device         *stm32.TIM_Type
    23  }
    24  
    25  const (
    26  	// All STM32 do a constant 16ns per tick.  This keeps time
    27  	// conversion between ticks and ns fast (shift operation)
    28  	// at the expense of more complex logic when getting current
    29  	// time (which is already slow due to interfacing with hardware)
    30  	NS_PER_TICK = 16
    31  
    32  	// For very short sleeps a busy loop is used to avoid race
    33  	// conditions where a trigger would take longer to setup than
    34  	// the sleep duration.
    35  	MAX_BUSY_LOOP_NS = 10e3 // 10us
    36  
    37  	// The period of tick interrupts in nanoseconds
    38  	TICK_INTR_PERIOD_NS = 10e6 // 10ms = 100Hz
    39  
    40  	// The number of ticks that happen per overflow interrupt
    41  	TICK_PER_INTR = TICK_INTR_PERIOD_NS / NS_PER_TICK
    42  )
    43  
    44  var (
    45  	// Tick count since boot
    46  	tickCount volatile.Register64
    47  
    48  	// The timer used for counting ticks
    49  	tickTimer *machine.TIM
    50  
    51  	// The max counter value (fractional part)
    52  	countMax uint32
    53  )
    54  
    55  func ticksToNanoseconds(ticks timeUnit) int64 {
    56  	return int64(ticks) * NS_PER_TICK
    57  }
    58  
    59  func nanosecondsToTicks(ns int64) timeUnit {
    60  	return timeUnit(ns / NS_PER_TICK)
    61  }
    62  
    63  // number of ticks since start.
    64  //
    65  //go:linkname ticks runtime.ticks
    66  func ticks() timeUnit {
    67  	// For some ways of capturing the time atomically, see this thread:
    68  	// https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617
    69  	// Here, instead of re-reading the counter register if an overflow has been
    70  	// detected, we simply try again because that results in smaller code.
    71  	for {
    72  		mask := interrupt.Disable()
    73  		counter := tickTimer.Count()
    74  		overflows := uint64(tickCount.Get())
    75  		hasOverflow := tickTimer.Device.SR.HasBits(stm32.TIM_SR_UIF)
    76  		interrupt.Restore(mask)
    77  
    78  		if hasOverflow {
    79  			continue
    80  		}
    81  
    82  		return timeUnit(overflows*TICK_PER_INTR + countToTicks(counter))
    83  	}
    84  }
    85  
    86  //
    87  // --  Ticks  ---
    88  //
    89  
    90  // Enable the timer used to count ticks.
    91  //
    92  // For precise sleeps use a timer with at least one OutputCompare
    93  // channel otherwise minimum reliable sleep resolution is bounded
    94  // by TICK_INTR_PERIOD_NS.
    95  //
    96  // Typically avoid TIM6 / TIM7 as they often do not include an
    97  // output comparator.
    98  func initTickTimer(tim *machine.TIM) {
    99  	tickTimer = tim
   100  	tickTimer.Configure(machine.PWMConfig{Period: TICK_INTR_PERIOD_NS})
   101  
   102  	countMax = tickTimer.Top()
   103  	tickTimer.SetWraparoundInterrupt(handleTick)
   104  }
   105  
   106  func handleTick() {
   107  	// increment tick count
   108  	tickCount.Set(tickCount.Get() + 1)
   109  }
   110  
   111  //
   112  //  ---  Sleep  ---
   113  //
   114  
   115  func sleepTicks(d timeUnit) {
   116  	// If there is a scheduler, we sleep until any kind of CPU event up to
   117  	// a maximum of the requested sleep duration.
   118  	//
   119  	// The scheduler will call again if there is nothing to do and a further
   120  	// sleep is required.
   121  	if hasScheduler {
   122  		timerSleep(uint64(d))
   123  		return
   124  	}
   125  
   126  	// There's no scheduler, so we sleep until at least the requested number
   127  	// of ticks has passed.  For short sleeps, this forms a busy loop since
   128  	// timerSleep will return immediately.
   129  	end := ticks() + d
   130  	for ticks() < end {
   131  		timerSleep(uint64(d))
   132  	}
   133  }
   134  
   135  // timerSleep sleeps for 'at most' ticks, but possibly less.
   136  func timerSleep(ticks uint64) {
   137  	// If the sleep is super-small (<10us), busy loop by returning
   138  	// to the scheduler (if any).  This avoids a busy loop here
   139  	// that might delay tasks from being scheduled triggered by
   140  	// an interrupt (e.g. channels).
   141  	if ticksToNanoseconds(timeUnit(ticks)) < MAX_BUSY_LOOP_NS {
   142  		return
   143  	}
   144  
   145  	// If the sleep is long, the tick interrupt will occur before
   146  	// the sleep expires, so just use that.  This routine will be
   147  	// called again if the sleep is incomplete.
   148  	if ticks >= TICK_PER_INTR {
   149  		waitForEvents()
   150  		return
   151  	}
   152  
   153  	// Sleeping for less than one tick interrupt, now see if the
   154  	// next tick interrupt will occur before the sleep expires.  If
   155  	// so, use that interrupt.  (this routine will be called
   156  	// again if sleep is incomplete)
   157  	cnt := tickTimer.Count()
   158  	ticksUntilOverflow := countToTicks(countMax - cnt)
   159  	if ticksUntilOverflow <= ticks {
   160  		waitForEvents()
   161  		return
   162  	}
   163  
   164  	// The sleep is now known to be:
   165  	//  - More than a few CPU cycles
   166  	//  - Less than one interrupt period
   167  	//  - Expiring before the next interrupt
   168  	//
   169  	// Setup a PWM channel to trigger an interrupt.
   170  	// NOTE: ticks is known to be < MAX_UINT32 at this point.
   171  	tickTimer.SetMatchInterrupt(0, handleSleep)
   172  	tickTimer.Set(0, cnt+ticksToCount(ticks))
   173  
   174  	// Wait till either the timer or some other event wakes
   175  	// up the CPU
   176  	waitForEvents()
   177  
   178  	// In case it was not the sleep interrupt that woke the
   179  	// CPU, disable the sleep interrupt now.
   180  	tickTimer.Unset(0)
   181  }
   182  
   183  func handleSleep(ch uint8) {
   184  	// Disable the sleep interrupt
   185  	tickTimer.Unset(0)
   186  }
   187  
   188  func countToTicks(count uint32) uint64 {
   189  	return (uint64(count) * TICK_PER_INTR) / uint64(countMax)
   190  }
   191  
   192  func ticksToCount(ticks uint64) uint32 {
   193  	return uint32((ticks * uint64(countMax)) / TICK_PER_INTR)
   194  }