diff options
author | Ron Evans <[email protected]> | 2019-01-13 21:45:18 +0100 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2019-01-18 18:30:27 +0100 |
commit | e2be7ccf76ae9f8548394e715193e03ab47c18d6 (patch) | |
tree | dcfc568aed5a7bf6692f9407b4694bd510371eab /src/runtime/runtime_atsamd21g18.go | |
parent | 1f511786d3a56c1c665ff013749f8665f7a1a663 (diff) | |
download | tinygo-e2be7ccf76ae9f8548394e715193e03ab47c18d6.tar.gz tinygo-e2be7ccf76ae9f8548394e715193e03ab47c18d6.zip |
sam: add support for Atmel SAMD21 based ItsyBitsy M0
Signed-off-by: Ron Evans <[email protected]>
Diffstat (limited to 'src/runtime/runtime_atsamd21g18.go')
-rw-r--r-- | src/runtime/runtime_atsamd21g18.go | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/runtime/runtime_atsamd21g18.go b/src/runtime/runtime_atsamd21g18.go new file mode 100644 index 000000000..6de9ce54d --- /dev/null +++ b/src/runtime/runtime_atsamd21g18.go @@ -0,0 +1,285 @@ +// +build sam,atsamd21g18a + +package runtime + +import ( + "device/arm" + "device/sam" + "unsafe" +) + +type timeUnit int64 + +//go:export Reset_Handler +func main() { + preinit() + initAll() + mainWrapper() + abort() +} + +func init() { + initClocks() + initRTC() + + // Turn on clock to SERCOM0 for Serial + sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM0_ + + // TODO: connect to UART +} + +func putchar(c byte) { + // TODO: write byte to UART + //machine.UART0.WriteByte(c) +} + +func initClocks() { + // Set 1 Flash Wait State for 48MHz, required for 3.3V operation according to SAMD21 Datasheet + sam.NVMCTRL.CTRLB |= (sam.NVMCTRL_CTRLB_RWS_HALF << sam.NVMCTRL_CTRLB_RWS_Pos) + + // Turn on the digital interface clock + sam.PM.APBAMASK |= sam.PM_APBAMASK_GCLK_ + // turn off RTC + sam.PM.APBAMASK &^= sam.PM_APBAMASK_RTC_ + + // Enable OSC32K clock (Internal 32.768Hz oscillator). + // This requires registers that are not included in the SVD file. + // This is from samd21g18a.h and nvmctrl.h: + // + // #define NVMCTRL_OTP4 0x00806020 + // + // #define SYSCTRL_FUSES_OSC32K_CAL_ADDR (NVMCTRL_OTP4 + 4) + // #define SYSCTRL_FUSES_OSC32K_CAL_Pos 6 /** (NVMCTRL_OTP4) OSC32K Calibration */ + // #define SYSCTRL_FUSES_OSC32K_CAL_Msk (0x7Fu << SYSCTRL_FUSES_OSC32K_CAL_Pos) + // #define SYSCTRL_FUSES_OSC32K_CAL(value) ((SYSCTRL_FUSES_OSC32K_CAL_Msk & ((value) << SYSCTRL_FUSES_OSC32K_CAL_Pos))) + // u32_t fuse = *(u32_t *)FUSES_OSC32K_CAL_ADDR; + // u32_t calib = (fuse & FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos; + fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4)) + calib := (fuse & uint32(0x7f<<6)) >> 6 + + // SYSCTRL_OSC32K_CALIB(calib) | + // SYSCTRL_OSC32K_STARTUP(0x6u) | + // SYSCTRL_OSC32K_EN32K | SYSCTRL_OSC32K_ENABLE; + sam.SYSCTRL.OSC32K = sam.RegValue((calib << sam.SYSCTRL_OSC32K_CALIB_Pos) | + (0x6 << sam.SYSCTRL_OSC32K_STARTUP_Pos) | + sam.SYSCTRL_OSC32K_EN32K | + sam.SYSCTRL_OSC32K_EN1K | + sam.SYSCTRL_OSC32K_ENABLE) + // Wait for oscillator stabilization + for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_OSC32KRDY) == 0 { + } + + // Software reset the module to ensure it is re-initialized correctly + sam.GCLK.CTRL = sam.GCLK_CTRL_SWRST + // Wait for reset to complete + for (sam.GCLK.CTRL&sam.GCLK_CTRL_SWRST) > 0 && (sam.GCLK.STATUS&sam.GCLK_STATUS_SYNCBUSY) > 0 { + } + + // Put OSC32K as source of Generic Clock Generator 1 + sam.GCLK.GENDIV = sam.RegValue((1 << sam.GCLK_GENDIV_ID_Pos) | + (0 << sam.GCLK_GENDIV_DIV_Pos)) + waitForSync() + + // GCLK_GENCTRL_ID(1) | GCLK_GENCTRL_SRC_OSC32K | GCLK_GENCTRL_GENEN; + sam.GCLK.GENCTRL = sam.RegValue((1 << sam.GCLK_GENCTRL_ID_Pos) | + (sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) | + sam.GCLK_GENCTRL_GENEN) + waitForSync() + + // Use Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference) + sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_DFLL48 << sam.GCLK_CLKCTRL_ID_Pos) | + (sam.GCLK_CLKCTRL_GEN_GCLK1 << sam.GCLK_CLKCTRL_GEN_Pos) | + sam.GCLK_CLKCTRL_CLKEN) + waitForSync() + + // Remove the OnDemand mode, Bug http://avr32.icgroup.norway.atmel.com/bugzilla/show_bug.cgi?id=9905 + sam.SYSCTRL.DFLLCTRL = sam.SYSCTRL_DFLLCTRL_ENABLE + // Wait for ready + for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_DFLLRDY) == 0 { + } + + // Handle DFLL calibration based on info learned from Arduino SAMD implementation, + // using default values. + sam.SYSCTRL.DFLLVAL |= (0x1f << sam.SYSCTRL_DFLLVAL_COARSE_Pos) + sam.SYSCTRL.DFLLVAL |= (0x1ff << sam.SYSCTRL_DFLLVAL_FINE_Pos) + + // Write full configuration to DFLL control register + // SYSCTRL_DFLLMUL_CSTEP( 0x1f / 4 ) | // Coarse step is 31, half of the max value + // SYSCTRL_DFLLMUL_FSTEP( 10 ) | + // SYSCTRL_DFLLMUL_MUL( (48000) ) ; + sam.SYSCTRL.DFLLMUL = sam.RegValue((31 << sam.SYSCTRL_DFLLMUL_CSTEP_Pos) | + (10 << sam.SYSCTRL_DFLLMUL_FSTEP_Pos) | + (48000 << sam.SYSCTRL_DFLLMUL_MUL_Pos)) + + // disable DFLL + sam.SYSCTRL.DFLLCTRL = 0 + waitForSync() + + sam.SYSCTRL.DFLLCTRL |= sam.SYSCTRL_DFLLCTRL_MODE | + sam.SYSCTRL_DFLLCTRL_CCDIS | + sam.SYSCTRL_DFLLCTRL_USBCRM | + sam.SYSCTRL_DFLLCTRL_BPLCKC + // Wait for ready + for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_DFLLRDY) == 0 { + } + + // Re-enable the DFLL + sam.SYSCTRL.DFLLCTRL |= sam.SYSCTRL_DFLLCTRL_ENABLE + // Wait for ready + for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_DFLLRDY) == 0 { + } + + // Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz. + sam.GCLK.GENDIV = sam.RegValue((0 << sam.GCLK_GENDIV_ID_Pos) | + (0 << sam.GCLK_GENDIV_DIV_Pos)) + waitForSync() + + sam.GCLK.GENCTRL = sam.RegValue((0 << sam.GCLK_GENCTRL_ID_Pos) | + (sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) | + sam.GCLK_GENCTRL_IDC | + sam.GCLK_GENCTRL_GENEN) + waitForSync() + + // Modify PRESCaler value of OSC8M to have 8MHz + sam.SYSCTRL.OSC8M |= (sam.SYSCTRL_OSC8M_PRESC_0 << sam.SYSCTRL_OSC8M_PRESC_Pos) + sam.SYSCTRL.OSC8M &^= (1 << sam.SYSCTRL_OSC8M_ONDEMAND_Pos) + // Wait for oscillator stabilization + for (sam.SYSCTRL.PCLKSR & sam.SYSCTRL_PCLKSR_OSC8MRDY) == 0 { + } + + // Use OSC8M as source for Generic Clock Generator 3 + sam.GCLK.GENDIV = sam.RegValue((3 << sam.GCLK_GENDIV_ID_Pos)) + waitForSync() + + sam.GCLK.GENCTRL = sam.RegValue((3 << sam.GCLK_GENCTRL_ID_Pos) | + (sam.GCLK_GENCTRL_SRC_OSC8M << sam.GCLK_GENCTRL_SRC_Pos) | + sam.GCLK_GENCTRL_GENEN) + waitForSync() + + // Use OSC32K as source for Generic Clock Generator 2 + // OSC32K/1 -> GCLK2 at 32KHz + sam.GCLK.GENDIV = sam.RegValue(2 << sam.GCLK_GENDIV_ID_Pos) + waitForSync() + + sam.GCLK.GENCTRL = sam.RegValue((2 << sam.GCLK_GENCTRL_ID_Pos) | + (sam.GCLK_GENCTRL_SRC_OSC32K << sam.GCLK_GENCTRL_SRC_Pos) | + sam.GCLK_GENCTRL_GENEN) + waitForSync() + + // Use GCLK2 for RTC + sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_RTC << sam.GCLK_CLKCTRL_ID_Pos) | + (sam.GCLK_CLKCTRL_GEN_GCLK2 << sam.GCLK_CLKCTRL_GEN_Pos) | + sam.GCLK_CLKCTRL_CLKEN) + waitForSync() + + // Set the CPU, APBA, B, and C dividers + sam.PM.CPUSEL = sam.PM_CPUSEL_CPUDIV_DIV1 + sam.PM.APBASEL = sam.PM_APBASEL_APBADIV_DIV1 + sam.PM.APBBSEL = sam.PM_APBBSEL_APBBDIV_DIV1 + sam.PM.APBCSEL = sam.PM_APBCSEL_APBCDIV_DIV1 + + // Disable automatic NVM write operations + sam.NVMCTRL.CTRLB |= sam.NVMCTRL_CTRLB_MANW +} + +func initRTC() { + // turn on digital interface clock + sam.PM.APBAMASK |= sam.PM_APBAMASK_RTC_ + + // disable RTC + sam.RTC.MODE0.CTRL = 0 + waitForSync() + + // reset RTC + sam.RTC.MODE0.CTRL |= sam.RTC_MODE0_CTRL_SWRST + waitForSync() + + // set Mode0 to 32-bit counter (mode 0) with prescaler 1 and GCLK2 is 32KHz/1 + sam.RTC.MODE0.CTRL = sam.RegValue16((sam.RTC_MODE0_CTRL_MODE_COUNT32 << sam.RTC_MODE0_CTRL_MODE_Pos) | + (sam.RTC_MODE0_CTRL_PRESCALER_DIV1 << sam.RTC_MODE0_CTRL_PRESCALER_Pos) | + sam.RTC_MODE0_CTRL_MATCHCLR) + waitForSync() + + sam.RTC.MODE0.COMP0 = 0xffffffff + waitForSync() + + // re-enable RTC + sam.RTC.MODE0.CTRL |= sam.RTC_MODE0_CTRL_ENABLE + waitForSync() + + arm.EnableIRQ(sam.IRQ_RTC) +} + +func waitForSync() { + for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 { + } +} + +// 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 + +// 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 { + // request read of count + sam.RTC.MODE0.READREQ = sam.RTC_MODE0_READREQ_RREQ + waitForSync() + + rtcCounter := uint64(sam.RTC.MODE0.COUNT) * 30 // 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 < 30 { + // have to have at least one clock count + ticks = 30 + } + + // request read of count + sam.RTC.MODE0.READREQ = sam.RTC_MODE0_READREQ_RREQ + waitForSync() + + // set compare value + cnt := sam.RTC.MODE0.COUNT + sam.RTC.MODE0.COMP0 = sam.RegValue(uint32(cnt) + (ticks / 30)) // each counter tick == 30.5us + waitForSync() + + // enable IRQ for CMP0 compare + sam.RTC.MODE0.INTENSET |= 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 = sam.RTC_MODE0_INTENSET_CMP0 + + timerWakeup = true +} |