diff --git a/src/machine/board_microbit.go b/src/machine/board_microbit.go new file mode 100644 index 00000000..9a23718b --- /dev/null +++ b/src/machine/board_microbit.go @@ -0,0 +1,32 @@ +// +build nrf,microbit + +package machine + +// The micro:bit does not have a 32kHz crystal on board. +const HasLowFrequencyCrystal = false + +// Buttons on the micro:bit (A and B) +const ( + BUTTON = BUTTON1 + BUTTON1 = 5 + BUTTON2 = 11 +) + +// UART pins +const ( + UART_TX_PIN = 24 + UART_RX_PIN = 25 +) + +// ADC pins +const ( + ADC0 = 0 + ADC1 = 1 + ADC2 = 2 +) + +// I2C pins +const ( + SDA_PIN = 20 + SCL_PIN = 19 +) diff --git a/src/machine/board_pca10040.go b/src/machine/board_pca10040.go index 3ba0c5f7..065fda19 100644 --- a/src/machine/board_pca10040.go +++ b/src/machine/board_pca10040.go @@ -2,6 +2,9 @@ package machine +// The PCA10040 has a low-frequency (32kHz) crystal oscillator on board. +const HasLowFrequencyCrystal = true + // LEDs on the PCA10040 (nRF52832 dev board) const ( LED = LED1 diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go index 9308b9e7..f155da07 100644 --- a/src/machine/machine_nrf.go +++ b/src/machine/machine_nrf.go @@ -19,22 +19,25 @@ const ( // Configure this pin with the given configuration. func (p GPIO) Configure(config GPIOConfig) { cfg := config.Mode | nrf.GPIO_PIN_CNF_DRIVE_S0S1 | nrf.GPIO_PIN_CNF_SENSE_Disabled - nrf.P0.PIN_CNF[p.Pin] = nrf.RegValue(cfg) + port, pin := p.getPortPin() + port.PIN_CNF[pin] = nrf.RegValue(cfg) } // Set the pin to high or low. // Warning: only use this on an output pin! func (p GPIO) Set(high bool) { + port, pin := p.getPortPin() if high { - nrf.P0.OUTSET = 1 << p.Pin + port.OUTSET = 1 << pin } else { - nrf.P0.OUTCLR = 1 << p.Pin + port.OUTCLR = 1 << pin } } // Get returns the current value of a GPIO pin. func (p GPIO) Get() bool { - return (nrf.P0.IN>>p.Pin)&1 != 0 + port, pin := p.getPortPin() + return (port.IN>>pin)&1 != 0 } // UART @@ -62,7 +65,7 @@ func (uart UART) Configure(config UARTConfig) { nrf.UART0.INTENSET = nrf.UART_INTENSET_RXDRDY_Msk // Enable RX IRQ. - arm.EnableIRQ(nrf.IRQ_UARTE0_UART0) + arm.EnableIRQ(nrf.IRQ_UART0) } // SetBaudRate sets the communication speed for the UART. @@ -89,8 +92,7 @@ func (uart UART) WriteByte(c byte) error { return nil } -//go:export UARTE0_UART0_IRQHandler -func handleUART0() { +func (uart UART) handleInterrupt() { if nrf.UART0.EVENTS_RXDRDY != 0 { bufferPut(byte(nrf.UART0.RXD)) nrf.UART0.EVENTS_RXDRDY = 0x0 @@ -128,13 +130,15 @@ func (i2c I2C) Configure(config I2CConfig) { } // do config - nrf.P0.PIN_CNF[config.SCL] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | + sclPort, sclPin := GPIO{config.SCL}.getPortPin() + sclPort.PIN_CNF[sclPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) | (nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) | (nrf.GPIO_PIN_CNF_SENSE_Disabled << nrf.GPIO_PIN_CNF_SENSE_Pos) - nrf.P0.PIN_CNF[config.SDA] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | + sdaPort, sdaPin := GPIO{config.SDA}.getPortPin() + sdaPort.PIN_CNF[sdaPin] = (nrf.GPIO_PIN_CNF_DIR_Input << nrf.GPIO_PIN_CNF_DIR_Pos) | (nrf.GPIO_PIN_CNF_INPUT_Connect << nrf.GPIO_PIN_CNF_INPUT_Pos) | (nrf.GPIO_PIN_CNF_PULL_Pullup << nrf.GPIO_PIN_CNF_PULL_Pos) | (nrf.GPIO_PIN_CNF_DRIVE_S0D1 << nrf.GPIO_PIN_CNF_DRIVE_Pos) | diff --git a/src/machine/machine_nrf51.go b/src/machine/machine_nrf51.go new file mode 100644 index 00000000..29abf989 --- /dev/null +++ b/src/machine/machine_nrf51.go @@ -0,0 +1,17 @@ +// +build nrf51 + +package machine + +import ( + "device/nrf" +) + +// Get peripheral and pin number for this GPIO pin. +func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) { + return nrf.GPIO, p.Pin +} + +//go:export UART0_IRQHandler +func handleUART0() { + UART0.handleInterrupt() +} diff --git a/src/machine/machine_nrf52.go b/src/machine/machine_nrf52.go index f62c10f1..1022df58 100644 --- a/src/machine/machine_nrf52.go +++ b/src/machine/machine_nrf52.go @@ -7,6 +7,16 @@ import ( "unsafe" ) +// Get peripheral and pin number for this GPIO pin. +func (p GPIO) getPortPin() (*nrf.GPIO_Type, uint8) { + return nrf.P0, p.Pin +} + +//go:export UARTE0_UART0_IRQHandler +func handleUART0() { + UART0.handleInterrupt() +} + // InitADC initializes the registers needed for ADC. func InitADC() { return // no specific setup on nrf52 machine. diff --git a/src/runtime/runtime_nrf.go b/src/runtime/runtime_nrf.go index 06754950..893a48a0 100644 --- a/src/runtime/runtime_nrf.go +++ b/src/runtime/runtime_nrf.go @@ -28,7 +28,9 @@ func init() { } func initLFCLK() { - nrf.CLOCK.LFCLKSRC = nrf.CLOCK_LFCLKSTAT_SRC_Xtal + if machine.HasLowFrequencyCrystal { + nrf.CLOCK.LFCLKSRC = nrf.CLOCK_LFCLKSTAT_SRC_Xtal + } nrf.CLOCK.TASKS_LFCLKSTART = 1 for nrf.CLOCK.EVENTS_LFCLKSTARTED == 0 { } diff --git a/targets/arm.ld b/targets/arm.ld index 1cf41783..59bbf34b 100644 --- a/targets/arm.ld +++ b/targets/arm.ld @@ -10,6 +10,7 @@ SECTIONS *(.text*) *(.rodata) *(.rodata*) + . = ALIGN(4); } >FLASH_TEXT /* Put the stack at the bottom of RAM, so that the application will diff --git a/targets/microbit.json b/targets/microbit.json new file mode 100644 index 00000000..02f4fc26 --- /dev/null +++ b/targets/microbit.json @@ -0,0 +1,25 @@ +{ + "llvm-target": "armv6m-none-eabi", + "build-tags": ["microbit", "nrf51822", "nrf51", "nrf", "arm", "js", "wasm"], + "linker": "arm-none-eabi-gcc", + "pre-link-args": [ + "-nostdlib", + "-nostartfiles", + "-mcpu=cortex-m0", + "-mthumb", + "-T", "targets/nrf51.ld", + "-Wl,--gc-sections", + "-fno-exceptions", "-fno-unwind-tables", + "-ffunction-sections", "-fdata-sections", + "-Os", + "-DNRF51", + "-Ilib/CMSIS/CMSIS/Include", + "lib/nrfx/mdk/system_nrf51.c", + "src/device/nrf/nrf51.s" + ], + "objcopy": "arm-none-eabi-objcopy", + "flash": "openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg -c 'program {hex} reset exit'", + "ocd-daemon": ["openocd", "-f", "interface/cmsis-dap.cfg", "-f", "target/nrf51.cfg"], + "gdb": "arm-none-eabi-gdb", + "gdb-initial-cmds": ["target remote :3333", "monitor halt", "load", "monitor reset", "c"] +} diff --git a/targets/nrf51.ld b/targets/nrf51.ld new file mode 100644 index 00000000..442ae1b0 --- /dev/null +++ b/targets/nrf51.ld @@ -0,0 +1,10 @@ + +MEMORY +{ + FLASH_TEXT (rw) : ORIGIN = 0x00000000, LENGTH = 256K /* .text */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 16K +} + +_stack_size = 2K; + +INCLUDE "targets/arm.ld" diff --git a/tools/gen-device-svd.py b/tools/gen-device-svd.py index ab361d13..0fd1ecf4 100755 --- a/tools/gen-device-svd.py +++ b/tools/gen-device-svd.py @@ -54,19 +54,16 @@ def readSVD(path, sourceURL): if groupNameTags: groupName = getText(groupNameTags[0]) - for interrupt in periphEl.findall('interrupt'): + interruptEls = periphEl.findall('interrupt') + for interrupt in interruptEls: intrName = getText(interrupt.find('name')) intrIndex = int(getText(interrupt.find('value'))) - if intrName in interrupts: - if interrupts[intrName]['index'] != intrIndex: - raise ValueError('interrupt with the same name has different indexes: ' + intrName) - interrupts[intrName]['description'] += ' // ' + description - else: - interrupts[intrName] = { - 'name': intrName, - 'index': intrIndex, - 'description': description, - } + addInterrupt(interrupts, intrName, intrIndex, description) + # As a convenience, also use the peripheral name as the interrupt + # name. Only do that for the nrf for now, as the stm32 .svd files + # don't always put interrupts in the correct peripheral... + if len(interruptEls) == 1 and deviceName.startswith('nrf'): + addInterrupt(interrupts, name, intrIndex, description) if periphEl.get('derivedFrom') or groupName in groups: if periphEl.get('derivedFrom'): @@ -150,6 +147,20 @@ def readSVD(path, sourceURL): return device +def addInterrupt(interrupts, intrName, intrIndex, description): + if intrName in interrupts: + if interrupts[intrName]['index'] != intrIndex: + raise ValueError('interrupt with the same name has different indexes: %s (%d vs %d)' + % (intrName, interrupts[intrName]['index'], intrIndex)) + if description not in interrupts[intrName]['description'].split(' // '): + interrupts[intrName]['description'] += ' // ' + description + else: + interrupts[intrName] = { + 'name': intrName, + 'index': intrIndex, + 'description': description, + } + def parseRegister(groupName, regEl, baseAddress, bitfieldPrefix=''): regName = getText(regEl.find('name')) regDescription = getText(regEl.find('description')) @@ -400,8 +411,10 @@ Default_Handler: '''.format(**device.metadata)) num = 0 for intr in device.interrupts: + if intr['index'] == num - 1: + continue if intr['index'] < num: - raise ValueError('interrupt numbers are not sorted or contain a duplicate') + raise ValueError('interrupt numbers are not sorted') while intr['index'] > num: out.write(' .long 0\n') num += 1