mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 11:37:46 +00:00
cbaa58a2d9
This is the kind that is used in Go (actually CGo) for exporting functions. I think it's best to use //export instead of our custom //go:export pragma, for consistency (they are equivalent in TinyGo). Therefore I've updated all instances to the standard format (except for two that are updated in https://github.com/tinygo-org/tinygo/pull/1024). No smoke tests changed (when comparing the output hash), except for some wasm tests that include DWARF debug info and tend to be flaky anyway.
306 lines
9.2 KiB
Go
306 lines
9.2 KiB
Go
// +build sam,atsamd51
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"device/arm"
|
|
"device/sam"
|
|
"machine"
|
|
"runtime/interrupt"
|
|
"runtime/volatile"
|
|
)
|
|
|
|
type timeUnit int64
|
|
|
|
func postinit() {}
|
|
|
|
//export Reset_Handler
|
|
func main() {
|
|
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) {
|
|
// disable IRQ for CMP0 compare
|
|
sam.RTC_MODE0.INTFLAG.SetBits(sam.RTC_MODE0_INTENSET_CMP0)
|
|
|
|
timerWakeup.Set(1)
|
|
})
|
|
irq.SetPriority(0xc0)
|
|
irq.Enable()
|
|
}
|
|
|
|
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
|
|
)
|
|
|
|
var timerWakeup volatile.Register8
|
|
|
|
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.Set(0)
|
|
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.Get() == 0 {
|
|
arm.Asm("wfi")
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|