Skip to content

Commit

Permalink
boards: add makepython-nrf52840
Browse files Browse the repository at this point in the history
  • Loading branch information
bradjc committed Jan 19, 2024
1 parent b8ff9be commit 4e24b5d
Show file tree
Hide file tree
Showing 8 changed files with 1,014 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ members = [
"boards/msp_exp432p401r",
"boards/microbit_v2",
"boards/wm1110dev",
"boards/makepython-nrf52840",
"boards/nordic/nrf52840dk",
"boards/nordic/nrf52840_dongle",
"boards/nordic/nrf52dk",
Expand Down
21 changes: 21 additions & 0 deletions boards/makepython-nrf52840/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 or the MIT License.
# SPDX-License-Identifier: Apache-2.0 OR MIT
# Copyright Tock Contributors 2022.

[package]
name = "makepython-nrf52840"
version.workspace = true
authors.workspace = true
build = "build.rs"
edition.workspace = true

[dependencies]
cortexm4 = { path = "../../arch/cortex-m4" }
kernel = { path = "../../kernel" }
nrf52 = { path = "../../chips/nrf52" }
nrf52840 = { path = "../../chips/nrf52840" }
components = { path = "../components" }
nrf52_components = { path = "../nordic/nrf52_components" }

capsules-core = { path = "../../capsules/core" }
capsules-extra = { path = "../../capsules/extra" }
30 changes: 30 additions & 0 deletions boards/makepython-nrf52840/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Licensed under the Apache License, Version 2.0 or the MIT License.
# SPDX-License-Identifier: Apache-2.0 OR MIT
# Copyright Tock Contributors 2022.

# Makefile for building the tock kernel for the Arduino Nano 33 BLE board.

TOCK_ARCH=cortex-m4
TARGET=thumbv7em-none-eabi
PLATFORM=makepython-nrf52840

include ../Makefile.common

ifdef PORT
FLAGS += --port $(PORT)
endif

# Default target for installing the kernel.
.PHONY: install
install: program

# Upload the kernel using tockloader and the tock bootloader
.PHONY: program
program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
tockloader $(FLAGS) flash --address 0x10000 $<

.PHONY: flash-bootloader
flash-bootloader:
curl -L --output /tmp/makepython-nrf52840-bootloader_v1.1.3.bin https://github.com/tock/tock-bootloader/releases/download/v1.1.3/makepython-nrf52840-bootloader_v1.1.3.bin
tockloader flash --address 0 /tmp/makepython-nrf52840-bootloader_v1.1.3.bin
rm /tmp/makepython-nrf52840-bootloader_v1.1.3.bin
52 changes: 52 additions & 0 deletions boards/makepython-nrf52840/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
MakePython nRF52840
===================

<img src="https://www.makerfabs.com/image/cache/makerfabs/MakePython%20nRF52840/MakePython%20nRF52840-1-1000x750.jpg" width="35%">

The [MakePython nRF52840](https://www.makerfabs.com/makepython-nrf52840.html) is
a development board with the Nordic nRF52840 SoC and a 128 x 64 pixel OLED
display.


## Getting Started

First, follow the [Tock Getting Started guide](../../doc/Getting_Started.md).

The MakePython nRF52840 is designed to be programmed using an external JLink
programmer. We would like to avoid this requirement, so we use the [Tock
Bootloader](https://github.com/tock/tock-bootloader) which allows us to program
the board over the UART connection. However, we still require the programmer one
time to flash the bootloader.

To flash the bootloader we must connect a JLink programmer. The easiest way is
to use an nRF52840dk board.

### Connect the nRF52840dk to the MakePython-nRF52840

First we jumper the board as shown with the following pin mappings

| nRF52840dk | MakePython-nRF52840 |
|------------|---------------------|
| GND | GND |
| SWD SEL | +3V3 |
| SWD CLK | SWDCLK |
| SWD IO | SWDDIO |

Make sure _both_ the nRF52840dk board and the MakePython-nRF52840 board are
attached to your computer via two USB connections.

Then:

```
make flash-bootloader
```

This will use JLinkExe to flash the bootloader using the nRF52840dk's onboard
jtag hardware.

### Using the Bootloader

The bootloader activates when the reset button is pressed twice in quick
succession. The green LED will stay on when the bootloader is active.

Once the bootloader is installed tockloader will work as expected.
8 changes: 8 additions & 0 deletions boards/makepython-nrf52840/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

fn main() {
println!("cargo:rerun-if-changed=layout.ld");
println!("cargo:rerun-if-changed=../kernel_layout.ld");
}
14 changes: 14 additions & 0 deletions boards/makepython-nrf52840/layout.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* Licensed under the Apache License, Version 2.0 or the MIT License. */
/* SPDX-License-Identifier: Apache-2.0 OR MIT */
/* Copyright Tock Contributors 2023. */

MEMORY
{
rom (rx) : ORIGIN = 0x00010000, LENGTH = 256K
prog (rx) : ORIGIN = 0x00050000, LENGTH = 704K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
}

PAGE_SIZE = 4K;

INCLUDE ../kernel_layout.ld
144 changes: 144 additions & 0 deletions boards/makepython-nrf52840/src/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

use core::fmt::Write;
use core::panic::PanicInfo;

use cortexm4;
use kernel::debug;
use kernel::debug::IoWrite;
use kernel::hil::led;
use kernel::hil::uart::{self};
use kernel::ErrorCode;
use nrf52840::gpio::Pin;

use crate::CHIP;
use crate::PROCESSES;
use crate::PROCESS_PRINTER;
use kernel::hil::uart::Transmit;
use kernel::utilities::cells::VolatileCell;

struct Writer {
initialized: bool,
}

static mut WRITER: Writer = Writer { initialized: false };

impl Write for Writer {
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
self.write(s.as_bytes());
Ok(())
}
}

const BUF_LEN: usize = 512;
static mut STATIC_PANIC_BUF: [u8; BUF_LEN] = [0; BUF_LEN];

static mut DUMMY: DummyUsbClient = DummyUsbClient {
fired: VolatileCell::new(false),
};

struct DummyUsbClient {
fired: VolatileCell<bool>,
}

impl uart::TransmitClient for DummyUsbClient {
fn transmitted_buffer(&self, _: &'static mut [u8], _: usize, _: Result<(), ErrorCode>) {
self.fired.set(true);
}
}

impl IoWrite for Writer {
fn write(&mut self, buf: &[u8]) -> usize {
if !self.initialized {
self.initialized = true;
}
// Here we mimic a synchronous UART output by calling transmit_buffer
// on the CDC stack and then spinning on USB interrupts until the transaction
// is complete. If the USB or CDC stack panicked, this may fail. It will also
// fail if the panic occurred prior to the USB connection being initialized.
// In the latter case, the LEDs should still blink in the panic pattern.

// spin so that if any USB DMA is ongoing it will finish
// we should only need this on the first call to write()
let mut i = 0;
loop {
i += 1;
cortexm4::support::nop();
if i > 10000 {
break;
}
}

// copy_from_slice() requires equal length slices
// This will truncate any writes longer than BUF_LEN, but simplifies the
// code. In practice, BUF_LEN=512 always seems sufficient for the size of
// individual calls to write made by the panic handler.
let mut max = BUF_LEN;
if buf.len() < BUF_LEN {
max = buf.len();
}

unsafe {
// If CDC_REF_FOR_PANIC is not yet set we panicked very early,
// and not much we can do. Don't want to double fault,
// so just return.
super::CDC_REF_FOR_PANIC.map(|cdc| {
// Lots of unsafe dereferencing of global static mut objects here.
// However, this should be okay, because it all happens within
// a single thread, and:
// - This is the only place the global CDC_REF_FOR_PANIC is used, the logic is the same
// as applies for the global CHIP variable used in the panic handler.
// - We do create multiple mutable references to the STATIC_PANIC_BUF, but we never
// access the STATIC_PANIC_BUF after a slice of it is passed to transmit_buffer
// until the slice has been returned in the uart callback.
// - Similarly, only this function uses the global DUMMY variable, and we do not
// mutate it.
let usb = &mut cdc.controller();
STATIC_PANIC_BUF[..max].copy_from_slice(&buf[..max]);
let static_buf = &mut STATIC_PANIC_BUF;
cdc.set_transmit_client(&DUMMY);
let _ = cdc.transmit_buffer(static_buf, max);
loop {
if let Some(interrupt) = cortexm4::nvic::next_pending() {
if interrupt == 39 {
usb.handle_interrupt();
}
let n = cortexm4::nvic::Nvic::new(interrupt);
n.clear_pending();
n.enable();
}
if DUMMY.fired.get() {
// buffer finished transmitting, return so we can output additional
// messages when requested by the panic handler.
break;
}
}
DUMMY.fired.set(false);
});
}
buf.len()
}
}

/// Default panic handler for the Nano 33 Board.
///
/// We just use the standard default provided by the debug module in the kernel.
#[cfg(not(test))]
#[no_mangle]
#[panic_handler]
pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! {
let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P1_10);
let led = &mut led::LedLow::new(led_kernel_pin);
let writer = &mut WRITER;
debug::panic(
&mut [led],
writer,
pi,
&cortexm4::support::nop,
&PROCESSES,
&CHIP,
&PROCESS_PRINTER,
)
}
Loading

0 comments on commit 4e24b5d

Please sign in to comment.