aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRon Evans <[email protected]>2019-01-13 21:45:18 +0100
committerAyke van Laethem <[email protected]>2019-01-18 18:30:27 +0100
commite2be7ccf76ae9f8548394e715193e03ab47c18d6 (patch)
treedcfc568aed5a7bf6692f9407b4694bd510371eab
parent1f511786d3a56c1c665ff013749f8665f7a1a663 (diff)
downloadtinygo-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.yml1
-rw-r--r--src/machine/board_itsybitsy-m0.go31
-rw-r--r--src/machine/machine_atsamd21g18.go45
-rw-r--r--src/machine/machine_dummy.go2
-rw-r--r--src/runtime/runtime_atsamd21g18.go285
-rw-r--r--targets/atsamd21g18.json15
-rw-r--r--targets/atsamd21g18.ld10
-rw-r--r--targets/itsybitsy-m0.json5
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}"
+}