mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 12:33:42 +00:00
2c15f36702
* runtime/atsamd51: fix clock init code The DPLL0 initialization should set LDRFRAC and LDR, not LDRFRAC twice. Also explain what the magic numbers are doing.
308 lines
9.2 KiB
Go
308 lines
9.2 KiB
Go
// +build sam,atsamd51
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"device/arm"
|
|
"device/sam"
|
|
"machine"
|
|
)
|
|
|
|
type timeUnit int64
|
|
|
|
//go:export Reset_Handler
|
|
func main() {
|
|
preinit()
|
|
initAll()
|
|
callMain()
|
|
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.Set(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) {
|
|
}
|
|
|
|
arm.SetPriority(sam.IRQ_RTC, 0xc0)
|
|
arm.EnableIRQ(sam.IRQ_RTC)
|
|
}
|
|
|
|
func waitForSync() {
|
|
for sam.RTC_MODE0.SYNCBUSY.HasBits(sam.RTC_MODE0_SYNCBUSY_COUNT) {
|
|
}
|
|
}
|
|
|
|
// treat all ticks params coming from runtime as being in microseconds
|
|
const tickMicros = 1000
|
|
|
|
var (
|
|
timestamp timeUnit // ticks since boottime
|
|
timerLastCounter uint64
|
|
)
|
|
|
|
//go:volatile
|
|
type isrFlag bool
|
|
|
|
var timerWakeup isrFlag
|
|
|
|
const asyncScheduler = false
|
|
|
|
// sleepTicks should sleep for d number of microseconds.
|
|
func sleepTicks(d timeUnit) {
|
|
for d != 0 {
|
|
ticks() // update timestamp
|
|
ticks := uint32(d)
|
|
timerSleep(ticks)
|
|
d -= timeUnit(ticks)
|
|
}
|
|
}
|
|
|
|
// ticks returns number of microseconds since start.
|
|
func ticks() timeUnit {
|
|
waitForSync()
|
|
|
|
rtcCounter := (uint64(sam.RTC_MODE0.COUNT.Get()) * 305) / 10 // each counter tick == 30.5us
|
|
offset := (rtcCounter - timerLastCounter) // change since last measurement
|
|
timerLastCounter = rtcCounter
|
|
timestamp += timeUnit(offset) // TODO: not precise
|
|
return timestamp
|
|
}
|
|
|
|
// ticks are in microseconds
|
|
func timerSleep(ticks uint32) {
|
|
timerWakeup = false
|
|
if ticks < 260 {
|
|
// 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 = 260
|
|
}
|
|
|
|
// request read of count
|
|
waitForSync()
|
|
|
|
// set compare value
|
|
cnt := sam.RTC_MODE0.COUNT.Get()
|
|
|
|
sam.RTC_MODE0.COMP[0].Set(uint32(cnt) + (ticks * 10 / 305)) // each counter tick == 30.5us
|
|
|
|
// enable IRQ for CMP0 compare
|
|
sam.RTC_MODE0.INTENSET.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
|
|
|
for !timerWakeup {
|
|
arm.Asm("wfi")
|
|
}
|
|
}
|
|
|
|
//go:export RTC_IRQHandler
|
|
func handleRTC() {
|
|
// disable IRQ for CMP0 compare
|
|
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
|
|
|
timerWakeup = true
|
|
}
|
|
|
|
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[10].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[40].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
|
sam.GCLK_PCHCTRL_CHEN)
|
|
sam.GCLK.PCHCTRL[41].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
|
sam.GCLK_PCHCTRL_CHEN)
|
|
}
|