diff --git a/boards/microbit_v2-bootloader/Cargo.lock b/boards/microbit_v2-bootloader/Cargo.lock new file mode 100644 index 0000000..f971ce8 --- /dev/null +++ b/boards/microbit_v2-bootloader/Cargo.lock @@ -0,0 +1,162 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bootloader" +version = "0.1.0" +dependencies = [ + "kernel", + "tock-bootloader-protocol", +] + +[[package]] +name = "bootloader_attributes" +version = "0.1.0" + +[[package]] +name = "bootloader_cortexm" +version = "0.1.0" +dependencies = [ + "bootloader", + "kernel", +] + +[[package]] +name = "bootloader_nrf52" +version = "0.1.0" +dependencies = [ + "bootloader", + "cortexm4", + "kernel", + "nrf52", +] + +[[package]] +name = "byteorder" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" + +[[package]] +name = "capsules" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "enum_primitive", + "kernel", +] + +[[package]] +name = "components" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "capsules", + "kernel", +] + +[[package]] +name = "cortexm" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "kernel", +] + +[[package]] +name = "cortexm4" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "cortexm", + "kernel", +] + +[[package]] +name = "enum_primitive" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" + +[[package]] +name = "kernel" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "tock-cells", + "tock-registers", + "tock-tbf", +] + +[[package]] +name = "microbit_v2-bootloader" +version = "0.1.0" +dependencies = [ + "bootloader", + "bootloader_attributes", + "bootloader_cortexm", + "bootloader_nrf52", + "capsules", + "components", + "cortexm4", + "kernel", + "nrf52", + "nrf52833", +] + +[[package]] +name = "nrf52" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "cortexm4", + "enum_primitive", + "kernel", + "nrf5x", + "tock-rt0", +] + +[[package]] +name = "nrf52833" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "cortexm4", + "kernel", + "nrf52", + "tock-rt0", +] + +[[package]] +name = "nrf5x" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" +dependencies = [ + "enum_primitive", + "kernel", +] + +[[package]] +name = "tock-bootloader-protocol" +version = "0.2.2" +dependencies = [ + "byteorder", +] + +[[package]] +name = "tock-cells" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" + +[[package]] +name = "tock-registers" +version = "0.6.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" + +[[package]] +name = "tock-rt0" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" + +[[package]] +name = "tock-tbf" +version = "0.1.0" +source = "git+https://github.com/tock/tock?branch=master#af1efe87f3f52f9dc923f0ca936091f7681ce59a" diff --git a/boards/microbit_v2-bootloader/Cargo.toml b/boards/microbit_v2-bootloader/Cargo.toml new file mode 100644 index 0000000..d775e4e --- /dev/null +++ b/boards/microbit_v2-bootloader/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "microbit_v2-bootloader" +version = "0.1.0" +authors = ["Tock Project Developers "] +build = "build.rs" +edition = "2018" + +[dependencies] +cortexm4 = { git = "https://github.com/tock/tock", branch = "master" } +capsules = { git = "https://github.com/tock/tock", branch = "master" } +kernel = { git = "https://github.com/tock/tock", branch = "master" } +nrf52 = { git = "https://github.com/tock/tock", branch = "master" } +nrf52833 = { git = "https://github.com/tock/tock", branch = "master" } +components = { git = "https://github.com/tock/tock", branch = "master" } + +# For Development +# cortexm4 = { path = "../../../tock/arch/cortex-m4" } +# capsules = { path = "../../../tock/capsules" } +# kernel = { path = "../../../tock/kernel" } +# nrf52 = { path = "../../../tock/chips/nrf52" } +# nrf52833 = { path = "../../../tock/chips/nrf52833" } +# components = { path = "../../../tock/boards/components" } + +bootloader = { path = "../../bootloader" } +bootloader_nrf52 = { path = "../../chips/bootloader_nrf52" } +bootloader_cortexm = { path = "../../arch/bootloader_cortexm" } + + +[build-dependencies] +bootloader_attributes = { path = "../../tools/bootloader_attributes" } + +[profile.dev] +panic = "abort" +lto = false +opt-level = "z" +debug = true + +[profile.release] +panic = "abort" +lto = true +opt-level = "z" +debug = true diff --git a/boards/microbit_v2-bootloader/Makefile b/boards/microbit_v2-bootloader/Makefile new file mode 100644 index 0000000..978687a --- /dev/null +++ b/boards/microbit_v2-bootloader/Makefile @@ -0,0 +1,22 @@ +# Makefile for building the Tock bootloader for nRF52 platforms using CDC-ACM +# over USB. + +TOCK_ARCH=cortex-m4 +TARGET=thumbv7em-none-eabi +PLATFORM=microbit_v2-bootloader + +include ../Common.mk + +TOCKLOADER=tockloader + +OPENOCD=openocd +OPENOCD_OPTIONS=-f openocd.cfg + +# Upload the kernel over JTAG +.PHONY: flash +flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin + $(OPENOCD) $(OPENOCD_OPTIONS) -c "program $<; verify_image $<; reset; shutdown;" + +.PHONY: flash +flash-debug: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/debug/$(PLATFORM).bin + $(OPENOCD) $(OPENOCD_OPTIONS) -c "program $<; verify_image $<; reset; shutdown;" diff --git a/boards/microbit_v2-bootloader/README.md b/boards/microbit_v2-bootloader/README.md new file mode 100644 index 0000000..f972fdf --- /dev/null +++ b/boards/microbit_v2-bootloader/README.md @@ -0,0 +1,28 @@ +BBC:MicroBit v2 Tock Bootloader +=================== + +This is the implementation of the Tock bootloader for the BBC:MicroBit v2 +board. The bootloader runs using the Debugger UART. + +Compiling +--------- + +To compile the bootloader, simply run the `make` command. + +``` +make +``` + +Flashing +-------- + +OpenOCD is needed to flash the bootloader. Running `make flash` will compile it and flash it. + +``` +make flash +``` + +Entering +-------- + +Entering the bootloader is done by holding Button A during reset. diff --git a/boards/microbit_v2-bootloader/build.rs b/boards/microbit_v2-bootloader/build.rs new file mode 100644 index 0000000..87cba62 --- /dev/null +++ b/boards/microbit_v2-bootloader/build.rs @@ -0,0 +1,12 @@ +extern crate bootloader_attributes; + +fn main() { + println!("cargo:rerun-if-changed=layout.ld"); + println!("cargo:rerun-if-changed=../kernel_layout.ld"); + + let mut f = bootloader_attributes::get_file(); + bootloader_attributes::write_flags(&mut f, "1.1.1", 0x8000); + bootloader_attributes::write_attribute(&mut f, "board", "microbit_v2"); + bootloader_attributes::write_attribute(&mut f, "arch", "cortex-m4"); + bootloader_attributes::write_attribute(&mut f, "appaddr", "0x40000"); +} diff --git a/boards/microbit_v2-bootloader/layout.ld b/boards/microbit_v2-bootloader/layout.ld new file mode 100644 index 0000000..7a116c2 --- /dev/null +++ b/boards/microbit_v2-bootloader/layout.ld @@ -0,0 +1,11 @@ +MEMORY +{ + rom (rx) : ORIGIN = 0x00000000, LENGTH = 32K + prog (rx) : ORIGIN = 0x00008000, LENGTH = 480K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K +} + +MPU_MIN_ALIGN = 8K; +PAGE_SIZE = 4K; + +INCLUDE ../kernel_layout.ld diff --git a/boards/microbit_v2-bootloader/openocd.cfg b/boards/microbit_v2-bootloader/openocd.cfg new file mode 100644 index 0000000..eb79ba6 --- /dev/null +++ b/boards/microbit_v2-bootloader/openocd.cfg @@ -0,0 +1,11 @@ +source [find interface/cmsis-dap.cfg] +transport select swd +source [find target/nrf52.cfg] + +set WORKAREASIZE 0x40000 + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $WORKAREASIZE -work-area-backup 0 + +flash bank $_CHIPNAME.flash nrf51 0x00000000 0 1 1 $_TARGETNAME +flash bank $_CHIPNAME.uicr nrf51 0x10001000 0 1 1 $_TARGETNAME + diff --git a/boards/microbit_v2-bootloader/src/main.rs b/boards/microbit_v2-bootloader/src/main.rs new file mode 100644 index 0000000..c35c4e4 --- /dev/null +++ b/boards/microbit_v2-bootloader/src/main.rs @@ -0,0 +1,297 @@ +//! Tock kernel for the bootloader on nrf52 over CDC/USB. +//! +//! It is based on nRF52833 SoC (Cortex M4 core with a BLE + IEEE 802.15.4 transceiver). + +#![no_std] +// Disable this attribute when documenting, as a workaround for +// https://github.com/rust-lang/rust/issues/62184. +#![cfg_attr(not(doc), no_main)] + +use core::panic::PanicInfo; + +use kernel::capabilities; +use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState}; +use kernel::component::Component; +use kernel::hil; +use kernel::hil::time::Alarm; +use kernel::hil::time::Counter; +#[allow(unused_imports)] +use kernel::{create_capability, debug, debug_gpio, debug_verbose, static_init}; + +use capsules::virtual_alarm::VirtualMuxAlarm; + +use nrf52833::gpio::Pin; +use nrf52833::interrupt_service::Nrf52833DefaultPeripherals; + +const UART_TXD: Pin = Pin::P0_06; +const UART_RXD: Pin = Pin::P1_08; + +const LED_ON_PIN: Pin = Pin::P0_20; +const BUTTON_A: Pin = Pin::P0_14; + +include!(concat!(env!("OUT_DIR"), "/attributes.rs")); + +// Number of concurrent processes this platform supports. +const NUM_PROCS: usize = 0; + +static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] = + [None; NUM_PROCS]; + +static mut CHIP: Option<&'static nrf52833::chip::NRF52> = None; + +/// Dummy buffer that causes the linker to reserve enough space for the stack. +#[no_mangle] +#[link_section = ".stack_buffer"] +pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000]; + +/// Function to allow the bootloader to exit by reseting the chip. +fn bootloader_exit() { + unsafe { + cortexm4::scb::reset(); + } +} + +/// Supported drivers by the platform +pub struct Platform { + bootloader: &'static bootloader::bootloader::Bootloader< + 'static, + bootloader::uart_receive_multiple_timeout::UartReceiveMultipleTimeout< + 'static, + VirtualMuxAlarm<'static, nrf52::rtc::Rtc<'static>>, + >, + bootloader::flash_large_to_small::FlashLargeToSmall<'static, nrf52::nvmc::Nvmc>, + >, +} + +impl kernel::Platform for Platform { + fn with_driver(&self, _driver_num: usize, f: F) -> R + where + F: FnOnce(Option<&dyn kernel::Driver>) -> R, + { + f(None) + } +} + +/// Entry point in the vector table called on hard reset. +#[no_mangle] +pub unsafe fn reset_handler() { + // Loads relocations and clears BSS + nrf52833::init(); + let ppi = static_init!(nrf52833::ppi::Ppi, nrf52833::ppi::Ppi::new()); + // Initialize chip peripheral drivers + let nrf52833_peripherals = static_init!( + Nrf52833DefaultPeripherals, + Nrf52833DefaultPeripherals::new(ppi) + ); + + // set up circular peripheral dependencies + nrf52833_peripherals.init(); + let base_peripherals = &nrf52833_peripherals.nrf52; + + let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&PROCESSES)); + + //-------------------------------------------------------------------------- + // BOOTLOADER ENTRY + //-------------------------------------------------------------------------- + + // Decide very early if we want to stay in the bootloader so we don't run a + // bunch of init code just to reset into the kernel. + + let bootloader_entry_mode = static_init!( + bootloader::bootloader_entry_gpio::BootloaderEntryGpio, + bootloader::bootloader_entry_gpio::BootloaderEntryGpio::new( + &nrf52833_peripherals.gpio_port[BUTTON_A] + ) + ); + + let bootloader_jumper = static_init!( + bootloader_cortexm::jumper::CortexMJumper, + bootloader_cortexm::jumper::CortexMJumper::new() + ); + + let active_notifier_led = static_init!( + kernel::hil::led::LedHigh<'static, nrf52833::gpio::GPIOPin>, + kernel::hil::led::LedHigh::new(&nrf52833_peripherals.gpio_port[LED_ON_PIN]) + ); + + let bootloader_active_notifier = static_init!( + bootloader::active_notifier_ledon::ActiveNotifierLedon, + bootloader::active_notifier_ledon::ActiveNotifierLedon::new(active_notifier_led) + ); + + let bootloader_enterer = static_init!( + bootloader::bootloader::BootloaderEnterer<'static>, + bootloader::bootloader::BootloaderEnterer::new( + bootloader_entry_mode, + bootloader_jumper, + bootloader_active_notifier + ) + ); + + // First decide if we want to actually run the bootloader or not. + bootloader_enterer.check(); + + //-------------------------------------------------------------------------- + // CAPABILITIES + //-------------------------------------------------------------------------- + + // Create capabilities that the board needs to call certain protected kernel + // functions. + let main_loop_capability = create_capability!(capabilities::MainLoopCapability); + + //-------------------------------------------------------------------------- + // Deferred Call (Dynamic) Setup + //-------------------------------------------------------------------------- + + let dynamic_deferred_call_clients = + static_init!([DynamicDeferredCallClientState; 5], Default::default()); + let dynamic_deferred_caller = static_init!( + DynamicDeferredCall, + DynamicDeferredCall::new(dynamic_deferred_call_clients) + ); + DynamicDeferredCall::set_global_instance(dynamic_deferred_caller); + + //-------------------------------------------------------------------------- + // ALARM & TIMER + //-------------------------------------------------------------------------- + + let rtc = &base_peripherals.rtc; + rtc.start(); + + let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc) + .finalize(components::alarm_mux_component_helper!(nrf52::rtc::Rtc)); + + // //-------------------------------------------------------------------------- + // // UART DEBUGGING + // //-------------------------------------------------------------------------- + + // let channel = nrf52_components::UartChannelComponent::new( + // UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD)), + // mux_alarm, + // &base_peripherals.uarte0, + // ) + // .finalize(()); + + // // Create a shared UART channel for the console and for kernel debug. + // let uart_mux = + // components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller) + // .finalize(()); + + // // Create the debugger object that handles calls to `debug!()`. + // components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(()); + + //-------------------------------------------------------------------------- + // BOOTLOADER + //-------------------------------------------------------------------------- + + // Setup receive with timeout. + let recv_auto_virtual_alarm = static_init!( + VirtualMuxAlarm<'static, nrf52833::rtc::Rtc>, + VirtualMuxAlarm::new(mux_alarm) + ); + + let recv_auto_uart = static_init!( + bootloader::uart_receive_multiple_timeout::UartReceiveMultipleTimeout< + 'static, + VirtualMuxAlarm<'static, nrf52833::rtc::Rtc>, + >, + bootloader::uart_receive_multiple_timeout::UartReceiveMultipleTimeout::new( + &base_peripherals.uarte0, + recv_auto_virtual_alarm, + &mut bootloader::uart_receive_multiple_timeout::BUF + ) + ); + recv_auto_virtual_alarm.set_alarm_client(recv_auto_uart); + + // Setup the UART pins + &base_peripherals.uarte0.initialize( + nrf52833::pinmux::Pinmux::new(UART_TXD as u32), + nrf52833::pinmux::Pinmux::new(UART_RXD as u32), + None, + None, + ); + + let nrfpagebuffer = static_init!(nrf52::nvmc::NrfPage, nrf52::nvmc::NrfPage::default()); + + let flash_adapter = static_init!( + bootloader::flash_large_to_small::FlashLargeToSmall<'static, nrf52833::nvmc::Nvmc>, + bootloader::flash_large_to_small::FlashLargeToSmall::new( + &base_peripherals.nvmc, + nrfpagebuffer, + ) + ); + hil::flash::HasClient::set_client(&base_peripherals.nvmc, flash_adapter); + + let pagebuffer = static_init!( + bootloader::flash_large_to_small::FiveTwelvePage, + bootloader::flash_large_to_small::FiveTwelvePage::default() + ); + + let bootloader = static_init!( + bootloader::bootloader::Bootloader< + 'static, + bootloader::uart_receive_multiple_timeout::UartReceiveMultipleTimeout< + 'static, + VirtualMuxAlarm<'static, nrf52833::rtc::Rtc>, + >, + bootloader::flash_large_to_small::FlashLargeToSmall<'static, nrf52::nvmc::Nvmc>, + >, + bootloader::bootloader::Bootloader::new( + recv_auto_uart, + flash_adapter, + &bootloader_exit, + pagebuffer, + &mut bootloader::bootloader::BUF + ) + ); + + hil::uart::Transmit::set_transmit_client(&base_peripherals.uarte0, bootloader); + hil::uart::Receive::set_receive_client(&base_peripherals.uarte0, recv_auto_uart); + hil::uart::Receive::set_receive_client(recv_auto_uart, bootloader); + hil::flash::HasClient::set_client(flash_adapter, bootloader); + + //-------------------------------------------------------------------------- + // FINAL SETUP AND BOARD BOOT + //-------------------------------------------------------------------------- + + // Start all of the clocks. Low power operation will require a better + // approach than this. + base_peripherals.clock.low_stop(); + base_peripherals.clock.high_stop(); + base_peripherals.clock.low_start(); + base_peripherals.clock.high_start(); + while !base_peripherals.clock.low_started() {} + while !base_peripherals.clock.high_started() {} + + let platform = Platform { bootloader }; + + let chip = static_init!( + nrf52833::chip::NRF52, + nrf52833::chip::NRF52::new(nrf52833_peripherals) + ); + CHIP = Some(chip); + + // Actually run the bootloader. + platform.bootloader.start(); + + //-------------------------------------------------------------------------- + // MAIN LOOP + //-------------------------------------------------------------------------- + + let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES) + .finalize(components::rr_component_helper!(NUM_PROCS)); + board_kernel.kernel_loop::<_, _, _, NUM_PROCS>( + &platform, + chip, + None, + scheduler, + &main_loop_capability, + ); +} + +#[cfg(not(test))] +#[no_mangle] +#[panic_handler] +pub unsafe extern "C" fn panic_fmt(_pi: &PanicInfo) -> ! { + loop {} +} diff --git a/bootloader/src/uart_receive_multiple_timeout.rs b/bootloader/src/uart_receive_multiple_timeout.rs index 830bf1c..d0d82ee 100644 --- a/bootloader/src/uart_receive_multiple_timeout.rs +++ b/bootloader/src/uart_receive_multiple_timeout.rs @@ -200,16 +200,22 @@ impl<'a, A: hil::time::Alarm<'a>> hil::uart::ReceiveClient for UartReceiveMultip // If everything is normal then we continue receiving. if rval == ReturnCode::SUCCESS { // Next we setup a timer to timeout if the receive has - // finished. - let interval = A::ticks_from_ms(4); + // finished. Six ms should be enough to receive up to 50 + // bytes. + let interval = A::ticks_from_ms(6); self.alarm.set_alarm(self.alarm.now(), interval); // Then we go back to receiving to see if there is more data // on its way. // - // Receive up to half of the buffer at a time so there is - // room if the host sends us more than we expect. - self.uart.receive_buffer(buffer, buffer.len() / 2); + // Receive either 50 bytes or half the buffer, whatever is + // lower. We only use half the buffer in case the host sends + // us more than we expect, as can happen with USB where we + // don't have a method for flow control. We receive up to 50 + // so that we don't need a long timeout in case the host is + // only sending us a small number of bytes. + self.uart + .receive_buffer(buffer, cmp::min(buffer.len() / 2, 50)); } else if rval == ReturnCode::ECANCEL { // The last receive was aborted meaning the receive has // finished.