diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..80fc334 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[target.thumbv7m-none-eabi] +# used to run the qemu_test.rs example with QEMU +runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" +rustflags = ["-C", "link-arg=-Tlink.x"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69a3600..c7a0f5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,21 @@ jobs: - if: ${{ matrix.toolchain == 'nightly' }} run: cargo check --target=${{ matrix.target }} --examples --all-features + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + targets: thumbv7m-none-eabi + toolchain: nightly + - name: Install QEMU + run: | + sudo apt update + sudo apt install qemu-system-arm + - run: qemu-system-arm --version + - run: cargo run --target thumbv7m-none-eabi --example integration_test --all-features + clippy: name: Clippy runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index eeae610..aa8ebee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,5 @@ version = "0.10.5" [dev-dependencies] cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" +cortex-m-semihosting = "0.5" +panic-semihosting = { version = "0.6", features = ["exit"] } diff --git a/examples/integration_test.rs b/examples/integration_test.rs new file mode 100644 index 0000000..a45ba87 --- /dev/null +++ b/examples/integration_test.rs @@ -0,0 +1,88 @@ +//! This is a very basic smoke test that runs in QEMU +//! Reference the QEMU section of the [Embedded Rust Book] for more information +//! +//! This only tests integration of the allocator on an embedded target. +//! Comprehensive allocator tests are located in the allocator dependency. +//! +//! After toolchain installation this test can be run with: +//! +//! ```bash +//! cargo +nightly run --target thumbv7m-none-eabi --example integration_test --all-features +//! ``` +//! +//! [Embedded Rust Book]: https://docs.rust-embedded.org/book/intro/index.html + +#![feature(allocator_api)] +#![no_main] +#![no_std] + +extern crate alloc; +extern crate panic_semihosting; + +use alloc::vec::Vec; +use core::mem::{size_of, MaybeUninit}; +use cortex_m_rt::entry; +use cortex_m_semihosting::{debug, hprintln}; +use embedded_alloc::Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +fn test_global_heap() { + assert_eq!(HEAP.used(), 0); + + let mut xs: Vec = alloc::vec![1]; + xs.push(2); + xs.extend(&[3, 4]); + + // do not optimize xs + core::hint::black_box(&mut xs); + + assert_eq!(xs.as_slice(), &[1, 2, 3, 4]); + assert_eq!(HEAP.used(), size_of::() * xs.len()); +} + +fn test_allocator_api() { + // small local heap + const HEAP_SIZE: usize = 16; + let heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + let local_heap: Heap = Heap::empty(); + unsafe { local_heap.init(heap_mem.as_ptr() as usize, HEAP_SIZE) } + + assert_eq!(local_heap.used(), 0); + + let mut v: Vec = Vec::new_in(local_heap); + v.push(0xCAFE); + v.extend(&[0xDEAD, 0xFEED]); + + // do not optimize v + core::hint::black_box(&mut v); + + assert_eq!(v.as_slice(), &[0xCAFE, 0xDEAD, 0xFEED]); +} + +#[entry] +fn main() -> ! { + { + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + } + + #[allow(clippy::type_complexity)] + let tests: &[(fn() -> (), &'static str)] = &[ + (test_global_heap, "test_global_heap"), + (test_allocator_api, "test_allocator_api"), + ]; + + for (test_fn, test_name) in tests { + hprintln!("{}: start", test_name); + test_fn(); + hprintln!("{}: pass", test_name); + } + + // exit QEMU with a success status + debug::exit(debug::EXIT_SUCCESS); + #[allow(clippy::empty_loop)] + loop {} +} diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..367c5c8 --- /dev/null +++ b/memory.x @@ -0,0 +1,6 @@ +MEMORY +{ + /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K +}