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()