Files
tinygo/src/runtime/runtime_atsamd51.go
T
Ayke van Laethem 8cd2a462b9 runtime: remove the asyncScheduler constant
There is no reason to specialize this per chip as it is only ever used
for JavaScript. Not only that, it is causing confusion and is yet
another quirk to learn when porting the runtime to a new
microcontroller.
2021-05-08 23:08:12 +02:00

361 lines
11 KiB
Go

// +build sam,atsamd51 sam,atsame5x
package runtime
import (
"device/arm"
"device/sam"
"machine"
"runtime/interrupt"
"runtime/volatile"
)
type timeUnit int64
func postinit() {}
//export Reset_Handler
func main() {
arm.SCB.CPACR.Set(0) // disable FPU if it is enabled
preinit()
run()
abort()
}
func init() {
initClocks()
initRTC()
initSERCOMClocks()
initUSBClock()
initADCClock()
// connect to USB CDC interface
machine.UART0.Configure(machine.UARTConfig{})
}
func putchar(c byte) {
machine.UART0.WriteByte(c)
}
func initClocks() {
// set flash wait state
sam.NVMCTRL.CTRLA.SetBits(0 << sam.NVMCTRL_CTRLA_RWS_Pos)
// software reset
sam.GCLK.CTRLA.SetBits(sam.GCLK_CTRLA_SWRST)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_SWRST) {
}
// Set OSCULP32K as source of Generic Clock Generator 3
// GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_XOSC32K].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSCULP32K) | GCLK_GENCTRL_GENEN; //generic clock gen 3
sam.GCLK.GENCTRL[3].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK3) {
}
// Set OSCULP32K as source of Generic Clock Generator 0
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_OSCULP32K << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
}
// Enable DFLL48M clock
sam.OSCCTRL.DFLLCTRLA.Set(0)
sam.OSCCTRL.DFLLMUL.Set((0x1 << sam.OSCCTRL_DFLLMUL_CSTEP_Pos) |
(0x1 << sam.OSCCTRL_DFLLMUL_FSTEP_Pos) |
(0x0 << sam.OSCCTRL_DFLLMUL_MUL_Pos))
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLMUL) {
}
sam.OSCCTRL.DFLLCTRLB.Set(0)
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLCTRLB) {
}
sam.OSCCTRL.DFLLCTRLA.SetBits(sam.OSCCTRL_DFLLCTRLA_ENABLE)
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_ENABLE) {
}
sam.OSCCTRL.DFLLVAL.Set(sam.OSCCTRL.DFLLVAL.Get())
for sam.OSCCTRL.DFLLSYNC.HasBits(sam.OSCCTRL_DFLLSYNC_DFLLVAL) {
}
sam.OSCCTRL.DFLLCTRLB.Set(sam.OSCCTRL_DFLLCTRLB_WAITLOCK |
sam.OSCCTRL_DFLLCTRLB_CCDIS |
sam.OSCCTRL_DFLLCTRLB_USBCRM)
for !sam.OSCCTRL.STATUS.HasBits(sam.OSCCTRL_STATUS_DFLLRDY) {
}
// set GCLK7 to run at 2MHz, using DFLL48M as clock source
// GCLK7 = 48MHz / 24 = 2MHz
sam.GCLK.GENCTRL[7].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
(24 << sam.GCLK_GENCTRL_DIV_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK7) {
}
// Set up the PLLs
// Set PLL0 to run at 120MHz, using GCLK7 as clock source
sam.GCLK.PCHCTRL[1].Set(sam.GCLK_PCHCTRL_CHEN |
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
// multiplier = 59 + 1 + (0/32) = 60
// PLL0 = 2MHz * 60 = 120MHz
sam.OSCCTRL.DPLL[0].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
(59 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos))
for sam.OSCCTRL.DPLL[0].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
}
// MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51, via Adafruit lib.
sam.OSCCTRL.DPLL[0].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
sam.OSCCTRL.DPLL[0].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
for !sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_CLKRDY) ||
!sam.OSCCTRL.DPLL[0].DPLLSTATUS.HasBits(sam.OSCCTRL_DPLL_DPLLSTATUS_LOCK) {
}
// Set PLL1 to run at 100MHz, using GCLK7 as clock source
sam.GCLK.PCHCTRL[2].Set(sam.GCLK_PCHCTRL_CHEN |
(sam.GCLK_PCHCTRL_GEN_GCLK7 << sam.GCLK_PCHCTRL_GEN_Pos))
// multiplier = 49 + 1 + (0/32) = 50
// PLL1 = 2MHz * 50 = 100MHz
sam.OSCCTRL.DPLL[1].DPLLRATIO.Set((0x0 << sam.OSCCTRL_DPLL_DPLLRATIO_LDRFRAC_Pos) |
(49 << sam.OSCCTRL_DPLL_DPLLRATIO_LDR_Pos))
for sam.OSCCTRL.DPLL[1].DPLLSYNCBUSY.HasBits(sam.OSCCTRL_DPLL_DPLLSYNCBUSY_DPLLRATIO) {
}
// // MUST USE LBYPASS DUE TO BUG IN REV A OF SAMD51
sam.OSCCTRL.DPLL[1].DPLLCTRLB.Set((sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_GCLK << sam.OSCCTRL_DPLL_DPLLCTRLB_REFCLK_Pos) |
sam.OSCCTRL_DPLL_DPLLCTRLB_LBYPASS)
sam.OSCCTRL.DPLL[1].DPLLCTRLA.Set(sam.OSCCTRL_DPLL_DPLLCTRLA_ENABLE)
// for !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_CLKRDY) ||
// !sam.OSCCTRL.DPLLSTATUS1.HasBits(sam.OSCCTRL_DPLLSTATUS_LOCK) {
// }
// Set up the peripheral clocks
// Set 48MHZ CLOCK FOR USB
sam.GCLK.GENCTRL[1].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_IDC |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK1) {
}
// // Set 100MHZ CLOCK FOR OTHER PERIPHERALS
// sam.GCLK.GENCTRL2.Set((sam.GCLK_GENCTRL_SRC_DPLL1 << sam.GCLK_GENCTRL_SRC_Pos) |
// sam.GCLK_GENCTRL_IDC |
// sam.GCLK_GENCTRL_GENEN)
// for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL2) {
// }
// // Set 12MHZ CLOCK FOR DAC
sam.GCLK.GENCTRL[4].Set((sam.GCLK_GENCTRL_SRC_DFLL << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_IDC |
(4 << sam.GCLK_GENCTRL_DIVSEL_Pos) |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK4) {
}
// // Set up main clock
sam.GCLK.GENCTRL[0].Set((sam.GCLK_GENCTRL_SRC_DPLL0 << sam.GCLK_GENCTRL_SRC_Pos) |
sam.GCLK_GENCTRL_IDC |
sam.GCLK_GENCTRL_GENEN)
for sam.GCLK.SYNCBUSY.HasBits(sam.GCLK_SYNCBUSY_GENCTRL_GCLK0) {
}
sam.MCLK.CPUDIV.Set(sam.MCLK_CPUDIV_DIV_DIV1)
// Use the LDO regulator by default
sam.SUPC.VREG.ClearBits(sam.SUPC_VREG_SEL)
// Start up the "Debug Watchpoint and Trace" unit, so that we can use
// it's 32bit cycle counter for timing.
//CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
//DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
func initRTC() {
// turn on digital interface clock
sam.MCLK.APBAMASK.SetBits(sam.MCLK_APBAMASK_RTC_)
// disable RTC
sam.RTC_MODE0.CTRLA.ClearBits(sam.RTC_MODE0_CTRLA_ENABLE)
//sam.RTC_MODE0.CTRLA.Set(0)
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
}
// reset RTC
sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_SWRST)
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_SWRST) {
}
// set to use ulp 32k oscillator
sam.OSC32KCTRL.OSCULP32K.SetBits(sam.OSC32KCTRL_OSCULP32K_EN32K)
sam.OSC32KCTRL.RTCCTRL.Set(sam.OSC32KCTRL_RTCCTRL_RTCSEL_ULP32K)
// set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1
sam.RTC_MODE0.CTRLA.Set((sam.RTC_MODE0_CTRLA_MODE_COUNT32 << sam.RTC_MODE0_CTRLA_MODE_Pos) |
(sam.RTC_MODE0_CTRLA_PRESCALER_DIV1 << sam.RTC_MODE0_CTRLA_PRESCALER_Pos) |
(sam.RTC_MODE0_CTRLA_COUNTSYNC))
// re-enable RTC
sam.RTC_MODE0.CTRLA.SetBits(sam.RTC_MODE0_CTRLA_ENABLE)
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_ENABLE) {
}
irq := interrupt.New(sam.IRQ_RTC, func(interrupt.Interrupt) {
flags := sam.RTC_MODE0.INTFLAG.Get()
if flags&sam.RTC_MODE0_INTENSET_CMP0 != 0 {
// The timer (for a sleep) has expired.
timerWakeup.Set(1)
}
if flags&sam.RTC_MODE0_INTENSET_OVF != 0 {
// The 32-bit RTC timer has overflowed.
rtcOverflows.Set(rtcOverflows.Get() + 1)
}
// Mark this interrupt has handled for CMP0 and OVF.
sam.RTC_MODE0.INTFLAG.Set(sam.RTC_MODE0_INTENSET_CMP0 | sam.RTC_MODE0_INTENSET_OVF)
})
sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_OVF)
irq.SetPriority(0xc0)
irq.Enable()
}
func waitForSync() {
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_COUNT) {
}
}
var rtcOverflows volatile.Register32 // number of times the RTC wrapped around
var timerWakeup volatile.Register8
// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
func ticksToNanoseconds(ticks timeUnit) int64 {
// The following calculation is actually the following, but with both sides
// reduced to reduce the risk of overflow:
// ticks * 1e9 / 32768
return int64(ticks) * 1953125 / 64
}
// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
func nanosecondsToTicks(ns int64) timeUnit {
// The following calculation is actually the following, but with both sides
// reduced to reduce the risk of overflow:
// ns * 32768 / 1e9
return timeUnit(ns * 64 / 1953125)
}
// sleepTicks should sleep for d number of microseconds.
func sleepTicks(d timeUnit) {
for d != 0 {
ticks := uint32(d)
if !timerSleep(ticks) {
return
}
d -= timeUnit(ticks)
}
}
// ticks returns the elapsed time since reset.
func ticks() timeUnit {
// For some ways of capturing the time atomically, see this thread:
// https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617
// Here, instead of re-reading the counter register if an overflow has been
// detected, we simply try again because that results in smaller code.
for {
mask := interrupt.Disable()
counter := readRTC()
overflows := rtcOverflows.Get()
hasOverflow := sam.RTC_MODE0.INTFLAG.Get()&sam.RTC_MODE0_INTENSET_OVF != 0
interrupt.Restore(mask)
if hasOverflow {
// There was an overflow while trying to capture the timer.
// Try again.
continue
}
// This is a 32-bit timer, so the number of timer overflows forms the
// upper 32 bits of this timer.
return timeUnit(overflows)<<32 + timeUnit(counter)
}
}
func readRTC() uint32 {
waitForSync()
return sam.RTC_MODE0.COUNT.Get()
}
// ticks are in microseconds
// Returns true if the timer completed.
// Returns false if another interrupt occured which requires an early return to scheduler.
func timerSleep(ticks uint32) bool {
timerWakeup.Set(0)
if ticks < 8 {
// due to delay waiting for the register value to sync, the minimum sleep value
// for the SAMD51 is 260us.
// For related info for SAMD21, see:
// https://community.atmel.com/comment/2507091#comment-2507091
ticks = 8
}
// request read of count
waitForSync()
// set compare value
cnt := sam.RTC_MODE0.COUNT.Get()
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + ticks)
// enable IRQ for CMP0 compare
sam.RTC_MODE0.INTENSET.Set(sam.RTC_MODE0_INTENSET_CMP0)
wait:
waitForEvents()
if timerWakeup.Get() != 0 {
return true
}
if hasScheduler {
// The interurpt may have awoken a goroutine, so bail out early.
// Disable IRQ for CMP0 compare.
sam.RTC_MODE0.INTENCLR.Set(sam.RTC_MODE0_INTENSET_CMP0)
return false
} else {
// This is running without a scheduler.
// The application expects this to sleep the whole time.
goto wait
}
}
func initUSBClock() {
// Turn on clock(s) for USB
//MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
//MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_USB_)
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_USB_)
// Put Generic Clock Generator 1 as source for USB
//GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_USB].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
func initADCClock() {
// Turn on clocks for ADC0/ADC1.
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC0_)
sam.MCLK.APBDMASK.SetBits(sam.MCLK_APBDMASK_ADC1_)
// Put Generic Clock Generator 1 as source for ADC0 and ADC1.
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_ADC1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
sam.GCLK_PCHCTRL_CHEN)
}
func waitForEvents() {
arm.Asm("wfe")
}