//go:build (sam && atsamd51) || (sam && atsame5x) package runtime import ( "device/arm" "device/sam" "machine" "machine/usb/cdc" "runtime/interrupt" "runtime/volatile" ) type timeUnit int64 //export Reset_Handler func main() { arm.SCB.CPACR.Set(0) // disable FPU if it is enabled preinit() run() exit(0) } func init() { initClocks() initRTC() initSERCOMClocks() initUSBClock() initADCClock() enableCache() cdc.EnableUSBCDC() machine.USBDev.Configure(machine.UARTConfig{}) machine.InitSerial() } func putchar(c byte) { machine.Serial.WriteByte(c) } func getchar() byte { for machine.Serial.Buffered() == 0 { Gosched() } v, _ := machine.Serial.ReadByte() return v } func buffered() int { return machine.Serial.Buffered() } 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; // Disable automatic NVM write operations sam.NVMCTRL.SetCTRLA_WMODE(sam.NVMCTRL_CTRLA_WMODE_MAN) } 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 occurred 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 enableCache() { sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN) } func waitForEvents() { arm.Asm("wfe") }