Merge #189 189: Added nucleo r=jrvanwhy a=alexandruradovici This adds the memory layout for building applications for the Nucelo F429zi and F446re boards. Co-authored-by: Alexandru Radovici <msg4alex@gmail.com>
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..45ddcde --- /dev/null +++ b/.gitmodules
@@ -0,0 +1,3 @@ +[submodule "tock"] + path = tock + url = https://github.com/tock/tock.git
diff --git a/.travis.yml b/.travis.yml index 66cee0b..52e5d38 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -13,36 +13,8 @@ cache: cargo -# Once Travis supports a version of Ubuntu that inlcudes QEMU 5.1+ -# we can apt install QEMU for RISC-V. -# Until then we need to build it ourselves -before_install: - # - sudo apt-get -y install qemu-system-misc - # addons: - # apt: - # update: true - - git clone https://github.com/alistair23/qemu.git -b riscv-tock.next - - pushd qemu - - ./configure --target-list=riscv32-softmmu - - make -j8 - - sudo ln -s $PWD/riscv32-softmmu/qemu-system-riscv32 /usr/bin/ - - popd - install: - - make setup - # Build Tock, it needs to be outside of the libtock-rs source - - pushd ../ - - git clone https://github.com/tock/tock.git - - cd tock/boards/hifive1 - # Use a known working version of Tock - - git checkout 152189fe077aea955332ee3c28ccbee519d8b072 - - make - - popd + - make -j8 setup script: - make test - # Run a QEMU instance of the HiFive1 app - - PLATFORM=hifive1 cargo rrv32imac --example libtock_test --features=alloc --features=__internal_disable_gpio_in_integration_test - - pushd test-runner - - cargo run - - popd
diff --git a/Cargo.toml b/Cargo.toml index ee68117..b8e0bec 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -58,8 +58,10 @@ debug = true [workspace] +exclude = [ "tock" ] members = [ "codegen", "core", - "test-runner" + "test-runner", + "tools/print-sizes", ]
diff --git a/Makefile b/Makefile index 92df884..36921bb 100644 --- a/Makefile +++ b/Makefile
@@ -16,6 +16,7 @@ @echo " - opentitan" @echo " - hifive1" @echo " - nrf52" + @echo " - apollo3" @echo @echo "Run 'make setup' to setup Rust to build libtock-rs." @echo "Run 'make <board>' to build libtock-rs for that board" @@ -23,6 +24,7 @@ @echo " Set the FEATURES flag to enable features" @echo "Run 'make flash-<board> EXAMPLE=<>' to flash EXAMPLE to that board" @echo "Run 'make test' to test any local changes you have made" + @echo "Run 'make print-sizes' to print size data for the example binaries" ifdef FEATURES features=--features=$(FEATURES) @@ -33,7 +35,7 @@ endif .PHONY: setup -setup: +setup: setup-qemu rustup target add thumbv7em-none-eabi rustup target add riscv32imac-unknown-none-elf rustup target add riscv32imc-unknown-none-elf @@ -42,20 +44,45 @@ cargo install elf2tab --version 0.4.0 cargo install stack-sizes +# Sets up QEMU in the tock/ directory. We use Tock's QEMU which may contain +# patches to better support boards that Tock supports. +.PHONY: setup-qemu +setup-qemu: + $(MAKE) -C tock emulation-setup + +# Builds a Tock kernel for the HiFive board for use by QEMU tests. +.PHONY: kernel-hifive +kernel-hifive: + $(MAKE) -C tock/boards/hifive1 \ + $(CURDIR)/tock/target/riscv32imac-unknown-none-elf/release/hifive1.elf + +# Prints out the sizes of the example binaries. +.PHONY: print-sizes +print-sizes: examples + cargo run --release -p print-sizes + +# Runs the libtock_test tests in QEMU on a simulated HiFive board. +.PHONY: test-qemu-hifive +test-qemu-hifive: kernel-hifive setup-qemu + PLATFORM=hifive1 cargo rrv32imac --example libtock_test --features=alloc \ + --features=__internal_disable_gpio_in_integration_test + cargo run -p test-runner + .PHONY: examples examples: - PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --examples + PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --examples -p libtock -p libtock-core PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --examples --features=alloc PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --example panic --features=custom_panic_handler,custom_alloc_error_handler PLATFORM=nrf52 cargo build --release --target=thumbv7em-none-eabi --example alloc_error --features=alloc,custom_alloc_error_handler - PLATFORM=opentitan cargo build --release --target=riscv32imc-unknown-none-elf --examples # Important: This is testing a platform without atomics support + # Important: This tests a platform without atomic instructions. + PLATFORM=opentitan cargo build --release --target=riscv32imc-unknown-none-elf --examples -p libtock -p libtock-core .PHONY: test -test: +test: examples test-qemu-hifive PLATFORM=nrf52 cargo fmt --all -- --check PLATFORM=nrf52 cargo clippy --workspace --all-targets PLATFORM=nrf52 cargo test --workspace - make examples + echo '[ SUCCESS ] libtock-rs tests pass' .PHONY: analyse-stack-sizes analyse-stack-sizes: @@ -127,5 +154,5 @@ .PHONY: clean clean: - rm -rf target - rm Cargo.lock + cargo clean + $(MAKE) -C tock clean
diff --git a/README.md b/README.md index a2bad2b..72506cd 100644 --- a/README.md +++ b/README.md
@@ -4,7 +4,11 @@ Rust userland library for Tock (WIP) -Tested with tock [Release 1.4.1](https://github.com/tock/tock/commit/7e37bf67761d83fd585cace4fb201e2864d300b1). +Generally this library was tested with tock [Release 1.5](https://github.com/tock/tock/releases/tag/release-1.5). +Since then changes have been made that might not work with the Tock +release 1.5, but instead target Tock master. For example this library +might support newer boards (Apollo3), changed boards (HiFive1 revB) or +new drivers (HMAC). The library works in principle on most boards, but there is currently the [showstopper bug #28](https://github.com/tock/libtock-rs/issues/28) that prevents @@ -29,7 +33,7 @@ 1. Clone the repository: ```shell - git clone https://github.com/tock/libtock-rs + git clone --recursive https://github.com/tock/libtock-rs cd libtock-rs ``` @@ -104,7 +108,7 @@ ## License -Licensed under either of +libtock-rs is licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) @@ -113,6 +117,8 @@ at your option. +Submodules have their own licenses. + ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted
diff --git a/boards/layout_nrf52.ld b/boards/layout_nrf52.ld index 419a0bc..40cdb10 100644 --- a/boards/layout_nrf52.ld +++ b/boards/layout_nrf52.ld
@@ -3,7 +3,7 @@ MEMORY { /* The application region is 64 bytes (0x40) */ FLASH (rx) : ORIGIN = 0x00030040, LENGTH = 0x0005FFC0 - SRAM (rwx) : ORIGIN = 0x20002000, LENGTH = 62K + SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 62K } /*
diff --git a/core/examples/empty_main.rs b/core/examples/empty_main.rs new file mode 100644 index 0000000..dd062bc --- /dev/null +++ b/core/examples/empty_main.rs
@@ -0,0 +1,14 @@ +// The most minimal libtock-core example possible. This file primarily exists +// for code size measurement, as this should create the smallest-possible +// libtock-core app. + +#![no_std] + +// If you don't *use* anything from libtock-core directly, cargo will not link +// it into the executable. However, we still need the runtime and lang items. +// Therefore a libtock-core app that doesn't directly mention anything in +// libtock-core needs to explicitly declare its dependency on libtock-core as +// follows. +extern crate libtock_core; + +fn main() {}
diff --git a/doc/Dependencies.md b/doc/Dependencies.md new file mode 100644 index 0000000..f157049 --- /dev/null +++ b/doc/Dependencies.md
@@ -0,0 +1,29 @@ +Third Party Dependencies +======================== + +This document is about dependencies that are required to build applications +using `libtock-rs`. These dependencies are not contained in the libtock-rs +repository, but are used by libtock-rs when libtock-rs is used as a dependency +of an application. Dependencies required to run `libtock-rs`' tests (such as +`make`) are outside the scope of this document. + +## Unaudited Required Dependencies + +`libtock-rs` has the following required build dependencies, none of which are +currently audited: + +* The Rust toolchain, including + [`cargo`](https://github.com/rust-lang/cargo), + [`rustc`](https://github.com/rust-lang/rust/tree/master/src/rustc), and + [`libcore`](https://github.com/rust-lang/rust/tree/master/src/libcore). The + specific toolchain version used is specified by the `rust-toolchain` file at + the root of the repository. +* [`syn`](https://crates.io/crates/syn), pulled in by `libtock_codegen`. +* [`quote`](https://crates.io/crates/quote), pulled in by `libtock_codegen`. +* [`proc-macro2`](https://crates.io/crates/proc-macro2), pulled in by + `libtock_codegen`. + +## Avoiding Optional Dependencies + +To avoid pulling in optional dependencies, users should use `libtock-core` +instead of `libtock`. `libtock-core` is in the `core/` directory.
diff --git a/examples-features/libtock_test.rs b/examples-features/libtock_test.rs index 5fe9e59..9a64e67 100644 --- a/examples-features/libtock_test.rs +++ b/examples-features/libtock_test.rs
@@ -1,5 +1,6 @@ // Libtock regression tests to be used with real hardware. -// Requires P0.03 and P0.04 to be connected (on a nRF52 DK). +// Requires P0.03 and P0.04 to be connected (on a nRF52 DK) and +// P0.01 and P0.03 to be connected (on a nRF52840dk). #![no_std] extern crate alloc;
diff --git a/test-runner/src/main.rs b/test-runner/src/main.rs index 99b941c..e297e8e 100644 --- a/test-runner/src/main.rs +++ b/test-runner/src/main.rs
@@ -14,14 +14,15 @@ async fn perform_tests() -> Result<(), Box<dyn std::error::Error>> { let mut failed_tests = Vec::new(); - let tests = Command::new("qemu-system-riscv32") + let tests = Command::new("tock/tools/qemu/riscv32-softmmu/qemu-system-riscv32") .arg("-M") .arg("sifive_e,revb=true") .arg("-kernel") - .arg("../../tock/target/riscv32imac-unknown-none-elf/release/hifive1") + .arg("tock/target/riscv32imac-unknown-none-elf/release/hifive1") .arg("-device") - .arg("loader,file=./../target/riscv32imac-unknown-none-elf/tab/hifive1/libtock_test/rv32imac.tbf,addr=0x20040000") + .arg("loader,file=target/riscv32imac-unknown-none-elf/tab/hifive1/libtock_test/rv32imac.tbf,addr=0x20040000") .arg("-nographic") + .stdin(Stdio::null()) .stdout(Stdio::piped()) .kill_on_drop(true) .spawn()?;
diff --git a/tock b/tock new file mode 160000 index 0000000..152189f --- /dev/null +++ b/tock
@@ -0,0 +1 @@ +Subproject commit 152189fe077aea955332ee3c28ccbee519d8b072
diff --git a/tools/flash.sh b/tools/flash.sh index c2e27a5..1a7d74c 100755 --- a/tools/flash.sh +++ b/tools/flash.sh
@@ -3,7 +3,7 @@ set -eux artifact="$(basename $1)" -rust_target_folder="$(readlink -f $(dirname $1)/../..)" +rust_target_folder="$(cd $(dirname $1)/../.. && pwd -P)" if [ -z $APP_HEAP_SIZE ]; then echo "Set APP_HEAP_SIZE to a value" exit 1
diff --git a/tools/print-sizes/Cargo.toml b/tools/print-sizes/Cargo.toml new file mode 100644 index 0000000..97bdc48 --- /dev/null +++ b/tools/print-sizes/Cargo.toml
@@ -0,0 +1,13 @@ +# Finds all the libtock-core and libtock-rs examples and prints the sizes of +# several of their sections. Searches the target/$ARCH/release directory. Note +# that print-sizes will not build the examples; that is done by the +# `print-sizes` Makefile action. + +[package] +authors = ["Tock Project Developers <tock-dev@googlegroups.com>"] +edition = "2018" +name = "print-sizes" +version = "0.1.0" + +[dependencies] +elf = "0.0.10"
diff --git a/tools/print-sizes/src/main.rs b/tools/print-sizes/src/main.rs new file mode 100644 index 0000000..dcd7777 --- /dev/null +++ b/tools/print-sizes/src/main.rs
@@ -0,0 +1,142 @@ +// Architectures that we expect the examples to be built for. +const ARCHITECTURES: [&str; 2] = ["riscv32imc-unknown-none-elf", "thumbv7em-none-eabi"]; + +// The order of these fields actually matters, because it affects the derived +// Ord impl. I have a suspicion that when I introduce size diffs into the CI, +// this order will make the eventual diffs easier to understand than other +// orderings. +#[derive(Eq, PartialEq, PartialOrd, Ord)] +struct Example { + name: String, + arch: &'static str, + path: std::path::PathBuf, +} + +// Finds the example binaries and returns a list of their paths. +fn find_examples() -> Vec<Example> { + // Find target/ using std::env::current_exe(). + let exe_dir = std::env::current_exe().expect("Unable to find executable location"); + let target_dir = exe_dir + .parent() + .expect("Unable to find target/ directory") + .parent() + .expect("Unable to find target/ directory"); + + let mut examples = Vec::new(); + + for arch in &ARCHITECTURES { + // Set examples_dir to target/$ARCH/examples/ + let mut examples_dir = target_dir.to_path_buf(); + examples_dir.push(arch); + examples_dir.push("release"); + examples_dir.push("examples"); + + // If the architecture's examples directory exists, iterate through the + // files through it and search for examples. If the directory doesn't + // exist we skip this architecture. + if let Ok(read_dir) = examples_dir.read_dir() { + for file in read_dir.filter_map(Result::ok) { + use std::os::unix::ffi::OsStrExt; + + // Skip entries that are not files. If file_type() returns + // Err(_) we skip the entry as well. + if !file.file_type().map_or(false, |t| t.is_file()) { + continue; + } + + // Skip files with dots (*.d files) and hyphens (-$HASH) in + // them. + if file.file_name().as_bytes().contains(&b'.') + || file.file_name().as_bytes().contains(&b'-') + { + continue; + } + + examples.push(Example { + name: file.file_name().to_string_lossy().into_owned(), + arch, + path: file.path(), + }); + } + } + } + + examples +} + +struct ElfSizes { + bss: u64, + data: u64, + rodata: u64, + text: u64, +} + +fn get_sizes(path: &std::path::Path) -> ElfSizes { + let file = elf::File::open_path(path).expect("Unable to open example binary"); + let mut sizes = ElfSizes { + bss: 0, + data: 0, + rodata: 0, + text: 0, + }; + for section in file.sections { + match section.shdr.name.as_ref() { + ".bss" => sizes.bss = section.shdr.size, + ".data" => sizes.data = section.shdr.size, + ".rodata" => sizes.rodata = section.shdr.size, + ".text" => sizes.text = section.shdr.size, + _ => {} + } + } + sizes +} + +struct ExampleData { + name: String, + arch: &'static str, + sizes: ElfSizes, +} + +fn main() { + let mut examples = find_examples(); + examples.sort_unstable(); + let example_data: Vec<_> = examples + .drain(..) + .map(|example| ExampleData { + name: example.name, + arch: example.arch, + sizes: get_sizes(&example.path), + }) + .collect(); + + let name_width = 20; + let arch_width = example_data + .iter() + .map(|a| a.arch.len()) + .max() + .expect("No examples found"); + let section_width = 7; + + // TODO: We do not currently print out .rodata's size. Currently, the linker + // script embeds .rodata in .text, so we don't see it as a separate section + // here. We should modify the linker script to put .rodata in its own + // section. Until that is done, .rodata's size will be counted as part of + // .text, so we'll just print .text's size for now. + println!( + "{0:1$} {2:3$} {4:>7$} {5:>7$} {6:>7$}", + "Example", name_width, "Architecture", arch_width, ".bss", ".data", ".text", section_width + ); + for data in example_data { + println!( + "{0:1$} {2:3$} {4:7$} {5:7$} {6:7$}", + data.name, + name_width, + data.arch, + arch_width, + data.sizes.bss, + data.sizes.data, + data.sizes.text, + section_width + ); + } +}