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  }