From 7db23d0bf0f838c0a94da3527f6c815b010d073c Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 29 Oct 2025 15:38:24 +0100 Subject: [PATCH 01/14] Embedded testing framework, running, but needs cleanup --- CMakeLists.txt | 6 + bsp_z7/freeRTOS/FreeRTOSConfig.h | 5 +- bsp_z7/ps7_cortexa9_0/include/xuartps.h | 3 +- .../libsrc/uartps/src/xuartps.h | 3 +- mission/freeRTOS_rust_helper.c | 38 +++-- mission/mission.c | 39 ++---- mission_rust/CMakeLists.txt | 6 +- mission_rust/Cargo.toml | 5 +- mission_rust/src/fsrc/sif.rs | 2 +- mission_rust/src/lib.rs | 39 +++--- mission_rust/src/low_level_tests/mod.rs | 131 ++++++++++++++++++ mission_rust/src/panic.rs | 35 ++++- 12 files changed, 243 insertions(+), 69 deletions(-) create mode 100644 mission_rust/src/low_level_tests/mod.rs diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cfbd4a..35d41c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,12 @@ if (${CMAKE_CROSSCOMPILING}) if(${ARM_SEMIHOSTING}) add_compile_definitions(ARM_SEMIHOSTING) endif() + + option(ROMEO_LOW_LEVEL_TESTS "enable semihosting for emulation" OFF) + + if(${ROMEO_LOW_LEVEL_TESTS}) + add_compile_definitions(LOW_LEVEL_TESTS) + endif() else() unset(ZYNQ_UART) unset(ZYNQ_UART CACHE) diff --git a/bsp_z7/freeRTOS/FreeRTOSConfig.h b/bsp_z7/freeRTOS/FreeRTOSConfig.h index d80aa50..bbfe1f0 100644 --- a/bsp_z7/freeRTOS/FreeRTOSConfig.h +++ b/bsp_z7/freeRTOS/FreeRTOSConfig.h @@ -91,7 +91,10 @@ #define configUSE_QUEUE_SETS 1 #define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 0 -#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1 +// TODO disable for non-testing? +#define configUSE_TASK_NOTIFICATIONS 1 +#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* Include the query-heap CLI command to query the free heap space. */ #define configINCLUDE_QUERY_HEAP_COMMAND 0 diff --git a/bsp_z7/ps7_cortexa9_0/include/xuartps.h b/bsp_z7/ps7_cortexa9_0/include/xuartps.h index 89ed5fd..9f52528 100644 --- a/bsp_z7/ps7_cortexa9_0/include/xuartps.h +++ b/bsp_z7/ps7_cortexa9_0/include/xuartps.h @@ -447,10 +447,11 @@ typedef struct { * u32 XUartPs_IsTransmitEmpty(XUartPs InstancePtr) * ******************************************************************************/ +#ifndef XUartPs_IsTransmitEmpty // Xilinx screwed up #define XUartPs_IsTransmitEmpty(InstancePtr) \ ((Xil_In32(((InstancePtr)->Config.BaseAddress) + (u32)XUARTPS_SR_OFFSET) & \ (u32)XUARTPS_SR_TXEMPTY) == (u32)XUARTPS_SR_TXEMPTY) - +#endif /************************** Function Prototypes *****************************/ diff --git a/bsp_z7/ps7_cortexa9_0/libsrc/uartps/src/xuartps.h b/bsp_z7/ps7_cortexa9_0/libsrc/uartps/src/xuartps.h index 89ed5fd..9f52528 100644 --- a/bsp_z7/ps7_cortexa9_0/libsrc/uartps/src/xuartps.h +++ b/bsp_z7/ps7_cortexa9_0/libsrc/uartps/src/xuartps.h @@ -447,10 +447,11 @@ typedef struct { * u32 XUartPs_IsTransmitEmpty(XUartPs InstancePtr) * ******************************************************************************/ +#ifndef XUartPs_IsTransmitEmpty // Xilinx screwed up #define XUartPs_IsTransmitEmpty(InstancePtr) \ ((Xil_In32(((InstancePtr)->Config.BaseAddress) + (u32)XUARTPS_SR_OFFSET) & \ (u32)XUARTPS_SR_TXEMPTY) == (u32)XUARTPS_SR_TXEMPTY) - +#endif /************************** Function Prototypes *****************************/ diff --git a/mission/freeRTOS_rust_helper.c b/mission/freeRTOS_rust_helper.c index 37cf13a..94bb42d 100644 --- a/mission/freeRTOS_rust_helper.c +++ b/mission/freeRTOS_rust_helper.c @@ -10,7 +10,6 @@ // TODO namespace the names // TODO panic if able, but not in runtime calls - // Wraps xTaskGetCurrentTaskHandle and returns NULL if no Task is running // // xTaskGetCurrentTaskHandle() will return a handle even if no task is @@ -33,8 +32,8 @@ StackType_t init_task_stack[configMINIMAL_STACK_SIZE * 10]; void freertos_init_and_start_scheduling(TaskFunction_t init_task) { // TaskHandle_t handle = - xTaskCreateStatic(init_task, "c_init", configMINIMAL_STACK_SIZE * 10, - NULL, configMAX_PRIORITIES - 1, init_task_stack, &init_task_data); + xTaskCreateStatic(init_task, "c_init", configMINIMAL_STACK_SIZE * 10, NULL, + configMAX_PRIORITIES - 1, init_task_stack, &init_task_data); // vTaskSetThreadLocalStoragePointer(handle, 0, NULL); vTaskStartScheduler(); @@ -194,7 +193,8 @@ uint8_t freertos_once(char *once_data_in, uint32_t once_data_len, once_data->local_mutex = xSemaphoreCreateRecursiveMutexStatic(&once_data->local_mutex_data); // if (once_data->local_mutex == NULL) { - // // will not happen, xSemaphoreCreateRecursiveMutexStatic returns its parameter + // // will not happen, xSemaphoreCreateRecursiveMutexStatic returns its + // parameter // // which we checked above to be != NULL // } once_data->once_state = INIT; @@ -236,7 +236,8 @@ uint8_t freertos_simple_once(uint8_t *once_data) { uint8_t result = 0; // This function is basically a flag stored in once_data, protected by // a critical section - // Critical section is ok, because simple arithmetic is bounded in execution time + // Critical section is ok, because simple arithmetic is bounded in execution + // time taskENTER_CRITICAL(); if (*once_data == 0) { *once_data = 1; @@ -253,9 +254,10 @@ uint32_t freertos_task_priority_max(void) { } // Note char* to keep sizeof correct on any platform -void *freertos_create_task_static(TaskFunction_t taskFunction, void *parameter, uint32_t priority, - char *task_data, uint32_t task_data_len, - char *stack, uint32_t stack_size) { +void *freertos_create_task_static(TaskFunction_t taskFunction, void *parameter, + uint32_t priority, char *task_data, + uint32_t task_data_len, char *stack, + uint32_t stack_size) { // TODO verify uint32_t vs configSTACK_DEPTH_TYPE if (task_data_len < sizeof(StaticTask_t)) { // printf("freertos_create_task_static: task data needs to be %zu long\n", @@ -275,9 +277,10 @@ void *freertos_create_task_static(TaskFunction_t taskFunction, void *parameter, // printf("freertos_create_task_static: Stack: %p %"PRIu32" %zu %zu\n", stack, // stack_size, stack_size_words, sizeof(StackType_t)); - - return xTaskCreateStatic(taskFunction, "rust", stack_size_words, parameter, priority, - (StackType_t *)stack, (StaticTask_t *)task_data); + + return xTaskCreateStatic(taskFunction, "rust", stack_size_words, parameter, + priority, (StackType_t *)stack, + (StaticTask_t *)task_data); } void *freertos_mutex_create_static(char *mutex_data, uint32_t mutex_data_len) { @@ -293,6 +296,15 @@ void *freertos_mutex_create_static(char *mutex_data, uint32_t mutex_data_len) { } // TODO: might be the wrong place -int freertos_get_sys_error(){ - return errno; +int freertos_get_sys_error() { return errno; } + +// private, for testing only TODO #define +void freertos_task_notify(void *task, uint32_t value) { + xTaskNotify(task, value, eSetValueWithOverwrite); +} + +uint32_t freertos_task_notify_wait() { + uint32_t value; + xTaskNotifyWait(0, 0, &value, portMAX_DELAY); + return value; } \ No newline at end of file diff --git a/mission/mission.c b/mission/mission.c index 4a8284b..4725d8b 100644 --- a/mission/mission.c +++ b/mission/mission.c @@ -14,32 +14,11 @@ #include #include +#ifndef LOW_LEVEL_TESTS void rust_main(void); - -void test_hardware() { - - int fd0 = hw_device_open("uart0", 5); - write(fd0, "UART0\n", 6); - int fd1 = hw_device_open("uart1", 5); - write(fd1, "UART1\n", 6); - - // uint8_t buffer[255]; - - // for (int i = 0; i< sizeof(buffer); i++) { - // buffer[i] = i; - // } - - // write(fd0, buffer, sizeof(buffer)); - - // vTaskDelay(10 / portTICK_PERIOD_MS); - - // write(1, "got:\n", 5); - - // int read_bytes = read(fd0, buffer, sizeof(buffer)); - // write(1, buffer, read_bytes); - // read_bytes = read(fd1, buffer, sizeof(buffer)); - // write(1, buffer, read_bytes); -} +#else +void rust_low_level_tests(void); +#endif // called to stop execution on error // to be implemented by bsp (do not return from it!) @@ -47,10 +26,18 @@ void done_error(); void init_task(void *_) { (void)_; +#ifndef LOW_LEVEL_TESTS rust_main(); +#else + rust_low_level_tests(); +#endif } +#ifndef LOW_LEVEL_TESTS #define STARTUP_MESSAGE1 "\nROMEO embedded obsw\nRelease: " +#else +#define STARTUP_MESSAGE1 "\nROMEO embedded obsw-tests\nRelease: " +#endif #define STARTUP_MESSAGE2 "\nBuild time: " void mission(void) { @@ -61,7 +48,7 @@ void mission(void) { write(1, BUILD_TIME_STRING, strlen(BUILD_TIME_STRING)); write(1, "\n", 1); - test_hardware(); + // test_hardware(); freertos_init_and_start_scheduling(init_task); diff --git a/mission_rust/CMakeLists.txt b/mission_rust/CMakeLists.txt index 06fbb87..d7e4f0c 100644 --- a/mission_rust/CMakeLists.txt +++ b/mission_rust/CMakeLists.txt @@ -10,9 +10,13 @@ if (${CMAKE_CROSSCOMPILING}) + if(${ROMEO_LOW_LEVEL_TESTS}) + set(FEATURES "--features=\"low_level_tests\"") + endif() + add_custom_target( mission_rust_internal - COMMAND cargo build -Zbuild-std=core --target=${CMAKE_SYSTEM_PROCESSOR} $<$:--release> + COMMAND cargo build -Zbuild-std=core --target=${CMAKE_SYSTEM_PROCESSOR} $<$:--release> ${FEATURES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/mission_rust/Cargo.toml b/mission_rust/Cargo.toml index 78f6b6b..66907c7 100644 --- a/mission_rust/Cargo.toml +++ b/mission_rust/Cargo.toml @@ -10,4 +10,7 @@ crate-type = ["staticlib"] panic = 'abort' [profile.release] -panic = 'abort' \ No newline at end of file +panic = 'abort' + +[features] +low_level_tests = [] \ No newline at end of file diff --git a/mission_rust/src/fsrc/sif.rs b/mission_rust/src/fsrc/sif.rs index 8d9074e..d4afdfa 100644 --- a/mission_rust/src/fsrc/sif.rs +++ b/mission_rust/src/fsrc/sif.rs @@ -73,7 +73,7 @@ impl Write for Stderr { macro_rules! sifln { ($(,)?) => ( //let mut stdout = Outbytes {}; - writeln!(Stdout {}); + {let _alwaysok = writeln!(crate::fsrc::sif::Stdout {});} ); ($($arg:tt)*) => ( {let _alwaysok = writeln!(crate::fsrc::sif::Stdout {}, $($arg)*);} diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index bfcc749..f85ca3c 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -6,6 +6,9 @@ mod dh; pub mod fsrc; mod panic; +#[cfg(feature = "low_level_tests")] +mod low_level_tests; + use core::fmt::Write; use core::time::Duration; @@ -210,28 +213,28 @@ fn init_task() -> ! { panic!("done"); - let test1 = TEST1.take().unwrap(); - let test2 = TEST2.take().unwrap(); - let receiver = RECEIVER.take().unwrap(); - let sender_handle = receiver.queue.sender(); - let clone = sender_handle.clone(); - let sender = SENDER.take_no_init().unwrap(); + // let test1 = TEST1.take().unwrap(); + // let test2 = TEST2.take().unwrap(); + // let receiver = RECEIVER.take().unwrap(); + // let sender_handle = receiver.queue.sender(); + // let clone = sender_handle.clone(); + // let sender = SENDER.take_no_init().unwrap(); - let mutex_copy = test1.mutex.clone(); - let a = Duration::from_millis(10); + // let mutex_copy = test1.mutex.clone(); + // let a = Duration::from_millis(10); - THREAD_2.take_no_init().unwrap().spawn(move || { - test2.run(mutex_copy, a); - }); + // THREAD_2.take_no_init().unwrap().spawn(move || { + // test2.run(mutex_copy, a); + // }); - THREAD_1.take_no_init().unwrap().spawn(move || { - test1.run(); - sender.run(clone); - }); + // THREAD_1.take_no_init().unwrap().spawn(move || { + // test1.run(); + // sender.run(clone); + // }); - THREAD_3.take_no_init().unwrap().spawn(move || { - receiver.run(); - }); + // THREAD_3.take_no_init().unwrap().spawn(move || { + // receiver.run(); + // }); sifln!("=====================Mission delete"); osal::thread::current().delete(); diff --git a/mission_rust/src/low_level_tests/mod.rs b/mission_rust/src/low_level_tests/mod.rs new file mode 100644 index 0000000..872654b --- /dev/null +++ b/mission_rust/src/low_level_tests/mod.rs @@ -0,0 +1,131 @@ +//! Low level tests which need to be run with FreeRTOS present +//! +//! They can not be run as standard rust tests as the depend on the C code. +//! As such this module is to be linked +//! as part of the main library into the low level C code. +//! +//! Entry into the tests is provided by the extern "C" function +//! rust_low_level_tests() to be called from C. It is assumed to be running in an init Thread +//! and will spawn a new testing thread for each unit test. + +use super::*; + +const OK: u32 = 0; +pub const PANIC: u32 = 1; + +macro_rules! function_with_name { + ($x:expr) => {{ + ($x, type_name_of($x)) + }}; +} + +fn type_name_of(_: T) -> &'static str { + core::any::type_name::() +} + +extern "C" { + // forward declaration of done[_error]() which are implemented in bsp. + // this is not part of ffi as users are not expected to use it. + fn done() -> !; + fn done_error() -> !; + + // Until further notice, these are private to testing + //uint32_t freertos_task_notify(void *task, uint32_t value) + fn freertos_task_notify(task: *const core::ffi::c_void, value: u32); + //uint32_t freertos_task_notify_wait() + fn freertos_task_notify_wait() -> u32; +} + +pub static mut CONTROLLER_TASK_HANDLE: *const core::ffi::c_void = 0 as *const core::ffi::c_void; + +/// Stack for test threads, is reused as tests are run one after another +/// +/// assumes we run in qemu, so memory is no consideration +static TEST_RUNNER_STACK: StaticReadOnceLock<[u8; 1024000]> = StaticReadOnceLock::new([0; 1024000]); +static TEST_RUNNER_THREAD_DATA: StaticReadOnceLock< + [core::ffi::c_char; osal::ffi::Sizes::TASK_DATA_SIZE], +> = StaticReadOnceLock::new([0; osal::ffi::Sizes::TASK_DATA_SIZE]); + +/// Runs the low level tests +/// +/// test are assumed to panic when failing. As we are running no-std with a custom panic, +/// tests are not continued after failing, as we have no reliable way of catching panics so far. +#[no_mangle] +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)]); +} + +/// 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 thread_data = TEST_RUNNER_THREAD_DATA.take_no_init().unwrap(); + + let number_of_tests = tests.len(); + + sifln!(); + if number_of_tests == 0 { + sifln!("no tests implemented 😢") + } else if number_of_tests == 1 { + sifln!("running 1 test") + } else { + sifln!("running {number_of_tests} tests"); + } + + let mut passed = 0; + let mut failed = 0; + + for test in tests { + // std::io::stdout().flush().unwrap(); + let test_function = test.0 as *const core::ffi::c_void; + unsafe { + osal::ffi::freertos_create_task_static( + run_one_test, + test_function, + 0, + thread_data.as_mut_ptr(), + thread_data.len().try_into().unwrap(), + stack.as_mut_ptr(), + stack.len().try_into().unwrap(), + ) + }; + let result = unsafe { freertos_task_notify_wait() }; + sif!("test {} ... ", test.1); + if result == OK { + sifln!("✅ ok"); + passed+=1; + } else { + sifln!("❌ FAILED"); + failed+=1; + } + } + sifln!(); + + if failed == 0 { + sifln!("test result: ✅ ok. {passed} passed; {failed} failed"); + unsafe { done() } + } else { + sifln!("test result: ❌ FAILED. {passed} passed; {failed} failed"); + unsafe { done_error() } + } + +} + +#[no_mangle] +extern "C" fn run_one_test(parameter: *mut core::ffi::c_void) -> ! { + let function: fn() = unsafe { core::mem::transmute(parameter) }; + function(); + unsafe { + freertos_task_notify(CONTROLLER_TASK_HANDLE, OK); + } + osal::thread::current().delete(); +} + +fn mutex() {} + +fn fail() { + assert_eq!(1, 2); +} diff --git a/mission_rust/src/panic.rs b/mission_rust/src/panic.rs index 26f0205..1f06dc2 100644 --- a/mission_rust/src/panic.rs +++ b/mission_rust/src/panic.rs @@ -1,8 +1,15 @@ -use core::panic::PanicInfo; use core::fmt::Write; +use core::panic::PanicInfo; +// forward declaration of done_error() which is implemented in bsp. +// this is not part of ffi as users are not expected to use it. extern "C" { - fn done_error(); // exit code != 0 + #[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(not(test))] @@ -18,9 +25,25 @@ fn panic(panic: &PanicInfo<'_>) -> ! { crate::fsrc::osal::thread::current().name(), panic ); - //TODO: stop RTOS, exit if hosted - unsafe { done_error() }; - loop {} + #[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( + super::low_level_tests::CONTROLLER_TASK_HANDLE, + super::low_level_tests::PANIC, + ); + } + super::osal::thread::current().delete(); + } } #[no_mangle] @@ -41,4 +64,4 @@ extern "C" fn rust_assert_called(ptr: *const core::ffi::c_char, line: core::ffi: #[no_mangle] extern "C" fn rust_alloc_failed() { panic!("allocation failed!"); -} \ No newline at end of file +} -- 2.43.0 From de26bfe1f4e360d88bd82a4a793b03d0bcffabcb Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Sat, 1 Nov 2025 15:29:35 +0100 Subject: [PATCH 02/14] refining low level tests --- bsp_z7/main.c | 14 +++++++++++++- mission_rust/src/lib.rs | 24 ++++++++++++++++-------- mission_rust/src/low_level_tests/mod.rs | 11 ++++++++--- mission_rust/src/panic.rs | 1 - 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/bsp_z7/main.c b/bsp_z7/main.c index 4e28bc4..dc0758d 100644 --- a/bsp_z7/main.c +++ b/bsp_z7/main.c @@ -15,6 +15,8 @@ #include "xscutimer.h" #include "xuartps_hw.h" +#include + /* * Configure the hardware as necessary to run this demo. */ @@ -172,11 +174,21 @@ void vApplicationIdleHook(void) { management options. If there is a lot of heap memory free then the configTOTAL_HEAP_SIZE value in FreeRTOSConfig.h can be reduced to free up RAM. */ - // xFreeHeapSpace = xPortGetFreeHeapSize(); + // xFreeHeapSpace = xPortGetFreeHeapSize(); // xMinimumEverFreeHeapSpace = xPortGetMinimumEverFreeHeapSize(); // /* Remove compiler warning about xFreeHeapSpace being set but never used. // */ (void)xFreeHeapSpace; (void)xMinimumEverFreeHeapSpace; +#ifdef LOW_LEVEL_TESTS +#define ERROR_MSG "\nPanic occured in the code executing the tests.\nWe'll consider this a failure:\n\ntest result: ❌ FAILED HORRIBLY\n" + // During low level tests, panics do not halt the execution but delete the current task. + // If this happens in the initial task, only the idle task remains. In this case, we + // simply end the execution. + + write(2, ERROR_MSG, sizeof(ERROR_MSG)); + + done_error(); +#endif } void vApplicationTickHook(void) { diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index f85ca3c..bd7090c 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -1,9 +1,15 @@ -#![no_std] +#![cfg_attr( + not(test), + no_std +)] + + #![feature(never_type)] //TODO os errors in API calls mod dh; pub mod fsrc; +#[cfg(not(test))] mod panic; #[cfg(feature = "low_level_tests")] @@ -36,7 +42,7 @@ extern "C" fn rust_main() { // we are already in a task (init task started by C), so we can use all APIs safely, // but start a new init task anyway to be able to control that task's stack here sifln!("Rust startup 🚀"); - THREAD_INIT.take_no_init().unwrap().spawn(init_task); + THREAD_INIT.take_no_init().unwrap_or_else(|| {loop{}}).spawn(init_task); // delete self, init task will take over thread::current().delete(); } @@ -188,10 +194,6 @@ static THREAD_3: StaticReadOnceLock< Duration::from_millis(200), )); -extern "C" { - fn done() -> !; -} - fn init_task() -> ! { sifln!("Mission enter"); @@ -208,10 +210,9 @@ fn init_task() -> ! { Duration::from_millis(500), 0.5, ); - + debugger.run().unwrap(); - panic!("done"); // let test1 = TEST1.take().unwrap(); // let test2 = TEST2.take().unwrap(); @@ -239,3 +240,10 @@ fn init_task() -> ! { sifln!("=====================Mission delete"); osal::thread::current().delete(); } + +#[test] +fn panic_test() { + println!("print"); + let test1 = TEST1.take().unwrap(); + panic!("oh noes"); +} diff --git a/mission_rust/src/low_level_tests/mod.rs b/mission_rust/src/low_level_tests/mod.rs index 872654b..8aff260 100644 --- a/mission_rust/src/low_level_tests/mod.rs +++ b/mission_rust/src/low_level_tests/mod.rs @@ -1,8 +1,8 @@ //! Low level tests which need to be run with FreeRTOS present //! -//! They can not be run as standard rust tests as the depend on the C code. +//! They can not be run as standard rust tests as they depend on the C code. //! As such this module is to be linked -//! as part of the main library into the low level C code. +//! as part of the main library with the low level C code. //! //! Entry into the tests is provided by the extern "C" function //! rust_low_level_tests() to be called from C. It is assumed to be running in an init Thread @@ -64,6 +64,8 @@ fn run_tests(tests: &mut [(fn(), &'static str)]) -> ! { let stack = TEST_RUNNER_STACK.take_no_init().unwrap(); let thread_data = TEST_RUNNER_THREAD_DATA.take_no_init().unwrap(); + panic!("oh noes"); + let number_of_tests = tests.len(); sifln!(); @@ -79,7 +81,10 @@ fn run_tests(tests: &mut [(fn(), &'static str)]) -> ! { let mut failed = 0; for test in tests { - // std::io::stdout().flush().unwrap(); + // 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; unsafe { osal::ffi::freertos_create_task_static( diff --git a/mission_rust/src/panic.rs b/mission_rust/src/panic.rs index 1f06dc2..0e44433 100644 --- a/mission_rust/src/panic.rs +++ b/mission_rust/src/panic.rs @@ -12,7 +12,6 @@ extern "C" { fn freertos_task_notify(task: *const core::ffi::c_void, value: u32); } -#[cfg(not(test))] #[panic_handler] fn panic(panic: &PanicInfo<'_>) -> ! { // unsafe { this breaks in ISR -- 2.43.0 From 2fd6c14fe3d653f29430bc53fc137f6dc7874110 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 17 Nov 2025 17:00:36 +0100 Subject: [PATCH 03/14] fork friendly submodule paths --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 5815ed4..7cb3ea4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/FreeRTOS/FreeRTOS-Kernel [submodule "contrib/lwip"] path = contrib/lwip - url = ../lwip + url = ../../romeo/lwip [submodule "fsbl-compiled"] path = fsbl-compiled - url = ../fsbl-compiled + url = ../../romeo/fsbl-compiled -- 2.43.0 From b42fb93d9882199a093ff9ba803fda6271b83e04 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 17 Nov 2025 22:39:35 +0100 Subject: [PATCH 04/14] adding 'lib' as crate target, does not break anything, allows doctests to be run --- mission_rust/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mission_rust/Cargo.toml b/mission_rust/Cargo.toml index 78f6b6b..d0a2189 100644 --- a/mission_rust/Cargo.toml +++ b/mission_rust/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["staticlib"] +crate-type = ["staticlib", "lib"] [profile.dev] panic = 'abort' -- 2.43.0 From 8bba968d021a31f26d266dfb2e51453d19284532 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 17 Nov 2025 22:41:04 +0100 Subject: [PATCH 05/14] panic-stubbing FreeRTOS calls for tests --- mission_rust/src/fsrc/osal/ffi.rs | 141 ++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/mission_rust/src/fsrc/osal/ffi.rs b/mission_rust/src/fsrc/osal/ffi.rs index 3c54ef4..1182aa1 100644 --- a/mission_rust/src/fsrc/osal/ffi.rs +++ b/mission_rust/src/fsrc/osal/ffi.rs @@ -23,6 +23,9 @@ impl Sizes { pub const TASK_MINIMAL_STACK_SIZE: usize = 10240; } + + +#[cfg(not(test))] extern "C" { //////////////////////// /// FreeRTOS API @@ -101,6 +104,144 @@ extern "C" { //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; } + +#[cfg(test)] +pub use test_api::*; + +#[cfg(test)] +mod test_api{ + use core::ptr; + + use super::*; + + //////////////////////// + /// FreeRTOS 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, + ) -> *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){ + 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 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){ + panic!("freeRTOS APIs are not implemented for tests"); + } + + pub fn freertos_task_stack_watermark() -> u32{ + panic!("freeRTOS APIs are not implemented for tests"); + 1 + } + + pub 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{ + 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, + ) -> *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, + ) -> 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, + ) -> 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, + ) -> *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{ + panic!("freeRTOS APIs are not implemented for tests"); + 1 + } + pub 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{ + panic!("freeRTOS APIs are not implemented for tests"); + 1 + } + + //int freertos_get_sys_error() + pub 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{ + panic!("freeRTOS APIs are not implemented for tests"); + 1 + } +} //////////////////////// /// libc API (read/write/etc) #[cfg(target_env = "gnu")] -- 2.43.0 From 141f24366cf69f8f6334738d5f4f08a3a946c850 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 17 Nov 2025 22:41:41 +0100 Subject: [PATCH 06/14] fixing rustdoc examples --- mission_rust/src/fsrc/introspection.rs | 38 +++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/mission_rust/src/fsrc/introspection.rs b/mission_rust/src/fsrc/introspection.rs index 38e155d..e9cf973 100644 --- a/mission_rust/src/fsrc/introspection.rs +++ b/mission_rust/src/fsrc/introspection.rs @@ -1,31 +1,31 @@ /// A trait to allow objects to be queried for some of their inner attributes at runtime. -/// +/// pub trait Introspection { /// Executes a function/closure on all members of the implementing struct. - /// + /// /// The parameters for the closure a reference to the member as well as the name of the member. - /// + /// /// To keep the trait object safe, we need to pass the closure as a reference. Otherwise, /// the trait can not be used as a `dyn` reference, because the function becomes generic. /// (Either using `impl FnMut` not `where F: FnMut` will be treated as generic making the /// trait not object safe). /// Additionally, the closure is typed as `FnMut`, to allow the closure to capture mutably. - /// - /// + /// + /// /// # Examples - /// + /// /// ```rust - /// use fsrc::introspection::Introspection; - /// + /// use mission_rust::fsrc::introspection::Introspection; + /// /// struct Empty {} - /// + /// /// impl Empty { /// fn getRandomNumber(&self) -> u8 { /// return 4 // chosen by fair dice roll. /// // guaranteed to be random. /// } /// } - /// + /// /// fn parsing_something(something: &dyn Introspection) { /// let mut a = 0; /// something.for_each_member(&mut |x, _| { @@ -39,18 +39,18 @@ pub trait Introspection { fn for_each_member(&self, f: &mut dyn FnMut(&dyn core::any::Any, &str) -> ()); /// Same as [for_each_member](Introspection::for_each_member), only with a return value for the closure. - /// + /// /// # Examples - /// + /// /// The return value allows using `x.downcast_ref()?` or similar constructs in the closure like in: - /// + /// /// ```rust - /// use fsrc::introspection::Introspection; - /// + /// use mission_rust::fsrc::introspection::Introspection; + /// /// struct Empty {} - /// + /// /// fn do_something(_: &Empty) {} - /// + /// /// fn parsing_something(something: &dyn Introspection) { /// something.for_each_member_return(&mut |x, _| { /// do_something(x.downcast_ref()?); @@ -58,6 +58,6 @@ pub trait Introspection { /// }); /// } /// ``` - /// + /// fn for_each_member_return(&self, f: &mut dyn FnMut(&dyn core::any::Any, &str) -> Option<()>); -} \ No newline at end of file +} -- 2.43.0 From 7f78b8f41e4831a081f4458ed1a7bb9adc569db6 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Mon, 17 Nov 2025 22:42:15 +0100 Subject: [PATCH 07/14] linux build is std --- bsp_linux/main.c | 3 ++- mission_rust/src/lib.rs | 17 ++++++++++------- mission_rust/src/panic.rs | 5 ++++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/bsp_linux/main.c b/bsp_linux/main.c index 6e80ac2..5748282 100644 --- a/bsp_linux/main.c +++ b/bsp_linux/main.c @@ -21,7 +21,8 @@ void done_error() { exit(1); } int test_socket(); // TODO link to GCC's personality or make the linux build not use it? -void rust_eh_personality() { puts("eh_personality"); } +// -> linux build is std at the moment +// void rust_eh_personality() { puts("eh_personality"); } void print_usage(const char *name) { fprintf(stderr, "Usage: %s [-s sim_ip] [-4|6]\n", name); diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index f05e40c..eb194d1 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -1,10 +1,10 @@ -#![no_std] +#![cfg_attr(target_env = "newlib", no_std)] #![feature(never_type)] #![feature(c_size_t)] // for ffi, tracking issue [88345] -//TODO os errors in API calls mod dh; pub mod fsrc; + mod panic; use core::fmt::Write; @@ -19,10 +19,7 @@ use osal::{ use crate::{ dh::EchoHandler, - fsrc::{ - dh::debug::DeviceHandlerDebugger, - osal::io::HardwareInterface, - }, + fsrc::{dh::debug::DeviceHandlerDebugger, osal::io::HardwareInterface}, }; static THREAD_INIT: StaticReadOnceLock< @@ -223,7 +220,13 @@ fn init_task() -> ! { THREAD_3.take_no_init().unwrap().spawn(move || { receiver.run(); }); - + sifln!("=====================Mission delete"); osal::thread::current().delete(); } + +mod tests { + #[test] + fn works_at_all() { + } +} diff --git a/mission_rust/src/panic.rs b/mission_rust/src/panic.rs index 26f0205..8efe79b 100644 --- a/mission_rust/src/panic.rs +++ b/mission_rust/src/panic.rs @@ -1,11 +1,14 @@ +#[cfg(target_env = "newlib")] use core::panic::PanicInfo; +#[cfg(target_env = "newlib")] use core::fmt::Write; +#[cfg(target_env = "newlib")] extern "C" { fn done_error(); // exit code != 0 } -#[cfg(not(test))] +#[cfg(target_env = "newlib")] #[panic_handler] fn panic(panic: &PanicInfo<'_>) -> ! { // unsafe { this breaks in ISR -- 2.43.0 From c482185ffd94fdeb601f6bfc660dbb505ba9156d Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 18 Nov 2025 12:43:15 +0100 Subject: [PATCH 08/14] looking for linux bug --- CMakeLists.txt | 12 +++++++----- bsp_linux/freeRTOS/FreeRTOSConfig.h | 2 +- mission_rust/src/fsrc/osal/thread.rs | 14 ++++++++++---- mission_rust/src/lib.rs | 2 +- mission_rust/src/low_level_tests/mod.rs | 12 +++++++----- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5468d19..430babf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,12 @@ 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) + +if(${ROMEO_LOW_LEVEL_TESTS}) + add_compile_definitions(LOW_LEVEL_TESTS) +endif() + # CMake options which are only available when crosscompiling if (${CMAKE_CROSSCOMPILING}) set(ZYNQ_UART UART1 CACHE STRING "Which PS UART to use for stdout") @@ -32,11 +38,7 @@ if (${CMAKE_CROSSCOMPILING}) add_compile_definitions(ARM_SEMIHOSTING) endif() - option(ROMEO_LOW_LEVEL_TESTS "enable semihosting for emulation" OFF) - - if(${ROMEO_LOW_LEVEL_TESTS}) - add_compile_definitions(LOW_LEVEL_TESTS) - endif() + else() unset(ZYNQ_UART) unset(ZYNQ_UART CACHE) diff --git a/bsp_linux/freeRTOS/FreeRTOSConfig.h b/bsp_linux/freeRTOS/FreeRTOSConfig.h index da4e77b..d27b640 100644 --- a/bsp_linux/freeRTOS/FreeRTOSConfig.h +++ b/bsp_linux/freeRTOS/FreeRTOSConfig.h @@ -46,7 +46,7 @@ #define configMINIMAL_STACK_SIZE ( ( unsigned short ) PTHREAD_STACK_MIN ) /* The stack size being passed is equal to the minimum stack size needed by pthread_create(). */ #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 200 * 1024 * 1024 ) ) #define configMAX_TASK_NAME_LEN ( 12 ) -#define configUSE_TRACE_FACILITY 1 +#define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_MUTEXES 1 diff --git a/mission_rust/src/fsrc/osal/thread.rs b/mission_rust/src/fsrc/osal/thread.rs index fd5fc2b..c82d724 100644 --- a/mission_rust/src/fsrc/osal/thread.rs +++ b/mission_rust/src/fsrc/osal/thread.rs @@ -206,7 +206,8 @@ 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 = self.stack.take_no_init().unwrap(); + let stack : &mut Box<[core::ffi::c_char;STACKSIZE]> = &mut Box::new([0;STACKSIZE]); 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 @@ -246,7 +247,8 @@ impl StaticThreadInner { ); } - let thread_data = self.thread_data.take_no_init().unwrap(); + // 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]); // 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 @@ -265,7 +267,11 @@ impl StaticThreadInner { panic!("could not create thread"); } - let handle = self.handle.take_no_init().unwrap(); + // let handle = self.handle.take_no_init().unwrap(); + let handle = &mut Box::new(ThreadHandle { + name: "todo", + freertos_handle: None, + }); handle.freertos_handle = Some(freertos_handle); // Store the Handle in task local storage, so we can use the rust types later on @@ -278,7 +284,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 3bae370..6f4849f 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -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; diff --git a/mission_rust/src/low_level_tests/mod.rs b/mission_rust/src/low_level_tests/mod.rs index 8aff260..862f647 100644 --- a/mission_rust/src/low_level_tests/mod.rs +++ b/mission_rust/src/low_level_tests/mod.rs @@ -41,7 +41,7 @@ pub static mut CONTROLLER_TASK_HANDLE: *const core::ffi::c_void = 0 as *const co /// Stack for test threads, is reused as tests are run one after another /// /// assumes we run in qemu, so memory is no consideration -static TEST_RUNNER_STACK: StaticReadOnceLock<[u8; 1024000]> = StaticReadOnceLock::new([0; 1024000]); +static TEST_RUNNER_STACK: StaticReadOnceLock<[core::ffi::c_char; 1024000]> = StaticReadOnceLock::new([0; 1024000]); static TEST_RUNNER_THREAD_DATA: StaticReadOnceLock< [core::ffi::c_char; osal::ffi::Sizes::TASK_DATA_SIZE], > = StaticReadOnceLock::new([0; osal::ffi::Sizes::TASK_DATA_SIZE]); @@ -56,15 +56,15 @@ extern "C" fn rust_low_level_tests() { unsafe { CONTROLLER_TASK_HANDLE = osal::ffi::freertos_task_current() }; run_tests(&mut [function_with_name!(mutex), function_with_name!(fail), function_with_name!(mutex)]); } - +use std::sync::{Arc, Mutex}; /// 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(); - - panic!("oh noes"); + let thread_data = Arc::new(Mutex::new([0i8;osal::ffi::Sizes::TASK_DATA_SIZE])); let number_of_tests = tests.len(); @@ -86,11 +86,13 @@ fn run_tests(tests: &mut [(fn(), &'static str)]) -> ! { // 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(); unsafe { osal::ffi::freertos_create_task_static( run_one_test, test_function, - 0, + 2, thread_data.as_mut_ptr(), thread_data.len().try_into().unwrap(), stack.as_mut_ptr(), -- 2.43.0 From e1daa74c5af156722c485a5bb035263eeafc6a09 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 18 Nov 2025 17:18:29 +0100 Subject: [PATCH 09/14] cleaned up low level tests. Run on linux as well now --- CMakeLists.txt | 5 +- bsp_linux/freeRTOS/FreeRTOSConfig.h | 11 ++- bsp_linux/main.c | 8 +- mission_rust/CMakeLists.txt | 10 +-- mission_rust/src/fsrc/osal/ffi.rs | 103 ++++++++++-------------- mission_rust/src/fsrc/osal/thread.rs | 14 +--- mission_rust/src/lib.rs | 6 +- mission_rust/src/low_level_tests/mod.rs | 39 ++++++--- mission_rust/src/panic.rs | 87 ++++++++++---------- 9 files changed, 143 insertions(+), 140 deletions(-) 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(); } } -- 2.43.0 From 785424dd22242b4319e2e4dc87f9b50ced05e278 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 18 Nov 2025 17:26:31 +0100 Subject: [PATCH 10/14] added qemu to docker --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 17a6e65..b5360dc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ FROM debian:12.7-slim -# Install required packages +# Install required packages TODO combine with next run, so the rm affects this one as well RUN apt-get update && apt-get upgrade --yes RUN DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \ @@ -14,6 +14,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends ca-certificates \ gcc-arm-none-eabi \ libnewlib-arm-none-eabi \ + qemu-system-arm \ # remove for image size && rm -rf /var/lib/apt/lists/* -- 2.43.0 From 8aa75620a0fd8eb6d3fe84fefe9d26800331c2f9 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 18 Nov 2025 17:39:31 +0100 Subject: [PATCH 11/14] docker: slimmer system update --- docker/Dockerfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index b5360dc..dbb13e6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,9 +1,8 @@ FROM debian:12.7-slim -# Install required packages TODO combine with next run, so the rm affects this one as well -RUN apt-get update && apt-get upgrade --yes - -RUN DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \ +# Install required packages +RUN dpkg-reconfigure debconf --frontend=noninteractive && apt-get update && apt-get upgrade --yes && \ + apt-get install --yes --no-install-recommends \ git \ ssh \ make \ -- 2.43.0 From 78515392b8d972cb9387d22f261287743921fc30 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 18 Nov 2025 19:17:48 +0100 Subject: [PATCH 12/14] ci build low level tests --- .gitea/workflows/ci.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index e5edc6c..c749284 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -19,6 +19,18 @@ jobs: run: | cd build_z7 make -j8 + - name: Reconfigure z7 + run: | + cd build_z7 + cmake -DARM_SEMIHOSTING=ON -DROMEO_LOW_LEVEL_TESTS=ON .. + - name: Build z7 low level tests + run: | + cd build_z7 + make -j8 + - name: Run z7 low level tests + run: | + cd build_z7 + qemu-system-arm -semihosting -nographic -monitor none -serial none -serial stdio -machine xilinx-zynq-a9 -m 500M -kernel romeo-low_level_tests - name: Configure linux run: | mkdir build_linux -- 2.43.0 From 916e047a20ae47bcf8e090a7a3ed8df620ab9a1f Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 18 Nov 2025 20:42:06 +0100 Subject: [PATCH 13/14] more tests in ci --- .gitea/workflows/ci.yaml | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index c749284..e85210a 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -10,33 +10,31 @@ jobs: with: token: ${{ secrets.ROMEO_NUMALFIX_TOKEN }} submodules: 'recursive' - - name: Configure z7 + - name: Build z7 run: | mkdir build_z7 cd build_z7 - cmake -DCMAKE_TOOLCHAIN_FILE=../bsp_z7/cmake/arm-none-eabi.toolchain .. - - name: Build z7 - run: | - cd build_z7 + cmake -DCMAKE_TOOLCHAIN_FILE=../bsp_z7/cmake/arm-none-eabi.toolchain -DCMAKE_BUILD_TYPE=Release .. make -j8 - - name: Reconfigure z7 + - name: Low level tests z7 run: | cd build_z7 cmake -DARM_SEMIHOSTING=ON -DROMEO_LOW_LEVEL_TESTS=ON .. - - name: Build z7 low level tests - run: | - cd build_z7 make -j8 - - name: Run z7 low level tests - run: | - cd build_z7 qemu-system-arm -semihosting -nographic -monitor none -serial none -serial stdio -machine xilinx-zynq-a9 -m 500M -kernel romeo-low_level_tests - - name: Configure linux + - name: Build linux run: | mkdir build_linux cd build_linux cmake .. - - name: Build linux + make -j8 + - name: Low level tests linux run: | cd build_linux + cmake -DROMEO_LOW_LEVEL_TESTS=ON .. make -j8 + ./romeo-low_level_tests + - name: Rust tests + run: | + cd mission_rust + cargo test \ No newline at end of file -- 2.43.0 From 63c6cf381f36f1ea68c8c91e51af1d11677d615d Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Tue, 18 Nov 2025 20:56:29 +0100 Subject: [PATCH 14/14] ci only on PR --- .gitea/workflows/ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index e85210a..0ee1610 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -1,5 +1,6 @@ name: ROMEO CI Builder -on: push +on: + pull_request jobs: Build Code: -- 2.43.0