From e480e9d135313b462041e53041507d1ef339085c Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Tue, 21 Jan 2020 21:23:10 -0600 Subject: [PATCH 1/4] Minimal NXP/Teensy support --- .gitignore | 2 + Makefile | 8 +- README.md | 2 + src/machine/board_teensy36.go | 39 +++ src/machine/machine_nxp.go | 33 ++ src/machine/machine_nxpmk66f18.go | 56 ++++ src/runtime/runtime_nxp.go | 5 + src/runtime/runtime_nxpmk66f18.go | 342 ++++++++++++++++++++ src/runtime/volatile/register_nxpmk66f18.go | 84 +++++ targets/nxpmk66f18.ld | 94 ++++++ targets/teensy36.json | 18 ++ tools/gen-device-svd/gen-device-svd.go | 79 ++++- 12 files changed, 754 insertions(+), 8 deletions(-) create mode 100644 src/machine/board_teensy36.go create mode 100644 src/machine/machine_nxp.go create mode 100644 src/machine/machine_nxpmk66f18.go create mode 100644 src/runtime/runtime_nxp.go create mode 100644 src/runtime/runtime_nxpmk66f18.go create mode 100644 src/runtime/volatile/register_nxpmk66f18.go create mode 100644 targets/nxpmk66f18.ld create mode 100644 targets/teensy36.json diff --git a/.gitignore b/.gitignore index 84f90ddbc3..760bbf0a19 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 diff --git a/Makefile b/Makefile index 5a1fd02c42..0e033dfbc3 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 1c8ab784f0..24231630ec 100644 --- a/README.md +++ b/README.md @@ -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 0000000000..1b78677b18 --- /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 0000000000..b29d4328cf --- /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 0000000000..ab15ed1361 --- /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 0000000000..08f6635c40 --- /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 0000000000..99617a4025 --- /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 0000000000..272205a4dc --- /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 0000000000..74e22761f6 --- /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 0000000000..fecd3c3570 --- /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 f376a69326..ee1a86bbb5 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 { From d850f9b35d04ac13b45e5a0e4ac0c66936cca3f9 Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Tue, 18 Feb 2020 01:09:16 -0600 Subject: [PATCH 2/4] Working on NXP/Teensy support --- src/device/arm/arm.go | 5 + src/machine/board_teensy36.go | 93 ++++++-- src/machine/buffer.go | 6 + src/machine/machine_nxpmk66f18.go | 57 +++-- src/machine/uart_nxpmk66f18.go | 251 ++++++++++++++++++++ src/runtime/interrupt/interrupt_cortexm.go | 5 + src/runtime/runtime_nxpmk66f18.go | 57 +++-- src/runtime/volatile/register_nxpmk66f18.go | 29 +-- targets/nxpmk66f18.ld | 8 +- targets/teensy36.json | 3 +- targets/teensy36.s | 19 ++ 11 files changed, 449 insertions(+), 84 deletions(-) create mode 100644 src/machine/uart_nxpmk66f18.go create mode 100644 targets/teensy36.s diff --git a/src/device/arm/arm.go b/src/device/arm/arm.go index 45226dab41..d7ccaab7e9 100644 --- a/src/device/arm/arm.go +++ b/src/device/arm/arm.go @@ -174,6 +174,11 @@ func EnableIRQ(irq uint32) { NVIC.ISER[irq>>5].Set(1 << (irq & 0x1F)) } +// Disable the given interrupt number. +func DisableIRQ(irq uint32) { + NVIC.ICER[irq>>5].Set(1 << (irq & 0x1F)) +} + // Set the priority of the given interrupt number. // Note that the priority is given as a 0-255 number, where some of the lower // bits are not implemented by the hardware. For example, to set a low interrupt diff --git a/src/machine/board_teensy36.go b/src/machine/board_teensy36.go index 1b78677b18..f7991af7b2 100644 --- a/src/machine/board_teensy36.go +++ b/src/machine/board_teensy36.go @@ -6,12 +6,12 @@ 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, -} +// //go:keep +// //go:section .flash_config +// var FlashControl = [16]byte{ +// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xF9, 0xFF, 0xFF, +// } func CPUFrequency() uint32 { return 180000000 @@ -20,20 +20,73 @@ func CPUFrequency() uint32 { // 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) +var pins = []pin{ + // {bit, control register, gpio register bank} + 0: {16, &nxp.PORTB.PCR16, nxp.GPIOB}, + 1: {17, &nxp.PORTB.PCR17, nxp.GPIOB}, + 2: {0, &nxp.PORTD.PCR0, nxp.GPIOD}, + 3: {12, &nxp.PORTA.PCR12, nxp.GPIOA}, + 4: {13, &nxp.PORTA.PCR13, nxp.GPIOA}, + 5: {7, &nxp.PORTD.PCR7, nxp.GPIOD}, + 6: {4, &nxp.PORTD.PCR4, nxp.GPIOD}, + 7: {2, &nxp.PORTD.PCR2, nxp.GPIOD}, + 8: {3, &nxp.PORTD.PCR3, nxp.GPIOD}, + 9: {3, &nxp.PORTC.PCR3, nxp.GPIOC}, + 10: {4, &nxp.PORTC.PCR4, nxp.GPIOC}, + 11: {6, &nxp.PORTC.PCR6, nxp.GPIOC}, + 12: {7, &nxp.PORTC.PCR7, nxp.GPIOC}, + 13: {5, &nxp.PORTC.PCR5, nxp.GPIOC}, + 14: {1, &nxp.PORTD.PCR1, nxp.GPIOD}, + 15: {0, &nxp.PORTC.PCR0, nxp.GPIOC}, + 16: {0, &nxp.PORTB.PCR0, nxp.GPIOB}, + 17: {1, &nxp.PORTB.PCR1, nxp.GPIOB}, + 18: {3, &nxp.PORTB.PCR3, nxp.GPIOB}, + 19: {2, &nxp.PORTB.PCR2, nxp.GPIOB}, + 20: {5, &nxp.PORTD.PCR5, nxp.GPIOD}, + 21: {6, &nxp.PORTD.PCR6, nxp.GPIOD}, + 22: {1, &nxp.PORTC.PCR1, nxp.GPIOC}, + 23: {2, &nxp.PORTC.PCR2, nxp.GPIOC}, + 24: {26, &nxp.PORTE.PCR26, nxp.GPIOE}, + 25: {5, &nxp.PORTA.PCR5, nxp.GPIOA}, + 26: {14, &nxp.PORTA.PCR14, nxp.GPIOA}, + 27: {15, &nxp.PORTA.PCR15, nxp.GPIOA}, + 28: {16, &nxp.PORTA.PCR16, nxp.GPIOA}, + 29: {18, &nxp.PORTB.PCR18, nxp.GPIOB}, + 30: {19, &nxp.PORTB.PCR19, nxp.GPIOB}, + 31: {10, &nxp.PORTB.PCR10, nxp.GPIOB}, + 32: {11, &nxp.PORTB.PCR11, nxp.GPIOB}, + 33: {24, &nxp.PORTE.PCR24, nxp.GPIOE}, + 34: {25, &nxp.PORTE.PCR25, nxp.GPIOE}, + 35: {8, &nxp.PORTC.PCR8, nxp.GPIOC}, + 36: {9, &nxp.PORTC.PCR9, nxp.GPIOC}, + 37: {10, &nxp.PORTC.PCR10, nxp.GPIOC}, + 38: {11, &nxp.PORTC.PCR11, nxp.GPIOC}, + 39: {17, &nxp.PORTA.PCR17, nxp.GPIOA}, + 40: {28, &nxp.PORTA.PCR28, nxp.GPIOA}, + 41: {29, &nxp.PORTA.PCR29, nxp.GPIOA}, + 42: {26, &nxp.PORTA.PCR26, nxp.GPIOA}, + 43: {20, &nxp.PORTB.PCR20, nxp.GPIOB}, + 44: {22, &nxp.PORTB.PCR22, nxp.GPIOB}, + 45: {23, &nxp.PORTB.PCR23, nxp.GPIOB}, + 46: {21, &nxp.PORTB.PCR21, nxp.GPIOB}, + 47: {8, &nxp.PORTD.PCR8, nxp.GPIOD}, + 48: {9, &nxp.PORTD.PCR9, nxp.GPIOD}, + 49: {4, &nxp.PORTB.PCR4, nxp.GPIOB}, + 50: {5, &nxp.PORTB.PCR5, nxp.GPIOB}, + 51: {14, &nxp.PORTD.PCR14, nxp.GPIOD}, + 52: {13, &nxp.PORTD.PCR13, nxp.GPIOD}, + 53: {12, &nxp.PORTD.PCR12, nxp.GPIOD}, + 54: {15, &nxp.PORTD.PCR15, nxp.GPIOD}, + 55: {11, &nxp.PORTD.PCR11, nxp.GPIOD}, + 56: {10, &nxp.PORTE.PCR10, nxp.GPIOE}, + 57: {11, &nxp.PORTE.PCR11, nxp.GPIOE}, + 58: {0, &nxp.PORTE.PCR0, nxp.GPIOE}, + 59: {1, &nxp.PORTE.PCR1, nxp.GPIOE}, + 60: {2, &nxp.PORTE.PCR2, nxp.GPIOE}, + 61: {3, &nxp.PORTE.PCR3, nxp.GPIOE}, + 62: {4, &nxp.PORTE.PCR4, nxp.GPIOE}, + 63: {5, &nxp.PORTE.PCR5, nxp.GPIOE}, } //go:inline -func (p Pin) registers() pinRegisters { - return _pinRegisters[p] -} +func (p Pin) reg() pin { return pins[p] } diff --git a/src/machine/buffer.go b/src/machine/buffer.go index ee12b9bcc3..1528c25be0 100644 --- a/src/machine/buffer.go +++ b/src/machine/buffer.go @@ -44,3 +44,9 @@ func (rb *RingBuffer) Get() (byte, bool) { } return 0, false } + +// Clear resets the head and tail pointer to zero. +func (rb *RingBuffer) Clear() { + rb.head.Set(0) + rb.tail.Set(0) +} diff --git a/src/machine/machine_nxpmk66f18.go b/src/machine/machine_nxpmk66f18.go index ab15ed1361..291fe1db70 100644 --- a/src/machine/machine_nxpmk66f18.go +++ b/src/machine/machine_nxpmk66f18.go @@ -7,19 +7,7 @@ import ( "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 +type FastPin struct { PDOR *volatile.BitRegister PSOR *volatile.BitRegister PCOR *volatile.BitRegister @@ -28,6 +16,12 @@ type pinRegisters struct { PDDR *volatile.BitRegister } +type pin struct { + Bit uint8 + PCR *volatile.Register32 + GPIO *nxp.GPIO_Type +} + // Configure this pin with the given configuration. func (p Pin) Configure(config PinConfig) { switch config.Mode { @@ -35,22 +29,47 @@ func (p Pin) Configure(config PinConfig) { panic("todo") case PinOutput: - p.registers().PDDR.Set() - p.registers().PCR.SetBits(PortControlRegisterSRE | PortControlRegisterDSE | PortControlRegisterMUX(1)) - p.registers().PCR.ClearBits(PortControlRegisterODE) + r := p.reg() + r.GPIO.PDDR.SetBits(1 << r.Bit) + r.PCR.SetBits(nxp.PORT_PCR0_SRE | nxp.PORT_PCR0_DSE | nxp.PORT_PCR0_MUX(1)) + r.PCR.ClearBits(nxp.PORT_PCR0_ODE) } } // Set changes the value of the GPIO pin. The pin must be configured as output. func (p Pin) Set(value bool) { + r := p.reg() if value { - p.registers().PSOR.Set() + r.GPIO.PSOR.Set(1 << r.Bit) } else { - p.registers().PCOR.Set() + r.GPIO.PCOR.Set(1 << r.Bit) } } // Get returns the current value of a GPIO pin. func (p Pin) Get() bool { - return p.registers().PDIR.Get() + r := p.reg() + return r.GPIO.PDIR.HasBits(1 << r.Bit) +} + +func (p Pin) Control() *volatile.Register32 { + return p.reg().PCR } + +func (p Pin) Fast() FastPin { + r := p.reg() + return FastPin{ + PDOR: r.GPIO.PDOR.Bit(r.Bit), + PSOR: r.GPIO.PSOR.Bit(r.Bit), + PCOR: r.GPIO.PCOR.Bit(r.Bit), + PTOR: r.GPIO.PTOR.Bit(r.Bit), + PDIR: r.GPIO.PDIR.Bit(r.Bit), + PDDR: r.GPIO.PDDR.Bit(r.Bit), + } +} + +func (p FastPin) Set() { p.PSOR.Set(true) } +func (p FastPin) Clear() { p.PCOR.Set(true) } +func (p FastPin) Toggle() { p.PTOR.Set(true) } +func (p FastPin) Write(v bool) { p.PDOR.Set(v) } +func (p FastPin) Read() bool { return p.PDIR.Get() } diff --git a/src/machine/uart_nxpmk66f18.go b/src/machine/uart_nxpmk66f18.go new file mode 100644 index 0000000000..f5cc0b182b --- /dev/null +++ b/src/machine/uart_nxpmk66f18.go @@ -0,0 +1,251 @@ +// +build nxp,mk66f18 + +package machine + +import ( + "device/arm" + "device/nxp" + "errors" + "runtime/volatile" + + _ "unsafe" // for go:linkname +) + +const ( + uartC2Enable = nxp.UART_C2_TE | nxp.UART_C2_RE | nxp.UART_C2_RIE | nxp.UART_C2_ILIE + uartC2TXActive = uartC2Enable | nxp.UART_C2_TIE + uartC2TXCompleting = uartC2Enable | nxp.UART_C2_TCIE + uartC2TXInactive = uartC2Enable + + uartIRQPriority = 64 +) + +var ( + ErrNotImplemented = errors.New("device has not been implemented") + ErrNotConfigured = errors.New("device has not been configured") +) + +type UARTConfig struct { + BaudRate uint32 +} + +type UART struct { + *nxp.UART_Type + RXPCR *volatile.Register32 + TXPCR *volatile.Register32 + SCGC *volatile.Register32 + SCGCMask uint32 + IRQNumber uint32 + + // state + RXBuffer RingBuffer + TXBuffer RingBuffer + Transmitting volatile.Register8 +} + +// 'UART0' in the K66 manual corresponds to 'UART1' on the Teensy's pinout +var UART1 = UART{UART_Type: nxp.UART0, RXPCR: pins[0].PCR, TXPCR: pins[1].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART0, IRQNumber: nxp.IRQ_UART0_RX_TX} +var UART2 = UART{UART_Type: nxp.UART1, RXPCR: pins[9].PCR, TXPCR: pins[10].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART1, IRQNumber: nxp.IRQ_UART1_RX_TX} +var UART3 = UART{UART_Type: nxp.UART2, RXPCR: pins[7].PCR, TXPCR: pins[8].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART2, IRQNumber: nxp.IRQ_UART2_RX_TX} +var UART4 = UART{UART_Type: nxp.UART3, RXPCR: pins[31].PCR, TXPCR: pins[32].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART3, IRQNumber: nxp.IRQ_UART3_RX_TX} +var UART5 = UART{UART_Type: nxp.UART4, RXPCR: pins[34].PCR, TXPCR: pins[33].PCR, SCGC: &nxp.SIM.SCGC1, SCGCMask: nxp.SIM_SCGC1_UART4, IRQNumber: nxp.IRQ_UART4_RX_TX} + +//go:export UART0_RX_TX_IRQHandler +func uart0StatusISR() { UART1.handleStatusInterrupt() } + +//go:export UART1_RX_TX_IRQHandler +func uart1StatusISR() { UART2.handleStatusInterrupt() } + +//go:export UART2_RX_TX_IRQHandler +func uart2StatusISR() { UART3.handleStatusInterrupt() } + +//go:export UART3_RX_TX_IRQHandler +func uart3StatusISR() { UART4.handleStatusInterrupt() } + +//go:export UART4_RX_TX_IRQHandler +func uart4StatusISR() { UART5.handleStatusInterrupt() } + +// Configure the UART. +func (u *UART) Configure(config UARTConfig) { + en := u.SCGC.HasBits(u.SCGCMask) + + // adapted from Teensy core's serial_begin + + if !en { + u.Transmitting.Set(0) + + // turn on the clock + u.SCGC.Set(u.SCGCMask) + + // configure pins + u.RXPCR.Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_PFE | nxp.PORT_PCR0_MUX(3)) + u.TXPCR.Set(nxp.PORT_PCR0_DSE | nxp.PORT_PCR0_SRE | nxp.PORT_PCR0_MUX(3)) + u.C1.Set(nxp.UART_C1_ILT) + } + + // default to 115200 baud + if config.BaudRate == 0 { + config.BaudRate = 115200 + } + + // copied from teensy core's BAUD2DIV macro + divisor := ((CPUFrequency() * 2) + ((config.BaudRate) >> 1)) / config.BaudRate + if divisor < 32 { + divisor = 32 + } + + if en { + // don't change baud rate mid transmit + u.Flush() + } + + // set the divisor + u.BDH.Set(uint8((divisor >> 13) & 0x1F)) + u.BDL.Set(uint8((divisor >> 5) & 0xFF)) + u.C4.Set(uint8(divisor & 0x1F)) + + if !en { + u.C1.Set(nxp.UART_C1_ILT) + + // configure TX and RX watermark + u.TWFIFO.Set(2) // causes bit TDRE of S1 to set + u.RWFIFO.Set(4) // causes bit RDRF of S1 to set + + u.PFIFO.Set(nxp.UART_PFIFO_TXFE | nxp.UART_PFIFO_RXFE) + u.C2.Set(uartC2TXInactive) + + arm.SetPriority(u.IRQNumber, uartIRQPriority) + arm.EnableIRQ(u.IRQNumber) + } +} + +func (u *UART) Disable() { + // adapted from Teensy core's serial_end + + // check if the device has been enabled already + if !u.SCGC.HasBits(u.SCGCMask) { + return + } + + u.Flush() + + arm.DisableIRQ(u.IRQNumber) + u.C2.Set(0) + + // reconfigure pin + u.RXPCR.Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_MUX(1)) + u.TXPCR.Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_MUX(1)) + + // clear flags + u.S1.Get() + u.D.Get() + u.RXBuffer.Clear() +} + +func (u *UART) Flush() { + for u.Transmitting.Get() != 0 { + // gosched() + } +} + +// adapted from Teensy core's uart0_status_isr +func (u *UART) handleStatusInterrupt() { + if u.S1.HasBits(nxp.UART_S1_RDRF | nxp.UART_S1_IDLE) { + intrs := arm.DisableInterrupts() + avail := u.RCFIFO.Get() + if avail == 0 { + // The only way to clear the IDLE interrupt flag is + // to read the data register. But reading with no + // data causes a FIFO underrun, which causes the + // FIFO to return corrupted data. If anyone from + // Freescale reads this, what a poor design! There + // write should be a write-1-to-clear for IDLE. + u.D.Get() + // flushing the fifo recovers from the underrun, + // but there's a possible race condition where a + // new character could be received between reading + // RCFIFO == 0 and flushing the FIFO. To minimize + // the chance, interrupts are disabled so a higher + // priority interrupt (hopefully) doesn't delay. + // TODO: change this to disabling the IDLE interrupt + // which won't be simple, since we already manage + // which transmit interrupts are enabled. + u.CFIFO.Set(nxp.UART_CFIFO_RXFLUSH) + arm.EnableInterrupts(intrs) + + } else { + arm.EnableInterrupts(intrs) + + for { + u.RXBuffer.Put(u.D.Get()) + avail-- + if avail <= 0 { + break + } + } + } + } + + c := u.C2.Get() + if c&nxp.UART_C2_TIE != 0 && u.S1.HasBits(nxp.UART_S1_TDRE) { + for { + n, ok := u.TXBuffer.Get() + if !ok { + break + } + + u.S1.Get() + u.D.Set(n) + + if u.TCFIFO.Get() >= 8 { + break + } + } + + if u.S1.HasBits(nxp.UART_S1_TDRE) { + u.Transmitting.Set(0) + u.C2.Set(uartC2TXCompleting) + } + } + + if c&nxp.UART_C2_TCIE != 0 && u.S1.HasBits(nxp.UART_S1_TC) { + u.C2.Set(uartC2TXInactive) + } +} + +//go:linkname gosched runtime.Gosched +func gosched() + +// WriteByte writes a byte of data to the UART. +func (u *UART) WriteByte(c byte) error { + if !u.SCGC.HasBits(u.SCGCMask) { + return ErrNotConfigured + } + + for !u.S1.HasBits(nxp.UART_S1_TDRE) { + gosched() + } + u.D.Set(c) + + // // wait for room on the buffer + // for !u.TXBuffer.Put(c) { + // gosched() + // } + + // var wrote bool + // for u.S1.HasBits(nxp.UART_S1_TDRE) { + // n, ok := u.TXBuffer.Get() + // if ok { + // u.D.Set(n) + // wrote = true + // } else { + // break + // } + // } + + // if wrote { + // u.Transmitting.Set(1) + // u.C2.Set(uartC2TXActive) + // } + return nil +} diff --git a/src/runtime/interrupt/interrupt_cortexm.go b/src/runtime/interrupt/interrupt_cortexm.go index c23f736d80..0653cb69a5 100644 --- a/src/runtime/interrupt/interrupt_cortexm.go +++ b/src/runtime/interrupt/interrupt_cortexm.go @@ -12,6 +12,11 @@ func (irq Interrupt) Enable() { arm.EnableIRQ(uint32(irq.num)) } +// Disable disables this interrupt. +func (irq Interrupt) Disable() { + arm.DisableIRQ(uint32(irq.num)) +} + // SetPriority sets the interrupt priority for this interrupt. A lower number // means a higher priority. Additionally, most hardware doesn't implement all // priority bits (only the uppoer bits). diff --git a/src/runtime/runtime_nxpmk66f18.go b/src/runtime/runtime_nxpmk66f18.go index 99617a4025..fef738f424 100644 --- a/src/runtime/runtime_nxpmk66f18.go +++ b/src/runtime/runtime_nxpmk66f18.go @@ -52,9 +52,31 @@ var ( SMC_PMSTAT_HSRUN = nxp.SMC_PMSTAT_PMSTAT(0x80) ) +var bootMsg = []byte("\r\n\r\nStartup complete, running main\r\n\r\n") + //go:section .resetHandler //go:export Reset_Handler func main() { + initSystem() + arm.Asm("CPSIE i") + initInternal() + startupLateHook() + + initAll() + machine.UART1.Configure(machine.UARTConfig{BaudRate: 115200}) + for _, c := range bootMsg { + for !machine.UART1.S1.HasBits(nxp.UART_S1_TDRE) { + } + machine.UART1.D.Set(c) + } + + callMain() + abort() +} + +// ported ResetHandler from mk20dx128.c from teensy3 core libraries +//go:noinline +func initSystem() { nxp.WDOG.UNLOCK.Set(WDOG_UNLOCK_SEQ1) nxp.WDOG.UNLOCK.Set(WDOG_UNLOCK_SEQ2) arm.Asm("nop") @@ -156,22 +178,11 @@ func main() { 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() { +//go:noinline +func initInternal() { arm.EnableIRQ(nxp.IRQ_PORTA) arm.EnableIRQ(nxp.IRQ_PORTB) arm.EnableIRQ(nxp.IRQ_PORTC) @@ -225,6 +236,11 @@ func initTeensyInternal() { nxp.TPM1.C1SC.Set(0x28) nxp.TPM1.SC.Set(nxp.FTM_SC_CLKS(1) | nxp.FTM_SC_PS(0)) + // configure the low-power timer + // nxp.LPTMR0.CSR.Set(nxp.LPTMR0_CSR_TIE) + // nxp.LPTMR0.PSR.Set(nxp.LPTMR0_PSR_PCS(3) | nxp.LPTMR0_PSR_PRESCALE(1)) // use main (external) clock, divided by 4 + // arm.EnableIRQ(nxp.IRQ_LPTMR0) + // analog_init(); // #if !defined(TEENSY_INIT_USB_DELAY_BEFORE) @@ -264,13 +280,8 @@ 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) { + machine.UART1.WriteByte(c) } // ??? @@ -280,18 +291,18 @@ const asyncScheduler = false const tickMicros = 1000 // number of ticks since boot -var tickMilliCount uint32 +var tickMilliCount volatile.Register32 //go:export SysTick_Handler func tickHandler() { - volatile.StoreUint32(&tickMilliCount, volatile.LoadUint32(&tickMilliCount)+1) + tickMilliCount.Set(tickMilliCount.Get() + 1) } // ticks are in microseconds func ticks() timeUnit { m := arm.DisableInterrupts() current := nxp.SysTick.CVR.Get() - count := tickMilliCount + count := tickMilliCount.Get() istatus := nxp.SystemControl.ICSR.Get() arm.EnableInterrupts(m) @@ -322,7 +333,7 @@ func sleepTicks(d timeUnit) { } start += 1000 } - // Gosched() + arm.Asm("wfi") } } diff --git a/src/runtime/volatile/register_nxpmk66f18.go b/src/runtime/volatile/register_nxpmk66f18.go index 272205a4dc..0f9b4cb80d 100644 --- a/src/runtime/volatile/register_nxpmk66f18.go +++ b/src/runtime/volatile/register_nxpmk66f18.go @@ -10,14 +10,14 @@ const bitbandBase = 0x42000000 const ptrBytes = unsafe.Sizeof(uintptr(0)) //go:inline -func bitbandAddress(reg uintptr, bit uintptr) uintptr { - if bit > ptrBytes*8 { +func bitbandAddress(reg uintptr, bit uint8) uintptr { + if uintptr(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 + return (reg-registerBase)*ptrBytes*8 + uintptr(bit)*ptrBytes + bitbandBase } // Special types that causes loads/stores to be volatile (necessary for @@ -40,17 +40,12 @@ func (r *BitRegister) Get() bool { // *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) +func (r *BitRegister) Set(v bool) { + var i uint32 + if v { + i = 1 + } + StoreUint32(&r.Reg, i) } // Bit maps bit N of register R to the corresponding bitband address. Bit panics @@ -58,7 +53,7 @@ func (r *BitRegister) Clear() { // the number of bits in a register minus one). // // go:inline -func (r *Register8) Bit(bit uintptr) *BitRegister { +func (r *Register8) Bit(bit uint8) *BitRegister { ptr := bitbandAddress(uintptr(unsafe.Pointer(&r.Reg)), bit) return (*BitRegister)(unsafe.Pointer(ptr)) } @@ -68,7 +63,7 @@ func (r *Register8) Bit(bit uintptr) *BitRegister { // the number of bits in a register minus one). // // go:inline -func (r *Register16) Bit(bit uintptr) *BitRegister { +func (r *Register16) Bit(bit uint8) *BitRegister { ptr := bitbandAddress(uintptr(unsafe.Pointer(&r.Reg)), bit) return (*BitRegister)(unsafe.Pointer(ptr)) } @@ -78,7 +73,7 @@ func (r *Register16) Bit(bit uintptr) *BitRegister { // the number of bits in a register minus one). // // go:inline -func (r *Register32) Bit(bit uintptr) *BitRegister { +func (r *Register32) Bit(bit uint8) *BitRegister { ptr := bitbandAddress(uintptr(unsafe.Pointer(&r.Reg)), bit) return (*BitRegister)(unsafe.Pointer(ptr)) } diff --git a/targets/nxpmk66f18.ld b/targets/nxpmk66f18.ld index 74e22761f6..3487264e6a 100644 --- a/targets/nxpmk66f18.ld +++ b/targets/nxpmk66f18.ld @@ -22,19 +22,19 @@ SECTIONS _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)) + _flash_config = .; + KEEP(*(.flash_config)) /* everything else */ + *(.resetHandler) *(.text) *(.text*) *(.rodata) *(.rodata*) . = ALIGN(4); + } >FLASH_TEXT = 0xFF /* Put the stack at the bottom of RAM, so that the application will diff --git a/targets/teensy36.json b/targets/teensy36.json index fecd3c3570..473aec2eb6 100644 --- a/targets/teensy36.json +++ b/targets/teensy36.json @@ -11,7 +11,8 @@ ], "linkerscript": "targets/nxpmk66f18.ld", "extra-files": [ - "src/device/nxp/mk66f18.s" + "src/device/nxp/mk66f18.s", + "targets/teensy36.s" ], "flash-command": "teensy_loader_cli -mmcu=mk66fx1m0 -v -w {hex}" } diff --git a/targets/teensy36.s b/targets/teensy36.s new file mode 100644 index 0000000000..4e17e7c9d8 --- /dev/null +++ b/targets/teensy36.s @@ -0,0 +1,19 @@ + +.section .flash_config +.global __flash_config + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xFF + .byte 0xDE + .byte 0xF9 + .byte 0xFF + .byte 0xFF \ No newline at end of file From ab0b6faed70e5aa5dbdc4bc1490ac83c69741462 Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Sun, 23 Feb 2020 20:09:44 -0600 Subject: [PATCH 3/4] Viable NXP/Teensy support - Fix UART & putChar - Timer-based sleep - Enable systick in abort - Buffered, interrupt-based UART TX - Use the new interrupt API and fix sleepTicks - Make pins behave more like other boards - Use the MCU's UART numbering - Allow interrupts to wake the scheduler (#1214) --- src/machine/board_teensy36.go | 175 +++++------ src/machine/machine_nxp.go | 33 --- src/machine/machine_nxpmk66f18.go | 271 ++++++++++++++++-- src/machine/uart.go | 2 +- src/machine/uart_nxpmk66f18.go | 240 ++++++++++------ src/runtime/runtime_nxp.go | 5 - src/runtime/runtime_nxpmk66f18.go | 214 ++++---------- src/runtime/time_nxpmk66f18.go | 163 +++++++++++ ...er_nxpmk66f18.go => bitband_nxpmk66f18.go} | 5 +- targets/nxpmk66f18.ld | 26 +- targets/teensy36.s | 8 +- 11 files changed, 707 insertions(+), 435 deletions(-) delete mode 100644 src/machine/machine_nxp.go delete mode 100644 src/runtime/runtime_nxp.go create mode 100644 src/runtime/time_nxpmk66f18.go rename src/runtime/volatile/{register_nxpmk66f18.go => bitband_nxpmk66f18.go} (93%) diff --git a/src/machine/board_teensy36.go b/src/machine/board_teensy36.go index f7991af7b2..f0eccecd3d 100644 --- a/src/machine/board_teensy36.go +++ b/src/machine/board_teensy36.go @@ -2,91 +2,100 @@ package machine -import ( - "device/nxp" -) - -// //go:keep -// //go:section .flash_config -// var FlashControl = [16]byte{ -// 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -// 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xF9, 0xFF, 0xFF, -// } +// CPUFrequency returns the frequency of the ARM core clock (180MHz) +func CPUFrequency() uint32 { return 180000000 } -func CPUFrequency() uint32 { - return 180000000 -} +// ClockFrequency returns the frequency of the external oscillator (16MHz) +func ClockFrequency() uint32 { return 16000000 } // LED on the Teensy -const LED Pin = 13 +const LED = PC05 + +// digital IO +const ( + D00 = PB16 + D01 = PB17 + D02 = PD00 + D03 = PA12 + D04 = PA13 + D05 = PD07 + D06 = PD04 + D07 = PD02 + D08 = PD03 + D09 = PC03 + D10 = PC04 + D11 = PC06 + D12 = PC07 + D13 = PC05 + D14 = PD01 + D15 = PC00 + D16 = PB00 + D17 = PB01 + D18 = PB03 + D19 = PB02 + D20 = PD05 + D21 = PD06 + D22 = PC01 + D23 = PC02 + D24 = PE26 + D25 = PA05 + D26 = PA14 + D27 = PA15 + D28 = PA16 + D29 = PB18 + D30 = PB19 + D31 = PB10 + D32 = PB11 + D33 = PE24 + D34 = PE25 + D35 = PC08 + D36 = PC09 + D37 = PC10 + D38 = PC11 + D39 = PA17 + D40 = PA28 + D41 = PA29 + D42 = PA26 + D43 = PB20 + D44 = PB22 + D45 = PB23 + D46 = PB21 + D47 = PD08 + D48 = PD09 + D49 = PB04 + D50 = PB05 + D51 = PD14 + D52 = PD13 + D53 = PD12 + D54 = PD15 + D55 = PD11 + D56 = PE10 + D57 = PE11 + D58 = PE00 + D59 = PE01 + D60 = PE02 + D61 = PE03 + D62 = PE04 + D63 = PE05 +) -var pins = []pin{ - // {bit, control register, gpio register bank} - 0: {16, &nxp.PORTB.PCR16, nxp.GPIOB}, - 1: {17, &nxp.PORTB.PCR17, nxp.GPIOB}, - 2: {0, &nxp.PORTD.PCR0, nxp.GPIOD}, - 3: {12, &nxp.PORTA.PCR12, nxp.GPIOA}, - 4: {13, &nxp.PORTA.PCR13, nxp.GPIOA}, - 5: {7, &nxp.PORTD.PCR7, nxp.GPIOD}, - 6: {4, &nxp.PORTD.PCR4, nxp.GPIOD}, - 7: {2, &nxp.PORTD.PCR2, nxp.GPIOD}, - 8: {3, &nxp.PORTD.PCR3, nxp.GPIOD}, - 9: {3, &nxp.PORTC.PCR3, nxp.GPIOC}, - 10: {4, &nxp.PORTC.PCR4, nxp.GPIOC}, - 11: {6, &nxp.PORTC.PCR6, nxp.GPIOC}, - 12: {7, &nxp.PORTC.PCR7, nxp.GPIOC}, - 13: {5, &nxp.PORTC.PCR5, nxp.GPIOC}, - 14: {1, &nxp.PORTD.PCR1, nxp.GPIOD}, - 15: {0, &nxp.PORTC.PCR0, nxp.GPIOC}, - 16: {0, &nxp.PORTB.PCR0, nxp.GPIOB}, - 17: {1, &nxp.PORTB.PCR1, nxp.GPIOB}, - 18: {3, &nxp.PORTB.PCR3, nxp.GPIOB}, - 19: {2, &nxp.PORTB.PCR2, nxp.GPIOB}, - 20: {5, &nxp.PORTD.PCR5, nxp.GPIOD}, - 21: {6, &nxp.PORTD.PCR6, nxp.GPIOD}, - 22: {1, &nxp.PORTC.PCR1, nxp.GPIOC}, - 23: {2, &nxp.PORTC.PCR2, nxp.GPIOC}, - 24: {26, &nxp.PORTE.PCR26, nxp.GPIOE}, - 25: {5, &nxp.PORTA.PCR5, nxp.GPIOA}, - 26: {14, &nxp.PORTA.PCR14, nxp.GPIOA}, - 27: {15, &nxp.PORTA.PCR15, nxp.GPIOA}, - 28: {16, &nxp.PORTA.PCR16, nxp.GPIOA}, - 29: {18, &nxp.PORTB.PCR18, nxp.GPIOB}, - 30: {19, &nxp.PORTB.PCR19, nxp.GPIOB}, - 31: {10, &nxp.PORTB.PCR10, nxp.GPIOB}, - 32: {11, &nxp.PORTB.PCR11, nxp.GPIOB}, - 33: {24, &nxp.PORTE.PCR24, nxp.GPIOE}, - 34: {25, &nxp.PORTE.PCR25, nxp.GPIOE}, - 35: {8, &nxp.PORTC.PCR8, nxp.GPIOC}, - 36: {9, &nxp.PORTC.PCR9, nxp.GPIOC}, - 37: {10, &nxp.PORTC.PCR10, nxp.GPIOC}, - 38: {11, &nxp.PORTC.PCR11, nxp.GPIOC}, - 39: {17, &nxp.PORTA.PCR17, nxp.GPIOA}, - 40: {28, &nxp.PORTA.PCR28, nxp.GPIOA}, - 41: {29, &nxp.PORTA.PCR29, nxp.GPIOA}, - 42: {26, &nxp.PORTA.PCR26, nxp.GPIOA}, - 43: {20, &nxp.PORTB.PCR20, nxp.GPIOB}, - 44: {22, &nxp.PORTB.PCR22, nxp.GPIOB}, - 45: {23, &nxp.PORTB.PCR23, nxp.GPIOB}, - 46: {21, &nxp.PORTB.PCR21, nxp.GPIOB}, - 47: {8, &nxp.PORTD.PCR8, nxp.GPIOD}, - 48: {9, &nxp.PORTD.PCR9, nxp.GPIOD}, - 49: {4, &nxp.PORTB.PCR4, nxp.GPIOB}, - 50: {5, &nxp.PORTB.PCR5, nxp.GPIOB}, - 51: {14, &nxp.PORTD.PCR14, nxp.GPIOD}, - 52: {13, &nxp.PORTD.PCR13, nxp.GPIOD}, - 53: {12, &nxp.PORTD.PCR12, nxp.GPIOD}, - 54: {15, &nxp.PORTD.PCR15, nxp.GPIOD}, - 55: {11, &nxp.PORTD.PCR11, nxp.GPIOD}, - 56: {10, &nxp.PORTE.PCR10, nxp.GPIOE}, - 57: {11, &nxp.PORTE.PCR11, nxp.GPIOE}, - 58: {0, &nxp.PORTE.PCR0, nxp.GPIOE}, - 59: {1, &nxp.PORTE.PCR1, nxp.GPIOE}, - 60: {2, &nxp.PORTE.PCR2, nxp.GPIOE}, - 61: {3, &nxp.PORTE.PCR3, nxp.GPIOE}, - 62: {4, &nxp.PORTE.PCR4, nxp.GPIOE}, - 63: {5, &nxp.PORTE.PCR5, nxp.GPIOE}, -} +var ( + TeensyUART1 = &UART0 + TeensyUART2 = &UART1 + TeensyUART3 = &UART2 + TeensyUART4 = &UART3 + TeensyUART5 = &UART4 +) -//go:inline -func (p Pin) reg() pin { return pins[p] } +const ( + defaultUART0RX = D00 + defaultUART0TX = D01 + defaultUART1RX = D09 + defaultUART1TX = D10 + defaultUART2RX = D07 + defaultUART2TX = D08 + defaultUART3RX = D31 + defaultUART3TX = D32 + defaultUART4RX = D34 + defaultUART4TX = D33 +) diff --git a/src/machine/machine_nxp.go b/src/machine/machine_nxp.go deleted file mode 100644 index b29d4328cf..0000000000 --- a/src/machine/machine_nxp.go +++ /dev/null @@ -1,33 +0,0 @@ -// +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 index 291fe1db70..3804b67b37 100644 --- a/src/machine/machine_nxpmk66f18.go +++ b/src/machine/machine_nxpmk66f18.go @@ -1,3 +1,32 @@ +// 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 machine @@ -5,69 +34,249 @@ package machine import ( "device/nxp" "runtime/volatile" + "unsafe" ) -type FastPin struct { - PDOR *volatile.BitRegister - PSOR *volatile.BitRegister - PCOR *volatile.BitRegister - PTOR *volatile.BitRegister - PDIR *volatile.BitRegister - PDDR *volatile.BitRegister -} +type PinMode uint8 + +const ( + PinInput PinMode = iota + PinInputPullUp + PinInputPullDown + PinOutput + PinOutputOpenDrain + PinDisable +) + +const ( + PA00 Pin = iota + PA01 + PA02 + PA03 + PA04 + PA05 + PA06 + PA07 + PA08 + PA09 + PA10 + PA11 + PA12 + PA13 + PA14 + PA15 + PA16 + PA17 + PA18 + PA19 + PA20 + PA21 + PA22 + PA23 + PA24 + PA25 + PA26 + PA27 + PA28 + PA29 +) + +const ( + PB00 Pin = iota + 32 + PB01 + PB02 + PB03 + PB04 + PB05 + PB06 + PB07 + PB08 + PB09 + PB10 + PB11 + _ + _ + _ + _ + PB16 + PB17 + PB18 + PB19 + PB20 + PB21 + PB22 + PB23 +) + +const ( + PC00 Pin = iota + 64 + PC01 + PC02 + PC03 + PC04 + PC05 + PC06 + PC07 + PC08 + PC09 + PC10 + PC11 + PC12 + PC13 + PC14 + PC15 + PC16 + PC17 + PC18 + PC19 +) + +const ( + PD00 Pin = iota + 96 + PD01 + PD02 + PD03 + PD04 + PD05 + PD06 + PD07 + PD08 + PD09 + PD10 + PD11 + PD12 + PD13 + PD14 + PD15 +) + +const ( + PE00 Pin = iota + 128 + PE01 + PE02 + PE03 + PE04 + PE05 + PE06 + PE07 + PE08 + PE09 + PE10 + PE11 + PE12 + PE13 + PE14 + PE15 + PE16 + PE17 + PE18 + PE19 + PE20 + PE21 + PE22 + PE23 + PE24 + PE25 + PE26 + PE27 + PE28 +) -type pin struct { - Bit uint8 - PCR *volatile.Register32 - GPIO *nxp.GPIO_Type +//go:inline +func (p Pin) reg() (*nxp.GPIO_Type, *volatile.Register32, uint8) { + var gpio *nxp.GPIO_Type + var pcr *nxp.PORT_Type + + if p < 32 { + gpio, pcr = nxp.GPIOA, nxp.PORTA + } else if p < 64 { + gpio, pcr = nxp.GPIOB, nxp.PORTB + } else if p < 96 { + gpio, pcr = nxp.GPIOC, nxp.PORTC + } else if p < 128 { + gpio, pcr = nxp.GPIOD, nxp.PORTD + } else if p < 160 { + gpio, pcr = nxp.GPIOE, nxp.PORTE + } else { + panic("invalid pin number") + } + + return gpio, &(*[32]volatile.Register32)(unsafe.Pointer(pcr))[p%32], uint8(p % 32) } // Configure this pin with the given configuration. func (p Pin) Configure(config PinConfig) { + gpio, pcr, pos := p.reg() + switch config.Mode { + case PinOutput: + gpio.PDDR.SetBits(1 << pos) + pcr.Set((1 << nxp.PORT_PCR0_MUX_Pos) | nxp.PORT_PCR0_SRE | nxp.PORT_PCR0_DSE) + + case PinOutputOpenDrain: + gpio.PDDR.SetBits(1 << pos) + pcr.Set((1 << nxp.PORT_PCR0_MUX_Pos) | nxp.PORT_PCR0_SRE | nxp.PORT_PCR0_DSE | nxp.PORT_PCR0_ODE) + case PinInput: - panic("todo") + gpio.PDDR.ClearBits(1 << pos) + pcr.Set((1 << nxp.PORT_PCR0_MUX_Pos)) - case PinOutput: - r := p.reg() - r.GPIO.PDDR.SetBits(1 << r.Bit) - r.PCR.SetBits(nxp.PORT_PCR0_SRE | nxp.PORT_PCR0_DSE | nxp.PORT_PCR0_MUX(1)) - r.PCR.ClearBits(nxp.PORT_PCR0_ODE) + case PinInputPullUp: + gpio.PDDR.ClearBits(1 << pos) + pcr.Set((1 << nxp.PORT_PCR0_MUX_Pos) | nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS) + + case PinInputPullDown: + gpio.PDDR.ClearBits(1 << pos) + pcr.Set((1 << nxp.PORT_PCR0_MUX_Pos) | nxp.PORT_PCR0_PE) + + case PinDisable: + gpio.PDDR.ClearBits(1 << pos) + pcr.Set((0 << nxp.PORT_PCR0_MUX_Pos)) } } // Set changes the value of the GPIO pin. The pin must be configured as output. func (p Pin) Set(value bool) { - r := p.reg() + gpio, _, pos := p.reg() if value { - r.GPIO.PSOR.Set(1 << r.Bit) + gpio.PSOR.Set(1 << pos) } else { - r.GPIO.PCOR.Set(1 << r.Bit) + gpio.PCOR.Set(1 << pos) } } // Get returns the current value of a GPIO pin. func (p Pin) Get() bool { - r := p.reg() - return r.GPIO.PDIR.HasBits(1 << r.Bit) + gpio, _, pos := p.reg() + return gpio.PDIR.HasBits(1 << pos) } func (p Pin) Control() *volatile.Register32 { - return p.reg().PCR + _, pcr, _ := p.reg() + return pcr } func (p Pin) Fast() FastPin { - r := p.reg() + gpio, _, pos := p.reg() return FastPin{ - PDOR: r.GPIO.PDOR.Bit(r.Bit), - PSOR: r.GPIO.PSOR.Bit(r.Bit), - PCOR: r.GPIO.PCOR.Bit(r.Bit), - PTOR: r.GPIO.PTOR.Bit(r.Bit), - PDIR: r.GPIO.PDIR.Bit(r.Bit), - PDDR: r.GPIO.PDDR.Bit(r.Bit), + PDOR: gpio.PDOR.Bit(pos), + PSOR: gpio.PSOR.Bit(pos), + PCOR: gpio.PCOR.Bit(pos), + PTOR: gpio.PTOR.Bit(pos), + PDIR: gpio.PDIR.Bit(pos), + PDDR: gpio.PDDR.Bit(pos), } } +type FastPin struct { + PDOR *volatile.BitRegister + PSOR *volatile.BitRegister + PCOR *volatile.BitRegister + PTOR *volatile.BitRegister + PDIR *volatile.BitRegister + PDDR *volatile.BitRegister +} + func (p FastPin) Set() { p.PSOR.Set(true) } func (p FastPin) Clear() { p.PCOR.Set(true) } func (p FastPin) Toggle() { p.PTOR.Set(true) } diff --git a/src/machine/uart.go b/src/machine/uart.go index f3f46a5712..c68324b610 100644 --- a/src/machine/uart.go +++ b/src/machine/uart.go @@ -1,4 +1,4 @@ -// +build avr nrf sam sifive stm32 k210 +// +build avr nrf sam sifive stm32 k210 nxp package machine diff --git a/src/machine/uart_nxpmk66f18.go b/src/machine/uart_nxpmk66f18.go index f5cc0b182b..3fb4d9496a 100644 --- a/src/machine/uart_nxpmk66f18.go +++ b/src/machine/uart_nxpmk66f18.go @@ -1,3 +1,32 @@ +// 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 machine @@ -6,6 +35,7 @@ import ( "device/arm" "device/nxp" "errors" + "runtime/interrupt" "runtime/volatile" _ "unsafe" // for go:linkname @@ -18,6 +48,10 @@ const ( uartC2TXInactive = uartC2Enable uartIRQPriority = 64 + + // determined from UARTx_PFIFO + uartRXFIFODepth = 8 + uartTXFIFODepth = 8 ) var ( @@ -25,61 +59,81 @@ var ( ErrNotConfigured = errors.New("device has not been configured") ) -type UARTConfig struct { - BaudRate uint32 -} +//go:linkname gosched runtime.Gosched +func gosched() -type UART struct { - *nxp.UART_Type - RXPCR *volatile.Register32 - TXPCR *volatile.Register32 - SCGC *volatile.Register32 - SCGCMask uint32 - IRQNumber uint32 +// PutcharUART writes a byte to the UART synchronously, without using interrupts +// or calling the scheduler +func PutcharUART(u UART, c byte) { + // ensure the UART has been configured + if !u.SCGC.HasBits(u.SCGCMask) { + u.configure(UARTConfig{}, false) + } - // state - RXBuffer RingBuffer - TXBuffer RingBuffer - Transmitting volatile.Register8 + for u.TCFIFO.Get() > 0 { + // busy wait + } + u.D.Set(c) + u.C2.Set(uartC2TXActive) } -// 'UART0' in the K66 manual corresponds to 'UART1' on the Teensy's pinout -var UART1 = UART{UART_Type: nxp.UART0, RXPCR: pins[0].PCR, TXPCR: pins[1].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART0, IRQNumber: nxp.IRQ_UART0_RX_TX} -var UART2 = UART{UART_Type: nxp.UART1, RXPCR: pins[9].PCR, TXPCR: pins[10].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART1, IRQNumber: nxp.IRQ_UART1_RX_TX} -var UART3 = UART{UART_Type: nxp.UART2, RXPCR: pins[7].PCR, TXPCR: pins[8].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART2, IRQNumber: nxp.IRQ_UART2_RX_TX} -var UART4 = UART{UART_Type: nxp.UART3, RXPCR: pins[31].PCR, TXPCR: pins[32].PCR, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART3, IRQNumber: nxp.IRQ_UART3_RX_TX} -var UART5 = UART{UART_Type: nxp.UART4, RXPCR: pins[34].PCR, TXPCR: pins[33].PCR, SCGC: &nxp.SIM.SCGC1, SCGCMask: nxp.SIM_SCGC1_UART4, IRQNumber: nxp.IRQ_UART4_RX_TX} +// PollUART manually checks a UART status and calls the ISR. This should only be +// called by runtime.abort. +func PollUART(u UART) { + if u.SCGC.HasBits(u.SCGCMask) { + u.handleStatusInterrupt(u.Interrupt) + } +} -//go:export UART0_RX_TX_IRQHandler -func uart0StatusISR() { UART1.handleStatusInterrupt() } +type UART = *UARTData -//go:export UART1_RX_TX_IRQHandler -func uart1StatusISR() { UART2.handleStatusInterrupt() } +type UARTData struct { + *nxp.UART_Type + SCGC *volatile.Register32 + SCGCMask uint32 -//go:export UART2_RX_TX_IRQHandler -func uart2StatusISR() { UART3.handleStatusInterrupt() } + DefaultRX Pin + DefaultTX Pin -//go:export UART3_RX_TX_IRQHandler -func uart3StatusISR() { UART4.handleStatusInterrupt() } + // state + Buffer RingBuffer // RX Buffer + TXBuffer RingBuffer + Configured bool + Transmitting volatile.Register8 + Interrupt interrupt.Interrupt +} -//go:export UART4_RX_TX_IRQHandler -func uart4StatusISR() { UART5.handleStatusInterrupt() } +var UART0 = UARTData{UART_Type: nxp.UART0, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART0, DefaultRX: defaultUART0RX, DefaultTX: defaultUART0TX} +var UART1 = UARTData{UART_Type: nxp.UART1, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART1, DefaultRX: defaultUART1RX, DefaultTX: defaultUART1TX} +var UART2 = UARTData{UART_Type: nxp.UART2, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART2, DefaultRX: defaultUART2RX, DefaultTX: defaultUART2TX} +var UART3 = UARTData{UART_Type: nxp.UART3, SCGC: &nxp.SIM.SCGC4, SCGCMask: nxp.SIM_SCGC4_UART3, DefaultRX: defaultUART3RX, DefaultTX: defaultUART3TX} +var UART4 = UARTData{UART_Type: nxp.UART4, SCGC: &nxp.SIM.SCGC1, SCGCMask: nxp.SIM_SCGC1_UART4, DefaultRX: defaultUART4RX, DefaultTX: defaultUART4TX} + +func init() { + UART0.Interrupt = interrupt.New(nxp.IRQ_UART0_RX_TX, UART0.handleStatusInterrupt) + UART1.Interrupt = interrupt.New(nxp.IRQ_UART1_RX_TX, UART1.handleStatusInterrupt) + UART2.Interrupt = interrupt.New(nxp.IRQ_UART2_RX_TX, UART2.handleStatusInterrupt) + UART3.Interrupt = interrupt.New(nxp.IRQ_UART3_RX_TX, UART3.handleStatusInterrupt) + UART4.Interrupt = interrupt.New(nxp.IRQ_UART4_RX_TX, UART4.handleStatusInterrupt) +} // Configure the UART. -func (u *UART) Configure(config UARTConfig) { - en := u.SCGC.HasBits(u.SCGCMask) +func (u UART) Configure(config UARTConfig) { + u.configure(config, true) +} - // adapted from Teensy core's serial_begin +func (u UART) configure(config UARTConfig, canSched bool) { + // from: serial_begin - if !en { + if !u.Configured { u.Transmitting.Set(0) // turn on the clock u.SCGC.Set(u.SCGCMask) // configure pins - u.RXPCR.Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_PFE | nxp.PORT_PCR0_MUX(3)) - u.TXPCR.Set(nxp.PORT_PCR0_DSE | nxp.PORT_PCR0_SRE | nxp.PORT_PCR0_MUX(3)) + u.DefaultRX.Control().Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_PFE | (3 << nxp.PORT_PCR0_MUX_Pos)) + u.DefaultTX.Control().Set(nxp.PORT_PCR0_DSE | nxp.PORT_PCR0_SRE | (3 << nxp.PORT_PCR0_MUX_Pos)) u.C1.Set(nxp.UART_C1_ILT) } @@ -89,14 +143,20 @@ func (u *UART) Configure(config UARTConfig) { } // copied from teensy core's BAUD2DIV macro - divisor := ((CPUFrequency() * 2) + ((config.BaudRate) >> 1)) / config.BaudRate + divisor := ((CPUFrequency() * 2) + (config.BaudRate >> 1)) / config.BaudRate if divisor < 32 { divisor = 32 } - if en { + if u.Configured { // don't change baud rate mid transmit - u.Flush() + if canSched { + u.Flush() + } else { + for u.Transmitting.Get() != 0 { + // busy wait flush + } + } } // set the divisor @@ -104,23 +164,27 @@ func (u *UART) Configure(config UARTConfig) { u.BDL.Set(uint8((divisor >> 5) & 0xFF)) u.C4.Set(uint8(divisor & 0x1F)) - if !en { + if !u.Configured { + u.Configured = true + u.C1.Set(nxp.UART_C1_ILT) // configure TX and RX watermark u.TWFIFO.Set(2) // causes bit TDRE of S1 to set u.RWFIFO.Set(4) // causes bit RDRF of S1 to set + // enable FIFOs u.PFIFO.Set(nxp.UART_PFIFO_TXFE | nxp.UART_PFIFO_RXFE) - u.C2.Set(uartC2TXInactive) - arm.SetPriority(u.IRQNumber, uartIRQPriority) - arm.EnableIRQ(u.IRQNumber) + // setup interrupts + u.C2.Set(uartC2TXInactive) + u.Interrupt.SetPriority(uartIRQPriority) + u.Interrupt.Enable() } } -func (u *UART) Disable() { - // adapted from Teensy core's serial_end +func (u UART) Disable() { + // from: serial_end // check if the device has been enabled already if !u.SCGC.HasBits(u.SCGCMask) { @@ -129,27 +193,29 @@ func (u *UART) Disable() { u.Flush() - arm.DisableIRQ(u.IRQNumber) + u.Interrupt.Disable() u.C2.Set(0) // reconfigure pin - u.RXPCR.Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_MUX(1)) - u.TXPCR.Set(nxp.PORT_PCR0_PE | nxp.PORT_PCR0_PS | nxp.PORT_PCR0_MUX(1)) + u.DefaultRX.Configure(PinConfig{Mode: PinInputPullUp}) + u.DefaultTX.Configure(PinConfig{Mode: PinInputPullUp}) // clear flags u.S1.Get() u.D.Get() - u.RXBuffer.Clear() + u.Buffer.Clear() } -func (u *UART) Flush() { +func (u UART) Flush() { for u.Transmitting.Get() != 0 { - // gosched() + gosched() } } -// adapted from Teensy core's uart0_status_isr -func (u *UART) handleStatusInterrupt() { +func (u UART) handleStatusInterrupt(interrupt.Interrupt) { + // from: uart0_status_isr + + // receive if u.S1.HasBits(nxp.UART_S1_RDRF | nxp.UART_S1_IDLE) { intrs := arm.DisableInterrupts() avail := u.RCFIFO.Get() @@ -177,7 +243,7 @@ func (u *UART) handleStatusInterrupt() { arm.EnableInterrupts(intrs) for { - u.RXBuffer.Put(u.D.Get()) + u.Buffer.Put(u.D.Get()) avail-- if avail <= 0 { break @@ -186,66 +252,54 @@ func (u *UART) handleStatusInterrupt() { } } - c := u.C2.Get() - if c&nxp.UART_C2_TIE != 0 && u.S1.HasBits(nxp.UART_S1_TDRE) { - for { - n, ok := u.TXBuffer.Get() - if !ok { + // transmit + if u.C2.HasBits(nxp.UART_C2_TIE) && u.S1.HasBits(nxp.UART_S1_TDRE) { + data := make([]byte, 0, uartTXFIFODepth) + avail := uartTXFIFODepth - u.TCFIFO.Get() + + // get avail bytes from ring buffer + for len(data) < int(avail) { + if b, ok := u.TXBuffer.Get(); ok { + data = append(data, b) + } else { break } + } - u.S1.Get() - u.D.Set(n) - - if u.TCFIFO.Get() >= 8 { - break + // write data to FIFO + l := len(data) + for i, b := range data { + if i == l-1 { + // only clear TDRE on last write, per the manual + u.S1.Get() } + u.D.Set(b) } + // if FIFO still has room, disable TIE, enable TCIE if u.S1.HasBits(nxp.UART_S1_TDRE) { - u.Transmitting.Set(0) u.C2.Set(uartC2TXCompleting) } } - if c&nxp.UART_C2_TCIE != 0 && u.S1.HasBits(nxp.UART_S1_TC) { + // transmit complete + if u.C2.HasBits(nxp.UART_C2_TCIE) && u.S1.HasBits(nxp.UART_S1_TC) { + u.Transmitting.Set(0) u.C2.Set(uartC2TXInactive) } } -//go:linkname gosched runtime.Gosched -func gosched() - // WriteByte writes a byte of data to the UART. -func (u *UART) WriteByte(c byte) error { - if !u.SCGC.HasBits(u.SCGCMask) { +func (u UART) WriteByte(c byte) error { + if !u.Configured { return ErrNotConfigured } - for !u.S1.HasBits(nxp.UART_S1_TDRE) { + for !u.TXBuffer.Put(c) { gosched() } - u.D.Set(c) - // // wait for room on the buffer - // for !u.TXBuffer.Put(c) { - // gosched() - // } - - // var wrote bool - // for u.S1.HasBits(nxp.UART_S1_TDRE) { - // n, ok := u.TXBuffer.Get() - // if ok { - // u.D.Set(n) - // wrote = true - // } else { - // break - // } - // } - - // if wrote { - // u.Transmitting.Set(1) - // u.C2.Set(uartC2TXActive) - // } + u.Transmitting.Set(1) + u.C2.Set(uartC2TXActive) return nil } diff --git a/src/runtime/runtime_nxp.go b/src/runtime/runtime_nxp.go deleted file mode 100644 index 08f6635c40..0000000000 --- a/src/runtime/runtime_nxp.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build nxp - -package runtime - -type timeUnit int64 diff --git a/src/runtime/runtime_nxpmk66f18.go b/src/runtime/runtime_nxpmk66f18.go index fef738f424..b03e1257c3 100644 --- a/src/runtime/runtime_nxpmk66f18.go +++ b/src/runtime/runtime_nxpmk66f18.go @@ -35,53 +35,41 @@ import ( "device/arm" "device/nxp" "machine" - "runtime/volatile" ) const ( - WDOG_UNLOCK_SEQ1 = 0xC520 - WDOG_UNLOCK_SEQ2 = 0xD928 + watchdogUnlockSequence1 = 0xC520 + watchdogUnlockSequence2 = 0xD928 - DEFAULT_FTM_MOD = 61440 - 1 - DEFAULT_FTM_PRESCALE = 1 + _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) +const ( + _SIM_SOPT2_IRC48SEL = 3 << nxp.SIM_SOPT2_PLLFLLSEL_Pos + _SMC_PMCTRL_HSRUN = 3 << nxp.SMC_PMCTRL_RUNM_Pos + _SMC_PMSTAT_HSRUN = 0x80 << nxp.SMC_PMSTAT_PMSTAT_Pos ) -var bootMsg = []byte("\r\n\r\nStartup complete, running main\r\n\r\n") - -//go:section .resetHandler //go:export Reset_Handler func main() { initSystem() arm.Asm("CPSIE i") initInternal() - startupLateHook() - - initAll() - machine.UART1.Configure(machine.UARTConfig{BaudRate: 115200}) - for _, c := range bootMsg { - for !machine.UART1.S1.HasBits(nxp.UART_S1_TDRE) { - } - machine.UART1.D.Set(c) - } - callMain() + run() abort() } -// ported ResetHandler from mk20dx128.c from teensy3 core libraries -//go:noinline func initSystem() { - nxp.WDOG.UNLOCK.Set(WDOG_UNLOCK_SEQ1) - nxp.WDOG.UNLOCK.Set(WDOG_UNLOCK_SEQ2) + // from: ResetHandler + + nxp.WDOG.UNLOCK.Set(watchdogUnlockSequence1) + nxp.WDOG.UNLOCK.Set(watchdogUnlockSequence2) arm.Asm("nop") arm.Asm("nop") - startupEarlyHook() + // TODO: hook for overriding? 'startupEarlyHook' + nxp.WDOG.STCTRLH.Set(nxp.WDOG_STCTRLH_ALLOWUPDATE) // enable clocks to always-used peripherals nxp.SIM.SCGC3.Set(nxp.SIM_SCGC3_ADC1 | nxp.SIM_SCGC3_FTM2 | nxp.SIM_SCGC3_FTM3) @@ -117,9 +105,9 @@ func initSystem() { // 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)) + nxp.MCG.C2.Set(uint8((2 << nxp.MCG_C2_RANGE_Pos) | 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))) + nxp.MCG.C1.Set(uint8((2 << nxp.MCG_C1_CLKS_Pos) | (4 << nxp.MCG_C1_FRDIV_Pos))) // wait for crystal oscillator to begin for !nxp.MCG.S.HasBits(nxp.MCG_S_OSCINIT0) { } @@ -127,7 +115,7 @@ func initSystem() { 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) { + for (nxp.MCG.S.Get() & nxp.MCG_S_CLKST_Msk) != (2 << nxp.MCG_S_CLKST_Pos) { } // now in FBE mode @@ -137,11 +125,11 @@ func initSystem() { // 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 { + 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)) + nxp.MCG.C5.Set((1 << nxp.MCG_C5_PRDIV_Pos)) + nxp.MCG.C6.Set(nxp.MCG_C6_PLLS | (29 << nxp.MCG_C6_VDIV_Pos)) // wait for PLL to start using xtal as its input for !nxp.MCG.S.HasBits(nxp.MCG_S_PLLST) { @@ -153,18 +141,18 @@ func initSystem() { // 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)) + nxp.SIM.CLKDIV1.Set((0 << nxp.SIM_CLKDIV1_OUTDIV1_Pos) | (2 << nxp.SIM_CLKDIV1_OUTDIV2_Pos) | (0 << nxp.SIM_CLKDIV1_OUTDIV1_Pos) | (6 << nxp.SIM_CLKDIV1_OUTDIV4_Pos)) + nxp.SIM.CLKDIV2.Set((0 << nxp.SIM_CLKDIV2_USBDIV_Pos)) // 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)) + nxp.MCG.C1.Set((0 << nxp.MCG_C1_CLKS_Pos) | (4 << nxp.MCG_C1_FRDIV_Pos)) // wait for PLL clock to be used - for (nxp.MCG.S.Get() & nxp.MCG_S_CLKST_Msk) != nxp.MCG_S_CLKST(3) { + for (nxp.MCG.S.Get() & nxp.MCG_S_CLKST_Msk) != (3 << nxp.MCG_S_CLKST_Pos) { } // 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)) + nxp.SIM.SOPT2.Set(nxp.SIM_SOPT2_USBSRC | _SIM_SOPT2_IRC48SEL | nxp.SIM_SOPT2_TRACECLKSEL | (6 << nxp.SIM_SOPT2_CLKOUTSEL_Pos)) // 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. @@ -174,23 +162,19 @@ func initSystem() { } // 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 + initSysTick() } -// ported _init_Teensyduino_internal_ from pins_teensy.c from teensy3 core libraries -//go:noinline func initInternal() { - 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) + // from: _init_Teensyduino_internal_ + // 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.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) @@ -209,145 +193,45 @@ func initInternal() { 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.FTM0.SC.Set((1 << nxp.FTM_SC_CLKS_Pos) | (_DEFAULT_FTM_PRESCALE << nxp.FTM_SC_PS_Pos)) nxp.FTM1.CNT.Set(0) - nxp.FTM1.MOD.Set(DEFAULT_FTM_MOD) + 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.FTM1.SC.Set((1 << nxp.FTM_SC_CLKS_Pos) | (_DEFAULT_FTM_PRESCALE << nxp.FTM_SC_PS_Pos)) + // causes a data bus error for unknown reasons // nxp.FTM2.CNT.Set(0) - // nxp.FTM2.MOD.Set(DEFAULT_FTM_MOD) + // 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.FTM2.SC.Set((1 << nxp.FTM_SC_CLKS_Pos) | (_DEFAULT_FTM_PRESCALE << nxp.FTM_SC_PS_Pos)) nxp.FTM3.CNT.Set(0) - nxp.FTM3.MOD.Set(DEFAULT_FTM_MOD) + 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.FTM3.SC.Set((1 << nxp.FTM_SC_CLKS_Pos) | (_DEFAULT_FTM_PRESCALE << nxp.FTM_SC_PS_Pos)) nxp.SIM.SCGC2.SetBits(nxp.SIM_SCGC2_TPM1) - nxp.SIM.SOPT2.SetBits(nxp.SIM_SOPT2_TPMSRC(2)) + nxp.SIM.SOPT2.SetBits((2 << nxp.SIM_SOPT2_TPMSRC_Pos)) 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)) + nxp.TPM1.SC.Set((1 << nxp.FTM_SC_CLKS_Pos) | (0 << nxp.FTM_SC_PS_Pos)) - // configure the low-power timer - // nxp.LPTMR0.CSR.Set(nxp.LPTMR0_CSR_TIE) - // nxp.LPTMR0.PSR.Set(nxp.LPTMR0_PSR_PCS(3) | nxp.LPTMR0_PSR_PRESCALE(1)) // use main (external) clock, divided by 4 - // arm.EnableIRQ(nxp.IRQ_LPTMR0) + // configure the sleep timer + initSleepTimer() // 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 -} +func postinit() {} func putchar(c byte) { - machine.UART1.WriteByte(c) + machine.PutcharUART(&machine.UART0, c) } // ??? const asyncScheduler = false - -// microseconds per tick -const tickMicros = 1000 - -// number of ticks since boot -var tickMilliCount volatile.Register32 - -//go:export SysTick_Handler -func tickHandler() { - tickMilliCount.Set(tickMilliCount.Get() + 1) -} - -// ticks are in microseconds -func ticks() timeUnit { - m := arm.DisableInterrupts() - current := nxp.SysTick.CVR.Get() - count := tickMilliCount.Get() - 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 - } - arm.Asm("wfi") - } -} - -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/time_nxpmk66f18.go b/src/runtime/time_nxpmk66f18.go new file mode 100644 index 0000000000..3836fc39a4 --- /dev/null +++ b/src/runtime/time_nxpmk66f18.go @@ -0,0 +1,163 @@ +// 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/interrupt" + "runtime/volatile" +) + +type timeUnit int64 + +func ticksToNanoseconds(ticks timeUnit) int64 { + return int64(ticks) * 1000 +} + +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(ns / 1000) +} + +// cyclesPerMilli-1 is used for the systick reset value +// the systick current value will be decremented on every clock cycle +// an interrupt is generated when the current value reaches 0 +// a value of freq/1000 generates a tick (irq) every millisecond (1/1000 s) +var cyclesPerMilli = machine.CPUFrequency() / 1000 + +// number of systick irqs (milliseconds) since boot +var systickCount volatile.Register64 + +func millisSinceBoot() uint64 { + return systickCount.Get() +} + +func initSysTick() { + nxp.SysTick.RVR.Set(cyclesPerMilli - 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((32 << nxp.SystemControl_SHPR3_PRI_15_Pos) | (32 << nxp.SystemControl_SHPR3_PRI_14_Pos)) // set systick and pendsv priority to 32 +} + +func initSleepTimer() { + nxp.SIM.SCGC5.SetBits(nxp.SIM_SCGC5_LPTMR) + nxp.LPTMR0.CSR.Set(nxp.LPTMR0_CSR_TIE) + + timerInterrupt = interrupt.New(nxp.IRQ_LPTMR0, timerWake) + timerInterrupt.Enable() +} + +//go:export SysTick_Handler +func tick() { + systickCount.Set(systickCount.Get() + 1) +} + +// ticks are in microseconds +func ticks() timeUnit { + mask := arm.DisableInterrupts() + current := nxp.SysTick.CVR.Get() // current value of the systick counter + count := millisSinceBoot() // number of milliseconds since boot + istatus := nxp.SystemControl.ICSR.Get() // interrupt status register + arm.EnableInterrupts(mask) + + micros := timeUnit(count * 1000) // a tick (1ms) = 1000 us + + // if the systick counter was about to reset and ICSR indicates a pending systick irq, increment count + if istatus&nxp.SystemControl_ICSR_PENDSTSET != 0 && current > 50 { + micros += 1000 + } else { + cycles := cyclesPerMilli - 1 - current // number of cycles since last 1ms tick + cyclesPerMicro := machine.CPUFrequency() / 1000000 + micros += timeUnit(cycles / cyclesPerMicro) + } + + return micros +} + +// sleepTicks spins for a number of microseconds +func sleepTicks(duration timeUnit) { + now := ticks() + end := duration + now + cyclesPerMicro := machine.ClockFrequency() / 1000000 + + if duration <= 0 { + return + } + + nxp.LPTMR0.PSR.Set((3 << nxp.LPTMR0_PSR_PCS_Pos) | nxp.LPTMR0_PSR_PBYP) // use 16MHz clock, undivided + + for now < end { + count := uint32(end-now) / cyclesPerMicro + if count > 65535 { + count = 65535 + } + + if !timerSleep(count) { + // return early due to interrupt + return + } + + now = ticks() + } +} + +var timerInterrupt interrupt.Interrupt +var timerActive volatile.Register32 + +func timerSleep(count uint32) bool { + timerActive.Set(1) + nxp.LPTMR0.CMR.Set(count) // set count + nxp.LPTMR0.CSR.SetBits(nxp.LPTMR0_CSR_TEN) // enable + + for { + arm.Asm("wfi") + if timerActive.Get() == 0 { + return true + } + + if hasScheduler { + // bail out, as the interrupt may have awoken a goroutine + break + } + + // if there is no scheduler, block for the entire count + } + + timerWake(timerInterrupt) + return false +} + +func timerWake(interrupt.Interrupt) { + timerActive.Set(0) + nxp.LPTMR0.CSR.Set(nxp.LPTMR0.CSR.Get()&^nxp.LPTMR0_CSR_TEN | nxp.LPTMR0_CSR_TCF) // clear flag and disable +} diff --git a/src/runtime/volatile/register_nxpmk66f18.go b/src/runtime/volatile/bitband_nxpmk66f18.go similarity index 93% rename from src/runtime/volatile/register_nxpmk66f18.go rename to src/runtime/volatile/bitband_nxpmk66f18.go index 0f9b4cb80d..0a4904db2d 100644 --- a/src/runtime/volatile/register_nxpmk66f18.go +++ b/src/runtime/volatile/bitband_nxpmk66f18.go @@ -7,17 +7,16 @@ import "unsafe" const registerBase = 0x40000000 const registerEnd = 0x40100000 const bitbandBase = 0x42000000 -const ptrBytes = unsafe.Sizeof(uintptr(0)) //go:inline func bitbandAddress(reg uintptr, bit uint8) uintptr { - if uintptr(bit) > ptrBytes*8 { + if uintptr(bit) > 32 { panic("invalid bit position") } if reg < registerBase || reg >= registerEnd { panic("register is out of range") } - return (reg-registerBase)*ptrBytes*8 + uintptr(bit)*ptrBytes + bitbandBase + return (reg-registerBase)*32 + uintptr(bit)*4 + bitbandBase } // Special types that causes loads/stores to be volatile (necessary for diff --git a/targets/nxpmk66f18.ld b/targets/nxpmk66f18.ld index 3487264e6a..470a5b384b 100644 --- a/targets/nxpmk66f18.ld +++ b/targets/nxpmk66f18.ld @@ -5,8 +5,8 @@ ENTRY(Reset_Handler) /* define memory layout */ MEMORY { - FLASH_TEXT (rx) : ORIGIN = 0x00000000, LENGTH = 1024K - RAM (rwx) : ORIGIN = 0x1FFF0000, LENGTH = 256K + FLASH_TEXT (rx) : ORIGIN = 0x00000000, LENGTH = 1024K + RAM (rwx) : ORIGIN = 0x1FFF0000, LENGTH = 256K } _stack_size = 2K; @@ -18,21 +18,18 @@ SECTIONS .text : { /* vector table MUST start at 0x0 */ - . = 0; - _vector_table = .; + . = 0; KEEP(*(.isr_vector)) /* flash configuration MUST be at 0x400 */ . = 0x400; - _flash_config = .; - KEEP(*(.flash_config)) + KEEP(*(.flash_config)) /* everything else */ - *(.resetHandler) *(.text) - *(.text*) + *(.text.*) *(.rodata) - *(.rodata*) + *(.rodata.*) . = ALIGN(4); } >FLASH_TEXT = 0xFF @@ -50,7 +47,7 @@ SECTIONS /* Start address (in flash) of .data, used by startup code. */ _sidata = LOADADDR(.data); - /* todo add .usbdescriptortable .dmabuffers .usbbuffers */ + /* this is where Teensy's LD script places .usbdescriptortable .dmabuffers .usbbuffers */ /* Globals with initial value */ .data : @@ -58,7 +55,7 @@ SECTIONS . = ALIGN(4); _sdata = .; /* used by startup code */ *(.data) - *(.data*) + *(.data.*) . = ALIGN(4); _edata = .; /* used by startup code */ } >RAM AT>FLASH_TEXT @@ -69,7 +66,7 @@ SECTIONS . = ALIGN(4); _sbss = .; /* used by startup code */ *(.bss) - *(.bss*) + *(.bss.*) *(COMMON) . = ALIGN(4); _ebss = .; /* used by startup code */ @@ -79,11 +76,6 @@ SECTIONS { *(.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*) } } diff --git a/targets/teensy36.s b/targets/teensy36.s index 4e17e7c9d8..ebfaac387b 100644 --- a/targets/teensy36.s +++ b/targets/teensy36.s @@ -1,7 +1,8 @@ - .section .flash_config .global __flash_config - .byte 0xFF +__flash_config: + .byte 0xFF + .byte 0xFF .byte 0xFF .byte 0xFF .byte 0xFF @@ -9,11 +10,10 @@ .byte 0xFF .byte 0xFF .byte 0xFF - .byte 0xFF .byte 0xFF .byte 0xFF .byte 0xFF .byte 0xDE .byte 0xF9 .byte 0xFF - .byte 0xFF \ No newline at end of file + .byte 0xFF From 69cd0f510f7d3c367d7b10618c67c8970995f1ba Mon Sep 17 00:00:00 2001 From: Ethan Reesor Date: Thu, 25 Jun 2020 22:00:30 -0500 Subject: [PATCH 4/4] Implement custom abort and fault handler for debugging --- src/runtime/runtime_cortexm.go | 43 --- src/runtime/runtime_cortexm_abort.go | 17 + src/runtime/runtime_cortexm_hardfault.go | 39 +++ .../runtime_cortexm_hardfault_debug.go | 304 ++++++++++++++++++ src/runtime/runtime_nxpmk66f18.go | 30 ++ 5 files changed, 390 insertions(+), 43 deletions(-) create mode 100644 src/runtime/runtime_cortexm_abort.go create mode 100644 src/runtime/runtime_cortexm_hardfault.go create mode 100644 src/runtime/runtime_cortexm_hardfault_debug.go diff --git a/src/runtime/runtime_cortexm.go b/src/runtime/runtime_cortexm.go index 160c60f73b..ae20f7dec5 100644 --- a/src/runtime/runtime_cortexm.go +++ b/src/runtime/runtime_cortexm.go @@ -3,7 +3,6 @@ package runtime import ( - "device/arm" "unsafe" ) @@ -40,16 +39,6 @@ func preinit() { } } -func abort() { - // disable all interrupts - arm.DisableInterrupts() - - // lock up forever - for { - arm.Asm("wfi") - } -} - // The stack layout at the moment an interrupt occurs. // Registers can be accessed if the stack pointer is cast to a pointer to this // struct. @@ -63,35 +52,3 @@ type interruptStack struct { PC uintptr PSR uintptr } - -// This function is called at HardFault. -// Before this function is called, the stack pointer is reset to the initial -// stack pointer (loaded from addres 0x0) and the previous stack pointer is -// passed as an argument to this function. This allows for easy inspection of -// the stack the moment a HardFault occurs, but it means that the stack will be -// corrupted by this function and thus this handler must not attempt to recover. -// -// For details, see: -// https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault -// https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/ -//export handleHardFault -func handleHardFault(sp *interruptStack) { - print("fatal error: ") - if uintptr(unsafe.Pointer(sp)) < 0x20000000 { - print("stack overflow") - } else { - // TODO: try to find the cause of the hard fault. Especially on - // Cortex-M3 and higher it is possible to find more detailed information - // in special status registers. - print("HardFault") - } - print(" with sp=", sp) - if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 { - // Only print the PC if it points into memory. - // It may not point into memory during a stack overflow, so check that - // first before accessing the stack. - print(" pc=", sp.PC) - } - println() - abort() -} diff --git a/src/runtime/runtime_cortexm_abort.go b/src/runtime/runtime_cortexm_abort.go new file mode 100644 index 0000000000..7eda1e5d8f --- /dev/null +++ b/src/runtime/runtime_cortexm_abort.go @@ -0,0 +1,17 @@ +// +build cortexm,!nxp + +package runtime + +import ( + "device/arm" +) + +func abort() { + // disable all interrupts + arm.DisableInterrupts() + + // lock up forever + for { + arm.Asm("wfi") + } +} diff --git a/src/runtime/runtime_cortexm_hardfault.go b/src/runtime/runtime_cortexm_hardfault.go new file mode 100644 index 0000000000..7e6179ff70 --- /dev/null +++ b/src/runtime/runtime_cortexm_hardfault.go @@ -0,0 +1,39 @@ +// +build cortexm,!nxp + +package runtime + +import ( + "unsafe" +) + +// This function is called at HardFault. +// Before this function is called, the stack pointer is reset to the initial +// stack pointer (loaded from addres 0x0) and the previous stack pointer is +// passed as an argument to this function. This allows for easy inspection of +// the stack the moment a HardFault occurs, but it means that the stack will be +// corrupted by this function and thus this handler must not attempt to recover. +// +// For details, see: +// https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault +// https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/ +//export handleHardFault +func handleHardFault(sp *interruptStack) { + print("fatal error: ") + if uintptr(unsafe.Pointer(sp)) < 0x20000000 { + print("stack overflow") + } else { + // TODO: try to find the cause of the hard fault. Especially on + // Cortex-M3 and higher it is possible to find more detailed information + // in special status registers. + print("HardFault") + } + print(" with sp=", sp) + if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 { + // Only print the PC if it points into memory. + // It may not point into memory during a stack overflow, so check that + // first before accessing the stack. + print(" pc=", sp.PC) + } + println() + abort() +} diff --git a/src/runtime/runtime_cortexm_hardfault_debug.go b/src/runtime/runtime_cortexm_hardfault_debug.go new file mode 100644 index 0000000000..25d3e5507d --- /dev/null +++ b/src/runtime/runtime_cortexm_hardfault_debug.go @@ -0,0 +1,304 @@ +// +build nxp + +package runtime + +import ( + "device/nxp" + "unsafe" +) + +const ( + SystemControl_CFSR_KnownFault = nxp.SystemControl_CFSR_IACCVIOL | nxp.SystemControl_CFSR_DACCVIOL | nxp.SystemControl_CFSR_MUNSTKERR | nxp.SystemControl_CFSR_MSTKERR | nxp.SystemControl_CFSR_MLSPERR | nxp.SystemControl_CFSR_IBUSERR | nxp.SystemControl_CFSR_PRECISERR | nxp.SystemControl_CFSR_IMPRECISERR | nxp.SystemControl_CFSR_UNSTKERR | nxp.SystemControl_CFSR_STKERR | nxp.SystemControl_CFSR_LSPERR | nxp.SystemControl_CFSR_UNDEFINSTR | nxp.SystemControl_CFSR_INVSTATE | nxp.SystemControl_CFSR_INVPC | nxp.SystemControl_CFSR_NOCP | nxp.SystemControl_CFSR_UNALIGNED | nxp.SystemControl_CFSR_DIVBYZERO +) + +// See runtime_cortexm_hardfault.go +//go:export handleHardFault +func handleHardFault(sp *interruptStack) { + fault := GetFaultStatus() + spValid := !fault.Bus().ImpreciseDataBusError() + + print("fatal error: ") + if spValid && uintptr(unsafe.Pointer(sp)) < 0x20000000 { + print("stack overflow? ") + } + if fault.Mem().InstructionAccessViolation() { + print("instruction access violation") + } + if fault.Mem().DataAccessViolation() { + print("data access violation") + } + if fault.Mem().WhileUnstackingException() { + print(" while unstacking exception") + } + if fault.Mem().WileStackingException() { + print(" while stacking exception") + } + if fault.Mem().DuringFPLazyStatePres() { + print(" during floating-point lazy state preservation") + } + + if fault.Bus().InstructionBusError() { + print("instruction bus error") + } + if fault.Bus().PreciseDataBusError() { + print("data bus error (precise)") + } + if fault.Bus().ImpreciseDataBusError() { + print("data bus error (imprecise)") + } + if fault.Bus().WhileUnstackingException() { + print(" while unstacking exception") + } + if fault.Bus().WhileStackingException() { + print(" while stacking exception") + } + if fault.Bus().DuringFPLazyStatePres() { + print(" during floating-point lazy state preservation") + } + + if fault.Usage().UndefinedInstruction() { + print("undefined instruction") + } + if fault.Usage().IllegalUseOfEPSR() { + print("illegal use of the EPSR") + } + if fault.Usage().IllegalExceptionReturn() { + print("illegal load of EXC_RETURN to the PC") + } + if fault.Usage().AttemptedToAccessCoprocessor() { + print("coprocessor access violation") + } + if fault.Usage().UnalignedMemoryAccess() { + print("unaligned memory access") + } + if fault.Usage().DivideByZero() { + print("divide by zero") + } + + if fault.Unknown() { + print("unknown hard fault") + } + + if addr, ok := fault.Mem().Address(); ok { + print(" with fault address ", addr) + } + + if addr, ok := fault.Bus().Address(); ok { + print(" with bus fault address ", addr) + } + if spValid { + print(" with sp=", sp) + if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 { + // Only print the PC if it points into memory. + // It may not point into memory during a stack overflow, so check that + // first before accessing the stack. + print(" pc=", sp.PC) + } + } + println() + abort() +} + +// Descriptions are sourced from the K66 SVD and +// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Cihcfefj.html + +// GetFaultStatus reads the System Control Block Configurable Fault Status +// Register and returns it as a FaultStatus. +func GetFaultStatus() FaultStatus { return FaultStatus(nxp.SystemControl.CFSR.Get()) } + +type FaultStatus uint32 +type MemFaultStatus uint32 +type BusFaultStatus uint32 +type UsageFaultStatus uint32 + +func (fs FaultStatus) Mem() MemFaultStatus { return MemFaultStatus(fs) } +func (fs FaultStatus) Bus() BusFaultStatus { return BusFaultStatus(fs) } +func (fs FaultStatus) Usage() UsageFaultStatus { return UsageFaultStatus(fs) } + +// Unknown returns true if the cause of the fault is not know +func (fs FaultStatus) Unknown() bool { return fs&SystemControl_CFSR_KnownFault == 0 } + +// InstructionAccessViolation: the processor attempted an instruction fetch from +// a location that does not permit execution +// +// "This fault occurs on any access to an XN region, even when the MPU is +// disabled or not present. +// +// When this bit is 1, the PC value stacked for the exception return points to +// the faulting instruction. The processor has not written a fault address to +// the MMAR." +func (fs MemFaultStatus) InstructionAccessViolation() bool { + return fs&nxp.SystemControl_CFSR_IACCVIOL != 0 +} + +// DataAccessViolation: the processor attempted a load or store at a location +// that does not permit the operation +// +// "When this bit is 1, the PC value stacked for the exception return points to +// the faulting instruction. The processor has loaded the MMAR with the address +// of the attempted access." +func (fs MemFaultStatus) DataAccessViolation() bool { return fs&nxp.SystemControl_CFSR_DACCVIOL != 0 } + +// WhileUnstackingException: unstack for an exception return has caused one or +// more access violations +// +// "This fault is chained to the handler. This means that when this bit is 1, +// the original return stack is still present. The processor has not adjusted +// the SP from the failing return, and has not performed a new save. The +// processor has not written a fault address to the MMAR." +func (fs MemFaultStatus) WhileUnstackingException() bool { + return fs&nxp.SystemControl_CFSR_MUNSTKERR != 0 +} + +// WileStackingException: stacking for an exception entry has caused one or more +// access violations +// +// "When this bit is 1, the SP is still adjusted but the values in the context +// area on the stack might be incorrect. The processor has not written a fault +// address to the MMAR." +func (fs MemFaultStatus) WileStackingException() bool { return fs&nxp.SystemControl_CFSR_MSTKERR != 0 } + +// DuringFPLazyStatePres: A MemManage fault occurred during floating-point lazy +// state preservation +func (fs MemFaultStatus) DuringFPLazyStatePres() bool { return fs&nxp.SystemControl_CFSR_MLSPERR != 0 } + +// InstructionBusError: instruction bus error +// +// "The processor detects the instruction bus error on prefetching an +// instruction, but it sets the IBUSERR flag to 1 only if it attempts to issue +// the faulting instruction. +// +// When the processor sets this bit is 1, it does not write a fault address to +// the BFAR." +func (fs BusFaultStatus) InstructionBusError() bool { return fs&nxp.SystemControl_CFSR_IBUSERR != 0 } + +// PreciseDataBusError: a data bus error has occurred, and the PC value stacked +// for the exception return points to the instruction that caused the fault +func (fs BusFaultStatus) PreciseDataBusError() bool { return fs&nxp.SystemControl_CFSR_PRECISERR != 0 } + +// ImpreciseDataBusError: a data bus error has occurred, but the return address +// in the stack frame is not related to the instruction that caused the error +// +// "When the processor sets this bit to 1, it does not write a fault address to +// the BFAR. +// +// This is an asynchronous fault. Therefore, if it is detected when the priority +// of the current process is higher than the BusFault priority, the BusFault +// becomes pending and becomes active only when the processor returns from all +// higher priority processes. If a precise fault occurs before the processor +// enters the handler for the imprecise BusFault, the handler detects both +// IMPRECISERR set to 1 and one of the precise fault status bits set to 1." +func (fs BusFaultStatus) ImpreciseDataBusError() bool { + return fs&nxp.SystemControl_CFSR_IMPRECISERR != 0 +} + +// WhileUnstackingException: unstack for an exception return has caused one or +// more BusFaults +// +// "This fault is chained to the handler. This means that when the processor +// sets this bit to 1, the original return stack is still present. The processor +// does not adjust the SP from the failing return, does not performed a new +// save, and does not write a fault address to the BFAR." +func (fs BusFaultStatus) WhileUnstackingException() bool { + return fs&nxp.SystemControl_CFSR_UNSTKERR != 0 +} + +// WhileStackingException: stacking for an exception entry has caused one or +// more BusFaults +// +// "When the processor sets this bit to 1, the SP is still adjusted but the +// values in the context area on the stack might be incorrect. The processor +// does not write a fault address to the BFAR." +func (fs BusFaultStatus) WhileStackingException() bool { return fs&nxp.SystemControl_CFSR_STKERR != 0 } + +// DuringFPLazyStatePres: A bus fault occurred during floating-point lazy state +// preservation +func (fs BusFaultStatus) DuringFPLazyStatePres() bool { return fs&nxp.SystemControl_CFSR_LSPERR != 0 } + +// UndefinedInstruction: the processor has attempted to execute an undefined +// instruction +// +// "When this bit is set to 1, the PC value stacked for the exception return +// points to the undefined instruction. +// +// An undefined instruction is an instruction that the processor cannot decode." +func (fs UsageFaultStatus) UndefinedInstruction() bool { + return fs&nxp.SystemControl_CFSR_UNDEFINSTR != 0 +} + +// IllegalUseOfEPSR: the processor has attempted to execute an instruction that +// makes illegal use of the EPSR +// +// "When this bit is set to 1, the PC value stacked for the exception return +// points to the instruction that attempted the illegal use of the EPSR. +// +// This bit is not set to 1 if an undefined instruction uses the EPSR." +func (fs UsageFaultStatus) IllegalUseOfEPSR() bool { return fs&nxp.SystemControl_CFSR_INVSTATE != 0 } + +// IllegalExceptionReturn: the processor has attempted an illegal load of +// EXC_RETURN to the PC +// +// "When this bit is set to 1, the PC value stacked for the exception return +// points to the instruction that tried to perform the illegal load of the PC." +func (fs UsageFaultStatus) IllegalExceptionReturn() bool { return fs&nxp.SystemControl_CFSR_INVPC != 0 } + +// AttemptedToAccessCoprocessor: the processor has attempted to access a +// coprocessor +func (fs UsageFaultStatus) AttemptedToAccessCoprocessor() bool { + return fs&nxp.SystemControl_CFSR_NOCP != 0 +} + +// UnalignedMemoryAccess: the processor has made an unaligned memory access +// +// "Enable trapping of unaligned accesses by setting the UNALIGN_TRP bit in the +// CCR to 1. +// +// Unaligned LDM, STM, LDRD, and STRD instructions always fault irrespective of +// the setting of UNALIGN_TRP." +func (fs UsageFaultStatus) UnalignedMemoryAccess() bool { + return fs&nxp.SystemControl_CFSR_UNALIGNED != 0 +} + +// DivideByZero: the processor has executed an SDIV or UDIV instruction with a +// divisor of 0 +// +// "When the processor sets this bit to 1, the PC value stacked for the +// exception return points to the instruction that performed the divide by zero. +// +// Enable trapping of divide by zero by setting the DIV_0_TRP bit in the CCR to +// 1." +func (fs UsageFaultStatus) DivideByZero() bool { return fs&nxp.SystemControl_CFSR_DIVBYZERO != 0 } + +// Address returns the MemManage Fault Address Register if the fault status +// indicates the address is valid. +// +// "If a MemManage fault occurs and is escalated to a HardFault because of +// priority, the HardFault handler must set this bit to 0. This prevents +// problems on return to a stacked active MemManage fault handler whose MMAR +// value has been overwritten." +func (fs MemFaultStatus) Address() (uintptr, bool) { + if fs&nxp.SystemControl_CFSR_MMARVALID == 0 { + return 0, false + } else { + return uintptr(nxp.SystemControl.MMFAR.Get()), true + } +} + +// Address returns the BusFault Address Register if the fault status +// indicates the address is valid. +// +// "The processor sets this bit to 1 after a BusFault where the address is +// known. Other faults can set this bit to 0, such as a MemManage fault +// occurring later. +// +// If a BusFault occurs and is escalated to a hard fault because of priority, +// the hard fault handler must set this bit to 0. This prevents problems if +// returning to a stacked active BusFault handler whose BFAR value has been +// overwritten."" +func (fs BusFaultStatus) Address() (uintptr, bool) { + if fs&nxp.SystemControl_CFSR_BFARVALID == 0 { + return 0, false + } else { + return uintptr(nxp.SystemControl.BFAR.Get()), true + } +} diff --git a/src/runtime/runtime_nxpmk66f18.go b/src/runtime/runtime_nxpmk66f18.go index b03e1257c3..84d421b7fd 100644 --- a/src/runtime/runtime_nxpmk66f18.go +++ b/src/runtime/runtime_nxpmk66f18.go @@ -235,3 +235,33 @@ func putchar(c byte) { // ??? const asyncScheduler = false + +func abort() { + println("!!! ABORT !!!") + + m := arm.DisableInterrupts() + arm.Asm("mov r12, #1") + arm.Asm("msr basepri, r12") // only execute interrupts of priority 0 + nxp.SystemControl.SHPR3.ClearBits(nxp.SystemControl_SHPR3_PRI_15_Msk) // set systick to priority 0 + arm.EnableInterrupts(m) + + machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + var v bool + for { + machine.LED.Set(v) + v = !v + + t := millisSinceBoot() + for millisSinceBoot()-t < 60 { + arm.Asm("wfi") + } + + // keep polling some communication while in fault + // mode, so we don't completely die. + // machine.PollUSB(&machine.USB0) + machine.PollUART(&machine.UART0) + machine.PollUART(&machine.UART1) + machine.PollUART(&machine.UART2) + } +}