mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
8cd2a462b9
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.
361 lines
11 KiB
Go
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")
|
|
}
|