diff options
author | Ethan Reesor <[email protected]> | 2020-01-21 21:23:10 -0600 |
---|---|---|
committer | Ron Evans <[email protected]> | 2020-07-08 21:58:15 +0200 |
commit | 079a789d492431cff100824421aa276a731ac369 (patch) | |
tree | 3a0b9f4dbd16e4b45cacd3fc99d7b3f8e1046eac | |
parent | ca8e1b075a0445f2ca72dfefd6db517c1b0cfec0 (diff) | |
download | tinygo-079a789d492431cff100824421aa276a731ac369.tar.gz tinygo-079a789d492431cff100824421aa276a731ac369.zip |
Minimal NXP/Teensy support
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/machine/board_teensy36.go | 39 | ||||
-rw-r--r-- | src/machine/machine_nxp.go | 33 | ||||
-rw-r--r-- | src/machine/machine_nxpmk66f18.go | 56 | ||||
-rw-r--r-- | src/runtime/runtime_nxp.go | 5 | ||||
-rw-r--r-- | src/runtime/runtime_nxpmk66f18.go | 342 | ||||
-rw-r--r-- | src/runtime/volatile/register_nxpmk66f18.go | 84 | ||||
-rw-r--r-- | targets/nxpmk66f18.ld | 94 | ||||
-rw-r--r-- | targets/teensy36.json | 18 | ||||
-rwxr-xr-x | tools/gen-device-svd/gen-device-svd.go | 79 |
12 files changed, 754 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore index 84f90ddbc..760bbf0a1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ src/device/avr/*.ld src/device/avr/*.s src/device/nrf/*.go src/device/nrf/*.s +src/device/nxp/*.go +src/device/nxp/*.s src/device/sam/*.go src/device/sam/*.s src/device/sifive/*.go @@ -51,7 +51,7 @@ else LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF' endif -.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-avr +.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine frontendopenmp instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target @@ -118,7 +118,7 @@ fmt-check: @unformatted=$$(gofmt -l $(FMT_PATHS)); [ -z "$$unformatted" ] && exit 0; echo "Unformatted:"; for fn in $$unformatted; do echo " $$fn"; done; exit 1 -gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte +gen-device: gen-device-avr gen-device-nrf gen-device-sam gen-device-sifive gen-device-stm32 gen-device-kendryte gen-device-nxp gen-device-avr: $(GO) build -o ./build/gen-device-avr ./tools/gen-device-avr/ @@ -133,6 +133,10 @@ gen-device-nrf: build/gen-device-svd ./build/gen-device-svd -source=https://github.com/NordicSemiconductor/nrfx/tree/master/mdk lib/nrfx/mdk/ src/device/nrf/ GO111MODULE=off $(GO) fmt ./src/device/nrf +gen-device-nxp: build/gen-device-svd + ./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/NXP lib/cmsis-svd/data/NXP/ src/device/nxp/ + GO111MODULE=off $(GO) fmt ./src/device/nxp + gen-device-sam: build/gen-device-svd ./build/gen-device-svd -source=https://github.com/posborne/cmsis-svd/tree/master/data/Atmel lib/cmsis-svd/data/Atmel/ src/device/sam/ GO111MODULE=off $(GO) fmt ./src/device/sam @@ -140,3 +140,5 @@ The original reasoning was: if [Python](https://micropython.org/) can run on mic This project is licensed under the BSD 3-clause license, just like the [Go project](https://golang.org/LICENSE) itself. Some code has been copied from the LLVM project and is therefore licensed under [a variant of the Apache 2.0 license](http://releases.llvm.org/10.0.0/LICENSE.TXT). This has been clearly indicated in the header of these files. + +Some code has been copied and/or ported from Paul Stoffregen's Teensy libraries and is therefore licensed under PJRC's license. This has been clearly indicated in the header of these files. diff --git a/src/machine/board_teensy36.go b/src/machine/board_teensy36.go new file mode 100644 index 000000000..1b78677b1 --- /dev/null +++ b/src/machine/board_teensy36.go @@ -0,0 +1,39 @@ +// +build nxp,mk66f18,teensy36 + +package machine + +import ( + "device/nxp" +) + +//go:keep +//go:section .flashconfig +var FlashConfig = [16]byte{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xF9, 0xFF, 0xFF, +} + +func CPUFrequency() uint32 { + return 180000000 +} + +// LED on the Teensy +const LED Pin = 13 + +var _pinRegisters [64]pinRegisters + +func init() { + _pinRegisters[13].Bit = 5 + _pinRegisters[13].PCR = &nxp.PORTC.PCR5 + _pinRegisters[13].PDOR = nxp.GPIOC.PDOR.Bit(5) + _pinRegisters[13].PSOR = nxp.GPIOC.PSOR.Bit(5) + _pinRegisters[13].PCOR = nxp.GPIOC.PCOR.Bit(5) + _pinRegisters[13].PTOR = nxp.GPIOC.PTOR.Bit(5) + _pinRegisters[13].PDIR = nxp.GPIOC.PDIR.Bit(5) + _pinRegisters[13].PDDR = nxp.GPIOC.PDDR.Bit(5) +} + +//go:inline +func (p Pin) registers() pinRegisters { + return _pinRegisters[p] +} diff --git a/src/machine/machine_nxp.go b/src/machine/machine_nxp.go new file mode 100644 index 000000000..b29d4328c --- /dev/null +++ b/src/machine/machine_nxp.go @@ -0,0 +1,33 @@ +// +build nxp + +package machine + +import ( + "device/arm" + "device/nxp" +) + +type PinMode uint8 + +const ( + PinInput PinMode = iota + PinOutput +) + +// Stop enters STOP (deep sleep) mode +func Stop() { + // set SLEEPDEEP to enable deep sleep + nxp.SystemControl.SCR.SetBits(nxp.SystemControl_SCR_SLEEPDEEP) + + // enter STOP mode + arm.Asm("wfi") +} + +// Wait enters WAIT (sleep) mode +func Wait() { + // clear SLEEPDEEP bit to disable deep sleep + nxp.SystemControl.SCR.ClearBits(nxp.SystemControl_SCR_SLEEPDEEP) + + // enter WAIT mode + arm.Asm("wfi") +} diff --git a/src/machine/machine_nxpmk66f18.go b/src/machine/machine_nxpmk66f18.go new file mode 100644 index 000000000..ab15ed136 --- /dev/null +++ b/src/machine/machine_nxpmk66f18.go @@ -0,0 +1,56 @@ +// +build nxp,mk66f18 + +package machine + +import ( + "device/nxp" + "runtime/volatile" +) + +const ( + PortControlRegisterSRE = nxp.PORT_PCR0_SRE + PortControlRegisterDSE = nxp.PORT_PCR0_DSE + PortControlRegisterODE = nxp.PORT_PCR0_ODE +) + +func PortControlRegisterMUX(v uint8) uint32 { + return (uint32(v) << nxp.PORT_PCR0_MUX_Pos) & nxp.PORT_PCR0_MUX_Msk +} + +type pinRegisters struct { + Bit uintptr + PCR *volatile.Register32 + PDOR *volatile.BitRegister + PSOR *volatile.BitRegister + PCOR *volatile.BitRegister + PTOR *volatile.BitRegister + PDIR *volatile.BitRegister + PDDR *volatile.BitRegister +} + +// Configure this pin with the given configuration. +func (p Pin) Configure(config PinConfig) { + switch config.Mode { + case PinInput: + panic("todo") + + case PinOutput: + p.registers().PDDR.Set() + p.registers().PCR.SetBits(PortControlRegisterSRE | PortControlRegisterDSE | PortControlRegisterMUX(1)) + p.registers().PCR.ClearBits(PortControlRegisterODE) + } +} + +// Set changes the value of the GPIO pin. The pin must be configured as output. +func (p Pin) Set(value bool) { + if value { + p.registers().PSOR.Set() + } else { + p.registers().PCOR.Set() + } +} + +// Get returns the current value of a GPIO pin. +func (p Pin) Get() bool { + return p.registers().PDIR.Get() +} diff --git a/src/runtime/runtime_nxp.go b/src/runtime/runtime_nxp.go new file mode 100644 index 000000000..08f6635c4 --- /dev/null +++ b/src/runtime/runtime_nxp.go @@ -0,0 +1,5 @@ +// +build nxp + +package runtime + +type timeUnit int64 diff --git a/src/runtime/runtime_nxpmk66f18.go b/src/runtime/runtime_nxpmk66f18.go new file mode 100644 index 000000000..99617a402 --- /dev/null +++ b/src/runtime/runtime_nxpmk66f18.go @@ -0,0 +1,342 @@ +// Derivative work of Teensyduino Core Library +// http://www.pjrc.com/teensy/ +// Copyright (c) 2017 PJRC.COM, LLC. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// 1. The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// 2. If the Software is incorporated into a build system that allows +// selection among a list of target devices, then similar target +// devices manufactured by PJRC.COM must be included in the list of +// target devices and selectable in the same manner. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// +build nxp,mk66f18 + +package runtime + +import ( + "device/arm" + "device/nxp" + "machine" + "runtime/volatile" +) + +const ( + WDOG_UNLOCK_SEQ1 = 0xC520 + WDOG_UNLOCK_SEQ2 = 0xD928 + + DEFAULT_FTM_MOD = 61440 - 1 + DEFAULT_FTM_PRESCALE = 1 +) + +var ( + SIM_SOPT2_IRC48SEL = nxp.SIM_SOPT2_PLLFLLSEL(3) + SMC_PMCTRL_HSRUN = nxp.SMC_PMCTRL_RUNM(3) + SMC_PMSTAT_HSRUN = nxp.SMC_PMSTAT_PMSTAT(0x80) +) + +//go:section .resetHandler +//go:export Reset_Handler +func main() { + nxp.WDOG.UNLOCK.Set(WDOG_UNLOCK_SEQ1) + nxp.WDOG.UNLOCK.Set(WDOG_UNLOCK_SEQ2) + arm.Asm("nop") + arm.Asm("nop") + startupEarlyHook() + + // enable clocks to always-used peripherals + nxp.SIM.SCGC3.Set(nxp.SIM_SCGC3_ADC1 | nxp.SIM_SCGC3_FTM2 | nxp.SIM_SCGC3_FTM3) + nxp.SIM.SCGC5.Set(0x00043F82) // clocks active to all GPIO + nxp.SIM.SCGC6.Set(nxp.SIM_SCGC6_RTC | nxp.SIM_SCGC6_FTM0 | nxp.SIM_SCGC6_FTM1 | nxp.SIM_SCGC6_ADC0 | nxp.SIM_SCGC6_FTF) + nxp.SystemControl.CPACR.Set(0x00F00000) + nxp.LMEM.PCCCR.Set(0x85000003) + + // release I/O pins hold, if we woke up from VLLS mode + if nxp.PMC.REGSC.HasBits(nxp.PMC_REGSC_ACKISO) { + nxp.PMC.REGSC.SetBits(nxp.PMC_REGSC_ACKISO) + } + + // since this is a write once register, make it visible to all F_CPU's + // so we can into other sleep modes in the future at any speed + nxp.SMC.PMPROT.Set(nxp.SMC_PMPROT_AHSRUN | nxp.SMC_PMPROT_AVLP | nxp.SMC_PMPROT_ALLS | nxp.SMC_PMPROT_AVLLS) + + preinit() + + // copy the vector table to RAM default all interrupts to medium priority level + // for (i=0; i < NVIC_NUM_INTERRUPTS + 16; i++) _VectorsRam[i] = _VectorsFlash[i]; + for i := uint32(0); i <= nxp.IRQ_max; i++ { + arm.SetPriority(i, 128) + } + // SCB_VTOR = (uint32_t)_VectorsRam; // use vector table in RAM + + // hardware always starts in FEI mode + // C1[CLKS] bits are written to 00 + // C1[IREFS] bit is written to 1 + // C6[PLLS] bit is written to 0 + // MCG_SC[FCDIV] defaults to divide by two for internal ref clock + // I tried changing MSG_SC to divide by 1, it didn't work for me + // enable capacitors for crystal + nxp.OSC.CR.Set(nxp.OSC_CR_SC8P | nxp.OSC_CR_SC2P | nxp.OSC_CR_ERCLKEN) + // enable osc, 8-32 MHz range, low power mode + nxp.MCG.C2.Set(uint8(nxp.MCG_C2_RANGE(2) | nxp.MCG_C2_EREFS)) + // switch to crystal as clock source, FLL input = 16 MHz / 512 + nxp.MCG.C1.Set(uint8(nxp.MCG_C1_CLKS(2) | nxp.MCG_C1_FRDIV(4))) + // wait for crystal oscillator to begin + for !nxp.MCG.S.HasBits(nxp.MCG_S_OSCINIT0) { + } + // wait for FLL to use oscillator + for nxp.MCG.S.HasBits(nxp.MCG_S_IREFST) { + } + // wait for MCGOUT to use oscillator + for (nxp.MCG.S.Get() & nxp.MCG_S_CLKST_Msk) != nxp.MCG_S_CLKST(2) { + } + + // now in FBE mode + // C1[CLKS] bits are written to 10 + // C1[IREFS] bit is written to 0 + // C1[FRDIV] must be written to divide xtal to 31.25-39 kHz + // C6[PLLS] bit is written to 0 + // C2[LP] is written to 0 + // we need faster than the crystal, turn on the PLL (F_CPU > 120000000) + nxp.SMC.PMCTRL.Set(SMC_PMCTRL_HSRUN) // enter HSRUN mode + for nxp.SMC.PMSTAT.Get() != SMC_PMSTAT_HSRUN { + } // wait for HSRUN + nxp.MCG.C5.Set(nxp.MCG_C5_PRDIV(1)) + nxp.MCG.C6.Set(nxp.MCG_C6_PLLS | nxp.MCG_C6_VDIV(29)) + + // wait for PLL to start using xtal as its input + for !nxp.MCG.S.HasBits(nxp.MCG_S_PLLST) { + } + // wait for PLL to lock + for !nxp.MCG.S.HasBits(nxp.MCG_S_LOCK0) { + } + // now we're in PBE mode + + // now program the clock dividers + // config divisors: 180 MHz core, 60 MHz bus, 25.7 MHz flash, USB = IRC48M + nxp.SIM.CLKDIV1.Set(nxp.SIM_CLKDIV1_OUTDIV1(0) | nxp.SIM_CLKDIV1_OUTDIV2(2) | nxp.SIM_CLKDIV1_OUTDIV4(6)) + nxp.SIM.CLKDIV2.Set(nxp.SIM_CLKDIV2_USBDIV(0)) + + // switch to PLL as clock source, FLL input = 16 MHz / 512 + nxp.MCG.C1.Set(nxp.MCG_C1_CLKS(0) | nxp.MCG_C1_FRDIV(4)) + // wait for PLL clock to be used + for (nxp.MCG.S.Get() & nxp.MCG_S_CLKST_Msk) != nxp.MCG_S_CLKST(3) { + } + // now we're in PEE mode + // trace is CPU clock, CLKOUT=OSCERCLK0 + // USB uses IRC48 + nxp.SIM.SOPT2.Set(nxp.SIM_SOPT2_USBSRC | SIM_SOPT2_IRC48SEL | nxp.SIM_SOPT2_TRACECLKSEL | nxp.SIM_SOPT2_CLKOUTSEL(6)) + + // If the RTC oscillator isn't enabled, get it started. For Teensy 3.6 + // we don't do this early. See comment above about slow rising power. + if !nxp.RTC.CR.HasBits(nxp.RTC_CR_OSCE) { + nxp.RTC.SR.Set(0) + nxp.RTC.CR.Set(nxp.RTC_CR_SC16P | nxp.RTC_CR_SC4P | nxp.RTC_CR_OSCE) + } + + // initialize the SysTick counter + nxp.SysTick.RVR.Set((machine.CPUFrequency() / 1000) - 1) + nxp.SysTick.CVR.Set(0) + nxp.SysTick.CSR.Set(nxp.SysTick_CSR_CLKSOURCE | nxp.SysTick_CSR_TICKINT | nxp.SysTick_CSR_ENABLE) + nxp.SystemControl.SHPR3.Set(0x20200000) // Systick = priority 32 + + arm.Asm("CPSIE i") + initTeensyInternal() + startupLateHook() + + // initAll() + runMain() + // abort() + + for { + + } +} + +// ported _init_Teensyduino_internal_ from pins_teensy.c from teensy3 core libraries +func initTeensyInternal() { + arm.EnableIRQ(nxp.IRQ_PORTA) + arm.EnableIRQ(nxp.IRQ_PORTB) + arm.EnableIRQ(nxp.IRQ_PORTC) + arm.EnableIRQ(nxp.IRQ_PORTD) + arm.EnableIRQ(nxp.IRQ_PORTE) + + nxp.FTM0.CNT.Set(0) + nxp.FTM0.MOD.Set(DEFAULT_FTM_MOD) + nxp.FTM0.C0SC.Set(0x28) // MSnB:MSnA = 10, ELSnB:ELSnA = 10 + nxp.FTM0.C1SC.Set(0x28) + nxp.FTM0.C2SC.Set(0x28) + nxp.FTM0.C3SC.Set(0x28) + nxp.FTM0.C4SC.Set(0x28) + nxp.FTM0.C5SC.Set(0x28) + nxp.FTM0.C6SC.Set(0x28) + nxp.FTM0.C7SC.Set(0x28) + + nxp.FTM3.C0SC.Set(0x28) + nxp.FTM3.C1SC.Set(0x28) + nxp.FTM3.C2SC.Set(0x28) + nxp.FTM3.C3SC.Set(0x28) + nxp.FTM3.C4SC.Set(0x28) + nxp.FTM3.C5SC.Set(0x28) + nxp.FTM3.C6SC.Set(0x28) + nxp.FTM3.C7SC.Set(0x28) + + nxp.FTM0.SC.Set(nxp.FTM_SC_CLKS(1) | nxp.FTM_SC_PS(DEFAULT_FTM_PRESCALE)) + nxp.FTM1.CNT.Set(0) + nxp.FTM1.MOD.Set(DEFAULT_FTM_MOD) + nxp.FTM1.C0SC.Set(0x28) + nxp.FTM1.C1SC.Set(0x28) + nxp.FTM1.SC.Set(nxp.FTM_SC_CLKS(1) | nxp.FTM_SC_PS(DEFAULT_FTM_PRESCALE)) + + // nxp.FTM2.CNT.Set(0) + // nxp.FTM2.MOD.Set(DEFAULT_FTM_MOD) + // nxp.FTM2.C0SC.Set(0x28) + // nxp.FTM2.C1SC.Set(0x28) + // nxp.FTM2.SC.Set(nxp.FTM_SC_CLKS(1) | nxp.FTM_SC_PS(DEFAULT_FTM_PRESCALE)) + + nxp.FTM3.CNT.Set(0) + nxp.FTM3.MOD.Set(DEFAULT_FTM_MOD) + nxp.FTM3.C0SC.Set(0x28) + nxp.FTM3.C1SC.Set(0x28) + nxp.FTM3.SC.Set(nxp.FTM_SC_CLKS(1) | nxp.FTM_SC_PS(DEFAULT_FTM_PRESCALE)) + + nxp.SIM.SCGC2.SetBits(nxp.SIM_SCGC2_TPM1) + nxp.SIM.SOPT2.SetBits(nxp.SIM_SOPT2_TPMSRC(2)) + nxp.TPM1.CNT.Set(0) + nxp.TPM1.MOD.Set(32767) + nxp.TPM1.C0SC.Set(0x28) + nxp.TPM1.C1SC.Set(0x28) + nxp.TPM1.SC.Set(nxp.FTM_SC_CLKS(1) | nxp.FTM_SC_PS(0)) + + // analog_init(); + + // #if !defined(TEENSY_INIT_USB_DELAY_BEFORE) + // #if TEENSYDUINO >= 142 + // #define TEENSY_INIT_USB_DELAY_BEFORE 25 + // #else + // #define TEENSY_INIT_USB_DELAY_BEFORE 50 + // #endif + // #endif + + // #if !defined(TEENSY_INIT_USB_DELAY_AFTER) + // #if TEENSYDUINO >= 142 + // #define TEENSY_INIT_USB_DELAY_AFTER 275 + // #else + // #define TEENSY_INIT_USB_DELAY_AFTER 350 + // #endif + // #endif + + // // for background about this startup delay, please see these conversations + // // https://forum.pjrc.com/threads/36606-startup-time-(400ms)?p=113980&viewfull=1#post113980 + // // https://forum.pjrc.com/threads/31290-Teensey-3-2-Teensey-Loader-1-24-Issues?p=87273&viewfull=1#post87273 + + // delay(TEENSY_INIT_USB_DELAY_BEFORE); + // usb_init(); + // delay(TEENSY_INIT_USB_DELAY_AFTER); +} + +func startupEarlyHook() { + // TODO allow override + // > programs using the watchdog timer or needing to initialize hardware as + // > early as possible can implement startup_early_hook() + + nxp.WDOG.STCTRLH.Set(nxp.WDOG_STCTRLH_ALLOWUPDATE) +} + +func startupLateHook() { + // TODO allow override +} + +//go:noinline +func runMain() { + // this is a separate function to ensure that Reset_Handler fits in 0x230 bytes regardless of whether (user) main requires scheduling + callMain() +} + +func putchar(c byte) { +} + +// ??? +const asyncScheduler = false + +// microseconds per tick +const tickMicros = 1000 + +// number of ticks since boot +var tickMilliCount uint32 + +//go:export SysTick_Handler +func tickHandler() { + volatile.StoreUint32(&tickMilliCount, volatile.LoadUint32(&tickMilliCount)+1) +} + +// ticks are in microseconds +func ticks() timeUnit { + m := arm.DisableInterrupts() + current := nxp.SysTick.CVR.Get() + count := tickMilliCount + istatus := nxp.SystemControl.ICSR.Get() + arm.EnableInterrupts(m) + + if istatus&nxp.SystemControl_ICSR_PENDSTSET != 0 && current > 50 { + count++ + } + + current = ((machine.CPUFrequency() / tickMicros) - 1) - current + return timeUnit(count*tickMicros + current/(machine.CPUFrequency()/1000000)) +} + +// sleepTicks spins for a number of microseconds +func sleepTicks(d timeUnit) { + // TODO actually sleep + + if d <= 0 { + return + } + + start := ticks() + ms := d / 1000 + + for { + for ticks()-start >= 1000 { + ms-- + if ms <= 0 { + return + } + start += 1000 + } + // Gosched() + } +} + +func Sleep(d int64) { + sleepTicks(timeUnit(d)) +} + +// func abort() { +// for { +// // keep polling some communication while in fault +// // mode, so we don't completely die. +// if nxp.SIM.SCGC4.HasBits(nxp.SIM_SCGC4_USBOTG) usb_isr(); +// if nxp.SIM.SCGC4.HasBits(nxp.SIM_SCGC4_UART0) uart0_status_isr(); +// if nxp.SIM.SCGC4.HasBits(nxp.SIM_SCGC4_UART1) uart1_status_isr(); +// if nxp.SIM.SCGC4.HasBits(nxp.SIM_SCGC4_UART2) uart2_status_isr(); +// } +// } diff --git a/src/runtime/volatile/register_nxpmk66f18.go b/src/runtime/volatile/register_nxpmk66f18.go new file mode 100644 index 000000000..272205a4d --- /dev/null +++ b/src/runtime/volatile/register_nxpmk66f18.go @@ -0,0 +1,84 @@ +// +build nxp,mk66f18 + +package volatile + +import "unsafe" + +const registerBase = 0x40000000 +const registerEnd = 0x40100000 +const bitbandBase = 0x42000000 +const ptrBytes = unsafe.Sizeof(uintptr(0)) + +//go:inline +func bitbandAddress(reg uintptr, bit uintptr) uintptr { + if bit > ptrBytes*8 { + panic("invalid bit position") + } + if reg < registerBase || reg >= registerEnd { + panic("register is out of range") + } + return (reg-registerBase)*ptrBytes*8 + bit*ptrBytes + bitbandBase +} + +// Special types that causes loads/stores to be volatile (necessary for +// memory-mapped registers). +type BitRegister struct { + Reg uint32 +} + +// Get returns the of the mapped register bit. It is the volatile equivalent of: +// +// *r.Reg +// +//go:inline +func (r *BitRegister) Get() bool { + return LoadUint32(&r.Reg) != 0 +} + +// Set sets the mapped register bit. It is the volatile equivalent of: +// +// *r.Reg = 1 +// +//go:inline +func (r *BitRegister) Set() { + StoreUint32(&r.Reg, 1) +} + +// Clear clears the mapped register bit. It is the volatile equivalent of: +// +// *r.Reg = 0 +// +//go:inline +func (r *BitRegister) Clear() { + StoreUint32(&r.Reg, 0) +} + +// Bit maps bit N of register R to the corresponding bitband address. Bit panics +// if R is not an AIPS or GPIO register or if N is out of range (greater than +// the number of bits in a register minus one). +// +// go:inline +func (r *Register8) Bit(bit uintptr) *BitRegister { + ptr := bitbandAddress(uintptr(unsafe.Pointer(&r.Reg)), bit) + return (*BitRegister)(unsafe.Pointer(ptr)) +} + +// Bit maps bit N of register R to the corresponding bitband address. Bit panics +// if R is not an AIPS or GPIO register or if N is out of range (greater than +// the number of bits in a register minus one). +// +// go:inline +func (r *Register16) Bit(bit uintptr) *BitRegister { + ptr := bitbandAddress(uintptr(unsafe.Pointer(&r.Reg)), bit) + return (*BitRegister)(unsafe.Pointer(ptr)) +} + +// Bit maps bit N of register R to the corresponding bitband address. Bit panics +// if R is not an AIPS or GPIO register or if N is out of range (greater than +// the number of bits in a register minus one). +// +// go:inline +func (r *Register32) Bit(bit uintptr) *BitRegister { + ptr := bitbandAddress(uintptr(unsafe.Pointer(&r.Reg)), bit) + return (*BitRegister)(unsafe.Pointer(ptr)) +} diff --git a/targets/nxpmk66f18.ld b/targets/nxpmk66f18.ld new file mode 100644 index 000000000..74e22761f --- /dev/null +++ b/targets/nxpmk66f18.ld @@ -0,0 +1,94 @@ + +/* Unused, but here to silence a linker warning. */ +ENTRY(Reset_Handler) + +/* define memory layout */ +MEMORY +{ + FLASH_TEXT (rx) : ORIGIN = 0x00000000, LENGTH = 1024K + RAM (rwx) : ORIGIN = 0x1FFF0000, LENGTH = 256K +} + +_stack_size = 2K; + +/* define output sections */ +SECTIONS +{ + /* Program code and read-only data goes to FLASH_TEXT. */ + .text : + { + /* vector table MUST start at 0x0 */ + . = 0; + _vector_table = .; + KEEP(*(.isr_vector)) + + /* this works as long as reset handler doesn't overflow past 0x400 */ + *(.resetHandler) + + /* flash configuration MUST be at 0x400 */ + . = 0x400; + KEEP(*(.flashconfig)) + + /* everything else */ + *(.text) + *(.text*) + *(.rodata) + *(.rodata*) + . = ALIGN(4); + } >FLASH_TEXT = 0xFF + + /* Put the stack at the bottom of RAM, so that the application will + * crash on stack overflow instead of silently corrupting memory. + * See: http://blog.japaric.io/stack-overflow-protection/ */ + .stack (NOLOAD) : + { + . = ALIGN(4); + . += _stack_size; + _stack_top = .; + } >RAM + + /* Start address (in flash) of .data, used by startup code. */ + _sidata = LOADADDR(.data); + + /* todo add .usbdescriptortable .dmabuffers .usbbuffers */ + + /* Globals with initial value */ + .data : + { + . = ALIGN(4); + _sdata = .; /* used by startup code */ + *(.data) + *(.data*) + . = ALIGN(4); + _edata = .; /* used by startup code */ + } >RAM AT>FLASH_TEXT + + /* Zero-initialized globals */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* used by startup code */ + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; /* used by startup code */ + } >RAM + + /DISCARD/ : + { + *(.ARM.exidx) /* causes 'no memory region specified' error in lld */ + *(.ARM.exidx.*) /* causes spurious 'undefined reference' errors */ + + /* all this makes it much harder to debug via disassembly */ + *(.debug*) + *(.ARM.*) + *(.comment*) + } +} + +/* For the memory allocator. */ +_heap_start = _ebss; +_heap_end = ORIGIN(RAM) + LENGTH(RAM); +_globals_start = _sdata; +_globals_end = _ebss; diff --git a/targets/teensy36.json b/targets/teensy36.json new file mode 100644 index 000000000..fecd3c357 --- /dev/null +++ b/targets/teensy36.json @@ -0,0 +1,18 @@ +{ + "inherits": ["cortex-m"], + "llvm-target": "armv7em-none-eabi", + "cpu": "cortex-m4", + "build-tags": ["teensy36", "teensy", "mk66f18", "nxp"], + "cflags": [ + "--target=armv7em-none-eabi", + "-Qunused-arguments", + "-mfloat-abi=hard", + "-mfpu=fpv4-sp-d16" + ], + "linkerscript": "targets/nxpmk66f18.ld", + "extra-files": [ + "src/device/nxp/mk66f18.s" + ], + "flash-command": "teensy_loader_cli -mmcu=mk66fx1m0 -v -w {hex}" +} + diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index f376a6932..ee1a86bbb 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -16,6 +16,7 @@ import ( ) var validName = regexp.MustCompile("^[a-zA-Z0-9_]+$") +var enumBitSpecifier = regexp.MustCompile("^#[x01]+$") type SVDFile struct { XMLName xml.Name `xml:"device"` @@ -41,6 +42,7 @@ type SVDRegister struct { Name string `xml:"name"` Description string `xml:"description"` Dim *string `xml:"dim"` + DimIndex *string `xml:"dimIndex"` DimIncrement string `xml:"dimIncrement"` Size *string `xml:"size"` Fields []*SVDField `xml:"fields>field"` @@ -484,13 +486,24 @@ func parseBitfields(groupName, regName string, fieldEls []*SVDField, bitfieldPre } for _, enumEl := range fieldEl.EnumeratedValues { enumName := enumEl.Name + if strings.EqualFold(enumName, "reserved") || !validName.MatchString(enumName) { + continue + } if !unicode.IsUpper(rune(enumName[0])) && !unicode.IsDigit(rune(enumName[0])) { enumName = strings.ToUpper(enumName) } enumDescription := strings.Replace(enumEl.Description, "\n", " ", -1) enumValue, err := strconv.ParseUint(enumEl.Value, 0, 32) if err != nil { - panic(err) + if enumBitSpecifier.MatchString(enumEl.Value) { + // NXP SVDs use the form #xx1x, #x0xx, etc for values + enumValue, err = strconv.ParseUint(strings.Replace(enumEl.Value[1:], "x", "0", -1), 2, 32) + if err != nil { + panic(err) + } + } else { + panic(err) + } } fields = append(fields, Bitfield{ name: fmt.Sprintf("%s_%s%s_%s_%s", groupName, bitfieldPrefix, regName, fieldName, enumName), @@ -545,6 +558,59 @@ func (r *Register) dim() int { return int(dim) } +func (r *Register) dimIndex() []string { + defer func() { + if err := recover(); err != nil { + fmt.Println("register", r.name()) + panic(err) + } + }() + + dim := r.dim() + if r.element.DimIndex == nil { + if dim <= 0 { + return nil + } + + idx := make([]string, dim) + for i := range idx { + idx[i] = strconv.FormatInt(int64(i), 10) + } + return idx + } + + t := strings.Split(*r.element.DimIndex, "-") + if len(t) == 2 { + x, err := strconv.ParseInt(t[0], 0, 32) + if err != nil { + panic(err) + } + y, err := strconv.ParseInt(t[1], 0, 32) + if err != nil { + panic(err) + } + + if x < 0 || y < x || y-x != int64(dim-1) { + panic("invalid dimIndex") + } + + idx := make([]string, dim) + for i := x; i <= y; i++ { + idx[i-x] = strconv.FormatInt(i, 10) + } + return idx + } else if len(t) > 2 { + panic("invalid dimIndex") + } + + s := strings.Split(*r.element.DimIndex, ",") + if len(s) != dim { + panic("invalid dimIndex") + } + + return s +} + func (r *Register) size() int { if r.element.Size != nil { size, err := strconv.ParseInt(*r.element.Size, 0, 32) @@ -568,10 +634,10 @@ func parseRegister(groupName string, regEl *SVDRegister, baseAddress uint64, bit // a "spaced array" of registers, special processing required // we need to generate a separate register for each "element" var results []*PeripheralField - for i := uint64(0); i < uint64(reg.dim()); i++ { - regAddress := reg.address() + (i * dimIncrement) + for i, j := range reg.dimIndex() { + regAddress := reg.address() + (uint64(i) * dimIncrement) results = append(results, &PeripheralField{ - name: strings.ToUpper(strings.Replace(reg.name(), "%s", strconv.FormatUint(i, 10), -1)), + name: strings.ToUpper(strings.Replace(reg.name(), "%s", j, -1)), address: regAddress, description: reg.description(), array: -1, @@ -589,11 +655,12 @@ func parseRegister(groupName string, regEl *SVDRegister, baseAddress uint64, bit regName = strings.ToUpper(regName) } + bitfields := parseBitfields(groupName, regName, regEl.Fields, bitfieldPrefix) return []*PeripheralField{&PeripheralField{ name: regName, address: reg.address(), description: reg.description(), - bitfields: parseBitfields(groupName, regName, regEl.Fields, bitfieldPrefix), + bitfields: bitfields, array: reg.dim(), elementSize: reg.size(), }} @@ -770,7 +837,7 @@ var ( if register.array != -1 { regType = fmt.Sprintf("[%d]%s", register.array, regType) } - fmt.Fprintf(w, "\t%s %s\n", register.name, regType) + fmt.Fprintf(w, "\t%s %s // 0x%X\n", register.name, regType, register.address-peripheral.BaseAddress) // next address if lastCluster { |