forked from ROMEO/fsw-ws
fork/ci #2
@@ -1,5 +1,6 @@
|
||||
name: ROMEO CI Builder
|
||||
on: push
|
||||
on:
|
||||
pull_request
|
||||
|
||||
jobs:
|
||||
Build Code:
|
||||
@@ -10,21 +11,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
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=../bsp_z7/cmake/arm-none-eabi.toolchain -DCMAKE_BUILD_TYPE=Release ..
|
||||
make -j8
|
||||
- name: Low level tests z7
|
||||
run: |
|
||||
cd build_z7
|
||||
cmake -DARM_SEMIHOSTING=ON -DROMEO_LOW_LEVEL_TESTS=ON ..
|
||||
make -j8
|
||||
- name: Configure linux
|
||||
qemu-system-arm -semihosting -nographic -monitor none -serial none -serial stdio -machine xilinx-zynq-a9 -m 500M -kernel romeo-low_level_tests
|
||||
- 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
|
||||
+2
-2
@@ -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
|
||||
|
||||
@@ -17,6 +17,13 @@ set(ROMEO_WARNING_FLAGS
|
||||
-Wpedantic
|
||||
-Werror) # TODO so far, this only affects mission code, not bsp
|
||||
|
||||
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
|
||||
if (${CMAKE_CROSSCOMPILING})
|
||||
set(ZYNQ_UART UART1 CACHE STRING "Which PS UART to use for stdout")
|
||||
|
||||
@@ -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
|
||||
@@ -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. */
|
||||
|
||||
+4
-1
@@ -8,6 +8,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <hardware/interfaces.h>
|
||||
|
||||
const char *sim_ip = "localhost";
|
||||
int ai_family = AF_UNSPEC;
|
||||
@@ -21,13 +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, 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 <hardware/interfaces.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
hw_device_open(
|
||||
|
||||
@@ -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
|
||||
|
||||
+13
-1
@@ -15,6 +15,8 @@
|
||||
#include "xscutimer.h"
|
||||
#include "xuartps_hw.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
|
||||
+3
-3
@@ -1,9 +1,8 @@
|
||||
FROM debian:12.7-slim
|
||||
|
||||
# Install required packages
|
||||
RUN apt-get update && apt-get upgrade --yes
|
||||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
|
||||
RUN dpkg-reconfigure debconf --frontend=noninteractive && apt-get update && apt-get upgrade --yes && \
|
||||
apt-get install --yes --no-install-recommends \
|
||||
git \
|
||||
ssh \
|
||||
make \
|
||||
@@ -14,6 +13,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/*
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -11,7 +11,11 @@
|
||||
#include <git_version.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef LOW_LEVEL_TESTS
|
||||
void rust_main(void);
|
||||
#else
|
||||
void rust_low_level_tests(void);
|
||||
#endif
|
||||
|
||||
// called to stop execution on error
|
||||
// to be implemented by bsp (do not return from it!)
|
||||
@@ -19,10 +23,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) {
|
||||
|
||||
@@ -7,11 +7,15 @@
|
||||
|
||||
#TODO look into corrosion cmake plugin
|
||||
|
||||
if(${ROMEO_LOW_LEVEL_TESTS})
|
||||
set(FEATURES "--features=\"low_level_tests\"")
|
||||
endif()
|
||||
|
||||
if (${CMAKE_CROSSCOMPILING})
|
||||
|
||||
add_custom_target(
|
||||
mission_rust_internal
|
||||
COMMAND cargo build -Zbuild-std=core --target=${CMAKE_SYSTEM_PROCESSOR} $<$<CONFIG:Release>:--release>
|
||||
COMMAND cargo build -Zbuild-std=core --target=${CMAKE_SYSTEM_PROCESSOR} $<$<CONFIG:Release>:--release> ${FEATURES}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
@@ -27,7 +31,7 @@ else()
|
||||
|
||||
add_custom_target(
|
||||
mission_rust_internal
|
||||
COMMAND cargo build $<$<CONFIG:Release>:--release>
|
||||
COMMAND cargo build $<$<CONFIG:Release>:--release> ${FEATURES}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
|
||||
@@ -4,10 +4,13 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
crate-type = ["staticlib", "lib"]
|
||||
|
||||
[profile.dev]
|
||||
panic = 'abort'
|
||||
|
||||
[profile.release]
|
||||
panic = 'abort'
|
||||
panic = 'abort'
|
||||
|
||||
[features]
|
||||
low_level_tests = []
|
||||
@@ -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<()>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ impl Sizes {
|
||||
pub const TASK_MINIMAL_STACK_SIZE: usize = 10240;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(not(test))]
|
||||
extern "C" {
|
||||
////////////////////////
|
||||
/// FreeRTOS API
|
||||
@@ -30,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,
|
||||
@@ -101,6 +104,127 @@ 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 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 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");
|
||||
}
|
||||
|
||||
// pub fn stop_it();
|
||||
|
||||
pub unsafe fn freertos_task_suspend(_handle: *const core::ffi::c_void){
|
||||
panic!("freeRTOS APIs are not implemented for tests");
|
||||
}
|
||||
|
||||
pub unsafe fn freertos_task_delete(_handle: *const core::ffi::c_void) -> !{
|
||||
panic!("freeRTOS APIs are not implemented for tests")
|
||||
}
|
||||
|
||||
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 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 unsafe fn freertos_task_delay(_milliseconds: u32){
|
||||
panic!("freeRTOS APIs are not implemented for tests");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// TODO this should be passed by compile time value
|
||||
pub unsafe fn freertos_task_priority_max() -> u32{
|
||||
panic!("freeRTOS APIs are not implemented for tests");
|
||||
}
|
||||
|
||||
//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 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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
pub unsafe fn freertos_mutex_take(_mutex: *const core::ffi::c_void) -> u8{
|
||||
panic!("freeRTOS APIs are not implemented for tests");
|
||||
}
|
||||
pub unsafe fn freertos_mutex_give(_mutex: *const core::ffi::c_void) -> u8{
|
||||
panic!("freeRTOS APIs are not implemented for tests");
|
||||
}
|
||||
|
||||
//uint8_t freertos_simple_once(uint8_t *once_data)
|
||||
pub unsafe fn freertos_simple_once(_once_data: *const u8) -> u8{
|
||||
panic!("freeRTOS APIs are not implemented for tests");
|
||||
}
|
||||
|
||||
//int freertos_get_sys_error()
|
||||
pub unsafe fn freertos_get_sys_error() -> core::ffi::c_int{
|
||||
panic!("freeRTOS APIs are not implemented for tests");
|
||||
}
|
||||
|
||||
////////////////////////
|
||||
/// 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 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");
|
||||
}
|
||||
}
|
||||
////////////////////////
|
||||
/// libc API (read/write/etc)
|
||||
#[cfg(target_env = "gnu")]
|
||||
|
||||
+23
-18
@@ -1,12 +1,14 @@
|
||||
#![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]
|
||||
//TODO os errors in API calls
|
||||
|
||||
mod dh;
|
||||
pub mod fsrc;
|
||||
mod panic;
|
||||
|
||||
#[cfg(feature = "low_level_tests")]
|
||||
mod low_level_tests;
|
||||
|
||||
use core::fmt::Write;
|
||||
use core::time::Duration;
|
||||
|
||||
@@ -19,10 +21,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<
|
||||
@@ -34,7 +33,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();
|
||||
}
|
||||
@@ -198,7 +197,7 @@ fn init_task() -> ! {
|
||||
Duration::from_millis(500),
|
||||
0.5,
|
||||
);
|
||||
|
||||
|
||||
debugger.run().unwrap();
|
||||
|
||||
let test1 = TEST1.take().unwrap();
|
||||
@@ -208,22 +207,28 @@ fn init_task() -> ! {
|
||||
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();
|
||||
});
|
||||
|
||||
|
||||
sifln!("=====================Mission delete");
|
||||
osal::thread::current().delete();
|
||||
}
|
||||
|
||||
mod tests {
|
||||
#[test]
|
||||
fn work_at_all() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
//! Low level tests which need to be run with FreeRTOS present
|
||||
//!
|
||||
//! 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 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
|
||||
//! 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>(_: T) -> &'static str {
|
||||
core::any::type_name::<T>()
|
||||
}
|
||||
|
||||
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<[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]);
|
||||
|
||||
/// 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!(work_at_all)]);
|
||||
}
|
||||
|
||||
#[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 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;
|
||||
let mut priority = 1;
|
||||
|
||||
for test in tests {
|
||||
let test_function = test.0 as *const core::ffi::c_void;
|
||||
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,
|
||||
priority,
|
||||
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;
|
||||
}
|
||||
// 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!();
|
||||
|
||||
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 work_at_all() {}
|
||||
|
||||
fn fail() {
|
||||
assert_eq!(1, 2);
|
||||
}
|
||||
+49
-22
@@ -1,26 +1,53 @@
|
||||
use core::panic::PanicInfo;
|
||||
use core::fmt::Write;
|
||||
#[cfg(any(target_env = "newlib", feature = "low_level_tests"))]
|
||||
pub use no_std::*;
|
||||
|
||||
extern "C" {
|
||||
fn done_error(); // exit code != 0
|
||||
}
|
||||
#[cfg(any(target_env = "newlib", feature = "low_level_tests"))]
|
||||
mod no_std {
|
||||
use core::fmt::Write;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[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
|
||||
);
|
||||
//TODO: stop RTOS, exit if hosted
|
||||
unsafe { done_error() };
|
||||
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);
|
||||
}
|
||||
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -41,4 +68,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!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user