github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_atsamd21.go (about)

     1  //go:build sam && atsamd21
     2  
     3  package runtime
     4  
     5  import (
     6  	"device/arm"
     7  	"device/sam"
     8  	"machine"
     9  	"machine/usb/cdc"
    10  	"runtime/interrupt"
    11  	"runtime/volatile"
    12  	"unsafe"
    13  )
    14  
    15  type timeUnit int64
    16  
    17  //export Reset_Handler
    18  func main() {
    19  	preinit()
    20  	run()
    21  	exit(0)
    22  }
    23  
    24  func init() {
    25  	initClocks()
    26  	initRTC()
    27  	initSERCOMClocks()
    28  	initUSBClock()
    29  	initADCClock()
    30  
    31  	cdc.EnableUSBCDC()
    32  	machine.USBDev.Configure(machine.UARTConfig{})
    33  	machine.InitSerial()
    34  }
    35  
    36  func putchar(c byte) {
    37  	machine.Serial.WriteByte(c)
    38  }
    39  
    40  func getchar() byte {
    41  	for machine.Serial.Buffered() == 0 {
    42  		Gosched()
    43  	}
    44  	v, _ := machine.Serial.ReadByte()
    45  	return v
    46  }
    47  
    48  func buffered() int {
    49  	return machine.Serial.Buffered()
    50  }
    51  
    52  func initClocks() {
    53  	// Set 1 Flash Wait State for 48MHz, required for 3.3V operation according to SAMD21 Datasheet
    54  	sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_RWS_HALF << sam.NVMCTRL_CTRLB_RWS_Pos)
    55  
    56  	// Turn on the digital interface clock
    57  	sam.PM.APBAMASK.SetBits(sam.PM_APBAMASK_GCLK_)
    58  	// turn off RTC
    59  	sam.PM.APBAMASK.ClearBits(sam.PM_APBAMASK_RTC_)
    60  
    61  	// Enable OSC32K clock (Internal 32.768Hz oscillator).
    62  	// This requires registers that are not included in the SVD file.
    63  	// This is from samd21g18a.h and nvmctrl.h:
    64  	//
    65  	// #define NVMCTRL_OTP4 0x00806020
    66  	//
    67  	// #define SYSCTRL_FUSES_OSC32K_CAL_ADDR (NVMCTRL_OTP4 + 4)
    68  	// #define SYSCTRL_FUSES_OSC32K_CAL_Pos 6 /** (NVMCTRL_OTP4) OSC32K Calibration */
    69  	// #define SYSCTRL_FUSES_OSC32K_CAL_Msk (0x7Fu << SYSCTRL_FUSES_OSC32K_CAL_Pos)
    70  	// #define SYSCTRL_FUSES_OSC32K_CAL(value) ((SYSCTRL_FUSES_OSC32K_CAL_Msk & ((value) << SYSCTRL_FUSES_OSC32K_CAL_Pos)))
    71  	// u32_t fuse = *(u32_t *)FUSES_OSC32K_CAL_ADDR;
    72  	// u32_t calib = (fuse & FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos;
    73  	fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
    74  	calib := (fuse & uint32(0x7f<<6)) >> 6
    75  
    76  	// SYSCTRL_OSC32K_CALIB(calib) |
    77  	//  SYSCTRL_OSC32K_STARTUP(0x6u) |
    78  	//  SYSCTRL_OSC32K_EN32K | SYSCTRL_OSC32K_ENABLE;
    79  	sam.SYSCTRL.OSC32K.Set((calib << sam.SYSCTRL_OSC32K_CALIB_Pos) |
    80  		(0x6 << sam.SYSCTRL_OSC32K_STARTUP_Pos) |
    81  		sam.SYSCTRL_OSC32K_EN32K |
    82  		sam.SYSCTRL_OSC32K_EN1K |
    83  		sam.SYSCTRL_OSC32K_ENABLE)
    84  	// Wait for oscillator stabilization
    85  	for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_OSC32KRDY) {
    86  	}
    87  
    88  	// Software reset the module to ensure it is re-initialized correctly
    89  	sam.GCLK.CTRL.Set(sam.GCLK_CTRL_SWRST)
    90  	// Wait for reset to complete
    91  	for sam.GCLK.CTRL.HasBits(sam.GCLK_CTRL_SWRST) && sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
    92  	}
    93  
    94  	// Put OSC32K as source of Generic Clock Generator 1
    95  	sam.GCLK.GENDIV.Set((1 << sam.GCLK_GENDIV_ID_Pos) |
    96  		(0 << sam.GCLK_GENDIV_DIV_Pos))
    97  	waitForSync()
    98  
    99  	// GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_OSC32K | GCLK_GENCTRL_GENEN;
   100  	sam.GCLK.GENCTRL.Set((1 << sam.GCLK_GENCTRL_ID_Pos) |
   101  		(sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) |
   102  		sam.GCLK_GENCTRL_GENEN)
   103  	waitForSync()
   104  
   105  	// Use Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference)
   106  	sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_DFLL48 << sam.GCLK_CLKCTRL_ID_Pos) |
   107  		(sam.GCLK_CLKCTRL_GEN_GCLK1 << sam.GCLK_CLKCTRL_GEN_Pos) |
   108  		sam.GCLK_CLKCTRL_CLKEN)
   109  	waitForSync()
   110  
   111  	// Remove the OnDemand mode, Bug http://avr32.icgroup.norway.atmel.com/bugzilla/show_bug.cgi?id=9905
   112  	sam.SYSCTRL.DFLLCTRL.Set(sam.SYSCTRL_DFLLCTRL_ENABLE)
   113  	// Wait for ready
   114  	for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) {
   115  	}
   116  
   117  	// Handle DFLL calibration based on info learned from Arduino SAMD implementation,
   118  	// using value stored in fuse.
   119  	// #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL_ADDR (NVMCTRL_OTP4 + 4)
   120  	// #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Pos 26           /**< \brief (NVMCTRL_OTP4) DFLL48M Coarse Calibration */
   121  	// #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Msk (0x3Fu << SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Pos)
   122  	// #define SYSCTRL_FUSES_DFLL48M_COARSE_CAL(value) ((SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Msk & ((value) << SYSCTRL_FUSES_DFLL48M_COARSE_CAL_Pos)))
   123  	coarse := (fuse >> 26) & 0x3F
   124  	if coarse == 0x3f {
   125  		coarse = 0x1f
   126  	}
   127  
   128  	sam.SYSCTRL.DFLLVAL.SetBits(coarse << sam.SYSCTRL_DFLLVAL_COARSE_Pos)
   129  	sam.SYSCTRL.DFLLVAL.SetBits(0x1ff << sam.SYSCTRL_DFLLVAL_FINE_Pos)
   130  
   131  	// Write full configuration to DFLL control register
   132  	// SYSCTRL_DFLLMUL_CSTEP( 0x1f / 4 ) | // Coarse step is 31, half of the max value
   133  	// SYSCTRL_DFLLMUL_FSTEP( 10 ) |
   134  	// SYSCTRL_DFLLMUL_MUL( (48000) ) ;
   135  	sam.SYSCTRL.DFLLMUL.Set(((31 / 4) << sam.SYSCTRL_DFLLMUL_CSTEP_Pos) |
   136  		(10 << sam.SYSCTRL_DFLLMUL_FSTEP_Pos) |
   137  		(48000 << sam.SYSCTRL_DFLLMUL_MUL_Pos))
   138  
   139  	// disable DFLL
   140  	sam.SYSCTRL.DFLLCTRL.Set(0)
   141  	waitForSync()
   142  
   143  	sam.SYSCTRL.DFLLCTRL.SetBits(sam.SYSCTRL_DFLLCTRL_MODE |
   144  		sam.SYSCTRL_DFLLCTRL_CCDIS |
   145  		sam.SYSCTRL_DFLLCTRL_USBCRM |
   146  		sam.SYSCTRL_DFLLCTRL_BPLCKC)
   147  	// Wait for ready
   148  	for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) {
   149  	}
   150  
   151  	// Re-enable the DFLL
   152  	sam.SYSCTRL.DFLLCTRL.SetBits(sam.SYSCTRL_DFLLCTRL_ENABLE)
   153  	// Wait for ready
   154  	for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_DFLLRDY) {
   155  	}
   156  
   157  	// Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz.
   158  	sam.GCLK.GENDIV.Set((0 << sam.GCLK_GENDIV_ID_Pos) |
   159  		(0 << sam.GCLK_GENDIV_DIV_Pos))
   160  	waitForSync()
   161  
   162  	sam.GCLK.GENCTRL.Set((0 << sam.GCLK_GENCTRL_ID_Pos) |
   163  		(sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) |
   164  		sam.GCLK_GENCTRL_IDC |
   165  		sam.GCLK_GENCTRL_GENEN)
   166  	waitForSync()
   167  
   168  	// Modify PRESCaler value of OSC8M to have 8MHz
   169  	sam.SYSCTRL.OSC8M.SetBits(sam.SYSCTRL_OSC8M_PRESC_0 << sam.SYSCTRL_OSC8M_PRESC_Pos)
   170  	sam.SYSCTRL.OSC8M.ClearBits(1 << sam.SYSCTRL_OSC8M_ONDEMAND_Pos)
   171  	// Wait for oscillator stabilization
   172  	for !sam.SYSCTRL.PCLKSR.HasBits(sam.SYSCTRL_PCLKSR_OSC8MRDY) {
   173  	}
   174  
   175  	// Use OSC8M as source for Generic Clock Generator 3
   176  	sam.GCLK.GENDIV.Set((3 << sam.GCLK_GENDIV_ID_Pos))
   177  	waitForSync()
   178  
   179  	sam.GCLK.GENCTRL.Set((3 << sam.GCLK_GENCTRL_ID_Pos) |
   180  		(sam.GCLK_GENCTRL_SRC_OSC8M << sam.GCLK_GENCTRL_SRC_Pos) |
   181  		sam.GCLK_GENCTRL_GENEN)
   182  	waitForSync()
   183  
   184  	// Use OSC32K as source for Generic Clock Generator 2
   185  	// OSC32K/1 -> GCLK2 at 32KHz
   186  	sam.GCLK.GENDIV.Set(2 << sam.GCLK_GENDIV_ID_Pos)
   187  	waitForSync()
   188  
   189  	sam.GCLK.GENCTRL.Set((2 << sam.GCLK_GENCTRL_ID_Pos) |
   190  		(sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) |
   191  		sam.GCLK_GENCTRL_GENEN)
   192  	waitForSync()
   193  
   194  	// Use GCLK2 for RTC
   195  	sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_RTC << sam.GCLK_CLKCTRL_ID_Pos) |
   196  		(sam.GCLK_CLKCTRL_GEN_GCLK2 << sam.GCLK_CLKCTRL_GEN_Pos) |
   197  		sam.GCLK_CLKCTRL_CLKEN)
   198  	waitForSync()
   199  
   200  	// Set the CPU, APBA, B, and C dividers
   201  	sam.PM.CPUSEL.Set(sam.PM_CPUSEL_CPUDIV_DIV1)
   202  	sam.PM.APBASEL.Set(sam.PM_APBASEL_APBADIV_DIV1)
   203  	sam.PM.APBBSEL.Set(sam.PM_APBBSEL_APBBDIV_DIV1)
   204  	sam.PM.APBCSEL.Set(sam.PM_APBCSEL_APBCDIV_DIV1)
   205  
   206  	// Disable automatic NVM write operations
   207  	sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_MANW)
   208  }
   209  
   210  func initRTC() {
   211  	// turn on digital interface clock
   212  	sam.PM.APBAMASK.SetBits(sam.PM_APBAMASK_RTC_)
   213  
   214  	// disable RTC
   215  	sam.RTC_MODE0.CTRL.Set(0)
   216  	waitForSync()
   217  
   218  	// reset RTC
   219  	sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_SWRST)
   220  	waitForSync()
   221  
   222  	// set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1
   223  	sam.RTC_MODE0.CTRL.Set((sam.RTC_MODE0_CTRL_MODE_COUNT32 << sam.RTC_MODE0_CTRL_MODE_Pos) |
   224  		(sam.RTC_MODE0_CTRL_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos))
   225  	waitForSync()
   226  
   227  	// re-enable RTC
   228  	sam.RTC_MODE0.CTRL.SetBits(sam.RTC_MODE0_CTRL_ENABLE)
   229  	waitForSync()
   230  
   231  	rtcInterrupt := interrupt.New(sam.IRQ_RTC, func(intr interrupt.Interrupt) {
   232  		flags := sam.RTC_MODE0.INTFLAG.Get()
   233  		if flags&sam.RTC_MODE0_INTENSET_CMP0 != 0 {
   234  			// The timer (for a sleep) has expired.
   235  			timerWakeup.Set(1)
   236  		}
   237  		if flags&sam.RTC_MODE0_INTENSET_OVF != 0 {
   238  			// The 32-bit RTC timer has overflowed.
   239  			rtcOverflows.Set(rtcOverflows.Get() + 1)
   240  		}
   241  		// Mark this interrupt has handled for CMP0 and OVF.
   242  		sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0 | sam.RTC_MODE0_INTENSET_OVF)
   243  	})
   244  	sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_OVF)
   245  	rtcInterrupt.SetPriority(0xc0)
   246  	rtcInterrupt.Enable()
   247  }
   248  
   249  func waitForSync() {
   250  	for sam.GCLK.STATUS.HasBits(sam.GCLK_STATUS_SYNCBUSY) {
   251  	}
   252  }
   253  
   254  var rtcOverflows volatile.Register32 // number of times the RTC wrapped around
   255  
   256  var timerWakeup volatile.Register8
   257  
   258  // ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
   259  func ticksToNanoseconds(ticks timeUnit) int64 {
   260  	// The following calculation is actually the following, but with both sides
   261  	// reduced to reduce the risk of overflow:
   262  	//     ticks * 1e9 / 32768
   263  	return int64(ticks) * 1953125 / 64
   264  }
   265  
   266  // nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
   267  func nanosecondsToTicks(ns int64) timeUnit {
   268  	// The following calculation is actually the following, but with both sides
   269  	// reduced to reduce the risk of overflow:
   270  	//     ns * 32768 / 1e9
   271  	return timeUnit(ns * 64 / 1953125)
   272  }
   273  
   274  // sleepTicks should sleep for d number of microseconds.
   275  func sleepTicks(d timeUnit) {
   276  	for d != 0 {
   277  		ticks := uint32(d)
   278  		if !timerSleep(ticks) {
   279  			// Bail out early to handle a non-time interrupt.
   280  			return
   281  		}
   282  		d -= timeUnit(ticks)
   283  	}
   284  }
   285  
   286  // ticks returns the elapsed time since reset.
   287  func ticks() timeUnit {
   288  	// For some ways of capturing the time atomically, see this thread:
   289  	// https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617
   290  	// Here, instead of re-reading the counter register if an overflow has been
   291  	// detected, we simply try again because that results in smaller code.
   292  	for {
   293  		mask := interrupt.Disable()
   294  		counter := readRTC()
   295  		overflows := rtcOverflows.Get()
   296  		hasOverflow := sam.RTC_MODE0.INTFLAG.Get()&sam.RTC_MODE0_INTENSET_OVF != 0
   297  		interrupt.Restore(mask)
   298  
   299  		if hasOverflow {
   300  			// There was an overflow while trying to capture the timer.
   301  			// Try again.
   302  			continue
   303  		}
   304  
   305  		// This is a 32-bit timer, so the number of timer overflows forms the
   306  		// upper 32 bits of this timer.
   307  		return timeUnit(overflows)<<32 + timeUnit(counter)
   308  	}
   309  }
   310  
   311  func readRTC() uint32 {
   312  	// request read of count
   313  	sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ)
   314  	waitForSync()
   315  
   316  	return sam.RTC_MODE0.COUNT.Get()
   317  }
   318  
   319  // ticks are in microseconds
   320  // Returns true if the timer completed.
   321  // Returns false if another interrupt occured which requires an early return to scheduler.
   322  func timerSleep(ticks uint32) bool {
   323  	timerWakeup.Set(0)
   324  	if ticks < 7 {
   325  		// Due to around 6 clock ticks delay waiting for the register value to
   326  		// sync, the minimum sleep value for the SAMD21 is 214us.
   327  		// For related info, see:
   328  		// https://community.atmel.com/comment/2507091#comment-2507091
   329  		ticks = 7
   330  	}
   331  
   332  	// request read of count
   333  	sam.RTC_MODE0.READREQ.Set(sam.RTC_MODE0_READREQ_RREQ)
   334  	waitForSync()
   335  
   336  	// set compare value
   337  	cnt := sam.RTC_MODE0.COUNT.Get()
   338  	sam.RTC_MODE0.COMP0.Set(uint32(cnt) + ticks)
   339  	waitForSync()
   340  
   341  	// enable IRQ for CMP0 compare
   342  	sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_CMP0)
   343  
   344  wait:
   345  	waitForEvents()
   346  	if timerWakeup.Get() != 0 {
   347  		return true
   348  	}
   349  	if hasScheduler {
   350  		// The interurpt may have awoken a goroutine, so bail out early.
   351  		// Disable IRQ for CMP0 compare.
   352  		sam.RTC_MODE0.INTENCLR.Set(sam.RTC_MODE0_INTENSET_CMP0)
   353  		return false
   354  	} else {
   355  		// This is running without a scheduler.
   356  		// The application expects this to sleep the whole time.
   357  		goto wait
   358  	}
   359  }
   360  
   361  func initUSBClock() {
   362  	// Turn on clock for USB
   363  	sam.PM.APBBMASK.SetBits(sam.PM_APBBMASK_USB_)
   364  
   365  	// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference)
   366  	sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_USB << sam.GCLK_CLKCTRL_ID_Pos) |
   367  		(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
   368  		sam.GCLK_CLKCTRL_CLKEN)
   369  	waitForSync()
   370  }
   371  
   372  func initADCClock() {
   373  	// Turn on clock for ADC
   374  	sam.PM.APBCMASK.SetBits(sam.PM_APBCMASK_ADC_)
   375  
   376  	// Put Generic Clock Generator 0 as source for Generic Clock Multiplexer for ADC.
   377  	sam.GCLK.CLKCTRL.Set((sam.GCLK_CLKCTRL_ID_ADC << sam.GCLK_CLKCTRL_ID_Pos) |
   378  		(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
   379  		sam.GCLK_CLKCTRL_CLKEN)
   380  	waitForSync()
   381  }
   382  
   383  func waitForEvents() {
   384  	arm.Asm("wfe")
   385  }