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 | |
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]>
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | src/machine/board_itsybitsy-m0.go | 31 | ||||
-rw-r--r-- | src/machine/machine_atsamd21g18.go | 45 | ||||
-rw-r--r-- | src/machine/machine_dummy.go | 2 | ||||
-rw-r--r-- | src/runtime/runtime_atsamd21g18.go | 285 | ||||
-rw-r--r-- | targets/atsamd21g18.json | 15 | ||||
-rw-r--r-- | targets/atsamd21g18.ld | 10 | ||||
-rw-r--r-- | targets/itsybitsy-m0.json | 5 |
8 files changed, 393 insertions, 1 deletions
diff --git a/.travis.yml b/.travis.yml index 0f221d4e9..383ae150a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,3 +31,4 @@ script: - tinygo build -o blinky2.reel.elf -target=reelboard examples/blinky2 - tinygo build -o blinky1.pca10056.elf -target=pca10056 examples/blinky1 - tinygo build -o blinky2.pca10056.elf -target=pca10056 examples/blinky2 + - tinygo build -o blinky1.samd21.elf -target=itsybitsy-m0 examples/blinky1 diff --git a/src/machine/board_itsybitsy-m0.go b/src/machine/board_itsybitsy-m0.go new file mode 100644 index 000000000..33f76a99b --- /dev/null +++ b/src/machine/board_itsybitsy-m0.go @@ -0,0 +1,31 @@ +// +build sam,atsamd21g18a,itsybitsy_m0 + +package machine + +// GPIO Pins +const ( + D0 = 11 // RX: SERCOM0/PAD[3] + D1 = 10 // TX: SERCOM0/PAD[2] + D2 = 14 + D3 = 9 + D4 = 8 + D5 = 15 + D6 = 20 + D7 = 21 + D8 = 6 + D9 = 7 + D10 = 18 + D11 = 16 + D12 = 19 + D13 = 17 +) + +const ( + LED = D13 +) + +// UART pins +const ( + UART_TX_PIN = 0 + UART_RX_PIN = 0 +) diff --git a/src/machine/machine_atsamd21g18.go b/src/machine/machine_atsamd21g18.go new file mode 100644 index 000000000..1c1ed83e0 --- /dev/null +++ b/src/machine/machine_atsamd21g18.go @@ -0,0 +1,45 @@ +// +build sam,atsamd21g18a + +// Peripheral abstraction layer for the atsamd21g18. +// +// Datasheet: +// http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf +// +package machine + +import ( + "device/sam" +) + +const CPU_FREQUENCY = 48000000 + +type GPIOMode uint8 + +const ( + GPIO_INPUT = 0 + GPIO_OUTPUT = 1 +) + +// Configure this pin with the given configuration. +func (p GPIO) Configure(config GPIOConfig) { + if config.Mode == GPIO_OUTPUT { // set output bit + sam.PORT.DIRSET0 = (1 << p.Pin) + } else { + sam.PORT.DIRCLR0 = (1 << p.Pin) + } +} + +// Get returns the current value of a GPIO pin. +func (p GPIO) Get() bool { + return (sam.PORT.IN0>>p.Pin)&1 > 0 +} + +// Set the pin to high or low. +// Warning: only use this on an output pin! +func (p GPIO) Set(high bool) { + if high { + sam.PORT.OUTSET0 = (1 << p.Pin) + } else { + sam.PORT.OUTCLR0 = (1 << p.Pin) + } +} diff --git a/src/machine/machine_dummy.go b/src/machine/machine_dummy.go index 99237d2c5..6d180a594 100644 --- a/src/machine/machine_dummy.go +++ b/src/machine/machine_dummy.go @@ -1,4 +1,4 @@ -// +build !avr,!nrf,!stm32 +// +build !avr,!nrf,!sam,!stm32 package machine 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 +} diff --git a/targets/atsamd21g18.json b/targets/atsamd21g18.json new file mode 100644 index 000000000..c0d48400b --- /dev/null +++ b/targets/atsamd21g18.json @@ -0,0 +1,15 @@ +{ + "inherits": ["cortex-m"], + "llvm-target": "armv6m-none-eabi", + "build-tags": ["atsamd21g18", "sam"], + "cflags": [ + "--target=armv6m-none-eabi", + "-Qunused-arguments" + ], + "ldflags": [ + "-T", "targets/atsamd21g18.ld" + ], + "extra-files": [ + "src/device/sam/atsamd21g18a.s" + ] +} diff --git a/targets/atsamd21g18.ld b/targets/atsamd21g18.ld new file mode 100644 index 000000000..5bfcf4543 --- /dev/null +++ b/targets/atsamd21g18.ld @@ -0,0 +1,10 @@ + +MEMORY +{ + FLASH_TEXT (rw) : ORIGIN = 0x00000000+0x2000, LENGTH = 0x00040000-0x2000 /* First 8KB used by bootloader */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x00008000 +} + +_stack_size = 2K; + +INCLUDE "targets/arm.ld" diff --git a/targets/itsybitsy-m0.json b/targets/itsybitsy-m0.json new file mode 100644 index 000000000..a38ce5c31 --- /dev/null +++ b/targets/itsybitsy-m0.json @@ -0,0 +1,5 @@ +{ + "inherits": ["atsamd21g18"], + "build-tags": ["sam", "atsamd21g18a", "itsybitsy_m0"], + "flash": "bossac -d -i -e -w -v -R --offset=0x2000 {hex}" +} |