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

     1  // Derivative work of Teensyduino Core Library
     2  // http://www.pjrc.com/teensy/
     3  // Copyright (c) 2017 PJRC.COM, LLC.
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining
     6  // a copy of this software and associated documentation files (the
     7  // "Software"), to deal in the Software without restriction, including
     8  // without limitation the rights to use, copy, modify, merge, publish,
     9  // distribute, sublicense, and/or sell copies of the Software, and to
    10  // permit persons to whom the Software is furnished to do so, subject to
    11  // the following conditions:
    12  //
    13  // 1. The above copyright notice and this permission notice shall be
    14  // included in all copies or substantial portions of the Software.
    15  //
    16  // 2. If the Software is incorporated into a build system that allows
    17  // selection among a list of target devices, then similar target
    18  // devices manufactured by PJRC.COM must be included in the list of
    19  // target devices and selectable in the same manner.
    20  //
    21  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    22  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    23  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    24  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
    25  // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
    26  // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    27  // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    28  // SOFTWARE.
    29  
    30  //go:build nxp && mk66f18
    31  
    32  package runtime
    33  
    34  import (
    35  	"device/arm"
    36  	"device/nxp"
    37  	"machine"
    38  	"runtime/interrupt"
    39  	"runtime/volatile"
    40  )
    41  
    42  type timeUnit int64
    43  
    44  func ticksToNanoseconds(ticks timeUnit) int64 {
    45  	return int64(ticks) * 1000
    46  }
    47  
    48  func nanosecondsToTicks(ns int64) timeUnit {
    49  	return timeUnit(ns / 1000)
    50  }
    51  
    52  // cyclesPerMilli-1 is used for the systick reset value.
    53  // The systick current value will be decremented on every clock cycle.
    54  // An interrupt is generated when the current value reaches 0.
    55  // A value of freq/1000 generates a tick (irq) every millisecond (1/1000 s).
    56  var cyclesPerMilli = machine.CPUFrequency() / 1000
    57  
    58  // number of systick irqs (milliseconds) since boot
    59  var systickCount volatile.Register64
    60  
    61  func millisSinceBoot() uint64 {
    62  	return systickCount.Get()
    63  }
    64  
    65  func initSysTick() {
    66  	nxp.SysTick.RVR.Set(cyclesPerMilli - 1)
    67  	nxp.SysTick.CVR.Set(0)
    68  	nxp.SysTick.CSR.Set(nxp.SysTick_CSR_CLKSOURCE | nxp.SysTick_CSR_TICKINT | nxp.SysTick_CSR_ENABLE)
    69  	nxp.SystemControl.SHPR3.Set((32 << nxp.SystemControl_SHPR3_PRI_15_Pos) | (32 << nxp.SystemControl_SHPR3_PRI_14_Pos)) // set systick and pendsv priority to 32
    70  }
    71  
    72  func initSleepTimer() {
    73  	nxp.SIM.SCGC5.SetBits(nxp.SIM_SCGC5_LPTMR)
    74  	nxp.LPTMR0.CSR.Set(nxp.LPTMR0_CSR_TIE)
    75  
    76  	timerInterrupt = interrupt.New(nxp.IRQ_LPTMR0, timerWake)
    77  	timerInterrupt.Enable()
    78  }
    79  
    80  //go:export SysTick_Handler
    81  func tick() {
    82  	systickCount.Set(systickCount.Get() + 1)
    83  }
    84  
    85  // ticks are in microseconds
    86  func ticks() timeUnit {
    87  	mask := arm.DisableInterrupts()
    88  	current := nxp.SysTick.CVR.Get()        // current value of the systick counter
    89  	count := millisSinceBoot()              // number of milliseconds since boot
    90  	istatus := nxp.SystemControl.ICSR.Get() // interrupt status register
    91  	arm.EnableInterrupts(mask)
    92  
    93  	micros := timeUnit(count * 1000) // a tick (1ms) = 1000 us
    94  
    95  	// if the systick counter was about to reset and ICSR indicates a pending systick irq, increment count
    96  	if istatus&nxp.SystemControl_ICSR_PENDSTSET != 0 && current > 50 {
    97  		micros += 1000
    98  	} else {
    99  		cycles := cyclesPerMilli - 1 - current // number of cycles since last 1ms tick
   100  		cyclesPerMicro := machine.CPUFrequency() / 1000000
   101  		micros += timeUnit(cycles / cyclesPerMicro)
   102  	}
   103  
   104  	return micros
   105  }
   106  
   107  // sleepTicks spins for a number of microseconds
   108  func sleepTicks(duration timeUnit) {
   109  	now := ticks()
   110  	end := duration + now
   111  	cyclesPerMicro := machine.ClockFrequency() / 1000000
   112  
   113  	if duration <= 0 {
   114  		return
   115  	}
   116  
   117  	nxp.LPTMR0.PSR.Set((3 << nxp.LPTMR0_PSR_PCS_Pos) | nxp.LPTMR0_PSR_PBYP) // use 16MHz clock, undivided
   118  
   119  	for now < end {
   120  		count := uint32(end-now) / cyclesPerMicro
   121  		if count > 65535 {
   122  			count = 65535
   123  		}
   124  
   125  		if !timerSleep(count) {
   126  			// return early due to interrupt
   127  			return
   128  		}
   129  
   130  		now = ticks()
   131  	}
   132  }
   133  
   134  var timerInterrupt interrupt.Interrupt
   135  var timerActive volatile.Register32
   136  
   137  func timerSleep(count uint32) bool {
   138  	timerActive.Set(1)
   139  	nxp.LPTMR0.CMR.Set(count)                  // set count
   140  	nxp.LPTMR0.CSR.SetBits(nxp.LPTMR0_CSR_TEN) // enable
   141  
   142  	for {
   143  		arm.Asm("wfi")
   144  		if timerActive.Get() == 0 {
   145  			return true
   146  		}
   147  
   148  		if hasScheduler {
   149  			// bail out, as the interrupt may have awoken a goroutine
   150  			break
   151  		}
   152  
   153  		// if there is no scheduler, block for the entire count
   154  	}
   155  
   156  	timerWake(timerInterrupt)
   157  	return false
   158  }
   159  
   160  func timerWake(interrupt.Interrupt) {
   161  	timerActive.Set(0)
   162  	nxp.LPTMR0.CSR.Set(nxp.LPTMR0.CSR.Get()&^nxp.LPTMR0_CSR_TEN | nxp.LPTMR0_CSR_TCF) // clear flag and disable
   163  }