github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_nrf.go (about) 1 //go:build nrf && !nrf52840 2 3 package runtime 4 5 import ( 6 "device/arm" 7 "device/nrf" 8 "machine" 9 "runtime/interrupt" 10 "runtime/volatile" 11 ) 12 13 type timeUnit int64 14 15 //go:linkname systemInit SystemInit 16 func systemInit() 17 18 //export Reset_Handler 19 func main() { 20 if nrf.FPUPresent { 21 arm.SCB.CPACR.Set(0) // disable FPU if it is enabled 22 } 23 systemInit() 24 preinit() 25 run() 26 exit(0) 27 } 28 29 func init() { 30 machine.InitSerial() 31 initLFCLK() 32 initRTC() 33 } 34 35 func initLFCLK() { 36 if machine.HasLowFrequencyCrystal { 37 nrf.CLOCK.LFCLKSRC.Set(nrf.CLOCK_LFCLKSTAT_SRC_Xtal) 38 } 39 nrf.CLOCK.TASKS_LFCLKSTART.Set(1) 40 for nrf.CLOCK.EVENTS_LFCLKSTARTED.Get() == 0 { 41 } 42 nrf.CLOCK.EVENTS_LFCLKSTARTED.Set(0) 43 } 44 45 func initRTC() { 46 nrf.RTC1.TASKS_START.Set(1) 47 intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) { 48 if nrf.RTC1.EVENTS_COMPARE[0].Get() != 0 { 49 nrf.RTC1.EVENTS_COMPARE[0].Set(0) 50 nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0) 51 nrf.RTC1.EVENTS_COMPARE[0].Set(0) 52 rtc_wakeup.Set(1) 53 } 54 if nrf.RTC1.EVENTS_OVRFLW.Get() != 0 { 55 nrf.RTC1.EVENTS_OVRFLW.Set(0) 56 rtcOverflows.Set(rtcOverflows.Get() + 1) 57 } 58 }) 59 nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_OVRFLW) 60 intr.SetPriority(0xc0) // low priority 61 intr.Enable() 62 } 63 64 func putchar(c byte) { 65 machine.Serial.WriteByte(c) 66 } 67 68 func getchar() byte { 69 for machine.Serial.Buffered() == 0 { 70 Gosched() 71 } 72 v, _ := machine.Serial.ReadByte() 73 return v 74 } 75 76 func buffered() int { 77 return machine.Serial.Buffered() 78 } 79 80 func sleepTicks(d timeUnit) { 81 for d != 0 { 82 ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side) 83 rtc_sleep(ticks) 84 d -= timeUnit(ticks) 85 } 86 } 87 88 var rtcOverflows volatile.Register32 // number of times the RTC wrapped around 89 90 // ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds. 91 func ticksToNanoseconds(ticks timeUnit) int64 { 92 // The following calculation is actually the following, but with both sides 93 // reduced to reduce the risk of overflow: 94 // ticks * 1e9 / 32768 95 return int64(ticks) * 1953125 / 64 96 } 97 98 // nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz). 99 func nanosecondsToTicks(ns int64) timeUnit { 100 // The following calculation is actually the following, but with both sides 101 // reduced to reduce the risk of overflow: 102 // ns * 32768 / 1e9 103 return timeUnit(ns * 64 / 1953125) 104 } 105 106 // Monotonically increasing numer of ticks since start. 107 func ticks() timeUnit { 108 // For some ways of capturing the time atomically, see this thread: 109 // https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617 110 // Here, instead of re-reading the counter register if an overflow has been 111 // detected, we simply try again because that results in (slightly) smaller 112 // code and is perhaps easier to prove correct. 113 for { 114 mask := interrupt.Disable() 115 counter := uint32(nrf.RTC1.COUNTER.Get()) 116 overflows := rtcOverflows.Get() 117 hasOverflow := nrf.RTC1.EVENTS_OVRFLW.Get() != 0 118 interrupt.Restore(mask) 119 120 if hasOverflow { 121 // There was an overflow. Try again. 122 continue 123 } 124 125 // The counter is 24 bits in size, so the number of overflows form the 126 // upper 32 bits (together 56 bits, which covers 71493 years at 127 // 32768kHz: I'd argue good enough for most purposes). 128 return timeUnit(overflows)<<24 + timeUnit(counter) 129 } 130 } 131 132 var rtc_wakeup volatile.Register8 133 134 func rtc_sleep(ticks uint32) { 135 nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_COMPARE0) 136 rtc_wakeup.Set(0) 137 if ticks == 1 { 138 // Race condition (even in hardware) at ticks == 1. 139 // TODO: fix this in a better way by detecting it, like the manual 140 // describes. 141 ticks = 2 142 } 143 nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff) 144 for rtc_wakeup.Get() == 0 { 145 waitForEvents() 146 } 147 }