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

     1  //go:build mimxrt1062
     2  
     3  package runtime
     4  
     5  import (
     6  	"device/arm"
     7  	"device/nxp"
     8  	"runtime/interrupt"
     9  	"runtime/volatile"
    10  	"unsafe"
    11  )
    12  
    13  type timeUnit int64
    14  
    15  const (
    16  	lastCycle      = SYSTICK_FREQ/1000 - 1
    17  	cyclesPerMicro = CORE_FREQ / 1000000
    18  )
    19  
    20  const (
    21  	pitFreq           = OSC_FREQ // PIT/GPT are muxed to 24 MHz OSC
    22  	pitCyclesPerMicro = pitFreq / 1000000
    23  	pitSleepTimer     = 0 // x4 32-bit PIT timers [0..3]
    24  )
    25  
    26  var (
    27  	tickCount  volatile.Register64
    28  	cycleCount volatile.Register32
    29  	pitActive  volatile.Register32
    30  	pitTimeout interrupt.Interrupt
    31  )
    32  
    33  var (
    34  	// debug exception and monitor control
    35  	DEM_CR     = (*volatile.Register32)(unsafe.Pointer(uintptr(0xe000edfc)))
    36  	DWT_CR     = (*volatile.Register32)(unsafe.Pointer(uintptr(0xe0001000)))
    37  	DWT_CYCCNT = (*volatile.Register32)(unsafe.Pointer(uintptr(0xe0001004)))
    38  )
    39  
    40  func ticksToNanoseconds(ticks timeUnit) int64 {
    41  	return int64(ticks) * 1000
    42  }
    43  
    44  func nanosecondsToTicks(ns int64) timeUnit {
    45  	return timeUnit(ns / 1000)
    46  }
    47  
    48  func initSysTick() {
    49  
    50  	const (
    51  		traceEnable      = 0x01000000 // enable debugging & monitoring blocks
    52  		cycleCountEnable = 0x00000001 // cycle count register
    53  	)
    54  
    55  	// disable SysTick if already running
    56  	if arm.SYST.SYST_CSR.HasBits(arm.SYST_CSR_ENABLE_Msk) {
    57  		arm.SYST.SYST_CSR.ClearBits(arm.SYST_CSR_ENABLE_Msk)
    58  	}
    59  
    60  	// zeroize the counter
    61  	tickCount.Set(0)
    62  	arm.SYST.SYST_RVR.Set(lastCycle)
    63  	arm.SYST.SYST_CVR.Set(0)
    64  	arm.SYST.SYST_CSR.Set(arm.SYST_CSR_TICKINT | arm.SYST_CSR_ENABLE)
    65  
    66  	// set SysTick and PendSV priority to 32
    67  	nxp.SystemControl.SHPR3.Set((0x20 << nxp.SCB_SHPR3_PRI_15_Pos) |
    68  		(0x20 << nxp.SCB_SHPR3_PRI_14_Pos))
    69  
    70  	// turn on cycle counter
    71  	DEM_CR.SetBits(traceEnable)
    72  	DWT_CR.SetBits(cycleCountEnable)
    73  	cycleCount.Set(DWT_CYCCNT.Get())
    74  
    75  	// enable PIT, disable counters
    76  	nxp.PIT.MCR.Set(0)
    77  	for i := range nxp.PIT.TIMER {
    78  		nxp.PIT.TIMER[i].TCTRL.Set(0)
    79  	}
    80  
    81  	// register sleep timer interrupt
    82  	pitTimeout = interrupt.New(nxp.IRQ_PIT, timerWake)
    83  	pitTimeout.SetPriority(0x21)
    84  	pitTimeout.Enable()
    85  }
    86  
    87  func initRTC() {
    88  	if !nxp.SNVS.LPCR.HasBits(nxp.SNVS_LPCR_SRTC_ENV) {
    89  		// if SRTC isn't running, start it with default Jan 1, 2019
    90  		nxp.SNVS.LPSRTCLR.Set(uint32((0x5c2aad80 << 15) & 0xFFFFFFFF))
    91  		nxp.SNVS.LPSRTCMR.Set(uint32(0x5c2aad80 >> 17))
    92  		nxp.SNVS.LPCR.SetBits(nxp.SNVS_LPCR_SRTC_ENV)
    93  	}
    94  }
    95  
    96  //go:export SysTick_Handler
    97  func tick() {
    98  	tickCount.Set(tickCount.Get() + 1)
    99  	cycleCount.Set(DWT_CYCCNT.Get())
   100  }
   101  
   102  func ticks() timeUnit {
   103  	mask := arm.DisableInterrupts()
   104  	tick := tickCount.Get()
   105  	cycs := cycleCount.Get()
   106  	curr := DWT_CYCCNT.Get()
   107  	arm.EnableInterrupts(mask)
   108  	var diff uint32
   109  	if curr < cycs { // cycle counter overflow/rollover occurred
   110  		diff = (0xFFFFFFFF - cycs) + curr
   111  	} else {
   112  		diff = curr - cycs
   113  	}
   114  	frac := uint64(diff*0xFFFFFFFF/cyclesPerMicro) >> 32
   115  	if frac > 1000 {
   116  		frac = 1000
   117  	}
   118  	return timeUnit(1000*tick + frac)
   119  }
   120  
   121  func sleepTicks(duration timeUnit) {
   122  	if duration >= 0 {
   123  		curr := ticks()
   124  		last := curr + duration // 64-bit overflow unlikely
   125  		for curr < last {
   126  			cycles := timeUnit((last - curr) / pitCyclesPerMicro)
   127  			if cycles > 0xFFFFFFFF {
   128  				cycles = 0xFFFFFFFF
   129  			}
   130  			if !timerSleep(uint32(cycles)) {
   131  				return // return early due to interrupt
   132  			}
   133  			curr = ticks()
   134  		}
   135  	}
   136  }
   137  
   138  func timerSleep(cycles uint32) bool {
   139  	pitActive.Set(1)
   140  	nxp.PIT.TIMER[pitSleepTimer].LDVAL.Set(cycles)
   141  	nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(nxp.PIT_TIMER_TCTRL_TIE)     // enable interrupts
   142  	nxp.PIT.TIMER[pitSleepTimer].TCTRL.SetBits(nxp.PIT_TIMER_TCTRL_TEN) // start timer
   143  	for {
   144  		//arm.Asm("wfi") // TODO: causes hardfault! why?
   145  		if pitActive.Get() == 0 {
   146  			return true
   147  		}
   148  		if hasScheduler {
   149  			break // some other interrupt occurred and needs servicing
   150  		}
   151  	}
   152  	timerWake(interrupt.Interrupt{}) // clear and disable timer
   153  	return false
   154  }
   155  
   156  func timerWake(interrupt.Interrupt) {
   157  	pitActive.Set(0)
   158  	// TFLGn[TIF] are set to 1 when a timeout occurs on the associated timer, and
   159  	// are cleared to 0 by writing a 1 to the corresponding TFLGn[TIF].
   160  	nxp.PIT.TIMER[pitSleepTimer].TFLG.Set(nxp.PIT_TIMER_TFLG_TIF) // clear interrupt flag
   161  	nxp.PIT.TIMER[pitSleepTimer].TCTRL.Set(0)                     // disable timer/interrupt enable flags
   162  }