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 }