diff --git a/.travis.yml b/.travis.yml index 875025b..f7c6859 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,6 @@ language: rust cache: cargo: true - directories: - - $HOME/gcc-arm-none-eabi-6_2-2016q4 - - $HOME/local_cargo os: - linux @@ -13,17 +10,11 @@ os: # If you change this, you must also change Getting_Started.md, Makefile.common, # and Vagrantfile. rust: - - nightly-2018-04-19 - -before_install: - - tools/travis-install-gcc.sh - - export PATH="$PATH:$HOME/gcc-arm-none-eabi-6_2-2016q4/bin" - -before_script: - - tools/install_cargo_fmt.sh + - nightly-2018-08-16 script: - export PATH=$HOME/.cargo/bin:$PATH - tools/run_cargo_fmt.sh diff - make -C boards/hail-bootloader + - make -C boards/nrf52-bootloader diff --git a/boards/Common.mk b/boards/Common.mk index 7d9bb55..94e9b7e 100644 --- a/boards/Common.mk +++ b/boards/Common.mk @@ -5,17 +5,24 @@ MAKEFLAGS += -R MAKEFILE_COMMON_PATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -TOOLCHAIN ?= arm-none-eabi +TOOLCHAIN ?= llvm CARGO ?= cargo -# This will hopefully move into Cargo.toml (or Cargo.toml.local) eventually -RUSTFLAGS_FOR_CARGO_LINKING := "-C link-arg=-nostartfiles -C link-arg=-Tlayout.ld" -CC := $(TOOLCHAIN)-gcc -SIZE ?= $(TOOLCHAIN)-size -OBJCOPY ?= $(TOOLCHAIN)-objcopy -OBJDUMP ?= $(TOOLCHAIN)-objdump -OBJDUMP_FLAGS += --disassemble-all --source --disassembler-options=force-thumb -C --section-headers +# This will hopefully move into Cargo.toml (or Cargo.toml.local) eventually. +# lld uses the page size to align program sections. It defaults to 4096 and this +# puts a gap between before the .relocate section. `zmax-page-size=512` tells +# lld the actual page size so it doesn't have to be conservative. +RUSTFLAGS_FOR_CARGO_LINKING := -C link-arg=-Tlayout.ld -C linker=rust-lld \ +-Z linker-flavor=ld.lld -C relocation-model=dynamic-no-pic \ +-C link-arg=-zmax-page-size=512 + +# Disallow warnings for continuous integration builds. Disallowing them here +# ensures that warnings during testing won't prevent compilation from succeeding. +# We are OK with warnings in bootloader land! +# ifeq ($(CI),true) +# RUSTFLAGS_FOR_CARGO_LINKING += -D warnings +# endif # http://stackoverflow.com/questions/10858261/abort-makefile-if-variable-not-set # Check that given variables are set and all have non-empty values, @@ -33,6 +40,7 @@ __check_defined = \ $(call check_defined, PLATFORM) +$(call check_defined, TARGET) # If environment variable V is non-empty, be verbose ifneq ($(V),) @@ -43,24 +51,23 @@ Q=@ VERBOSE = endif -# Check that gcc version is new enough (> 5.1) - used during linking -CC_VERSION_MAJOR := $(shell $(TOOLCHAIN)-gcc -dumpversion | cut -d '.' -f1) -ifeq (1,$(shell expr $(CC_VERSION_MAJOR) \>= 6)) - # no-op -else - ifneq (5,$(CC_VERSION_MAJOR)) - $(info CC=$(CC)) - $(info $$(CC) -dumpversion: $(shell $(CC) -dumpversion)) - $(error Your compiler is too old. Need gcc version > 5.1) - endif - CC_VERSION_MINOR := $(shell $(CC) -dumpversion | cut -d '.' -f2) - ifneq (1,$(shell expr $(CC_VERSION_MINOR) \> 1)) - $(info CC=$(CC)) - $(info $$(CC) -dumpversion: $(shell $(CC) -dumpversion)) - $(error Your compiler is too old. Need gcc version > 5.1) - endif +export TOCK_KERNEL_VERSION := $(shell git describe --always || echo notgit) + + +# Validate that rustup is new enough +MINIMUM_RUSTUP_VERSION := 1.11.0 +RUSTUP_VERSION := $(strip $(word 2, $(shell rustup --version))) +ifeq ($(shell $(MAKEFILE_COMMON_PATH)../tools/semver.sh $(RUSTUP_VERSION) \< $(MINIMUM_RUSTUP_VERSION)), true) + $(warning Required tool `rustup` is out-of-date.) + $(warning Running `rustup update` in 3 seconds (ctrl-c to cancel)) + $(shell sleep 3s) + DUMMY := $(shell rustup update) endif +LLVM_TOOLS_INSTALLED := $(shell rustup component list | grep 'llvm-tools-preview.*(installed)' -q; echo $$?) +ifeq ($(LLVM_TOOLS_INSTALLED),1) + $(shell rustup component add llvm-tools-preview) +endif ifneq ($(shell rustup component list | grep rust-src),rust-src (installed)) $(shell rustup component add rust-src) endif @@ -68,8 +75,17 @@ ifneq ($(shell rustup target list | grep "$(TARGET) (installed)"),$(TARGET) (ins $(shell rustup target add $(TARGET)) endif -# Need some dummy value here -export TOCK_KERNEL_VERSION=5 +ifeq ($(TOOLCHAIN),llvm) + # If the user is using the standard toolchain we need to get the full path. + # rustup should take care of this for us by putting in a proxy in .cargo/bin, + # but until that is setup we workaround it. + TOOLCHAIN = "$(shell dirname $(shell find `rustc --print sysroot` -name llvm-size))/llvm" +endif + +SIZE ?= $(TOOLCHAIN)-size +OBJCOPY ?= $(TOOLCHAIN)-objcopy +OBJDUMP ?= $(TOOLCHAIN)-objdump +OBJDUMP_FLAGS += --disassemble-all --source --arch-name=thumb --section-headers # Dump configuration for verbose builds ifneq ($(V),) @@ -82,6 +98,7 @@ ifneq ($(V),) $(info OBJCOPY=$(OBJCOPY)) $(info PLATFORM=$(PLATFORM)) $(info TARGET=$(TARGET)) + $(info TOCK_KERNEL_VERSION=$(TOCK_KERNEL_VERSION)) $(info TOOLCHAIN=$(TOOLCHAIN)) $(info ) $(info $(OBJCOPY) --version = $(shell $(OBJCOPY) --version)) @@ -90,42 +107,69 @@ ifneq ($(V),) $(info ) endif +.PRECIOUS: %.elf +# Support rules + +# User-facing targets .PHONY: all -all: target/$(TARGET)/release/$(PLATFORM).bin +all: release -.PHONY: lst -lst: target/$(TARGET)/release/$(PLATFORM).lst +# `make check` runs the Rust compiler but does not actually output the final +# binary. This makes checking for Rust errors much faster. +.PHONY: check +check: + $(Q)RUSTFLAGS="$(RUSTFLAGS_FOR_CARGO_LINKING)" $(CARGO) check --target=$(TARGET) $(VERBOSE) --release -target: - @mkdir -p target +.PHONY: clean +clean:: + $(Q)$(CARGO) clean $(VERBOSE) + +.PHONY: release +release: target/$(TARGET)/release/$(PLATFORM).bin + +.PHONY: debug +debug: target/$(TARGET)/debug/$(PLATFORM).bin + +.PHONY: debug-lst +debug-lst: target/$(TARGET)/debug/$(PLATFORM).lst .PHONY: doc doc: | target $(Q)RUSTDOCFLAGS=--document-private-items $(CARGO) doc $(VERBOSE) --release --target=$(TARGET) -target/$(TARGET)/release/$(PLATFORM).elf: target/$(TARGET)/release/$(PLATFORM) - $(Q)cp target/$(TARGET)/release/$(PLATFORM) target/$(TARGET)/release/$(PLATFORM).elf +.PHONY: lst +lst: target/$(TARGET)/release/$(PLATFORM).lst -target/$(TARGET)/release/$(PLATFORM).lst: target/$(TARGET)/release/$(PLATFORM).elf - $(Q)$(OBJDUMP) $(OBJDUMP_FLAGS) $< > target/$(TARGET)/release/$(PLATFORM).lst +.PHONY: release +release: target/$(TARGET)/release/$(PLATFORM).bin -.PHONY: target/$(TARGET)/release/$(PLATFORM) -target/$(TARGET)/release/$(PLATFORM): - $(Q)RUSTFLAGS=$(RUSTFLAGS_FOR_CARGO_LINKING) $(CARGO) build --target=$(TARGET) $(VERBOSE) --release - $(Q)$(SIZE) $@ -target/$(TARGET)/release/$(PLATFORM).hex: target/$(TARGET)/release/$(PLATFORM).elf - $(Q)$(OBJCOPY) -Oihex $^ $@ +# Support rules -target/$(TARGET)/release/$(PLATFORM).bin: target/$(TARGET)/release/$(PLATFORM).elf - $(Q)$(OBJCOPY) -Obinary $^ $@ +target: + @mkdir -p target -# `make check` runs the Rust compiler but does not actually output the final -# binary. This makes checking for Rust errors much faster. -.PHONY: check -check: - $(Q)RUSTFLAGS=$(RUSTFLAGS_FOR_CARGO_LINKING) $(CARGO) check --target=$(TARGET) $(VERBOSE) --release +# Cargo outputs an elf file (just without a file extension) +%.elf: % + $(Q)cp $< $@ -.PHONY: clean -clean:: - $(Q)$(CARGO) clean $(VERBOSE) +%.bin: %.elf + $(Q)$(OBJCOPY) --output-target=binary $^ $@ + +%.lst: %.elf + $(Q)$(OBJDUMP) $(OBJDUMP_FLAGS) $< > $@ + + +# Cargo-drivers +# We want to always invoke cargo (yay nested build systems), so these need to +# be phony, which means they can't be pattern rules. + +.PHONY: target/$(TARGET)/release/$(PLATFORM) +target/$(TARGET)/release/$(PLATFORM): + $(Q)RUSTFLAGS="$(RUSTFLAGS_FOR_CARGO_LINKING)" $(CARGO) build --target=$(TARGET) $(VERBOSE) --release + $(Q)$(SIZE) $@ + +.PHONY: target/$(TARGET)/debug/$(PLATFORM) +target/$(TARGET)/debug/$(PLATFORM): + $(Q)RUSTFLAGS="$(RUSTFLAGS_FOR_CARGO_LINKING)" $(CARGO) build $(VERBOSE) --target=$(TARGET) + $(Q)$(SIZE) $@ diff --git a/boards/hail-bootloader/Cargo.lock b/boards/hail-bootloader/Cargo.lock index ced4b81..4b8758a 100644 --- a/boards/hail-bootloader/Cargo.lock +++ b/boards/hail-bootloader/Cargo.lock @@ -3,7 +3,7 @@ name = "bootloader" version = "0.1.0" dependencies = [ "kernel 0.1.0 (git+https://github.com/tock/tock)", - "tockloader-proto 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tockloader-proto 0.2.1 (git+https://github.com/tock/tockloader-proto-rs)", ] [[package]] @@ -12,13 +12,22 @@ version = "0.1.0" [[package]] name = "byteorder" -version = "1.2.2" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "capsules" version = "0.1.0" -source = "git+https://github.com/tock/tock#871e1dbd1e247d1d5a97714a60014780c5844331" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" +dependencies = [ + "enum_primitive 0.1.0 (git+https://github.com/tock/tock)", + "kernel 0.1.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "cortexm" +version = "0.1.0" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" dependencies = [ "kernel 0.1.0 (git+https://github.com/tock/tock)", ] @@ -26,11 +35,17 @@ dependencies = [ [[package]] name = "cortexm4" version = "0.1.0" -source = "git+https://github.com/tock/tock#871e1dbd1e247d1d5a97714a60014780c5844331" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" dependencies = [ + "cortexm 0.1.0 (git+https://github.com/tock/tock)", "kernel 0.1.0 (git+https://github.com/tock/tock)", ] +[[package]] +name = "enum_primitive" +version = "0.1.0" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" + [[package]] name = "hailbootloader" version = "0.1.0" @@ -46,29 +61,47 @@ dependencies = [ [[package]] name = "kernel" version = "0.1.0" -source = "git+https://github.com/tock/tock#871e1dbd1e247d1d5a97714a60014780c5844331" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" +dependencies = [ + "tock-cells 0.1.0 (git+https://github.com/tock/tock)", + "tock-registers 0.2.0 (git+https://github.com/tock/tock)", +] [[package]] name = "sam4l" version = "0.1.0" -source = "git+https://github.com/tock/tock#871e1dbd1e247d1d5a97714a60014780c5844331" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" dependencies = [ "cortexm4 0.1.0 (git+https://github.com/tock/tock)", "kernel 0.1.0 (git+https://github.com/tock/tock)", ] +[[package]] +name = "tock-cells" +version = "0.1.0" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" + +[[package]] +name = "tock-registers" +version = "0.2.0" +source = "git+https://github.com/tock/tock#ede88f41c8e7073d6115272d52f574526ce4ed9a" + [[package]] name = "tockloader-proto" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tock/tockloader-proto-rs#945aedf6efb77221bc014ed590c56ff934ccdcac" dependencies = [ - "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] -"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum capsules 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum cortexm 0.1.0 (git+https://github.com/tock/tock)" = "" "checksum cortexm4 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum enum_primitive 0.1.0 (git+https://github.com/tock/tock)" = "" "checksum kernel 0.1.0 (git+https://github.com/tock/tock)" = "" "checksum sam4l 0.1.0 (git+https://github.com/tock/tock)" = "" -"checksum tockloader-proto 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8997ca9de041a35f8322bb9f5e72785e7bf2413198f17a475ab9a90c3cc102d8" +"checksum tock-cells 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum tock-registers 0.2.0 (git+https://github.com/tock/tock)" = "" +"checksum tockloader-proto 0.2.1 (git+https://github.com/tock/tockloader-proto-rs)" = "" diff --git a/boards/hail-bootloader/build.rs b/boards/hail-bootloader/build.rs index da78de0..182fcb6 100644 --- a/boards/hail-bootloader/build.rs +++ b/boards/hail-bootloader/build.rs @@ -6,7 +6,7 @@ fn main() { println!("cargo:rerun-if-changed=../kernel_layout.ld"); let mut f = bootloader_attributes::get_file(); - bootloader_attributes::write_flags(&mut f, 512, "1.0.0"); + bootloader_attributes::write_flags(&mut f, 512, "1.0.1"); bootloader_attributes::write_attribute(&mut f, "board", "hail"); bootloader_attributes::write_attribute(&mut f, "arch", "cortex-m4"); bootloader_attributes::write_attribute(&mut f, "jldevice", "ATSAM4LC8C"); diff --git a/boards/hail-bootloader/layout.ld b/boards/hail-bootloader/layout.ld index b617a76..3e2a44b 100644 --- a/boards/hail-bootloader/layout.ld +++ b/boards/hail-bootloader/layout.ld @@ -1,9 +1,8 @@ /* Memory Spaces Definitions, 448K flash, 64K ram */ -ROM_ORIGIN = 0x00000000; -ROM_LENGTH = 0x00010000; -RAM_ORIGIN = 0x20000000; -RAM_LENGTH = 0x00020000; - -MPU_MIN_ALIGN = 8K; +MEMORY +{ + rom (rx) : ORIGIN = 0x00000000, LENGTH = 0x00010000 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00020000 +} INCLUDE ../kernel_layout.ld \ No newline at end of file diff --git a/boards/hail-bootloader/src/main.rs b/boards/hail-bootloader/src/main.rs index adaad6d..38710df 100644 --- a/boards/hail-bootloader/src/main.rs +++ b/boards/hail-bootloader/src/main.rs @@ -2,25 +2,31 @@ #![no_std] #![no_main] -#![feature(asm, const_fn, lang_items)] +#![feature(asm, const_fn, lang_items, panic_implementation)] extern crate bootloader; extern crate cortexm4; -#[macro_use(static_init)] +#[macro_use(create_capability, static_init)] extern crate kernel; +extern crate capsules; extern crate sam4l; +use core::panic::PanicInfo; + +use kernel::capabilities; use kernel::hil; use kernel::hil::Controller; use kernel::Platform; -use core::fmt::*; -use core::str; - include!(concat!(env!("OUT_DIR"), "/attributes.rs")); // No processes are supported. -static mut PROCESSES: [Option<&'static mut kernel::Process<'static>>; 0] = []; +static mut PROCESSES: [Option<&'static kernel::procs::ProcessType>; 0] = []; + +/// 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]; struct HailBootloader { bootloader: &'static bootloader::bootloader::Bootloader< @@ -29,7 +35,6 @@ struct HailBootloader { sam4l::flashcalw::FLASHCALW, sam4l::gpio::GPIOPin, >, - ipc: kernel::ipc::IPC, } impl Platform for HailBootloader { @@ -111,6 +116,12 @@ pub unsafe fn reset_handler() { set_pin_primary_functions(); + // Create main kernel object. This contains the main loop function. + let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&PROCESSES)); + + // Initialize USART0 for Uart + sam4l::usart::USART0.set_mode(sam4l::usart::UsartMode::Uart); + pub static mut PAGEBUFFER: sam4l::flashcalw::Sam4lPage = sam4l::flashcalw::Sam4lPage::new(); sam4l::flashcalw::FLASH_CONTROLLER.configure(); @@ -134,18 +145,20 @@ pub unsafe fn reset_handler() { let hail = HailBootloader { bootloader: bootloader, - ipc: kernel::ipc::IPC::new(), }; - let mut chip = sam4l::chip::Sam4l::new(); + let chip = static_init!(sam4l::chip::Sam4l, sam4l::chip::Sam4l::new()); hail.bootloader.initialize(); - kernel::main(&hail, &mut chip, &mut PROCESSES, &hail.ipc); + let main_loop_capability = create_capability!(capabilities::MainLoopCapability); + board_kernel.kernel_loop(&hail, chip, None, &main_loop_capability); } /// Panic handler. #[cfg(not(test))] #[no_mangle] -#[lang = "panic_fmt"] -pub unsafe extern "C" fn panic_fmt(_args: Arguments, _file: &'static str, _line: u32) {} +#[panic_implementation] +pub unsafe extern "C" fn panic_fmt(_pi: &PanicInfo) -> ! { + loop {} +} diff --git a/boards/kernel_layout.ld b/boards/kernel_layout.ld index 695ccb3..c8a9074 100644 --- a/boards/kernel_layout.ld +++ b/boards/kernel_layout.ld @@ -24,26 +24,34 @@ */ -/* - * CCFG - Customer Configuration - * - * Chip specific to TI family chips. It is a configuration area - * needed to validate the image - otherwise it is not possible to boot. - */ -CCFG_ORIGIN = DEFINED(CCFG_ORIGIN) ? CCFG_ORIGIN : 0; -CCFG_LENGTH = DEFINED(CCFG_LENGTH) ? CCFG_LENGTH : 0; - -MEMORY +SECTIONS { - rom (rx) : ORIGIN = ROM_ORIGIN, LENGTH = ROM_LENGTH - ccfg (rx) : ORIGIN = CCFG_ORIGIN, LENGTH = CCFG_LENGTH - ram (rwx) : ORIGIN = RAM_ORIGIN, LENGTH = RAM_LENGTH -} + .stack (NOLOAD) : + { + /* Kernel stack. + * + * Tock places the kernel stack at the bottom of SRAM so that the + * kernel will trigger memory fault if it exceeds its stack depth, + * rather than silently overwriting valuable data. + */ + . = ALIGN(8); + _sstack = .; -__stack_size__ = DEFINED(__stack_size__) ? __stack_size__ : 0x1000; + /* For GNU LD, we can just advance the location pointer (".") here to + * reserve space for the stack. That, however, doesn't seem to work + * for LLVM LLD. The resulting ELF has a stack section that shows the + * correct size, but the next section (in our case .relocate) is not + * moved down as well, instead it sits at the same address as .stack. + * To work around this, we declare a dummy buffer and then insert it + * here in the .stack section. This sets the stack size correctly and + * places the .relocate section at the correct address. */ + KEEP(*(.stack_buffer)) + /*. = . + 0x1000;*/ /*This is the original method. */ + + . = ALIGN(8); + _estack = .; + } > ram -SECTIONS -{ /* STATIC ELEMENTS FOR TOCK KERNEL */ .vector_table : { @@ -74,7 +82,9 @@ SECTIONS /* Remaining attributes */ KEEP(*(.attributes .attribute.*)) FILL(0x0) - . = _attribute_table_start + 1536; + /* This end address needs to be large enough that attributes are + on a different page than the start of .text. */ + . = _attribute_table_start + 3072; } > rom .text : @@ -187,23 +197,6 @@ SECTIONS KEEP(*(.ccfg)) } > ccfg - .stack (NOLOAD) : - { - /* Kernel stack. - * - * Tock places the kernel stack at the bottom of SRAM so that the - * kernel will trigger memory fault if it exceeds its stack depth, - * rather than silently overwriting valuable data. - */ - . = ALIGN(8); - _sstack = .; - - . = . + __stack_size__; - - . = ALIGN(8); - _estack = .; - } > ram - /* Kernel data that must be relocated. This is program data that is diff --git a/boards/nrf52-bootloader/Cargo.lock b/boards/nrf52-bootloader/Cargo.lock new file mode 100644 index 0000000..f9bde49 --- /dev/null +++ b/boards/nrf52-bootloader/Cargo.lock @@ -0,0 +1,118 @@ +[[package]] +name = "bootloader" +version = "0.1.0" +dependencies = [ + "kernel 0.1.0 (git+https://github.com/tock/tock)", + "tockloader-proto 0.2.1 (git+https://github.com/tock/tockloader-proto-rs)", +] + +[[package]] +name = "bootloader_attributes" +version = "0.1.0" + +[[package]] +name = "byteorder" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "capsules" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" +dependencies = [ + "enum_primitive 0.1.0 (git+https://github.com/tock/tock)", + "kernel 0.1.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "cortexm" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" +dependencies = [ + "kernel 0.1.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "cortexm4" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" +dependencies = [ + "cortexm 0.1.0 (git+https://github.com/tock/tock)", + "kernel 0.1.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "enum_primitive" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" + +[[package]] +name = "kernel" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" +dependencies = [ + "tock-cells 0.1.0 (git+https://github.com/tock/tock)", + "tock-registers 0.2.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "nrf52" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" +dependencies = [ + "cortexm4 0.1.0 (git+https://github.com/tock/tock)", + "kernel 0.1.0 (git+https://github.com/tock/tock)", + "nrf5x 0.1.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "nrf52bootloader" +version = "0.1.0" +dependencies = [ + "bootloader 0.1.0", + "bootloader_attributes 0.1.0", + "capsules 0.1.0 (git+https://github.com/tock/tock)", + "cortexm4 0.1.0 (git+https://github.com/tock/tock)", + "kernel 0.1.0 (git+https://github.com/tock/tock)", + "nrf52 0.1.0 (git+https://github.com/tock/tock)", + "nrf5x 0.1.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "nrf5x" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" +dependencies = [ + "kernel 0.1.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "tock-cells" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" + +[[package]] +name = "tock-registers" +version = "0.2.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" + +[[package]] +name = "tockloader-proto" +version = "0.2.1" +source = "git+https://github.com/tock/tockloader-proto-rs#945aedf6efb77221bc014ed590c56ff934ccdcac" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" +"checksum capsules 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum cortexm 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum cortexm4 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum enum_primitive 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum kernel 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum nrf52 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum nrf5x 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum tock-cells 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum tock-registers 0.2.0 (git+https://github.com/tock/tock)" = "" +"checksum tockloader-proto 0.2.1 (git+https://github.com/tock/tockloader-proto-rs)" = "" diff --git a/boards/nrf52-bootloader/Cargo.toml b/boards/nrf52-bootloader/Cargo.toml new file mode 100644 index 0000000..41b2341 --- /dev/null +++ b/boards/nrf52-bootloader/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "nrf52bootloader" +version = "0.1.0" +authors = ["Tock Project Developers "] +build = "build.rs" + +[profile.dev] +panic = "abort" +lto = false +opt-level = "z" +debug = true + +[profile.release] +panic = "abort" +lto = true +opt-level = "z" +debug = true + +[dependencies] +cortexm4 = { git = "https://github.com/tock/tock" } +capsules = { git = "https://github.com/tock/tock" } +kernel = { git = "https://github.com/tock/tock" } +nrf52 = { git = "https://github.com/tock/tock" } +nrf5x = { git = "https://github.com/tock/tock" } +bootloader = { path = "../../bootloader" } + +[build-dependencies] +bootloader_attributes = { path = "../../tools/bootloader_attributes" } diff --git a/boards/nrf52-bootloader/Makefile b/boards/nrf52-bootloader/Makefile new file mode 100644 index 0000000..f5bd19a --- /dev/null +++ b/boards/nrf52-bootloader/Makefile @@ -0,0 +1,24 @@ +# Makefile for building the tock kernel for the nRF development kit + +TOCK_ARCH=cortex-m4 +TARGET=thumbv7em-none-eabi +PLATFORM=nrf52bootloader + +include ../Common.mk + +TOCKLOADER=tockloader + +# Where in the SAM4L flash to load the kernel with `tockloader` +KERNEL_ADDRESS=0x00000 + +# Upload programs over uart with tockloader +ifdef PORT + TOCKLOADER_GENERAL_FLAGS += --port $(PORT) +endif + +TOCKLOADER_JTAG_FLAGS = --jlink --board nrf52dk + +# Upload the kernel over JTAG +.PHONY: flash +flash: target/$(TARGET)/release/$(PLATFORM).bin + $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< diff --git a/boards/nrf52-bootloader/README.md b/boards/nrf52-bootloader/README.md new file mode 100644 index 0000000..29c920d --- /dev/null +++ b/boards/nrf52-bootloader/README.md @@ -0,0 +1,14 @@ +Platform-Specific Instructions: nRF52840-DK +=================================== + +The [nRF52840 Development +Kit](https://www.nordicsemi.com/eng/Products/nRF52840-DK) is a platform +based around the nRF52840, an SoC with an ARM Cortex-M4 and a BLE +radio. The kit is Arduino shield compatible and includes several +buttons. + +This is the Tock bootloader for this platform. + +``` +make flash +``` diff --git a/boards/nrf52-bootloader/build.rs b/boards/nrf52-bootloader/build.rs new file mode 100644 index 0000000..3281ff1 --- /dev/null +++ b/boards/nrf52-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, 512, "1.0.1"); + bootloader_attributes::write_attribute(&mut f, "board", "nrf52dk"); + bootloader_attributes::write_attribute(&mut f, "arch", "cortex-m4"); + bootloader_attributes::write_attribute(&mut f, "jldevice", "nrf52"); +} diff --git a/boards/nrf52-bootloader/layout.ld b/boards/nrf52-bootloader/layout.ld new file mode 100644 index 0000000..c901c0b --- /dev/null +++ b/boards/nrf52-bootloader/layout.ld @@ -0,0 +1,8 @@ +MEMORY +{ + rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K + prog (rx) : ORIGIN = 0x00030000, LENGTH = 832K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K +} + +INCLUDE ../kernel_layout.ld diff --git a/boards/nrf52-bootloader/src/main.rs b/boards/nrf52-bootloader/src/main.rs new file mode 100644 index 0000000..eb6270d --- /dev/null +++ b/boards/nrf52-bootloader/src/main.rs @@ -0,0 +1,213 @@ +//! Tock bootloader for the nRF52840dk. + +#![no_std] +#![no_main] +#![feature(panic_implementation)] + +extern crate capsules; +#[allow(unused_imports)] +#[macro_use( + create_capability, + debug, + debug_verbose, + debug_gpio, + static_init +)] +extern crate kernel; +extern crate bootloader; +extern crate cortexm4; +extern crate nrf52; +extern crate nrf5x; + +use core::panic::PanicInfo; + +use capsules::virtual_alarm::VirtualMuxAlarm; +use kernel::capabilities; +use kernel::hil; + +// // The nRF52840DK LEDs (see back of board) +// const LED1_PIN: usize = 13; +// const LED2_PIN: usize = 14; +// const LED3_PIN: usize = 15; +// const LED4_PIN: usize = 16; + +// The nRF52840DK buttons (see back of board) +const BUTTON1_PIN: usize = 11; +// const BUTTON2_PIN: usize = 12; +// const BUTTON3_PIN: usize = 24; +// const BUTTON4_PIN: usize = 25; +const BUTTON_RST_PIN: usize = 18; + +const UART_RTS: usize = 5; +const UART_TXD: usize = 6; +const UART_CTS: usize = 7; +const UART_RXD: usize = 8; + +include!(concat!(env!("OUT_DIR"), "/attributes.rs")); + +static mut PROCESSES: [Option<&'static kernel::procs::ProcessType>; 0] = []; + +/// 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]; + +/// Supported drivers by the platform +pub struct Nrf52Bootloader { + bootloader: &'static bootloader::bootloader::Bootloader< + 'static, + bootloader::uart_receive_timeout::UartReceiveTimeout< + 'static, + VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>, + >, + nrf52::nvmc::Nvmc, + nrf5x::gpio::GPIOPin, + >, +} + +impl kernel::Platform for Nrf52Bootloader { + fn with_driver(&self, driver_num: usize, f: F) -> R + where + F: FnOnce(Option<&kernel::Driver>) -> R, + { + match driver_num { + _ => f(None), + } + } +} + +/// Entry point in the vector table called on hard reset. +#[no_mangle] +pub unsafe fn reset_handler() { + // Loads relocations and clears BSS + nrf52::init(); + + // Make non-volatile memory writable and activate the reset button + let uicr = nrf52::uicr::Uicr::new(); + nrf52::nvmc::NVMC.erase_uicr(); + nrf52::nvmc::NVMC.configure_writeable(); + while !nrf52::nvmc::NVMC.is_ready() {} + uicr.set_psel0_reset_pin(BUTTON_RST_PIN); + while !nrf52::nvmc::NVMC.is_ready() {} + uicr.set_psel1_reset_pin(BUTTON_RST_PIN); + + // Create capabilities that the board needs to call certain protected kernel + // functions. + let main_loop_capability = create_capability!(capabilities::MainLoopCapability); + + // kernel::debug::assign_gpios( + // Some(&nrf5x::gpio::PORT[LED1_PIN]), + // None, + // None, + // ); + + // let led_pins = static_init!( + // [(&'static nrf5x::gpio::GPIOPin, capsules::led::ActivationMode); 1], + // [ + // ( + // &nrf5x::gpio::PORT[LED1_PIN], + // capsules::led::ActivationMode::ActiveLow + // ), + // ] + // ); + + // // LEDs + // let led = static_init!( + // capsules::led::LED<'static, nrf5x::gpio::GPIOPin>, + // capsules::led::LED::new(led_pins) + // ); + + // Create main kernel object. This contains the main loop function. + let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&PROCESSES)); + + // Setup the timer infrastructure for faking uart receive with a timeout. + let rtc = &nrf5x::rtc::RTC; + rtc.start(); + let mux_alarm = static_init!( + capsules::virtual_alarm::MuxAlarm<'static, nrf5x::rtc::Rtc>, + capsules::virtual_alarm::MuxAlarm::new(&nrf5x::rtc::RTC) + ); + rtc.set_client(mux_alarm); + + // Setup receive with timeout. + let recv_auto_virtual_alarm = static_init!( + VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>, + VirtualMuxAlarm::new(mux_alarm) + ); + + let recv_auto_uart = static_init!( + bootloader::uart_receive_timeout::UartReceiveTimeout< + 'static, + VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>, + >, + bootloader::uart_receive_timeout::UartReceiveTimeout::new( + &nrf52::uart::UARTE0, + recv_auto_virtual_alarm, + &nrf5x::gpio::PORT[UART_RXD] + ) + ); + recv_auto_virtual_alarm.set_client(recv_auto_uart); + nrf5x::gpio::PORT[UART_RXD].set_client(recv_auto_uart); + recv_auto_uart.initialize(); + + // Setup the UART pins + nrf52::uart::UARTE0.initialize( + nrf5x::pinmux::Pinmux::new(UART_TXD as u32), + nrf5x::pinmux::Pinmux::new(UART_RXD as u32), + nrf5x::pinmux::Pinmux::new(UART_CTS as u32), + nrf5x::pinmux::Pinmux::new(UART_RTS as u32), + ); + + // Create the bootloader object. + static mut PAGEBUFFER: nrf52::nvmc::NrfPage = nrf52::nvmc::NrfPage::new(); + let bootloader = static_init!( + bootloader::bootloader::Bootloader< + 'static, + bootloader::uart_receive_timeout::UartReceiveTimeout< + 'static, + VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>, + >, + nrf52::nvmc::Nvmc, + nrf5x::gpio::GPIOPin, + >, + bootloader::bootloader::Bootloader::new( + recv_auto_uart, + &mut nrf52::nvmc::NVMC, + &nrf5x::gpio::PORT[BUTTON1_PIN], + &mut PAGEBUFFER, + &mut bootloader::bootloader::BUF + ) + ); + hil::uart::UART::set_client(&nrf52::uart::UARTE0, bootloader); + hil::flash::HasClient::set_client(&nrf52::nvmc::NVMC, bootloader); + + // Start all of the clocks. Low power operation will require a better + // approach than this. + nrf52::clock::CLOCK.low_stop(); + nrf52::clock::CLOCK.high_stop(); + + nrf52::clock::CLOCK.low_set_source(nrf52::clock::LowClockSource::XTAL); + nrf52::clock::CLOCK.low_start(); + nrf52::clock::CLOCK.high_set_source(nrf52::clock::HighClockSource::XTAL); + nrf52::clock::CLOCK.high_start(); + while !nrf52::clock::CLOCK.low_started() {} + while !nrf52::clock::CLOCK.high_started() {} + + let platform = Nrf52Bootloader { + bootloader: bootloader, + }; + + let chip = static_init!(nrf52::chip::NRF52, nrf52::chip::NRF52::new()); + + platform.bootloader.initialize(); + + board_kernel.kernel_loop(&platform, chip, None, &main_loop_capability); +} + +/// Panic handler. +#[cfg(not(test))] +#[no_mangle] +#[panic_implementation] +pub unsafe extern "C" fn panic_fmt(_pi: &PanicInfo) -> ! { + loop {} +} diff --git a/bootloader/Cargo.lock b/bootloader/Cargo.lock index 3d5ac6e..41cc843 100644 --- a/bootloader/Cargo.lock +++ b/bootloader/Cargo.lock @@ -3,28 +3,44 @@ name = "bootloader" version = "0.1.0" dependencies = [ "kernel 0.1.0 (git+https://github.com/tock/tock)", - "tockloader-proto 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tockloader-proto 0.2.1 (git+https://github.com/tock/tockloader-proto-rs)", ] [[package]] name = "byteorder" -version = "1.2.2" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "kernel" version = "0.1.0" -source = "git+https://github.com/tock/tock#871e1dbd1e247d1d5a97714a60014780c5844331" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" +dependencies = [ + "tock-cells 0.1.0 (git+https://github.com/tock/tock)", + "tock-registers 0.2.0 (git+https://github.com/tock/tock)", +] + +[[package]] +name = "tock-cells" +version = "0.1.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" + +[[package]] +name = "tock-registers" +version = "0.2.0" +source = "git+https://github.com/tock/tock#616cb87ef9ea1829fd7ce2d222a218371090e8fc" [[package]] name = "tockloader-proto" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/tock/tockloader-proto-rs#ae1ce682df49e1670b389691265fecefd7732a29" dependencies = [ - "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] -"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum kernel 0.1.0 (git+https://github.com/tock/tock)" = "" -"checksum tockloader-proto 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8997ca9de041a35f8322bb9f5e72785e7bf2413198f17a475ab9a90c3cc102d8" +"checksum tock-cells 0.1.0 (git+https://github.com/tock/tock)" = "" +"checksum tock-registers 0.2.0 (git+https://github.com/tock/tock)" = "" +"checksum tockloader-proto 0.2.1 (git+https://github.com/tock/tockloader-proto-rs)" = "" diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml index a77b32a..8227155 100644 --- a/bootloader/Cargo.toml +++ b/bootloader/Cargo.toml @@ -5,4 +5,4 @@ authors = ["Tock Project Developers "] [dependencies] kernel = { git = "https://github.com/tock/tock" } -tockloader-proto = "0.2.1" +tockloader-proto = { git = "https://github.com/tock/tockloader-proto-rs" } diff --git a/bootloader/src/bootloader.rs b/bootloader/src/bootloader.rs index 9380a02..87d8b80 100644 --- a/bootloader/src/bootloader.rs +++ b/bootloader/src/bootloader.rs @@ -2,7 +2,8 @@ use core::cell::Cell; use core::cmp; -use kernel::common::take_cell::TakeCell; + +use kernel::common::cells::TakeCell; use kernel::hil; use bootloader_crc; @@ -10,12 +11,16 @@ use bootloader_crc; extern crate tockloader_proto; // Main buffer that commands are received into and sent from. -pub static mut BUF: [u8; 600] = [0; 600]; +// Need a buffer big enough for 4096 byte pages on the nRF52. +pub static mut BUF: [u8; 4224] = [0; 4224]; // How long to wait, in bit periods, after receiving a byte for the next // byte before timing out and calling `receive_complete`. const UART_RECEIVE_TIMEOUT: u8 = 100; +const FLAGS_ADDRESS: usize = 0x400; +const FIRST_ATTRIBUTE_ADDRESS: usize = 0x600; + // Bootloader constants const ESCAPE_CHAR: u8 = 0xFC; @@ -55,7 +60,7 @@ enum State { pub struct Bootloader< 'a, - U: hil::uart::UARTAdvanced + 'a, + U: hil::uart::UARTReceiveAdvanced + 'a, F: hil::flash::Flash + 'static, G: hil::gpio::Pin + 'a, > { @@ -67,8 +72,12 @@ pub struct Bootloader< state: Cell, } -impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpio::Pin + 'a> - Bootloader<'a, U, F, G> +impl< + 'a, + U: hil::uart::UARTReceiveAdvanced + 'a, + F: hil::flash::Flash + 'a, + G: hil::gpio::Pin + 'a, + > Bootloader<'a, U, F, G> { pub fn new( uart: &'a U, @@ -89,7 +98,7 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi pub fn initialize(&self) { // Setup UART and start listening. - self.uart.init(hil::uart::UARTParams { + self.uart.configure(hil::uart::UARTParameters { baud_rate: 115200, stop_bits: hil::uart::StopBits::One, parity: hil::uart::Parity::None, @@ -147,8 +156,12 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi } } -impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpio::Pin + 'a> - hil::uart::Client for Bootloader<'a, U, F, G> +impl< + 'a, + U: hil::uart::UARTReceiveAdvanced + 'a, + F: hil::flash::Flash + 'a, + G: hil::gpio::Pin + 'a, + > hil::uart::Client for Bootloader<'a, U, F, G> { fn transmit_complete(&self, buffer: &'static mut [u8], error: hil::uart::Error) { if error != hil::uart::Error::CommandComplete { @@ -185,11 +198,12 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi fn receive_complete(&self, buffer: &'static mut [u8], rx_len: usize, error: hil::uart::Error) { if error != hil::uart::Error::CommandComplete { - // self.led.clear(); return; } // Tool to parse incoming bootloader messages. + // This is currently allocated on the stack, but it too needs a big + // buffer, and we need to do something about that. let mut decoder = tockloader_proto::CommandDecoder::new(); // Whether we want to reset the position in the buffer in the // decoder. @@ -207,7 +221,6 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi need_reset = false; } - // match decoder.receive(buffer[i]) { match decoder.receive(buffer[i]) { Ok(None) => {} Ok(Some(tockloader_proto::Command::Ping)) => { @@ -217,7 +230,6 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi } Ok(Some(tockloader_proto::Command::Reset)) => { need_reset = true; - // If there are more bytes in the buffer we want to continue // parsing those. Otherwise, we want to go back to receive. if i == rx_len - 1 { @@ -229,7 +241,11 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi self.state.set(State::Info); self.buffer.replace(buffer); self.page_buffer.take().map(move |page| { - self.flash.read_page(2, page); + // Calculate the page index given that flags start + // at address 1024. + let page_index = FLAGS_ADDRESS / page.as_mut().len(); + + self.flash.read_page(page_index, page); }); break; } @@ -294,7 +310,14 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi self.state.set(State::GetAttribute { index: index }); self.buffer.replace(buffer); self.page_buffer.take().map(move |page| { - self.flash.read_page(3 + (index as usize / 8), page); + // Need to calculate which page to read to get the + // correct attribute (each attribute is 64 bytes long), + // where attributes start at address 0x600. + let page_len = page.as_mut().len(); + let read_address = FIRST_ATTRIBUTE_ADDRESS + (index as usize * 64); + let page_index = read_address / page_len; + + self.flash.read_page(page_index, page); }); break; } @@ -320,15 +343,29 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi // Initiate things by reading the correct flash page that // needs to be updated. self.page_buffer.take().map(move |page| { - self.flash.read_page(3 + (index as usize / 8), page); + // Need to calculate which page to read to get the + // correct attribute (each attribute is 64 bytes long), + // where attributes start at address 0x600. + let page_len = page.as_mut().len(); + let read_address = FIRST_ATTRIBUTE_ADDRESS + (index as usize * 64); + let page_index = read_address / page_len; + + self.flash.read_page(page_index, page); }); break; } Ok(Some(_)) => { + self.buffer.replace(buffer); self.send_response(RES_UNKNOWN); break; } + Err(tockloader_proto::Error::BadArguments) => { + self.buffer.replace(buffer); + self.send_response(RES_BADARGS); + break; + } Err(_) => { + self.buffer.replace(buffer); self.send_response(RES_INTERNAL_ERROR); break; } @@ -344,8 +381,12 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi } } -impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpio::Pin + 'a> - hil::flash::Client for Bootloader<'a, U, F, G> +impl< + 'a, + U: hil::uart::UARTReceiveAdvanced + 'a, + F: hil::flash::Flash + 'a, + G: hil::gpio::Pin + 'a, + > hil::flash::Client for Bootloader<'a, U, F, G> { fn read_complete(&self, pagebuffer: &'static mut F::Page, _error: hil::flash::Error) { match self.state.get() { @@ -365,10 +406,13 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi index += 1; } + // Calculate where in the page the flags start. + let page_offset = FLAGS_ADDRESS % pagebuffer.as_mut().len(); + // Version string is at most 8 bytes long, and starts // at index 14 in the bootloader page. for i in 0..8 { - let b = pagebuffer.as_mut()[i + 14]; + let b = pagebuffer.as_mut()[i + 14 + page_offset]; if b == 0 { break; } @@ -406,8 +450,16 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi buffer[0] = ESCAPE_CHAR; buffer[1] = RES_GET_ATTR; let mut j = 2; + + // Need to calculate where in the page to look for this + // attribute with attributes starting at address 0x600 and + // where each has length of 64 bytes. + let page_len = pagebuffer.as_mut().len(); + let read_address = FIRST_ATTRIBUTE_ADDRESS + (index as usize * 64); + let page_offset = read_address % page_len; + for i in 0..64 { - let b = pagebuffer.as_mut()[(((index as usize) % 8) * 64) + i]; + let b = pagebuffer.as_mut()[page_offset + i]; if b == ESCAPE_CHAR { // Need to escape the escape character. buffer[j] = ESCAPE_CHAR; @@ -426,13 +478,17 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi // and then write that all back to flash. State::SetAttribute { index } => { self.buffer.map(move |buffer| { + let page_len = pagebuffer.as_mut().len(); + let read_address = FIRST_ATTRIBUTE_ADDRESS + (index as usize * 64); + let page_offset = read_address % page_len; + let page_index = read_address / page_len; + // Copy the first 64 bytes of the buffer into the correct // spot in the page. - let start_index = ((index as usize) % 8) * 64; for i in 0..64 { - pagebuffer.as_mut()[start_index + i] = buffer[i]; + pagebuffer.as_mut()[page_offset + i] = buffer[i]; } - self.flash.write_page(3 + (index as usize / 8), pagebuffer); + self.flash.write_page(page_index, pagebuffer); }); } @@ -567,7 +623,6 @@ impl<'a, U: hil::uart::UARTAdvanced + 'a, F: hil::flash::Flash + 'a, G: hil::gpi self.buffer.take().map(move |buffer| { buffer[0] = ESCAPE_CHAR; buffer[1] = RES_OK; - // buffer[1] = 0x99; self.uart.transmit(buffer, 2); }); } diff --git a/bootloader/src/lib.rs b/bootloader/src/lib.rs index ef3b4d4..c2413d0 100644 --- a/bootloader/src/lib.rs +++ b/bootloader/src/lib.rs @@ -1,10 +1,11 @@ -#![feature(const_fn, const_cell_new, asm)] +#![feature(const_fn, asm)] // #![forbid(unsafe_code)] #![no_std] #[allow(unused_imports)] -#[macro_use(debug)] +#[macro_use(debug, debug_gpio)] extern crate kernel; pub mod bootloader; pub mod bootloader_crc; +pub mod uart_receive_timeout; diff --git a/bootloader/src/uart_receive_timeout.rs b/bootloader/src/uart_receive_timeout.rs new file mode 100644 index 0000000..95d7c20 --- /dev/null +++ b/bootloader/src/uart_receive_timeout.rs @@ -0,0 +1,128 @@ +//! Convert the normal UART interface to one with a timeout. +//! +//! This capsule provides the `hil::uart::UART` interface, and for the most part +//! just passes it through to the underlying `uart` peripheral. However, it +//! also provides a DIY version of `receive_automatic()` for platforms where +//! the hardware does not provide it natively. +//! +//! It does this by using the UART RX pin as an interrupt source, and a timer +//! to wait for the end of received bytes. On each interrupt from the UART bytes +//! the timer is reset, and when the timer finally fires then `abort_receive()` +//! is called to stop the receive. +//! +//! This module doesn't do anything on the UART client side, so the client +//! of the underlying uart driver should be set to the upper layer. +//! +//! Usage +//! ----- +//! +//! ``` +//! let recv_auto_virtual_alarm = static_init!( +//! VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>, +//! VirtualMuxAlarm::new(mux_alarm) +//! ); +//! +//! let recv_auto_uart = static_init!( +//! bootloader::uart_receive_timeout::UartReceiveTimeout<'static, VirtualMuxAlarm<'static, nrf5x::rtc::Rtc>>, +//! bootloader::uart_receive_timeout::UartReceiveTimeout::new(&nrf52::uart::UARTE0, +//! recv_auto_virtual_alarm, +//! &nrf5x::gpio::PORT[UART_RXD] +//! ) +//! ); +//! recv_auto_virtual_alarm.set_client(recv_auto_uart); +//! nrf5x::gpio::PORT[UART_RXD].set_client(recv_auto_uart); +//! recv_auto_uart.initialize(); +//! ``` + +use kernel::hil; +use kernel::hil::time::Frequency; +use kernel::ReturnCode; + +pub struct UartReceiveTimeout<'a, A: hil::time::Alarm + 'a> { + uart: &'static hil::uart::UART, + alarm: &'a A, + rx_pin: &'a hil::gpio::Pin, +} + +impl<'a, A: hil::time::Alarm> UartReceiveTimeout<'a, A> { + pub fn new( + uart: &'static hil::uart::UART, + alarm: &'a A, + rx_pin: &'a hil::gpio::Pin, + ) -> UartReceiveTimeout<'a, A> { + UartReceiveTimeout { + uart: uart, + alarm: alarm, + rx_pin: rx_pin, + } + } + + /// Setup the GPIO interrupt to wait for the end of UART bytes. + pub fn initialize(&self) { + self.rx_pin.make_input(); + self.rx_pin + .enable_interrupt(0, hil::gpio::InterruptMode::FallingEdge); + } +} + +impl<'a, A: hil::time::Alarm> hil::uart::UART for UartReceiveTimeout<'a, A> { + fn set_client(&self, _client: &'static hil::uart::Client) {} + + fn configure(&self, params: hil::uart::UARTParameters) -> ReturnCode { + self.uart.configure(params) + } + + fn transmit(&self, tx_data: &'static mut [u8], tx_len: usize) { + self.uart.transmit(tx_data, tx_len); + } + + fn receive(&self, rx_buffer: &'static mut [u8], rx_len: usize) { + self.uart.receive(rx_buffer, rx_len); + } + + fn abort_receive(&self) { + self.uart.abort_receive(); + } +} + +impl<'a, A: hil::time::Alarm> hil::uart::UARTReceiveAdvanced for UartReceiveTimeout<'a, A> { + fn receive_automatic(&self, rx_buffer: &'static mut [u8], interbyte_timeout: u8) { + // Just call receive with the entire buffer. + let len = rx_buffer.len(); + self.uart.receive(rx_buffer, len); + } +} + +impl<'a, A: hil::time::Alarm> hil::gpio::Client for UartReceiveTimeout<'a, A> { + // This is called when the UART RX pin toggles. + // We start a new timer on every toggle to wait for the end of incoming + // RX bytes. + fn fired(&self, _: usize) { + let interval = (30 as u32) * ::frequency() / 1000; + let tics = self.alarm.now().wrapping_add(interval); + self.alarm.set_alarm(tics); + } +} + +impl<'a, A: hil::time::Alarm> hil::time::Client for UartReceiveTimeout<'a, A> { + /// If the timer actually fires then we stopped receiving bytes. + fn fired(&self) { + self.uart.abort_receive(); + } +} + +// Callbacks from the underlying UART driver. +impl<'a, A: hil::time::Alarm> hil::uart::Client for UartReceiveTimeout<'a, A> { + // Called when the UART TX has finished. + fn transmit_complete(&self, _buffer: &'static mut [u8], _error: hil::uart::Error) {} + + // Called when a buffer is received on the UART. + fn receive_complete( + &self, + _buffer: &'static mut [u8], + _rx_len: usize, + _error: hil::uart::Error, + ) { + + } +} diff --git a/rust-toolchain b/rust-toolchain index b7e7556..c156b5b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2018-04-19 +nightly-2018-08-16 diff --git a/tools/install_cargo_fmt.sh b/tools/install_cargo_fmt.sh deleted file mode 100755 index 42fec49..0000000 --- a/tools/install_cargo_fmt.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# Verify that we're running in the base directory -if [ ! -x tools/run_cargo_fmt.sh ]; then - echo ERROR: $0 must be run from the tock repository root. - echo "" - exit 1 -fi - -# Peg a rustfmt version while things are unstable -# -# Note: We install a local copy of rustfmt so as not to interfere with any -# other use of rustfmt on the machine -RUSTFMT_VERSION=0.6.0 - -# For CI, want to install to a cached travis directory -if [[ "$CI" == "true" ]]; then - LOCAL_CARGO=$HOME/local_cargo -else - LOCAL_CARGO=$(pwd)/tools/local_cargo -fi - -mkdir -p $LOCAL_CARGO - -# Check if we actually need to do anything -needs_install=false -if [[ ! -x $LOCAL_CARGO/bin/rustfmt ]]; then - needs_install=true -elif [[ $($LOCAL_CARGO/bin/rustfmt --version | perl -pe '($_)=/([0-9]+([.][0-9]+)+)/') != "$RUSTFMT_VERSION" ]]; then - needs_install=true -fi - -if $needs_install; then - echo "INFO: rustfmt v$RUSTFMT_VERSION not installed. Installing." - echo "(This will take a few minutes)" - echo "" - - pushd $LOCAL_CARGO - mkdir -p .cargo - cat > .cargo/config < /dev/null - cargo-fmt -- --write-mode=check || let FAIL=FAIL+1 - popd > /dev/null - done - exit $FAIL -else - for f in $(find . | grep Cargo.toml); do - pushd $(dirname $f) > /dev/null - cargo-fmt -- --write-mode=overwrite || let FAIL=FAIL+1 - popd > /dev/null - done + CARGO_FMT_ARGS="-- --check" +fi +for f in $(find . | grep Cargo.toml); do + pushd $(dirname $f) > /dev/null + cargo-fmt $CARGO_FMT_ARGS || let FAIL=FAIL+1 + popd > /dev/null +done - if [[ $FAIL -ne 0 ]]; then +# rustfmt doesn't have an option for this, so do it manually +# Find folders with Cargo.toml files in them and check them (avoids matching this script!) +for f in $(find . | grep Cargo.toml); do + pushd $(dirname $f) > /dev/null + if $(git grep -q 'use .*\*;'); then echo - echo "$(tput bold)Error running rustfmt.$(tput sgr0)" - echo "See above for details" + echo "$(tput bold)Wildcard import(s) found in $(dirname $f).$(tput sgr0)" + echo "Tock style rules prohibit this use of wildcard imports." + echo + echo "The following wildcard imports were found:" + git grep 'use .*\*;' + let FAIL=FAIL+1 fi - exit $FAIL + popd > /dev/null +done + +if [[ $FAIL -ne 0 ]]; then + echo + echo "$(tput bold)Formatting errors.$(tput sgr0)" + echo "See above for details" fi +exit $FAIL