diff --git a/CMakeLists.txt b/CMakeLists.txt index 430babf..a59c9eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,10 +17,11 @@ set(ROMEO_WARNING_FLAGS -Wpedantic -Werror) # TODO so far, this only affects mission code, not bsp -option(ROMEO_LOW_LEVEL_TESTS "enable semihosting for emulation" OFF) +option(ROMEO_LOW_LEVEL_TESTS "build low level tests INSTEAD of normal binary" OFF) if(${ROMEO_LOW_LEVEL_TESTS}) add_compile_definitions(LOW_LEVEL_TESTS) + set(OBSW_NAME romeo-low_level_tests) endif() # CMake options which are only available when crosscompiling @@ -37,8 +38,6 @@ if (${CMAKE_CROSSCOMPILING}) if(${ARM_SEMIHOSTING}) add_compile_definitions(ARM_SEMIHOSTING) endif() - - else() unset(ZYNQ_UART) unset(ZYNQ_UART CACHE) diff --git a/bsp_linux/freeRTOS/FreeRTOSConfig.h b/bsp_linux/freeRTOS/FreeRTOSConfig.h index d27b640..fa99e9e 100644 --- a/bsp_linux/freeRTOS/FreeRTOSConfig.h +++ b/bsp_linux/freeRTOS/FreeRTOSConfig.h @@ -78,6 +78,14 @@ #define configRECORD_STACK_HIGH_ADDRESS 1 + +// see low_level_tests/mod.rs, some weird effect in FreeRTOS +#ifndef LOW_LEVEL_TESTS +#define configMAX_PRIORITIES ( 10 ) +#else +#define configMAX_PRIORITIES ( 1024 ) +#endif + /* Software timer related configuration options. The maximum possible task * priority is configMAX_PRIORITIES - 1. The priority of the timer task is * deliberately set higher to ensure it is correctly capped back to @@ -87,7 +95,8 @@ #define configTIMER_QUEUE_LENGTH 20 #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) -#define configMAX_PRIORITIES ( 10 ) + + /* Run time stats gathering configuration options. */ unsigned long ulGetRunTimeCounterValue( void ); /* Prototype of function that returns run time counter. */ diff --git a/bsp_linux/main.c b/bsp_linux/main.c index 5748282..ced7b19 100644 --- a/bsp_linux/main.c +++ b/bsp_linux/main.c @@ -8,6 +8,7 @@ #include #include #include +#include const char *sim_ip = "localhost"; int ai_family = AF_UNSPEC; @@ -21,14 +22,15 @@ void done_error() { exit(1); } int test_socket(); // TODO link to GCC's personality or make the linux build not use it? -// -> linux build is std at the moment -// void rust_eh_personality() { puts("eh_personality"); } +// -> linux build is std at the moment, only used for tests +#ifdef LOW_LEVEL_TESTS +void rust_eh_personality() { puts("eh_personality"); } +#endif void print_usage(const char *name) { fprintf(stderr, "Usage: %s [-s sim_ip] [-4|6]\n", name); } -#include int main(int argc, char **argv) { hw_device_open( diff --git a/mission_rust/CMakeLists.txt b/mission_rust/CMakeLists.txt index 6f8a8c5..9f37471 100644 --- a/mission_rust/CMakeLists.txt +++ b/mission_rust/CMakeLists.txt @@ -7,11 +7,11 @@ #TODO look into corrosion cmake plugin -if (${CMAKE_CROSSCOMPILING}) +if(${ROMEO_LOW_LEVEL_TESTS}) + set(FEATURES "--features=\"low_level_tests\"") +endif() - if(${ROMEO_LOW_LEVEL_TESTS}) - set(FEATURES "--features=\"low_level_tests\"") - endif() +if (${CMAKE_CROSSCOMPILING}) add_custom_target( mission_rust_internal @@ -31,7 +31,7 @@ else() add_custom_target( mission_rust_internal - COMMAND cargo build $<$:--release> + COMMAND cargo build $<$:--release> ${FEATURES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/mission_rust/src/fsrc/osal/ffi.rs b/mission_rust/src/fsrc/osal/ffi.rs index 1182aa1..42162b8 100644 --- a/mission_rust/src/fsrc/osal/ffi.rs +++ b/mission_rust/src/fsrc/osal/ffi.rs @@ -33,7 +33,7 @@ extern "C" { //void *create_task_static(TaskFunction_t taskFunction, void *parameter, // char *task_data, uint32_t task_data_len, char *stack, uint32_t stack_size) pub fn freertos_create_task_static( - taskFunction: TaskFunction, + task_function: TaskFunction, parameter: *const core::ffi::c_void, priority: u32, task_data: *mut core::ffi::c_char, @@ -110,8 +110,6 @@ pub use test_api::*; #[cfg(test)] mod test_api{ - use core::ptr; - use super::*; //////////////////////// @@ -119,127 +117,112 @@ mod test_api{ /// shimmed in mission/freeRTOS_rust_helper.c //void *create_task_static(TaskFunction_t taskFunction, void *parameter, // char *task_data, uint32_t task_data_len, char *stack, uint32_t stack_size) - pub fn freertos_create_task_static( - taskFunction: TaskFunction, - parameter: *const core::ffi::c_void, - priority: u32, - task_data: *mut core::ffi::c_char, - task_data_len: u32, - stack: *mut core::ffi::c_char, - stack_size: u32, + pub unsafe fn freertos_create_task_static( + _task_function: TaskFunction, + _parameter: *const core::ffi::c_void, + _priority: u32, + _task_data: *mut core::ffi::c_char, + _task_data_len: u32, + _stack: *mut core::ffi::c_char, + _stack_size: u32, ) -> *const core::ffi::c_void{ panic!("freeRTOS APIs are not implemented for tests"); - ptr::null() } // pub fn stop_it(); - pub fn freertos_task_suspend(handle: *const core::ffi::c_void){ + pub unsafe fn freertos_task_suspend(_handle: *const core::ffi::c_void){ panic!("freeRTOS APIs are not implemented for tests"); } - pub fn freertos_task_delete(handle: *const core::ffi::c_void) -> !{ - panic!("freeRTOS APIs are not implemented for tests"); - loop{} + pub unsafe fn freertos_task_delete(_handle: *const core::ffi::c_void) -> !{ + panic!("freeRTOS APIs are not implemented for tests") } - pub fn freertos_task_storage_set( - handle: *const core::ffi::c_void, - data: *const core::ffi::c_void, + pub unsafe fn freertos_task_storage_set( + _handle: *const core::ffi::c_void, + _data: *const core::ffi::c_void, ){ panic!("freeRTOS APIs are not implemented for tests"); } - pub fn freertos_task_storage_get(handle: *const core::ffi::c_void) -> *const core::ffi::c_void{ - panic!("freeRTOS APIs are not implemented for tests"); - ptr::null() - } - - pub fn freertos_task_delay(milliseconds: u32){ + pub unsafe fn freertos_task_storage_get(_handle: *const core::ffi::c_void) -> *const core::ffi::c_void{ panic!("freeRTOS APIs are not implemented for tests"); } - pub fn freertos_task_stack_watermark() -> u32{ + pub unsafe fn freertos_task_delay(_milliseconds: u32){ panic!("freeRTOS APIs are not implemented for tests"); - 1 } - pub fn freertos_task_current() -> *const core::ffi::c_void{ + pub unsafe fn freertos_task_stack_watermark() -> u32{ + panic!("freeRTOS APIs are not implemented for tests"); + } + + pub unsafe fn freertos_task_current() -> *const core::ffi::c_void{ panic!("freeRTOS APIs are not implemented for tests"); - ptr::null() } // TODO this should be passed by compile time value - pub fn freertos_task_priority_max() -> u32{ + pub unsafe fn freertos_task_priority_max() -> u32{ panic!("freeRTOS APIs are not implemented for tests"); - u32::MAX } //void *freertos_queue_create_static(uint32_t depth, uint32_t element_size, // char *queue_data, uint32_t queue_data_len, // uint8_t *queue, uint32_t queue_len) { - pub fn freertos_queue_create_static( - depth: u32, - element_size: u32, - queue_data: *mut core::ffi::c_char, - queue_data_len: u32, - queue: *mut u8, + pub unsafe fn freertos_queue_create_static( + _depth: u32, + _element_size: u32, + _queue_data: *mut core::ffi::c_char, + _queue_data_len: u32, + _queue: *mut u8, ) -> *const core::ffi::c_void{ panic!("freeRTOS APIs are not implemented for tests"); - ptr::null() } - pub fn freertos_queue_receive( - queue: *const core::ffi::c_void, - message: *const core::ffi::c_void, + pub unsafe fn freertos_queue_receive( + _queue: *const core::ffi::c_void, + _message: *const core::ffi::c_void, ) -> u8{ panic!("freeRTOS APIs are not implemented for tests"); - 1 } - pub fn freertos_queue_send( - queue: *const core::ffi::c_void, - message: *const core::ffi::c_void, + pub unsafe fn freertos_queue_send( + _queue: *const core::ffi::c_void, + _message: *const core::ffi::c_void, ) -> u8{ panic!("freeRTOS APIs are not implemented for tests"); - 1 } - pub fn freertos_mutex_create_static( - mutex_data: *const core::ffi::c_char, - mutex_data_len: u32, + pub unsafe fn freertos_mutex_create_static( + _mutex_data: *const core::ffi::c_char, + _mutex_data_len: u32, ) -> *const core::ffi::c_void{ panic!("freeRTOS APIs are not implemented for tests"); - ptr::null() } - pub fn freertos_mutex_take(mutex: *const core::ffi::c_void) -> u8{ + pub unsafe fn freertos_mutex_take(_mutex: *const core::ffi::c_void) -> u8{ panic!("freeRTOS APIs are not implemented for tests"); - 1 } - pub fn freertos_mutex_give(mutex: *const core::ffi::c_void) -> u8{ + pub unsafe fn freertos_mutex_give(_mutex: *const core::ffi::c_void) -> u8{ panic!("freeRTOS APIs are not implemented for tests"); - 1 } //uint8_t freertos_simple_once(uint8_t *once_data) - pub fn freertos_simple_once(once_data: *const u8) -> u8{ + pub unsafe fn freertos_simple_once(_once_data: *const u8) -> u8{ panic!("freeRTOS APIs are not implemented for tests"); - 1 } //int freertos_get_sys_error() - pub fn freertos_get_sys_error() -> core::ffi::c_int{ + pub unsafe fn freertos_get_sys_error() -> core::ffi::c_int{ panic!("freeRTOS APIs are not implemented for tests"); - 1 } //////////////////////// /// Harware Abstraction API in common/include/interfaces.h /// Used for access to peripherals to make switching between linux and embedded easier //int hw_device_open(const char * path, size_t path_len); - pub fn hw_device_open(path: *const core::ffi::c_char, path_len: core::ffi::c_size_t) -> core::ffi::c_int{ + pub unsafe fn hw_device_open(_path: *const core::ffi::c_char, _path_len: core::ffi::c_size_t) -> core::ffi::c_int{ panic!("freeRTOS APIs are not implemented for tests"); - 1 } } //////////////////////// diff --git a/mission_rust/src/fsrc/osal/thread.rs b/mission_rust/src/fsrc/osal/thread.rs index c82d724..fd5fc2b 100644 --- a/mission_rust/src/fsrc/osal/thread.rs +++ b/mission_rust/src/fsrc/osal/thread.rs @@ -206,8 +206,7 @@ impl StaticThreadInner { panic!("StaticThread(\"{}\")::spawn: priority {} is larger than maximum ({})", self.name, self.priority, unsafe{super::ffi::freertos_task_priority_max()}); } - // let stack = self.stack.take_no_init().unwrap(); - let stack : &mut Box<[core::ffi::c_char;STACKSIZE]> = &mut Box::new([0;STACKSIZE]); + let stack = self.stack.take_no_init().unwrap(); let closure_space = core::mem::size_of::(); // Will be checked later on, but this way we get a more precise output instead of a generic panic @@ -247,8 +246,7 @@ impl StaticThreadInner { ); } - // let thread_data = self.thread_data.take_no_init().unwrap(); - let thread_data: &mut Box<[core::ffi::c_char;ffi::Sizes::TASK_DATA_SIZE]> = &mut Box::new([0;ffi::Sizes::TASK_DATA_SIZE]); + let thread_data = self.thread_data.take_no_init().unwrap(); // SAFE: We only pass pointers derived from &'static references to the OS, so the pointers will // be valid for the complete runtime. This includes values borrowed by the closure @@ -267,11 +265,7 @@ impl StaticThreadInner { panic!("could not create thread"); } - // let handle = self.handle.take_no_init().unwrap(); - let handle = &mut Box::new(ThreadHandle { - name: "todo", - freertos_handle: None, - }); + let handle = self.handle.take_no_init().unwrap(); handle.freertos_handle = Some(freertos_handle); // Store the Handle in task local storage, so we can use the rust types later on @@ -284,7 +278,7 @@ impl StaticThreadInner { ) }; // this creates a copy of handle, as it impls Copy - **handle + *handle } } diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 6f4849f..9061f96 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(target_env = "newlib", no_std)] +#![cfg_attr(any(target_env = "newlib", feature = "low_level_tests"), no_std)] #![feature(never_type)] #![feature(c_size_t)] // for ffi, tracking issue [88345] @@ -6,7 +6,7 @@ mod dh; pub mod fsrc; mod panic; -//#[cfg(feature = "low_level_tests")] +#[cfg(feature = "low_level_tests")] mod low_level_tests; use core::fmt::Write; @@ -229,6 +229,6 @@ fn init_task() -> ! { mod tests { #[test] - fn works_at_all() { + fn work_at_all() { } } diff --git a/mission_rust/src/low_level_tests/mod.rs b/mission_rust/src/low_level_tests/mod.rs index 862f647..a9e8905 100644 --- a/mission_rust/src/low_level_tests/mod.rs +++ b/mission_rust/src/low_level_tests/mod.rs @@ -54,17 +54,25 @@ static TEST_RUNNER_THREAD_DATA: StaticReadOnceLock< extern "C" fn rust_low_level_tests() { // Safe because I do not care right now. TODO unsafe { CONTROLLER_TASK_HANDLE = osal::ffi::freertos_task_current() }; - run_tests(&mut [function_with_name!(mutex), function_with_name!(fail), function_with_name!(mutex)]); + run_tests(&mut [function_with_name!(work_at_all)]); } -use std::sync::{Arc, Mutex}; + +#[cfg(not(target_os = "linux"))] +fn increment_on_linux(number: &mut u32) { +} + +#[cfg(target_os = "linux")] +fn increment_on_linux(number: &mut u32) { + *number = *number + 1; +} + /// Runner for low level tests, will attempt to look a bit like std rust tests. /// expects a function and its name, to print it /// use the function_with_name! macro to generate the tuple fn run_tests(tests: &mut [(fn(), &'static str)]) -> ! { let stack = TEST_RUNNER_STACK.take_no_init().unwrap(); - let stack = Arc::new(Mutex::new([0i8;102400])); let thread_data = TEST_RUNNER_THREAD_DATA.take_no_init().unwrap(); - let thread_data = Arc::new(Mutex::new([0i8;osal::ffi::Sizes::TASK_DATA_SIZE])); + let number_of_tests = tests.len(); @@ -79,20 +87,21 @@ fn run_tests(tests: &mut [(fn(), &'static str)]) -> ! { let mut passed = 0; let mut failed = 0; + let mut priority = 1; for test in tests { - // Note for the pedantic: Static FreeRTOS can not delete tasks, only - // suspend them. So it is not formally correct to resuse the task data - // and stack. But as the suspended task will not be restarted, it does work - // in this (testing!) context. let test_function = test.0 as *const core::ffi::c_void; - let stack = &mut stack.lock().unwrap(); - let thread_data = &mut thread_data.lock().unwrap(); + for element in thread_data.iter_mut() { + *element = 0; + } + for element in stack.iter_mut() { + *element = 0; + } unsafe { osal::ffi::freertos_create_task_static( run_one_test, test_function, - 2, + priority, thread_data.as_mut_ptr(), thread_data.len().try_into().unwrap(), stack.as_mut_ptr(), @@ -108,6 +117,11 @@ fn run_tests(tests: &mut [(fn(), &'static str)]) -> ! { sifln!("❌ FAILED"); failed+=1; } + // some weird bug/effect in FreeRTOS on linux breaks here + // if a task priority is reused. + // So, we just increase the priority lineary. We mande sure in FreeRTOSConfig.h + // to have enough (1024) available. + increment_on_linux(&mut priority) } sifln!(); @@ -118,7 +132,6 @@ fn run_tests(tests: &mut [(fn(), &'static str)]) -> ! { sifln!("test result: ❌ FAILED. {passed} passed; {failed} failed"); unsafe { done_error() } } - } #[no_mangle] @@ -131,7 +144,7 @@ extern "C" fn run_one_test(parameter: *mut core::ffi::c_void) -> ! { osal::thread::current().delete(); } -fn mutex() {} +fn work_at_all() {} fn fail() { assert_eq!(1, 2); diff --git a/mission_rust/src/panic.rs b/mission_rust/src/panic.rs index c14a191..1899a4a 100644 --- a/mission_rust/src/panic.rs +++ b/mission_rust/src/panic.rs @@ -1,49 +1,52 @@ -#[cfg(target_env = "newlib")] -use core::panic::PanicInfo; -#[cfg(target_env = "newlib")] -use core::fmt::Write; +#[cfg(any(target_env = "newlib", feature = "low_level_tests"))] +pub use no_std::*; -#[cfg(target_env = "newlib")] -extern "C" { - #[cfg(not(feature = "low_level_tests"))] - fn done_error() -> !; // exit code != 0 - - #[cfg(feature = "low_level_tests")] - //uint32_t freertos_task_notify(void *task, uint32_t value) - fn freertos_task_notify(task: *const core::ffi::c_void, value: u32); -} +#[cfg(any(target_env = "newlib", feature = "low_level_tests"))] +mod no_std { + use core::fmt::Write; + use core::panic::PanicInfo; -#[cfg(target_env = "newlib")] -#[panic_handler] -fn panic(panic: &PanicInfo<'_>) -> ! { - // unsafe { this breaks in ISR - // osal::stop_it(); - // } - _ = writeln!(crate::fsrc::sif::Stderr {}, ""); - _ = writeln!( - crate::fsrc::sif::Stderr {}, - "Thread '{}' {}", - crate::fsrc::osal::thread::current().name(), - panic - ); - #[cfg(not(feature = "low_level_tests"))] - { - unsafe { done_error() }; - #[allow(unreachable_code)] // safeguard as this is only a weak contract with C - loop {} + + extern "C" { + #[cfg(not(feature = "low_level_tests"))] + fn done_error() -> !; // exit code != 0 + + #[cfg(feature = "low_level_tests")] + //uint32_t freertos_task_notify(void *task, uint32_t value) + fn freertos_task_notify(task: *const core::ffi::c_void, value: u32); } - #[cfg(feature = "low_level_tests")] - { - // When running low level tests, do not exit on panic - // but delete thread so the main thread can continue - // with the next test - unsafe { - freertos_task_notify( - super::low_level_tests::CONTROLLER_TASK_HANDLE, - super::low_level_tests::PANIC, - ); + + #[panic_handler] + fn panic(panic: &PanicInfo<'_>) -> ! { + // unsafe { this breaks in ISR + // osal::stop_it(); + // } + _ = writeln!(crate::fsrc::sif::Stderr {}, ""); + _ = writeln!( + crate::fsrc::sif::Stderr {}, + "Thread '{}' {}", + crate::fsrc::osal::thread::current().name(), + panic + ); + #[cfg(not(feature = "low_level_tests"))] + { + unsafe { done_error() }; + #[allow(unreachable_code)] // safeguard as this is only a weak contract with C + loop {} + } + #[cfg(feature = "low_level_tests")] + { + // When running low level tests, do not exit on panic + // but delete thread so the main thread can continue + // with the next test + unsafe { + freertos_task_notify( + crate::low_level_tests::CONTROLLER_TASK_HANDLE, + crate::low_level_tests::PANIC, + ); + } + crate::fsrc::osal::thread::current().delete(); } - super::osal::thread::current().delete(); } }