github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_avrtiny.go (about) 1 //go:build avrtiny 2 3 // Runtime for the newer AVRs introduced since around 2016 that work quite 4 // different from older AVRs like the atmega328p or even the attiny85. 5 // Because of these large differences, a new runtime and machine implementation 6 // is needed. 7 // Some key differences: 8 // * Peripherals are now logically separated, instead of all mixed together as 9 // one big bag of registers. No PORTA/DDRA etc registers anymore, instead a 10 // real PORT peripheral type with multiple instances. 11 // * There is a real RTC now! No need for using one of the timers as a time 12 // source, which then conflicts with using it as a PWM. 13 // * Flash and RAM are now in the same address space! This avoids the need for 14 // PROGMEM which couldn't (easily) be supported in Go anyway. Constant 15 // globals just get stored in flash, like on Cortex-M chips. 16 17 package runtime 18 19 import ( 20 "device/avr" 21 "runtime/interrupt" 22 "runtime/volatile" 23 ) 24 25 type timeUnit int64 26 27 //export main 28 func main() { 29 // Initialize RTC. 30 for avr.RTC.STATUS.Get() != 0 { 31 } 32 avr.RTC.CTRLA.Set(avr.RTC_CTRLA_RTCEN | avr.RTC_CTRLA_RUNSTDBY) 33 avr.RTC.INTCTRL.Set(avr.RTC_INTCTRL_OVF) // enable overflow interrupt 34 interrupt.New(avr.IRQ_RTC_CNT, rtcInterrupt) 35 36 // Configure sleep: 37 // - enable sleep mode 38 // - set sleep mode to STANDBY (mode 0x1) 39 avr.SLPCTRL.CTRLA.Set(avr.SLPCTRL_CTRLA_SEN | 0x1<<1) 40 41 // Enable interrupts after initialization. 42 avr.Asm("sei") 43 44 run() 45 exit(0) 46 } 47 48 func initUART() { 49 // no UART configured 50 } 51 52 func putchar(b byte) { 53 // no-op 54 } 55 56 // ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds. 57 func ticksToNanoseconds(ticks timeUnit) int64 { 58 // The following calculation is actually the following, but with both sides 59 // reduced to reduce the risk of overflow: 60 // ticks * 1e9 / 32768 61 return int64(ticks) * 1953125 / 64 62 } 63 64 // nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz). 65 func nanosecondsToTicks(ns int64) timeUnit { 66 // The following calculation is actually the following, but with both sides 67 // reduced to reduce the risk of overflow: 68 // ns * 32768 / 1e9 69 return timeUnit(ns * 64 / 1953125) 70 } 71 72 // Sleep for the given number of timer ticks. 73 func sleepTicks(d timeUnit) { 74 ticksStart := ticks() 75 sleepUntil := ticksStart + d 76 77 // Sleep until we're in the right 2-second interval. 78 for { 79 avr.Asm("cli") 80 overflows := rtcOverflows.Get() 81 if overflows >= uint32(sleepUntil>>16) { 82 // We're in the right 2-second interval. 83 // At this point we know that the difference between ticks() and 84 // sleepUntil is ≤0xffff. 85 avr.Asm("sei") 86 break 87 } 88 // Sleep some more, because we're not there yet. 89 avr.Asm("sei\nsleep") 90 } 91 92 // Now we know the sleep duration is small enough to fit in rtc.CNT. 93 94 // Update rtc.CMP (atomically). 95 cnt := uint16(sleepUntil) 96 low := uint8(cnt) 97 high := uint8(cnt >> 8) 98 avr.RTC.CMPH.Set(high) 99 avr.RTC.CMPL.Set(low) 100 101 // Disable interrupts, so we can change interrupt settings without racing. 102 avr.Asm("cli") 103 104 // Enable the CMP interrupt. 105 avr.RTC.INTCTRL.Set(avr.RTC_INTCTRL_OVF | avr.RTC_INTCTRL_CMP) 106 107 // Check whether we already reached CNT, in which case the interrupt may 108 // have triggered already (but maybe not, it's a race condition). 109 low2 := avr.RTC.CNTL.Get() 110 high2 := avr.RTC.CNTH.Get() 111 cnt2 := uint16(high2)<<8 | uint16(low2) 112 if cnt2 < cnt { 113 // We have not, so wait until the interrupt happens. 114 for { 115 // Sleep until the next interrupt happens. 116 avr.Asm("sei\nsleep\ncli") 117 if cmpMatch.Get() != 0 { 118 // The CMP interrupt occured, so we have slept long enough. 119 cmpMatch.Set(0) 120 break 121 } 122 } 123 } 124 125 // Disable the CMP interrupt, and restore things like they were before. 126 avr.RTC.INTCTRL.Set(avr.RTC_INTCTRL_OVF) 127 avr.Asm("sei") 128 } 129 130 // Number of RTC overflows, updated in the RTC interrupt handler. 131 // The RTC is running at 32768Hz so an overflow happens every 2 seconds. A 132 // 32-bit integer is large enough to run for about 279 years. 133 var rtcOverflows volatile.Register32 134 135 // Set to one in the RTC CMP interrupt, to signal the expected number of ticks 136 // have passed. 137 var cmpMatch volatile.Register8 138 139 // Return the number of RTC ticks that happened since reset. 140 func ticks() timeUnit { 141 var ovf uint32 142 var count uint16 143 for { 144 // Get the tick count and overflow value, in a 4-step process to avoid a 145 // race with the overflow interrupt. 146 mask := interrupt.Disable() 147 148 // 1. Get the overflow value. 149 ovf = rtcOverflows.Get() 150 151 // 2. Read the RTC counter. 152 // This way of reading is atomic (due to the TEMP register). 153 low := avr.RTC.CNTL.Get() 154 high := avr.RTC.CNTH.Get() 155 156 // 3. Get the interrupt flags. 157 intflags := avr.RTC.INTFLAGS.Get() 158 159 interrupt.Restore(mask) 160 161 // 4. Check whether an overflow happened somewhere in the last three 162 // steps. If so, just repeat the loop. 163 if intflags&avr.RTC_INTFLAGS_OVF == 0 { 164 count = uint16(high)<<8 | uint16(low) 165 break 166 } 167 } 168 169 // Create the 64-bit tick count, combining the two. 170 return timeUnit(ovf)<<16 | timeUnit(count) 171 } 172 173 // Interrupt handler for the RTC. 174 // It happens every two seconds, and while sleeping using the CMP interrupt. 175 func rtcInterrupt(interrupt.Interrupt) { 176 flags := avr.RTC.INTFLAGS.Get() 177 if flags&avr.RTC_INTFLAGS_OVF != 0 { 178 rtcOverflows.Set(rtcOverflows.Get() + 1) 179 } 180 if flags&avr.RTC_INTFLAGS_CMP != 0 { 181 cmpMatch.Set(1) 182 } 183 avr.RTC.INTFLAGS.Set(flags) // clear interrupts 184 } 185 186 func exit(code int) { 187 abort() 188 } 189 190 //export __vector_default 191 func abort()