github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_avr.go (about) 1 //go:build avr && !avrtiny 2 3 package runtime 4 5 import ( 6 "device/avr" 7 "machine" 8 "runtime/interrupt" 9 "runtime/volatile" 10 "unsafe" 11 ) 12 13 const BOARD = "arduino" 14 15 // timeUnit in nanoseconds 16 type timeUnit int64 17 18 // Watchdog timer periods. These can be off by a large margin (hence the jump 19 // between 64ms and 125ms which is not an exact double), so don't rely on this 20 // for accurate time keeping. 21 const ( 22 WDT_PERIOD_16MS = iota 23 WDT_PERIOD_32MS 24 WDT_PERIOD_64MS 25 WDT_PERIOD_125MS 26 WDT_PERIOD_250MS 27 WDT_PERIOD_500MS 28 WDT_PERIOD_1S 29 WDT_PERIOD_2S 30 ) 31 32 const timerRecalibrateInterval = 6e7 // 1 minute 33 34 var nextTimerRecalibrate timeUnit 35 36 //go:extern _sbss 37 var _sbss [0]byte 38 39 //go:extern _ebss 40 var _ebss [0]byte 41 42 //export main 43 func main() { 44 preinit() 45 initHardware() 46 run() 47 exit(0) 48 } 49 50 func preinit() { 51 // Initialize .bss: zero-initialized global variables. 52 ptr := unsafe.Pointer(&_sbss) 53 for ptr != unsafe.Pointer(&_ebss) { 54 *(*uint8)(ptr) = 0 55 ptr = unsafe.Add(ptr, 1) 56 } 57 } 58 59 func initHardware() { 60 initUART() 61 initMonotonicTimer() 62 nextTimerRecalibrate = ticks() + timerRecalibrateInterval 63 64 // Enable interrupts after initialization. 65 avr.Asm("sei") 66 } 67 68 func ticksToNanoseconds(ticks timeUnit) int64 { 69 return int64(ticks) 70 } 71 72 func nanosecondsToTicks(ns int64) timeUnit { 73 return timeUnit(ns) 74 } 75 76 // Sleep this number of ticks of nanoseconds. 77 func sleepTicks(d timeUnit) { 78 waitTill := ticks() + d 79 for { 80 // wait for interrupt 81 avr.Asm("sleep") 82 if waitTill <= ticks() { 83 // done waiting 84 return 85 } 86 if hasScheduler { 87 // The interrupt may have awoken a goroutine, so bail out early. 88 return 89 } 90 } 91 } 92 93 func ticks() (ticksReturn timeUnit) { 94 state := interrupt.Disable() 95 // use volatile since ticksCount can be changed when running on multi-core boards. 96 ticksReturn = timeUnit(volatile.LoadUint64((*uint64)(unsafe.Pointer(&ticksCount)))) 97 interrupt.Restore(state) 98 return 99 } 100 101 func exit(code int) { 102 abort() 103 } 104 105 func abort() { 106 // Disable interrupts and go to sleep. 107 // This can never be awoken except for reset, and is recogized as termination by simavr. 108 avr.Asm("cli") 109 for { 110 avr.Asm("sleep") 111 } 112 } 113 114 var ticksCount int64 // nanoseconds since start 115 var nanosecondsInTick int64 = 16000 // nanoseconds per each tick 116 117 func initMonotonicTimer() { 118 ticksCount = 0 119 120 interrupt.New(avr.IRQ_TIMER0_OVF, func(i interrupt.Interrupt) { 121 // use volatile 122 ticks := volatile.LoadUint64((*uint64)(unsafe.Pointer(&ticksCount))) 123 ticks += uint64(nanosecondsInTick) 124 volatile.StoreUint64((*uint64)(unsafe.Pointer(&ticksCount)), ticks) 125 }) 126 127 // initial initialization of the Timer0 128 // - Mask interrupt 129 avr.TIMSK0.ClearBits(avr.TIMSK0_TOIE0 | avr.TIMSK0_OCIE0A | avr.TIMSK0_OCIE0B) 130 131 // - Write new values to TCNT2, OCR2x, and TCCR2x. 132 avr.TCNT0.Set(0) 133 avr.OCR0A.Set(0xff) 134 // - Set mode 3 135 avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01) 136 // - Set prescaler 1 137 avr.TCCR0B.Set(avr.TCCR0B_CS00) 138 139 // - Unmask interrupt 140 avr.TIMSK0.SetBits(avr.TIMSK0_TOIE0) 141 } 142 143 //go:linkname adjustMonotonicTimer machine.adjustMonotonicTimer 144 func adjustMonotonicTimer() { 145 // adjust the nanosecondsInTick using volatile 146 mask := interrupt.Disable() 147 volatile.StoreUint64((*uint64)(unsafe.Pointer(&nanosecondsInTick)), uint64(currentNanosecondsInTick())) 148 interrupt.Restore(mask) 149 } 150 151 func currentNanosecondsInTick() int64 { 152 // this time depends on clk_IO, prescale, mode and OCR0A 153 // assuming the clock source is CPU clock 154 prescaler := int64(avr.TCCR0B.Get() & 0x7) 155 clock := (int64(1e12) / prescaler) / int64(machine.CPUFrequency()) 156 mode := avr.TCCR0A.Get() & 0x7 157 158 /* 159 Mode WGM02 WGM01 WGM00 Timer/Counter TOP Update of TOV Flag 160 Mode of Operation OCRx at Set on 161 0 0 0 0 Normal 0xFF Immediate MAX 162 1 0 0 1 PWM, Phase Correct 0xFF TOP BOTTOM 163 2 0 1 0 CTC OCRA Immediate MAX 164 3 0 1 1 Fast PWM 0xFF BOTTOM MAX 165 5 1 0 1 PWM, Phase Correct OCRA TOP BOTTOM 166 7 1 1 1 Fast PWM OCRA BOTTOM TOP 167 */ 168 switch mode { 169 case 0, 3: 170 // normal & fast PWM 171 // TOV0 Interrupt when moving from MAX (0xff) to 0x00 172 return clock * 256 / 1000 173 case 1: 174 // Phase Correct PWM 175 // TOV0 Interrupt when moving from MAX (0xff) to 0x00 176 return clock * 256 * 2 / 1000 177 case 2, 7: 178 // CTC & fast PWM 179 // TOV0 Interrupt when moving from MAX (OCRA) to 0x00 180 return clock * int64(avr.OCR0A.Get()) / 1000 181 case 5: 182 // Phase Correct PWM 183 // TOV0 Interrupt when moving from MAX (OCRA) to 0x00 184 return clock * int64(avr.OCR0A.Get()) * 2 / 1000 185 } 186 187 return clock / 1000 // for unknown 188 }