diff --git a/.travis.yml b/.travis.yml index f497d70..fde5b51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: trusty language: rust rust: - - beta + - nightly cache: directories: @@ -21,8 +21,8 @@ install: - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $TRAVIS_RUST_VERSION - source ~/.cargo/env - export QEMU_ARCHIVE=$HOME/qemueclipse.tgz - - export QEMU_URL=https://github.com/gnu-mcu-eclipse/qemu/releases/download/v2.8.0-6-20190517/gnu-mcu-eclipse-qemu-2.8.0-6-20190517-1329-centos64.tgz - - export QEMU_DIR=$HOME/gnu-mcu-eclipse/qemu/2.8.0-6-20190517-1329 + - export QEMU_URL=https://github.com/gnu-mcu-eclipse/qemu/releases/download/gae-2.7.0-20161128/gnuarmeclipse-qemu-debian64-2.7.0-201611282115-dev.tgz + - export QEMU_DIR=$HOME/qemu/2.7.0-201611282115-dev - if [ ! -e $QEMU_DIR/bin/qemu-system-gnuarmeclipse ]; then wget $QEMU_URL -O $QEMU_ARCHIVE && tar xzf $QEMU_ARCHIVE -C $HOME ; fi - export PATH=$PATH:$QEMU_DIR/bin:$HOME/.cargo/bin - rustup target add thumbv7m-none-eabi diff --git a/qemu_runner/src/builder.rs b/qemu_runner/src/builder.rs index 5d5a554..8a54342 100644 --- a/qemu_runner/src/builder.rs +++ b/qemu_runner/src/builder.rs @@ -6,6 +6,32 @@ use std::path::Path; use std::process::{Command, Stdio}; use std::ffi::OsString; +fn is_cargo(path: &str) -> bool { + let output = Command::new(path).arg("-V").output(); + if let Ok(output) = Command::new(path).arg("-V").output() { + let s = String::from_utf8_lossy(&output.stdout); + if s.contains("cargo") { + return true; + } + } + + false +} + +fn find_cargo_path() -> Option { + let mut p: Vec = vec!["cargo".to_string()]; + if let Some(home) = env::home_dir() { + p.push(format!("{}/.cargo/bin/cargo", home.display())); + } + + for path in p { + if is_cargo(&path) { + return Some(path.into()); + } + } + + None +} #[derive(Clone, Debug)] pub struct FoundFile { @@ -47,11 +73,16 @@ pub struct CrossbuildOptions { #[derive(Debug)] pub struct CrossbuiltTests { pub object_paths: Vec, - pub tests: Vec + pub tests: Vec, + pub library_path: String } pub fn crossbuild_rust_tests(options: &CrossbuildOptions) -> CrossbuiltTests { + // check if we can find cargo for cross building + let cargo_path = find_cargo_path(); + let cargo_path = cargo_path.expect("Cargo not found! Install Rust's package manager."); + let build_proj_root = { let p = Path::new(&options.tests_project_path); let mut absolute_path = ::std::env::current_dir().expect("Can't find current dir?"); @@ -59,92 +90,59 @@ pub fn crossbuild_rust_tests(options: &CrossbuildOptions) -> CrossbuiltTests { p.canonicalize().expect("Error canonicalizing") }; - // cross-build the tests library - let cargo_build = Command::new("cargo") - .current_dir(&options.tests_project_path) - .arg("build") - .arg("--verbose") - .arg("--target") - .arg(&options.target_arch) - .env("CARGO_INCREMENTAL", "") - .env("RUSTFLAGS", "--emit=obj") - .env("RUST_TARGET_PATH", &build_proj_root.to_str().expect("Missing path to proj root for target path?")) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output(); - - let output = cargo_build.expect("Cargo build of the tests projects failed"); - if !output.status.success() { - panic!("cargo build failed"); - } - // grab the list of tests to compile binaries for - let tests = { - // slightly hackish way that requires each test entrypoint to be in its - // own source file with a matching name - - let dir = format!("{}/src/", &options.tests_project_path); + let tests: Vec<_> = { + let dir = format!("{}/examples/", &options.tests_project_path); let tests = find_files(&dir, |n| { n.starts_with("test_") && n.ends_with(".rs") }).iter().cloned().map(|f| f.name).map(|n| { n.replace(".rs", "") }).collect(); tests }; - - let object_paths = { - let active_toolchain: String = { - let output = Command::new("rustup") - .arg("show") - .arg("active-toolchain") - .stderr(Stdio::inherit()) - .output() - .expect("Can't get a current toolchain"); - - let active_toolchain = String::from_utf8_lossy(&output.stdout); - let mut split = active_toolchain.split_whitespace(); - split.next().expect("active toolchain missing").trim().to_owned() - }; - - let rustup_sysroot = { - let home = env::home_dir().expect("missing profile home dir"); - format!("{}/.rustup/toolchains/{}/lib/rustlib/{}/lib/", - home.to_str().unwrap(), active_toolchain, &options.target_arch) - }; - - let mut sysroot_rlibs: Vec = find_files(&rustup_sysroot, |n| { - n.ends_with(".rlib") - }).iter().cloned().collect(); - - let tests_deps_dir = format!("{}/target/{}/debug/deps/", &options.tests_project_path, &options.target_arch); - - for sysroot_rlib in sysroot_rlibs { - copy(sysroot_rlib.absolute_path, format!("{}/{}.o", tests_deps_dir, sysroot_rlib.name.trim_right_matches(".rlib"))); + let mut built_tests = vec![]; + + for test in &tests { + // cross-build the tests library + let cargo_build = Command::new(&cargo_path) + .current_dir(&options.tests_project_path) + .arg("build") + + .arg("--example") + .arg(test) + + .arg("--verbose") + + .arg("--target") + .arg(&options.target_arch) + + //.env("RUSTFLAGS", "-C linker=arm-none-eabi-gcc -Z linker-flavor=gcc") + + .env("CARGO_INCREMENTAL", "0") + //.env("RUSTFLAGS", "--emit=obj") + //.env("RUST_TARGET_PATH", &build_proj_root.to_str().expect("Missing path to proj root for target path?")) + + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .output(); + + let output = cargo_build.expect("Cargo build of the tests projects failed"); + if !output.status.success() { + panic!("Cargo build failed"); } - let mut test_objects: Vec<_> = find_files(&tests_deps_dir, |n| { - n.ends_with(".o") - }).iter().cloned().collect(); - - test_objects.sort_by_key(|f| { - if f.name.contains("freertos_rs") { 0 } - else if f.name.contains("lazy_static") { 1 } - else if f.name.contains("liballoc") { 2 } - else if f.name.contains("libcompiler_builtins") { 3 } - else if f.name.contains("libcore") { 4 } - else if f.name.contains("librustc_std_workspace_core") { 5 } - else { 6 } - }); - - let mut test_objects: Vec<_> = test_objects.into_iter().map(|f| f.absolute_path).collect(); + built_tests.push(test.clone()); + } - let mut objects = vec![]; - objects.append(&mut test_objects); - objects + let library_path = { + let p = format!("{}/target/{}/debug/examples/", &options.tests_project_path, &options.target_arch); + let p = Path::new(&p); + p.canonicalize().unwrap().to_str().unwrap().into() }; - + CrossbuiltTests { - object_paths: object_paths, - tests: tests + object_paths: vec![], + tests: built_tests, + library_path: library_path } } @@ -168,19 +166,28 @@ pub fn build_test_binaries(options: &CrossbuildOptions, tests: &CrossbuiltTests) for test in &tests.tests { let mut test_renames = "".to_string(); + /* if test.contains("isr_timer4") { test_renames.push_str(&format!("testbed_timer4_isr = {}_timer4_isr;", test)); } + */ + + + let mut test_deps = vec![ + format!("{}/lib{}.a", &tests.library_path, &test) + ]; let test_binary_build = Command::new("make") .current_dir(&gcc_proj_dir) - .env("TEST_ENTRY", test.clone()) + .env("TEST_NAME", test.clone()) + .env("TEST_LIBRARY_PATH", format!("-L {}", &tests.library_path)) + .env("TEST_LIBRARY_PRE", format!("-l:lib{}.a", &test)) .env("TEST_OBJECTS", &test_objects) + .env("TEST_DEPS", test_deps.join(" ")) .env("TEST_RENAMES", test_renames) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .output(); - let output = test_binary_build.unwrap(); if !output.status.success() { panic!(format!("GCC ARM build for '{}' failed", test)); diff --git a/qemu_stm32_tests/Cargo.toml b/qemu_stm32_tests/Cargo.toml index a65c4c3..93ffd44 100644 --- a/qemu_stm32_tests/Cargo.toml +++ b/qemu_stm32_tests/Cargo.toml @@ -5,11 +5,50 @@ authors = ["Rudi Benkovic "] [lib] name = "qemu_stm32_tests" -crate-type = ["lib"] [dependencies] freertos_rs = { path = "../" } [dependencies.lazy_static] version = "1.3.0" -features = ["spin_no_std"] \ No newline at end of file +features = ["spin_no_std"] + +[[example]] +name = "test_basics" +crate-type = ["staticlib"] + +[[example]] +name = "test_delay" +crate-type = ["staticlib"] + +[[example]] +name = "test_mutex" +crate-type = ["staticlib"] + +[[example]] +name = "test_mem_leaks1" +crate-type = ["staticlib"] + +[[example]] +name = "test_timers" +crate-type = ["staticlib"] + +[[example]] +name = "test_stats" +crate-type = ["staticlib"] + +[[example]] +name = "test_processor" +crate-type = ["staticlib"] + +[[example]] +name = "test_sample1" +crate-type = ["staticlib"] + +[[example]] +name = "test_isr_timer4_notify" +crate-type = ["staticlib"] + +[[example]] +name = "test_isr_timer4_queue" +crate-type = ["staticlib"] \ No newline at end of file diff --git a/qemu_stm32_tests/examples/test_basics.rs b/qemu_stm32_tests/examples/test_basics.rs new file mode 100644 index 0000000..7fc8e3e --- /dev/null +++ b/qemu_stm32_tests/examples/test_basics.rs @@ -0,0 +1,24 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; + +freertos_rs_test!(TestBasics); + +pub struct TestBasics; +impl Test for TestBasics { + fn run(tb: &T) { + let check = shim_sanity_check(); + if check.is_err() { + T::debug_print(&format!("Shim sanity check failed: {:?}", check)); + T::exit_test(1); + } + + T::debug_print("Type sizes are OK!"); + + T::exit_test(0); + } +} \ No newline at end of file diff --git a/qemu_stm32_tests/examples/test_delay.rs b/qemu_stm32_tests/examples/test_delay.rs new file mode 100644 index 0000000..493f6b0 --- /dev/null +++ b/qemu_stm32_tests/examples/test_delay.rs @@ -0,0 +1,77 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; + +freertos_rs_test!(TestDelay); + +pub struct TestDelay; +impl Test for TestDelay { + fn run(tb: &T) { + let main_task = Task::new().name("main").start(|| { + let start = FreeRtosUtils::get_tick_count(); + + let counter = Arc::new(Mutex::new(0).unwrap()); + + { + let counter = counter.clone(); + let delay_task = Task::new().name("delay").start(move || { + for _ in 0..10 { + CurrentTask::delay(Duration::ms(100)); + + // increase the counter and immediately release it + { + let mut counter = counter.lock(Duration::infinite()).unwrap(); + *counter += 1; + } + } + }).unwrap(); + } + + CurrentTask::delay(Duration::ms(550)); + + { + let counter = counter.lock(Duration::infinite()).unwrap(); + assert_eq!(*counter, 5); + } + + CurrentTask::delay(Duration::ms(500)); + + { + let counter = counter.lock(Duration::infinite()).unwrap(); + assert_eq!(*counter, 10); + } + + // negative test: the counter should never increment + { + let counter = Arc::new(Mutex::new(0).unwrap()); + { + let counter = counter.clone(); + let task = Task::new().name("delay_long").start(move || { + for _ in 0..10 { + CurrentTask::delay(Duration::ms(1000)); + + // increase the counter and immediately release it + { + let mut counter = counter.lock(Duration::infinite()).unwrap(); + *counter += 1; + } + } + }); + } + + CurrentTask::delay(Duration::ms(500)); + let counter = counter.lock(Duration::infinite()).unwrap(); + assert_eq!(*counter, 0); + } + + T::exit_test(0); + }); + + let main_task = main_task.unwrap(); + T::start_kernel(); + } +} diff --git a/qemu_stm32_tests/examples/test_isr_timer4_notify.rs b/qemu_stm32_tests/examples/test_isr_timer4_notify.rs new file mode 100644 index 0000000..69fa1dc --- /dev/null +++ b/qemu_stm32_tests/examples/test_isr_timer4_notify.rs @@ -0,0 +1,64 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; + +static mut MAIN_TASK: Option = None; + +freertos_rs_test!(TestIsrTimer4Notify); + +pub struct TestIsrTimer4Notify; +impl Test for TestIsrTimer4Notify { + fn run(tb: &T) { + let main_task = Task::new().start(|| { + T::start_timer4_50ms(); + + let current_task = Task::current().unwrap(); + + loop { + match current_task.wait_for_notification(0, 0, Duration::ms(1000)) { + Ok(value) => { + T::debug_print(&format!("Received notification value {}", value)); + + if value == 10 { + T::ok() + } + }, + Err(e) => { + T::debug_print(&format!("Error receiving notification: {:?}", e)); + T::exit_test(1); + } + }; + + } + + + }).unwrap(); + + unsafe { + MAIN_TASK = Some(main_task); + } + + T::start_kernel(); + } +} + +static mut COUNTER: u32 = 0; + +#[no_mangle] +pub extern fn testbed_timer4_isr() { + let mut context = InterruptContext::new(); + let c = unsafe { + COUNTER += 1; + COUNTER + }; + + unsafe { + if let Some(ref task) = MAIN_TASK { + task.notify_from_isr(&context, TaskNotification::SetValue(c)).unwrap(); + } + } +} diff --git a/qemu_stm32_tests/examples/test_isr_timer4_queue.rs b/qemu_stm32_tests/examples/test_isr_timer4_queue.rs new file mode 100644 index 0000000..50aca64 --- /dev/null +++ b/qemu_stm32_tests/examples/test_isr_timer4_queue.rs @@ -0,0 +1,60 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; + +use qemu_stm32_tests::lazy_static::lazy_static; + +lazy_static! { + static ref QUEUE: Queue = Queue::new(15).unwrap(); +} + +freertos_rs_test!(TestIsrTimer4Queue); + +pub struct TestIsrTimer4Queue; +impl Test for TestIsrTimer4Queue { + fn run(tb: &T) { + let main = Task::new().start(|| { + + // trigger init of the queue object (lazy_static) + let ref q = QUEUE; + + T::start_timer4_50ms(); + + for i in 1..15 { + match QUEUE.receive(Duration::ms(1000)) { + Ok(received) => { + T::debug_print(&format!("Received queue item '{}'", received)); + + if received == 10 { + T::ok() + } + }, + Err(e) => { + T::debug_print(&format!("Error receiving item: {:?}", e)); + } + } + } + + T::exit_test(1); + + }).unwrap(); + + T::start_kernel(); + } +} + +static mut COUNTER: u32 = 0; + +#[no_mangle] +pub extern fn testbed_timer4_isr() { + let mut context = InterruptContext::new(); + let c = unsafe { + COUNTER += 1; + COUNTER + }; + QUEUE.send_from_isr(&mut context, c).unwrap(); +} diff --git a/qemu_stm32_tests/examples/test_mem_leaks1.rs b/qemu_stm32_tests/examples/test_mem_leaks1.rs new file mode 100644 index 0000000..b9c997c --- /dev/null +++ b/qemu_stm32_tests/examples/test_mem_leaks1.rs @@ -0,0 +1,208 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; + +use qemu_stm32_tests::freertos_rs::patterns::compute_task::*; +use qemu_stm32_tests::freertos_rs::patterns::processor::*; +use qemu_stm32_tests::freertos_rs::patterns::pub_sub::*; + +freertos_rs_test!(TestMemLeaks1); + +pub struct TestMemLeaks1; +impl Test for TestMemLeaks1 { + fn run(tb: &T) { + let main_task = Task::new().name("main").stack_size(2048).start(|| { + let start_memory_usage = T::heap_allocated_memory(); + let mut end_memory_usage = 0; + + // simple task spawn + for i in 0..100 { + Task::new().name(&format!("t_{}", i)).stack_size(256).start(move || { + let s = format!("Hello world {}", i); + }).unwrap(); + + CurrentTask::delay(Duration::ms(2)); + } + + // simple mutexes + for i in 0..100 { + let m = Mutex::new(0).unwrap(); + let mut v = m.lock(Duration::ms(50)).unwrap(); + *v += 1; + } + + // recursive mutexes + for i in 0..100 { + let m = RecursiveMutex::new(0).unwrap(); + let mut v = m.lock(Duration::ms(50)).unwrap(); + *v += 1; + } + + // deconstructing a mutex + { + let n = 50; + + let test_str = "Hello Heap World"; + + let mutexes = { + let mut v = vec![]; + for _ in 0..n { + let mutex = { + let m = Mutex::new(test_str.to_string()).unwrap(); + { + let l = m.lock(Duration::ms(50)).unwrap(); + assert_eq!(test_str, *l); + } + m + }; + + v.push(mutex); + } + + v + }; + + for mutex in mutexes.into_iter() { + let inner: String = mutex.into_inner(); + assert_eq!(test_str, inner); + } + } + + // simple queues + for i in 0..100 { + let q = Queue::new(10).unwrap(); + q.send(10, Duration::ms(5)).unwrap(); + q.receive(Duration::ms(100)).unwrap(); + } + + end_memory_usage = T::heap_allocated_memory(); + assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #1"); + + // compute tasks + { + let n = 12; + let res: u64 = 42; + + let tasks = { + let mut v = vec![]; + + for i in 0..n { + let name = format!("comp_{}", i); + let t = Task::new().name(&name).stack_size(256).compute(|| { + CurrentTask::delay(Duration::ms(200)); + 42 as u64 + }).unwrap(); + v.push(t); + } + + v + }; + + for task in tasks.into_iter() { + let result = task.into_result(Duration::ms(200)).unwrap(); + assert_eq!(res, result); + } + + } + + CurrentTask::delay(Duration::ms(200)); + + end_memory_usage = T::heap_allocated_memory(); + assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #2"); + + // pub sub + { + let w = Duration::ms(1); + + let publisher = QueuePublisher::new().unwrap(); + let sub1 = publisher.subscribe(10, w).unwrap(); + assert_eq!(1, publisher.send("A", w)); + let sub2 = publisher.subscribe(10, w).unwrap(); + let publisher2 = publisher.clone(); + assert_eq!(2, publisher2.send("B", w)); + + assert_eq!("A", sub1.receive(w).unwrap()); + assert_eq!("B", sub1.receive(w).unwrap()); + assert_eq!(Result::Err(FreeRtosError::QueueReceiveTimeout), sub1.receive(w)); + drop(sub1); + + assert_eq!("B", sub2.receive(w).unwrap()); + assert_eq!(Result::Err(FreeRtosError::QueueReceiveTimeout), sub2.receive(w)); + } + + end_memory_usage = T::heap_allocated_memory(); + assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #3"); + + // timers + { + let timer = Timer::new(Duration::ms(50)) + .set_auto_reload(false) + .create(|mut timer| { + let a = 1; + }).unwrap(); + + timer.start(Duration::infinite()).unwrap(); + + CurrentTask::delay(Duration::ms(100)) + } + + end_memory_usage = T::heap_allocated_memory(); + assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #4"); + + // processor + { + #[derive(PartialEq, Copy, Clone, Debug)] + enum ProcessorMsg { + Val(usize), + Shutdown + } + + let processor: Processor, usize> = Processor::new(5).unwrap(); + let client_1 = processor.new_client().unwrap(); + let client_2 = processor.new_client_with_reply(5, Duration::ms(5)).unwrap(); + let client_3 = processor.new_client().unwrap(); + let client_4 = processor.new_client_with_reply(15, Duration::ms(5)).unwrap(); + + let processor_task = Task::new().name("processor").start(move || { + loop { + if let Ok(m) = processor.get_receive_queue().receive(Duration::ms(10)) { + match m.get_val() { + ProcessorMsg::Val(v) => { + let processed = v + 1; + let r = processor.reply_val(m, processed, Duration::ms(10)).unwrap(); + }, + ProcessorMsg::Shutdown => { break; } + } + } + } + drop(processor); + T::debug_print("Processor shutting down"); + }).unwrap(); + + client_1.send_val(ProcessorMsg::Val(5), Duration::ms(5)); + client_2.send_val(ProcessorMsg::Val(6), Duration::ms(5)); + client_2.send_val(ProcessorMsg::Val(7), Duration::ms(5)); + client_2.call_val(ProcessorMsg::Val(8), Duration::ms(5)); + + client_3.send_val(ProcessorMsg::Shutdown, Duration::ms(5)); + + CurrentTask::delay(Duration::ms(50)); + + assert_eq!(Err(FreeRtosError::ProcessorHasShutDown), client_4.call_val(ProcessorMsg::Val(2), Duration::ms(5))); + } + + CurrentTask::delay(Duration::ms(300)); + + end_memory_usage = T::heap_allocated_memory(); + assert_eq!(start_memory_usage, end_memory_usage, "Mem usage final"); + + T::ok(); + }).unwrap(); + + T::start_kernel(); + } +} diff --git a/qemu_stm32_tests/examples/test_mutex.rs b/qemu_stm32_tests/examples/test_mutex.rs new file mode 100644 index 0000000..f97dec0 --- /dev/null +++ b/qemu_stm32_tests/examples/test_mutex.rs @@ -0,0 +1,87 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; +use qemu_stm32_tests::utils::Rnd; + +freertos_rs_test!(TestMutex); + +pub struct TestMutex; +impl Test for TestMutex { + fn run(tb: &T) { + + let n = 3000; + let m = 32; + let t = 6; + + let mut mutexes = vec![]; + for i in 0..m { + let m = Arc::new(Mutex::new(0).unwrap()); + mutexes.push(m); + } + let mut mutexes = Arc::new(Mutex::new(mutexes).unwrap()); + + let mut total = Arc::new(Mutex::new(0).unwrap()); + let mut rnd = Arc::new(Mutex::new(Rnd::new(100)).unwrap()); + + let main_task = Task::new().name("main").start(move || { + + for i in 1..(t+1) { + let t = format!("task_{}", i); + let mut mutexes = mutexes.clone(); + let mut rnd = rnd.clone(); + let mut total = total.clone(); + + let main_task = Task::current().unwrap(); + + Task::new().name(&t).start(move || { + + for _ in 0..n { + let (next_mutex_idx, delay_ms) = { + let mut rnd = rnd.lock(Duration::infinite()).unwrap(); + (rnd.next_num(m) as usize, rnd.next_num(5) as u32) + }; + + let mutex = { + let m = mutexes.lock(Duration::infinite()).unwrap(); + m[next_mutex_idx].clone() + }; + + { + let mut m = mutex.lock(Duration::infinite()).unwrap(); + *m += 1; + CurrentTask::delay(Duration::ms(delay_ms)); + } + + { + let mut total = total.lock(Duration::infinite()).unwrap(); + *total += 1; + } + } + + main_task.notify(TaskNotification::Increment); + + }).unwrap(); + } + + let main_task = Task::current().unwrap(); + let mut finished_tasks = 0; + loop { + let nv = main_task.take_notification(true, Duration::infinite()); + finished_tasks += nv; + if finished_tasks == t { + let total = total.lock(Duration::infinite()).unwrap(); + assert_eq!(*total, n * t); + + T::exit_test(0); + } + } + + }).unwrap(); + + T::start_kernel(); + } +} \ No newline at end of file diff --git a/qemu_stm32_tests/examples/test_processor.rs b/qemu_stm32_tests/examples/test_processor.rs new file mode 100644 index 0000000..e7cda5e --- /dev/null +++ b/qemu_stm32_tests/examples/test_processor.rs @@ -0,0 +1,62 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; +use qemu_stm32_tests::freertos_rs::patterns::processor::*; + +freertos_rs_test!(TestProcessor); + +pub struct TestProcessor; +impl Test for TestProcessor { + fn run(tb: &T) { + let main_task = Task::new().name("main").start(|| { + + let shutdown = 255; + + let processor: Processor, usize> = Processor::new(5).unwrap(); + let client_1 = processor.new_client().unwrap(); + let client_2 = processor.new_client_with_reply(1, Duration::ms(100)).unwrap(); + + let processor_task = Task::new().name("processor").start(move || { + + loop { + if let Ok(msg) = processor.get_receive_queue().receive(Duration::ms(10)) { + + if msg.get_val() == shutdown { + break; + } + + T::debug_print(&format!("Received val {}", msg.get_val())); + let processed_message = msg.get_val() + 1; + processor.reply_val(msg, processed_message, Duration::ms(10)).expect("Failed to send the reply"); + T::debug_print("Processed."); + } + } + + T::debug_print("Shutting down."); + + }).unwrap(); + + + client_1.send_val(1, Duration::ms(100)); + + let processed = client_2.call_val(2, Duration::ms(100)).expect("Missing the reply from the processor"); + assert_eq!(3, processed); + + client_1.send_val(shutdown, Duration::ms(100)); + + CurrentTask::delay(Duration::ms(10)); + + assert_eq!(Err(FreeRtosError::ProcessorHasShutDown), client_1.send_val(1, Duration::zero())); + assert_eq!(Err(FreeRtosError::ProcessorHasShutDown), client_2.send_val(1, Duration::zero())); + + T::ok(); + + }).unwrap(); + + T::start_kernel(); + } +} diff --git a/qemu_stm32_tests/examples/test_sample1.rs b/qemu_stm32_tests/examples/test_sample1.rs new file mode 100644 index 0000000..ae97d0d --- /dev/null +++ b/qemu_stm32_tests/examples/test_sample1.rs @@ -0,0 +1,57 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; +use qemu_stm32_tests::freertos_rs::patterns::compute_task::*; + +freertos_rs_test!(TestSample1); + +pub struct TestSample1; +impl Test for TestSample1 { + fn run(tb: &T) { + let main_task = Task::new().name("main").start(|| { + + // A shared queue + let queue = Arc::new(Queue::new(10).unwrap()); + + // Task that consumes integers from the shared queue. Returns the + // summed value when a new integer hasn't been posted for 100 ms. + let sum_task = { + let queue = queue.clone(); + + Task::new().name("sum").compute(move || { + let mut sum = 0; + + loop { + if let Ok(val) = queue.receive(Duration::ms(100)) { + sum += val; + } else { + break; + } + } + + sum + }).unwrap() + }; + + // Send the integers to the shared queue + for i in 1..11 { + queue.send(i, Duration::ms(15)).unwrap(); + } + + // Wait for the compute task to finish summing + let sum = sum_task.into_result(Duration::infinite()).unwrap(); + + // Check the result + assert_eq!(55, sum); + + T::ok(); + + }).unwrap(); + + T::start_kernel(); + } +} \ No newline at end of file diff --git a/qemu_stm32_tests/examples/test_stats.rs b/qemu_stm32_tests/examples/test_stats.rs new file mode 100644 index 0000000..66f72d8 --- /dev/null +++ b/qemu_stm32_tests/examples/test_stats.rs @@ -0,0 +1,39 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; + +freertos_rs_test!(TestStats); + +pub struct TestStats; +impl Test for TestStats { + fn run(tb: &T) { + let main_task = Task::new().name("main").start(|| { + + let task = Task::new().name("test1").start(|| { + CurrentTask::delay(Duration::ms(1000)) + }).unwrap(); + + let task = Task::new().name("test2").stack_size(1024).priority(TaskPriority(3)).start(|| { + CurrentTask::delay(Duration::ms(1000)) + }).unwrap(); + + CurrentTask::delay(Duration::ms(10)); + + let all_tasks_count = FreeRtosUtils::get_number_of_tasks(); + assert_eq!(5, all_tasks_count); + + let tasks = FreeRtosUtils::get_all_tasks(Some(10)); + assert_eq!(5, tasks.tasks.len()); + T::debug_print(&format!("{}", tasks)); + + T::ok(); + + }).unwrap(); + + T::start_kernel(); + } +} diff --git a/qemu_stm32_tests/examples/test_timers.rs b/qemu_stm32_tests/examples/test_timers.rs new file mode 100644 index 0000000..029fda5 --- /dev/null +++ b/qemu_stm32_tests/examples/test_timers.rs @@ -0,0 +1,100 @@ +#![no_main] +#![no_std] + +#[macro_use] +extern crate qemu_stm32_tests; + +use qemu_stm32_tests::prelude::v1::*; + + +freertos_rs_test!(TestTimers); + +pub struct TestTimers; +impl Test for TestTimers { + fn run(tb: &T) { + + let main_task = Task::new().name("main").start(|| { + + { + let counter_a = Arc::new(Mutex::new(0).unwrap()); + let counter_b = Arc::new(Mutex::new(100).unwrap()); + + { + let timer_a = { + let counter_a = counter_a.clone(); + + Timer::new(Duration::ms(50)) + .set_auto_reload(true) + .create(move |mut timer| { + if let Ok(mut counter_a) = counter_a.lock(Duration::ms(5)) { + *counter_a += 1; + } + }).unwrap() + }; + + let timer_b = { + let counter_b = counter_b.clone(); + + Timer::new(Duration::ms(100)) + .set_auto_reload(false) + .create(move |mut timer| { + if let Ok(mut counter_b) = counter_b.lock(Duration::ms(5)) { + *counter_b += 1; + } + }).unwrap() + }; + + timer_a.start(Duration::ms(1)).unwrap(); + timer_b.start(Duration::ms(1)).unwrap(); + + CurrentTask::delay(Duration::ms(225)); + + drop(timer_a); + drop(timer_b); + } + + let counter_a = counter_a.lock(Duration::infinite()).unwrap(); + assert_eq!(4, *counter_a); + + let counter_b = counter_b.lock(Duration::infinite()).unwrap(); + assert_eq!(101, *counter_b); + } + + // non-auto reloading timer test + { + let counter_a = Arc::new(Mutex::new(0).unwrap()); + + let timer_a = { + let counter_a = counter_a.clone(); + + Timer::new(Duration::ms(50)) + .set_auto_reload(false) + .create(move |mut timer| { + if let Ok(mut counter_a) = counter_a.lock(Duration::ms(5)) { + *counter_a += 1; + } + }).unwrap() + }; + + CurrentTask::delay(Duration::ms(100)); + + assert_eq!(0, *counter_a.lock(Duration::infinite()).unwrap()); + + timer_a.start(Duration::ms(1)).unwrap(); + + CurrentTask::delay(Duration::ms(200)); + assert_eq!(1, *counter_a.lock(Duration::infinite()).unwrap()); + + timer_a.start(Duration::ms(1)).unwrap(); + CurrentTask::delay(Duration::ms(200)); + + assert_eq!(2, *counter_a.lock(Duration::infinite()).unwrap()); + } + + T::ok(); + + }).unwrap(); + + T::start_kernel(); + } +} \ No newline at end of file diff --git a/qemu_stm32_tests/gcc/Inc/stm32f4xx_it.h b/qemu_stm32_tests/gcc/Inc/stm32f4xx_it.h index ca51990..70a8ad7 100644 --- a/qemu_stm32_tests/gcc/Inc/stm32f4xx_it.h +++ b/qemu_stm32_tests/gcc/Inc/stm32f4xx_it.h @@ -45,8 +45,6 @@ /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ -static uint8_t enable_tim4_itr = 0; - extern void testbed_timer4_isr() __attribute__((weak)); void SysTick_Handler(void); diff --git a/qemu_stm32_tests/gcc/Makefile b/qemu_stm32_tests/gcc/Makefile index e84b665..44f3f41 100644 --- a/qemu_stm32_tests/gcc/Makefile +++ b/qemu_stm32_tests/gcc/Makefile @@ -5,7 +5,7 @@ ###################################### # target ###################################### -TARGET = stm32_$(TEST_ENTRY) +TARGET = stm32_$(TEST_NAME) ###################################### # building variables @@ -108,9 +108,9 @@ CFLAGS += -std=c99 -MD -MP -MF .dep/$(@F).d # link script LDSCRIPT = STM32F407VGTx_FLASH.ld # libraries -LIBS = -lc -lm -lnosys -LIBDIR = -LDFLAGS = -mthumb -mcpu=cortex-m4 -mthumb -mthumb-interwork -mfloat-abi=soft -specs=nano.specs -T$(BUILD_DIR)/$(TARGET).ld -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections +LIBS = $(TEST_LIBRARY_PRE) -lc -lm -lnosys +LIBDIR = $(TEST_LIBRARY_PATH) +LDFLAGS = -mthumb -mcpu=cortex-m4 -mthumb -mthumb-interwork -mfloat-abi=soft -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections # default action: build all all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin @@ -132,11 +132,7 @@ $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) $(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR) $(AS) -c $(CFLAGS) $< -o $@ -$(BUILD_DIR)/$(TARGET).ld: Makefile | $(BUILD_DIR) - echo "testbed_main = $(TEST_ENTRY);" > $(BUILD_DIR)/$(TARGET).ld - echo "$(TEST_RENAMES)" >> $(BUILD_DIR)/$(TARGET).ld - -$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) $(BUILD_DIR)/$(TARGET).ld Makefile +$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) $(TEST_DEPS) Makefile $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(SZ) $@ diff --git a/qemu_stm32_tests/gcc/Src/syscalls.c b/qemu_stm32_tests/gcc/Src/syscalls.c index 3e6f7ff..1863b78 100644 --- a/qemu_stm32_tests/gcc/Src/syscalls.c +++ b/qemu_stm32_tests/gcc/Src/syscalls.c @@ -10,6 +10,7 @@ #include #include "stm32f4xx_hal.h" +#include "semihosting.h" /* Variables */ #undef errno @@ -20,8 +21,7 @@ extern int __io_getchar(void) __attribute__((weak)); extern void usart2_send_byte(uint8_t byte); static int __io_putchar(int ch) { - //usart2_send_byte(ch); - trace_write(ch); + return trace_write(ch); } /* diff --git a/qemu_stm32_tests/gcc/Src/testbed.c b/qemu_stm32_tests/gcc/Src/testbed.c index f91c2fe..49ca73b 100644 --- a/qemu_stm32_tests/gcc/Src/testbed.c +++ b/qemu_stm32_tests/gcc/Src/testbed.c @@ -14,7 +14,7 @@ void testbed_println(uint8_t* line, uint16_t line_len) { if (kernel_started == 1) { taskENTER_CRITICAL(); } - trace_print(line, line_len); + trace_print((char*) line, line_len); trace_print("\n", 1); if (kernel_started == 1) { taskEXIT_CRITICAL(); @@ -37,9 +37,8 @@ void testbed_init_timer4_50ms_isr() { Error_Handler(); } - osThreadId handle; osThreadDef(timerTask, timer4_emulator, osPriorityNormal, 0, 128); - handle = osThreadCreate(osThread(timerTask), NULL); + osThreadCreate(osThread(timerTask), NULL); } diff --git a/qemu_stm32_tests/src/freertos_alloc.rs b/qemu_stm32_tests/src/freertos_alloc.rs index e527b1e..b496ac8 100644 --- a/qemu_stm32_tests/src/freertos_alloc.rs +++ b/qemu_stm32_tests/src/freertos_alloc.rs @@ -7,43 +7,24 @@ enum c_void { extern { fn pvPortMalloc(size: u32) -> *mut c_void; - fn pvPortRealloc(p: *mut c_void, size: u32) -> *mut c_void; fn vPortFree(p: *mut c_void); } -#[no_mangle] -pub extern fn __rust_alloc(size: usize, align: usize, err: *mut u8) -> *mut u8 { - unsafe { pvPortMalloc(size as u32) as *mut u8 } +#[alloc_error_handler] +fn foo(_: core::alloc::Layout) -> ! { + panic!("OOM!"); } -#[no_mangle] -pub extern fn rust_oom(err: *const u8) -> ! { - panic!("OOM"); -} +use core::alloc::{GlobalAlloc, Layout}; -#[no_mangle] -pub extern fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize) { - unsafe { vPortFree(ptr as *mut c_void) } -} +pub struct FreeRtosAllocator; -#[no_mangle] -pub extern fn __rust_realloc(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - err: *mut u8) -> *mut u8 -{ - unsafe { pvPortRealloc(ptr as *mut c_void, new_size as u32) as *mut u8 } -} +unsafe impl GlobalAlloc for FreeRtosAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + pvPortMalloc(layout.size() as u32) as *mut u8 + } -#[no_mangle] -pub extern fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8 { - unsafe { - let ptr = __rust_alloc(size, align, err); - if !ptr.is_null() { - ::core::ptr::write_bytes(ptr, 0, size); - } - ptr - } + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + vPortFree(ptr as *mut c_void) + } } \ No newline at end of file diff --git a/qemu_stm32_tests/src/lib.rs b/qemu_stm32_tests/src/lib.rs index 8dfa8cf..5617eea 100644 --- a/qemu_stm32_tests/src/lib.rs +++ b/qemu_stm32_tests/src/lib.rs @@ -1,6 +1,12 @@ #![no_std] +#![feature(alloc_error_handler)] + +#[global_allocator] +static GLOBAL: freertos_alloc::FreeRtosAllocator = freertos_alloc::FreeRtosAllocator; + use core::panic::PanicInfo; +use testbed::Testbed; #[panic_handler] #[inline(never)] @@ -9,77 +15,49 @@ fn panic(info: &PanicInfo) -> ! { use core::fmt::Write; use alloc::string::*; - debug_print("Panicked!"); + testbed::QemuTestbed::debug_print("Panicked!"); { - debug_print(&format!("{:}", info)); + testbed::QemuTestbed::debug_print(&format!("{:}", info)); } - exit_test(98); + testbed::QemuTestbed::exit_test(98); loop {} } #[macro_use] -extern crate alloc; +#[macro_export] +pub extern crate alloc; -extern crate freertos_rs; +pub extern crate freertos_rs; #[macro_use] -extern crate lazy_static; - -extern { - fn testbed_println(line: *const u8, line_len: u16); - fn testbed_start_kernel(); - fn testbed_return(return_code: i8); - fn testbed_allocated_memory() -> u32; - fn testbed_init_timer4_50ms_isr(); -} - -pub fn debug_print(s: &str) { - let s = s.as_bytes(); - unsafe { - testbed_println(s.as_ptr(), s.len() as u16); - } -} +#[macro_export] +pub extern crate lazy_static; -pub fn start_kernel() { - unsafe { - testbed_start_kernel(); - } -} +pub mod testbed; +pub mod freertos_alloc; +pub mod prelude; +pub mod utils; -pub fn exit_test(return_code: i8) { - unsafe { - testbed_return(return_code); - } -} -pub fn heap_allocated_memory() -> u32 { - unsafe { - testbed_allocated_memory() - } +pub trait Test { + fn run(tb: &T); } -pub fn start_timer4_50ms() { - unsafe { - testbed_init_timer4_50ms_isr(); - } +pub fn run_test() { + let tb = testbed::QemuTestbed; + T::run(&tb); } - -pub mod freertos_alloc; -mod prelude; -mod utils; - -pub mod test_basics; -pub mod test_delay; -pub mod test_mutex; -pub mod test_mem_leaks1; -pub mod test_isr_timer4_queue; -pub mod test_isr_timer4_notify; -pub mod test_sample1; -pub mod test_stats; -pub mod test_processor; -pub mod test_timers; - +#[macro_export] +macro_rules! freertos_rs_test { + ($test: ty) => { + #[no_mangle] + pub extern "C" fn testbed_main() -> i8 { + run_test::<$test>(); + 0 + } + }; +} \ No newline at end of file diff --git a/qemu_stm32_tests/src/prelude/no_std.rs b/qemu_stm32_tests/src/prelude/no_std.rs index a7c1a83..a0d9de3 100644 --- a/qemu_stm32_tests/src/prelude/no_std.rs +++ b/qemu_stm32_tests/src/prelude/no_std.rs @@ -15,7 +15,11 @@ pub use alloc::rc::Rc; pub use alloc::boxed::Box; pub use alloc::sync::Arc; pub use alloc::vec::Vec; +pub use alloc::vec; pub use alloc::string::*; +pub use alloc::format; - +pub use freertos_rs::*; +pub use {Test, run_test}; +pub use testbed::Testbed; \ No newline at end of file diff --git a/qemu_stm32_tests/src/test_basics.rs b/qemu_stm32_tests/src/test_basics.rs deleted file mode 100644 index eaa6c91..0000000 --- a/qemu_stm32_tests/src/test_basics.rs +++ /dev/null @@ -1,13 +0,0 @@ -use super::*; -use freertos_rs::*; - -#[no_mangle] -pub extern fn test_basics() -> i8 { - let check = shim_sanity_check(); - if check.is_err() { - debug_print(&format!("Shim sanity check failed: {:?}", check)); - return 1; - } - - 0 -} \ No newline at end of file diff --git a/qemu_stm32_tests/src/test_delay.rs b/qemu_stm32_tests/src/test_delay.rs deleted file mode 100644 index 56861ac..0000000 --- a/qemu_stm32_tests/src/test_delay.rs +++ /dev/null @@ -1,49 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; - -#[no_mangle] -pub extern fn test_delay() -> i8 { - let main_task = Task::new().name("main").start(|| { - let start = FreeRtosUtils::get_tick_count(); - - let counter = Arc::new(Mutex::new(0).unwrap()); - - { - let counter = counter.clone(); - let delay_task = Task::new().name("delay").start(move || { - for _ in 0..10 { - CurrentTask::delay(Duration::ms(100)); - - // increase the counter and immediately release it - { - let mut counter = counter.lock(Duration::infinite()).unwrap(); - *counter += 1; - } - } - }).unwrap(); - } - - CurrentTask::delay(Duration::ms(550)); - - { - let counter = counter.lock(Duration::infinite()).unwrap(); - assert_eq!(*counter, 5); - } - - CurrentTask::delay(Duration::ms(500)); - - { - let counter = counter.lock(Duration::infinite()).unwrap(); - assert_eq!(*counter, 10); - } - - exit_test(0); - }); - let main_task = main_task.unwrap(); - - start_kernel(); - - 1 -} \ No newline at end of file diff --git a/qemu_stm32_tests/src/test_isr_timer4_notify.rs b/qemu_stm32_tests/src/test_isr_timer4_notify.rs deleted file mode 100644 index 2907efc..0000000 --- a/qemu_stm32_tests/src/test_isr_timer4_notify.rs +++ /dev/null @@ -1,59 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; - -static mut MAIN_TASK: Option = None; - -#[no_mangle] -pub extern fn test_isr_timer4_notify() -> i8 { - let main_task = Task::new().start(|| { - start_timer4_50ms(); - - let current_task = Task::current().unwrap(); - - loop { - match current_task.wait_for_notification(0, 0, Duration::ms(1000)) { - Ok(value) => { - debug_print(&format!("Received notification value {}", value)); - - if value == 10 { - exit_test(0); - } - }, - Err(e) => { - debug_print(&format!("Error receiving notification: {:?}", e)); - exit_test(1); - } - }; - - } - - - }).unwrap(); - - unsafe { - MAIN_TASK = Some(main_task); - } - - start_kernel(); - - 1 -} - -static mut COUNTER: u32 = 0; - -#[no_mangle] -pub extern fn test_isr_timer4_notify_timer4_isr() { - let mut context = InterruptContext::new(); - let c = unsafe { - COUNTER += 1; - COUNTER - }; - - unsafe { - if let Some(ref task) = MAIN_TASK { - task.notify_from_isr(&context, TaskNotification::SetValue(c)).unwrap(); - } - } -} diff --git a/qemu_stm32_tests/src/test_isr_timer4_queue.rs b/qemu_stm32_tests/src/test_isr_timer4_queue.rs deleted file mode 100644 index da85ae0..0000000 --- a/qemu_stm32_tests/src/test_isr_timer4_queue.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; - -lazy_static! { - static ref QUEUE: Queue = Queue::new(15).unwrap(); -} - -#[no_mangle] -pub extern fn test_isr_timer4_queue() -> i8 { - - let main = Task::new().start(|| { - - // trigger init of the queue object (lazy_static) - let ref q = QUEUE; - - start_timer4_50ms(); - - for i in 1..15 { - match QUEUE.receive(Duration::ms(1000)) { - Ok(received) => { - debug_print(&format!("Received queue item '{}'", received)); - - if received == 10 { - exit_test(0); - } - }, - Err(e) => { - debug_print(&format!("Error receiving item: {:?}", e)); - } - } - } - - exit_test(1); - - }).unwrap(); - - start_kernel(); - - 1 -} - -static mut COUNTER: u32 = 0; - -#[no_mangle] -pub extern fn test_isr_timer4_queue_timer4_isr() { - let mut context = InterruptContext::new(); - let c = unsafe { - COUNTER += 1; - COUNTER - }; - QUEUE.send_from_isr(&mut context, c).unwrap(); -} diff --git a/qemu_stm32_tests/src/test_mem_leaks1.rs b/qemu_stm32_tests/src/test_mem_leaks1.rs deleted file mode 100644 index 67bb4dc..0000000 --- a/qemu_stm32_tests/src/test_mem_leaks1.rs +++ /dev/null @@ -1,205 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; -use freertos_rs::patterns::compute_task::*; -use freertos_rs::patterns::processor::*; -use freertos_rs::patterns::pub_sub::*; - - -#[no_mangle] -pub fn test_mem_leaks1() -> i8 { - - let main_task = Task::new().name("main").stack_size(2048).start(|| { - let start_memory_usage = heap_allocated_memory(); - let mut end_memory_usage = 0; - - // simple task spawn - for i in 0..100 { - Task::new().name(&format!("t_{}", i)).stack_size(256).start(move || { - let s = format!("Hello world {}", i); - }).unwrap(); - - CurrentTask::delay(Duration::ms(2)); - } - - // simple mutexes - for i in 0..100 { - let m = Mutex::new(0).unwrap(); - let mut v = m.lock(Duration::ms(50)).unwrap(); - *v += 1; - } - - // recursive mutexes - for i in 0..100 { - let m = RecursiveMutex::new(0).unwrap(); - let mut v = m.lock(Duration::ms(50)).unwrap(); - *v += 1; - } - - // deconstructing a mutex - { - let n = 50; - - let test_str = "Hello Heap World"; - - let mutexes = { - let mut v = vec![]; - for _ in 0..n { - let mutex = { - let m = Mutex::new(test_str.to_string()).unwrap(); - { - let l = m.lock(Duration::ms(50)).unwrap(); - assert_eq!(test_str, *l); - } - m - }; - - v.push(mutex); - } - - v - }; - - for mutex in mutexes.into_iter() { - let inner: String = mutex.into_inner(); - assert_eq!(test_str, inner); - } - } - - // simple queues - for i in 0..100 { - let q = Queue::new(10).unwrap(); - q.send(10, Duration::ms(5)).unwrap(); - q.receive(Duration::ms(100)).unwrap(); - } - - end_memory_usage = heap_allocated_memory(); - assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #1"); - - // compute tasks - { - let n = 12; - let res: u64 = 42; - - let tasks = { - let mut v = vec![]; - - for i in 0..n { - let name = format!("comp_{}", i); - let t = Task::new().name(&name).stack_size(256).compute(|| { - CurrentTask::delay(Duration::ms(200)); - 42 as u64 - }).unwrap(); - v.push(t); - } - - v - }; - - for task in tasks.into_iter() { - let result = task.into_result(Duration::ms(200)).unwrap(); - assert_eq!(res, result); - } - - } - - CurrentTask::delay(Duration::ms(200)); - - end_memory_usage = heap_allocated_memory(); - assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #2"); - - // pub sub - { - let w = Duration::ms(1); - - let publisher = QueuePublisher::new().unwrap(); - let sub1 = publisher.subscribe(10, w).unwrap(); - assert_eq!(1, publisher.send("A", w)); - let sub2 = publisher.subscribe(10, w).unwrap(); - let publisher2 = publisher.clone(); - assert_eq!(2, publisher2.send("B", w)); - - assert_eq!("A", sub1.receive(w).unwrap()); - assert_eq!("B", sub1.receive(w).unwrap()); - assert_eq!(Result::Err(FreeRtosError::QueueReceiveTimeout), sub1.receive(w)); - drop(sub1); - - assert_eq!("B", sub2.receive(w).unwrap()); - assert_eq!(Result::Err(FreeRtosError::QueueReceiveTimeout), sub2.receive(w)); - } - - end_memory_usage = heap_allocated_memory(); - assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #3"); - - // timers - { - let timer = Timer::new(Duration::ms(50)) - .set_auto_reload(false) - .create(|mut timer| { - let a = 1; - }).unwrap(); - - timer.start(Duration::infinite()).unwrap(); - - CurrentTask::delay(Duration::ms(100)) - } - - end_memory_usage = heap_allocated_memory(); - assert_eq!(start_memory_usage, end_memory_usage, "Mem usage #4"); - - // processor - { - #[derive(PartialEq, Copy, Clone, Debug)] - enum ProcessorMsg { - Val(usize), - Shutdown - } - - let processor: Processor, usize> = Processor::new(5).unwrap(); - let client_1 = processor.new_client().unwrap(); - let client_2 = processor.new_client_with_reply(5, Duration::ms(5)).unwrap(); - let client_3 = processor.new_client().unwrap(); - let client_4 = processor.new_client_with_reply(15, Duration::ms(5)).unwrap(); - - let processor_task = Task::new().name("processor").start(move || { - loop { - if let Ok(m) = processor.get_receive_queue().receive(Duration::ms(10)) { - match m.get_val() { - ProcessorMsg::Val(v) => { - let processed = v + 1; - let r = processor.reply_val(m, processed, Duration::ms(10)).unwrap(); - }, - ProcessorMsg::Shutdown => { break; } - } - } - } - drop(processor); - debug_print("Processor shutting down"); - }).unwrap(); - - client_1.send_val(ProcessorMsg::Val(5), Duration::ms(5)); - client_2.send_val(ProcessorMsg::Val(6), Duration::ms(5)); - client_2.send_val(ProcessorMsg::Val(7), Duration::ms(5)); - client_2.call_val(ProcessorMsg::Val(8), Duration::ms(5)); - - client_3.send_val(ProcessorMsg::Shutdown, Duration::ms(5)); - - CurrentTask::delay(Duration::ms(50)); - - assert_eq!(Err(FreeRtosError::ProcessorHasShutDown), client_4.call_val(ProcessorMsg::Val(2), Duration::ms(5))); - } - - CurrentTask::delay(Duration::ms(300)); - - end_memory_usage = heap_allocated_memory(); - assert_eq!(start_memory_usage, end_memory_usage, "Mem usage final"); - - exit_test(0); - }).unwrap(); - - - start_kernel(); - - 1 -} diff --git a/qemu_stm32_tests/src/test_mutex.rs b/qemu_stm32_tests/src/test_mutex.rs deleted file mode 100644 index 11972d0..0000000 --- a/qemu_stm32_tests/src/test_mutex.rs +++ /dev/null @@ -1,85 +0,0 @@ -use super::*; -use prelude::v1::*; -use utils::*; - -use freertos_rs::*; - -#[no_mangle] -pub extern fn test_mutex() -> i8 { - let n = 3000; - let m = 32; - let t = 6; - - let mut mutexes = vec![]; - for i in 0..m { - let m = Arc::new(Mutex::new(0).unwrap()); - mutexes.push(m); - } - let mut mutexes = Arc::new(Mutex::new(mutexes).unwrap()); - - let mut total = Arc::new(Mutex::new(0).unwrap()); - let mut rnd = Arc::new(Mutex::new(Rnd::new(100)).unwrap()); - - let main_task = Task::new().name("main").start(move || { - - for i in 1..(t+1) { - let t = format!("task_{}", i); - let mut mutexes = mutexes.clone(); - let mut rnd = rnd.clone(); - let mut total = total.clone(); - - let main_task = Task::current().unwrap(); - - Task::new().name(&t).start(move || { - - for _ in 0..n { - let (next_mutex_idx, delay_ms) = { - let mut rnd = rnd.lock(Duration::infinite()).unwrap(); - (rnd.next_num(m) as usize, rnd.next_num(5) as u32) - }; - - //debug_print(&format!("Next mutex {}", next_mutex_idx)); - - let mutex = { - let m = mutexes.lock(Duration::infinite()).unwrap(); - m[next_mutex_idx].clone() - }; - - { - let mut m = mutex.lock(Duration::infinite()).unwrap(); - *m += 1; - CurrentTask::delay(Duration::ms(delay_ms)); - } - - { - let mut total = total.lock(Duration::infinite()).unwrap(); - *total += 1; - } - } - - //debug_print(&format!("Task {} finished.", i)); - - main_task.notify(TaskNotification::Increment); - - }).unwrap(); - } - - let main_task = Task::current().unwrap(); - let mut finished_tasks = 0; - loop { - let nv = main_task.take_notification(true, Duration::infinite()); - finished_tasks += nv; - if finished_tasks == t { - let total = total.lock(Duration::infinite()).unwrap(); - assert_eq!(*total, n * t); - - exit_test(0); - } - } - - }).unwrap(); - - start_kernel(); - - 1 -} \ No newline at end of file diff --git a/qemu_stm32_tests/src/test_processor.rs b/qemu_stm32_tests/src/test_processor.rs deleted file mode 100644 index 8a4b662..0000000 --- a/qemu_stm32_tests/src/test_processor.rs +++ /dev/null @@ -1,60 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; -use freertos_rs::patterns::processor::*; - -#[no_mangle] -pub extern fn test_processor() -> i8 { - - let main_task = Task::new().name("main").start(|| { - - let shutdown = 255; - - let processor: Processor, usize> = Processor::new(5).unwrap(); - let client_1 = processor.new_client().unwrap(); - let client_2 = processor.new_client_with_reply(1, Duration::ms(100)).unwrap(); - - let processor_task = Task::new().name("processor").start(move || { - - loop { - if let Ok(msg) = processor.get_receive_queue().receive(Duration::ms(10)) { - - if msg.get_val() == shutdown { - break; - } - - debug_print(&format!("Received val {}", msg.get_val())); - let processed_message = msg.get_val() + 1; - processor.reply_val(msg, processed_message, Duration::ms(10)).expect("Failed to send the reply"); - debug_print("Processed."); - } - } - - debug_print("Shutting down."); - - }).unwrap(); - - - client_1.send_val(1, Duration::ms(100)); - - let processed = client_2.call_val(2, Duration::ms(100)).expect("Missing the reply from the processor"); - assert_eq!(3, processed); - - client_1.send_val(shutdown, Duration::ms(100)); - - CurrentTask::delay(Duration::ms(10)); - - assert_eq!(Err(FreeRtosError::ProcessorHasShutDown), client_1.send_val(1, Duration::zero())); - assert_eq!(Err(FreeRtosError::ProcessorHasShutDown), client_2.send_val(1, Duration::zero())); - - exit_test(0); - - }).unwrap(); - - - - start_kernel(); - - 1 -} \ No newline at end of file diff --git a/qemu_stm32_tests/src/test_sample1.rs b/qemu_stm32_tests/src/test_sample1.rs deleted file mode 100644 index f2fc146..0000000 --- a/qemu_stm32_tests/src/test_sample1.rs +++ /dev/null @@ -1,53 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; -use freertos_rs::patterns::compute_task::*; - -#[no_mangle] -pub extern fn test_sample1() -> i8 { - - let main_task = Task::new().name("main").start(|| { - - // A shared queue - let queue = Arc::new(Queue::new(10).unwrap()); - - // Task that consumes integers from the shared queue. Returns the - // summed value when a new integer hasn't been posted for 100 ms. - let sum_task = { - let queue = queue.clone(); - - Task::new().name("sum").compute(move || { - let mut sum = 0; - - loop { - if let Ok(val) = queue.receive(Duration::ms(100)) { - sum += val; - } else { - break; - } - } - - sum - }).unwrap() - }; - - // Send the integers to the shared queue - for i in 1..11 { - queue.send(i, Duration::ms(15)).unwrap(); - } - - // Wait for the compute task to finish summing - let sum = sum_task.into_result(Duration::infinite()).unwrap(); - - // Check the result - assert_eq!(55, sum); - - exit_test(0); - - }).unwrap(); - - start_kernel(); - - 1 -} \ No newline at end of file diff --git a/qemu_stm32_tests/src/test_stats.rs b/qemu_stm32_tests/src/test_stats.rs deleted file mode 100644 index 20f0e5c..0000000 --- a/qemu_stm32_tests/src/test_stats.rs +++ /dev/null @@ -1,35 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; - -#[no_mangle] -pub extern fn test_stats() -> i8 { - - let main_task = Task::new().name("main").start(|| { - - let task = Task::new().name("test1").start(|| { - CurrentTask::delay(Duration::ms(1000)) - }).unwrap(); - - let task = Task::new().name("test2").stack_size(1024).priority(TaskPriority(3)).start(|| { - CurrentTask::delay(Duration::ms(1000)) - }).unwrap(); - - CurrentTask::delay(Duration::ms(10)); - - let all_tasks_count = FreeRtosUtils::get_number_of_tasks(); - assert_eq!(5, all_tasks_count); - - let tasks = FreeRtosUtils::get_all_tasks(Some(10)); - assert_eq!(5, tasks.tasks.len()); - debug_print(&format!("{}", tasks)); - - exit_test(0); - - }).unwrap(); - - start_kernel(); - - 1 -} \ No newline at end of file diff --git a/qemu_stm32_tests/src/test_timers.rs b/qemu_stm32_tests/src/test_timers.rs deleted file mode 100644 index ce6df7a..0000000 --- a/qemu_stm32_tests/src/test_timers.rs +++ /dev/null @@ -1,95 +0,0 @@ -use super::*; -use prelude::v1::*; - -use freertos_rs::*; - -#[no_mangle] -pub extern fn test_timers() -> i8 { - - let main_task = Task::new().name("main").start(|| { - - { - let counter_a = Arc::new(Mutex::new(0).unwrap()); - let counter_b = Arc::new(Mutex::new(100).unwrap()); - - { - let timer_a = { - let counter_a = counter_a.clone(); - - Timer::new(Duration::ms(50)) - .set_auto_reload(true) - .create(move |mut timer| { - if let Ok(mut counter_a) = counter_a.lock(Duration::ms(5)) { - *counter_a += 1; - } - }).unwrap() - }; - - let timer_b = { - let counter_b = counter_b.clone(); - - Timer::new(Duration::ms(100)) - .set_auto_reload(false) - .create(move |mut timer| { - if let Ok(mut counter_b) = counter_b.lock(Duration::ms(5)) { - *counter_b += 1; - } - }).unwrap() - }; - - timer_a.start(Duration::ms(1)).unwrap(); - timer_b.start(Duration::ms(1)).unwrap(); - - CurrentTask::delay(Duration::ms(225)); - - drop(timer_a); - drop(timer_b); - } - - let counter_a = counter_a.lock(Duration::infinite()).unwrap(); - assert_eq!(4, *counter_a); - - let counter_b = counter_b.lock(Duration::infinite()).unwrap(); - assert_eq!(101, *counter_b); - } - - // non-auto reloading timer test - { - let counter_a = Arc::new(Mutex::new(0).unwrap()); - - let timer_a = { - let counter_a = counter_a.clone(); - - Timer::new(Duration::ms(50)) - .set_auto_reload(false) - .create(move |mut timer| { - if let Ok(mut counter_a) = counter_a.lock(Duration::ms(5)) { - *counter_a += 1; - } - }).unwrap() - }; - - CurrentTask::delay(Duration::ms(100)); - - assert_eq!(0, *counter_a.lock(Duration::infinite()).unwrap()); - - timer_a.start(Duration::ms(1)).unwrap(); - - CurrentTask::delay(Duration::ms(200)); - assert_eq!(1, *counter_a.lock(Duration::infinite()).unwrap()); - - timer_a.start(Duration::ms(1)).unwrap(); - CurrentTask::delay(Duration::ms(200)); - - assert_eq!(2, *counter_a.lock(Duration::infinite()).unwrap()); - } - - - exit_test(0); - - }).unwrap(); - - start_kernel(); - - 1 -} \ No newline at end of file diff --git a/qemu_stm32_tests/src/testbed.rs b/qemu_stm32_tests/src/testbed.rs new file mode 100644 index 0000000..aed513e --- /dev/null +++ b/qemu_stm32_tests/src/testbed.rs @@ -0,0 +1,59 @@ + +pub trait Testbed { + fn debug_print(s: &str); + fn start_kernel() -> !; + fn exit_test(return_code: i8) -> !; + fn heap_allocated_memory() -> u32; + fn start_timer4_50ms(); + + fn ok() -> ! { + Self::exit_test(0) + } +} + +pub struct QemuTestbed; + +extern { + fn testbed_println(line: *const u8, line_len: u16); + fn testbed_start_kernel(); + fn testbed_return(return_code: i8); + fn testbed_allocated_memory() -> u32; + fn testbed_init_timer4_50ms_isr(); +} + +impl Testbed for QemuTestbed { + fn debug_print(s: &str) { + let s = s.as_bytes(); + unsafe { + testbed_println(s.as_ptr(), s.len() as u16); + } + } + + fn start_kernel() -> ! { + unsafe { + testbed_start_kernel(); + } + + unreachable!() + } + + fn exit_test(return_code: i8) -> ! { + unsafe { + testbed_return(return_code); + } + + unreachable!() + } + + fn heap_allocated_memory() -> u32 { + unsafe { + testbed_allocated_memory() + } + } + + fn start_timer4_50ms() { + unsafe { + testbed_init_timer4_50ms_isr(); + } + } +}