diff --git a/software/CMakeLists.txt b/software/CMakeLists.txt index ecdbe61..5b3a775 100644 --- a/software/CMakeLists.txt +++ b/software/CMakeLists.txt @@ -35,7 +35,7 @@ set(OST_SRCS system/debug.c system/picture.c system/filesystem.c - system/os.c + system/qor.c system/qor_armv6m.s system/audio_player.c system/ff/ff.c diff --git a/software/platform/raspberry-pico-w/CMakeLists.txt b/software/platform/raspberry-pico-w/CMakeLists.txt index e91746f..18c7ceb 100644 --- a/software/platform/raspberry-pico-w/CMakeLists.txt +++ b/software/platform/raspberry-pico-w/CMakeLists.txt @@ -31,7 +31,7 @@ add_library( pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/pico_i2s.pio) pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/i2s.pio) -target_link_libraries(${PROJECT_NAME} INTERFACE pico_stdlib) +target_link_libraries(${PROJECT_NAME} INTERFACE pico_stdlib hardware_exception) target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) target_sources(${PROJECT_NAME} INTERFACE diff --git a/software/platform/raspberry-pico-w/pico_hal_wrapper.c b/software/platform/raspberry-pico-w/pico_hal_wrapper.c index 155f5a7..e9e651c 100644 --- a/software/platform/raspberry-pico-w/pico_hal_wrapper.c +++ b/software/platform/raspberry-pico-w/pico_hal_wrapper.c @@ -64,7 +64,7 @@ const uint8_t SD_CARD_CS = 17; const uint8_t SD_CARD_PRESENCE = 24; -#include "os.h" +#include "qor.h" static bool sys_timer_callback(struct repeating_timer *t) { diff --git a/software/system/main.c b/software/system/main.c index c03acd9..ff1f4e9 100644 --- a/software/system/main.c +++ b/software/system/main.c @@ -3,7 +3,7 @@ #include "debug.h" #include "filesystem.h" #include "picture.h" -#include "os.h" +#include "qor.h" #include "rotary-button.h" #define RUN_TESTS 1 @@ -64,16 +64,32 @@ void ost_hal_panic() extern void qor_sleep(); +static qor_mbox_t b; + +typedef struct +{ + uint8_t ev; +} ost_event_t; + +ost_event_t ev_queue[10]; + +qor_tcb_t tcb1; +qor_tcb_t tcb2; + void UserTask_0(void *args) { // InstrumentTriggerPE11_Init(); // uint32_t count = 0; + + qor_mbox_init(&b, (void **)&ev_queue, 10); while (1) { ost_hal_gpio_set(OST_GPIO_DEBUG_LED, 1); // qor_sleep(); + ost_event_t *e = NULL; + qor_mbox_wait(&b, (void **)&e, 3); for (int i = 0; i < 65500; i++) { @@ -97,9 +113,17 @@ void UserTask_1(void *args) { while (1) { - ost_system_delay_ms(1000); + for (int i = 0; i < 65500; i++) + { + for (int j = 0; j < 100; j++) + ; + } debug_printf("X\n"); - ost_system_delay_ms(1000); + for (int i = 0; i < 65500; i++) + { + for (int j = 0; j < 100; j++) + ; + } } } /* @@ -129,7 +153,7 @@ void UserTask_3(void) } */ #include "pico/stdlib.h" -extern size_t __StackTop; + int main() { ost_system_initialize(); @@ -144,8 +168,8 @@ int main() // ost_audio_play("out2.wav"); OS_Init(THREADFREQ); - qor_create_thread(UserTask_0, 1, "UserTask_0"); - // qor_create_thread(UserTask_1, 2, "UserTask_1"); + qor_create_thread(&tcb1, UserTask_0, 1, "UserTask_0"); + qor_create_thread(&tcb2, UserTask_1, 2, "UserTask_1"); // OS_Thread_Create(UserTask_2, OS_SCHEDL_PRIO_MAIN_THREAD, "UserTask_2"); // OS_Thread_Create(OnboardUserButton_Task, OS_SCHEDL_PRIO_EVENT_THREAD, "OnboardUserButton_Task"); qor_start(); diff --git a/software/system/os.c b/software/system/os.c deleted file mode 100644 index 9dc2753..0000000 --- a/software/system/os.c +++ /dev/null @@ -1,299 +0,0 @@ -/** - * @brief - * - * // Quite OK RTOS scheduler is a very simple real-time, pre-emptive, tickless tasker - * Design goals: - * - Easily portable (limited assembly) - * - Tick-less - * - Preemptive - * - Only one inter-thread resource: mailboxes (no mutex, semaphore...) - - */ - -#include "ost_hal.h" -#include "debug.h" -#include "os.h" -#include - -#include "pico/critical_section.h" - -/** - * The fn OSAsm_ThreadSwitch, implemented in os_asm.s, is periodically called by the SchedlTimer (ISR). - * It preemptively switches to the next thread, that is, it stores the stack of the running - * thread and restores the stack of the next thread. - * It calls OS_Schedule to determine which thread is run next and update RunPt. - */ -extern void OSAsm_ThreadSwitch(void); - -static critical_section_t acrit; - -inline static void enable_irq() -{ - critical_section_exit(&acrit); -} - -inline static void disable_irq() -{ - critical_section_enter_blocking(&acrit); -} - -void timer_set_period(uint16_t i); - -void ost_tasker_sleep_for(uint32_t ms) -{ - timer_set_period(ms); -} - -void ost_tasker_init() -{ - ost_tasker_sleep_for(5000); // 5 seconds -} - -static uint32_t counter = 0; - -// void ost_tasker_timer_callback() -// { -// // debug_printf("%d\n", counter++); - -// qor_switch_context(); -// } - -#define OS_DEBUG - -/** - * TCBState indicates whether the TCB can be used by OS_ThreadCreate - * to create a new thread. - */ -typedef enum -{ - TCBStateFree, - TCBStateActive -} TCBState_t; - -void qor_sleep_ms(uint8_t svc, uint32_t ms) -{ - - __wfi; -} - -/** - * Thread Control Block - * - * IMPORTANT! - * The fn OSAsm_Start and OSAsm_ThreadSwitch, implemented in os_asm.s, expect the stack pointer - * to be placed first in the struct. Don't shuffle it! - */ -typedef struct TCB -{ - uint32_t *sp; /* Stack pointer, valid for threads not running */ - struct TCB *next; /* Pointer to circular-linked-list of TCBs */ - uint32_t sleep; /* Sleep duration in ms, zero means not sleeping */ - TCBState_t status; /* TCB active or free */ - Semaphore_t *blocked; /* Pointer to semaphore on which the thread is blocked, NULL if not blocked */ - uint8_t priority; /* Thread priority, 0 is highest, 255 is lowest */ - const char *name; /* Descriptive name to facilitate debugging */ - uint32_t pc; -} TCB_t; - -//================================================================================================== -// GLOBAL AND STATIC VARIABLES -//================================================================================================== - -static TCB_t TCBs[MAXNUMTHREADS]; -static uint32_t Stacks[MAXNUMTHREADS][STACKSIZE]; - -/* Pointer to the currently running thread */ -TCB_t *RunPt; - -static uint32_t global_stack[1000]; - -/* The variable ActiveTCBsCount tracks the number of TCBs in use by the OS */ -static uint32_t ActiveTCBsCount; - -static void OS_InitTCBsStatus(void) -{ - for (uint32_t idx = 0; idx < MAXNUMTHREADS; idx++) - { - TCBs[idx].status = TCBStateFree; - } -} - -void OS_Init(uint32_t scheduler_frequency_hz) -{ - critical_section_init(&acrit); - OS_InitTCBsStatus(); -} - -void OS_ExitLoop() -{ - for (;;) - ; -} - -extern void qor_go(); - -uint32_t *qor_initialize_stack(uint32_t *top_of_stack, thread_func_t task, void *args) -{ - // ARM Calling convention: the folowwing registers are automatically saved onto the stack by the processor (in this ordoer on the stack) - // DDI0419C_arm_architecture_v6m_reference_manual-1.pdf B1.5.6 Exception entry behavior - top_of_stack--; - /* From the "STM32 Cortex-M4 Programming Manual" on page 23: - * attempting to execute instructions when the T bit is 0 results in a fault or lockup */ - *top_of_stack = 0x01000000; /* Thumb Bit (PSR) */ - top_of_stack--; - *top_of_stack = (uint32_t)task; // PC Program Counter (R15) - top_of_stack--; - *top_of_stack = (uint32_t)OS_ExitLoop; /* (LR) Link Register (Return address) R14 */ - top_of_stack -= 5; // skip R12, R3, R2, R1 - *top_of_stack = (uint32_t)args; // R0 - top_of_stack -= 8; // R11 -> R4 - return top_of_stack; -} - -void qor_create_thread(thread_func_t task, uint8_t priority, const char *name) -{ - assert_or_panic(ActiveTCBsCount >= 0 && ActiveTCBsCount < MAXNUMTHREADS); - disable_irq(); - - /* Find next available TCB */ - int32_t new_tcb_idx = -1; - for (new_tcb_idx = 0; new_tcb_idx < MAXNUMTHREADS; new_tcb_idx++) - { - if (TCBs[new_tcb_idx].status == TCBStateFree) - { - break; - } - } - - if (new_tcb_idx >= 0) - { - if (new_tcb_idx == 0) - { - RunPt = &(TCBs[0]); - } - else - { - TCBs[new_tcb_idx].next = RunPt->next; - } - - TCBs[new_tcb_idx].sleep = 0; - TCBs[new_tcb_idx].status = TCBStateActive; - TCBs[new_tcb_idx].blocked = NULL; - TCBs[new_tcb_idx].priority = priority; - TCBs[new_tcb_idx].name = name; - TCBs[new_tcb_idx].sp = qor_initialize_stack(&Stacks[new_tcb_idx][STACKSIZE], task, (void *)name); - - RunPt->next = &(TCBs[new_tcb_idx]); - ActiveTCBsCount++; - } - - enable_irq(); -} - -void qor_start(void) -{ - assert_or_panic(ActiveTCBsCount > 0); - - /* Prevent the timer's ISR from firing before OSAsm_Start is called */ - disable_irq(); - - qor_go(); - - /* This statement should not be reached */ - ost_hal_panic(); -} - -void qor_scheduler(void) -{ - TCB_t *next_pt = RunPt->next; - TCB_t *iterating_pt = next_pt; - - /* Search for highest priority thread not sleeping or blocked */ - uint32_t max_priority = RunPt->priority; - TCB_t *best_pt = next_pt; - do - { - if ((iterating_pt->priority > max_priority) && (iterating_pt->sleep == 0) && (iterating_pt->blocked == NULL)) - { - best_pt = iterating_pt; - max_priority = best_pt->priority; - } - iterating_pt = iterating_pt->next; - } while (iterating_pt != next_pt); - - RunPt = best_pt; -} - -void OS_Thread_Suspend(void) -{ - // SchedlTimer_ResetCounter(); -} - -void OS_Thread_Sleep(uint32_t sleep_duration_ms) -{ - RunPt->sleep = sleep_duration_ms; - OS_Thread_Suspend(); -} - -void OS_DecrementTCBsSleepDuration(void) -{ - for (size_t tcb_idx = 0; tcb_idx < MAXNUMTHREADS; tcb_idx++) - { - if (TCBs[tcb_idx].sleep > 0) - { - TCBs[tcb_idx].sleep -= 1; - } - } -} - -void OS_Thread_Kill(void) -{ - assert_or_panic(ActiveTCBsCount > 1); - disable_irq(); - - TCB_t *previous_tcb = RunPt; - while (1) - { - previous_tcb = previous_tcb->next; - if (previous_tcb->next == RunPt) - break; - } - TCB_t *next_tcb = RunPt->next; - - previous_tcb->next = next_tcb; - RunPt->status = TCBStateFree; - - ActiveTCBsCount--; - enable_irq(); - OS_Thread_Suspend(); -} - -void OS_Semaphore_Wait(Semaphore_t *sem) -{ - disable_irq(); - (*sem) = (*sem) - 1; - if ((*sem) < 0) - { - RunPt->blocked = sem; /* Reason the thread is blocked */ - enable_irq(); - OS_Thread_Suspend(); - } - enable_irq(); -} - -void OS_Semaphore_Signal(Semaphore_t *sem) -{ - disable_irq(); - (*sem) = (*sem) + 1; - if ((*sem) <= 0) - { - /* Search for a TCB blocked on this semaphore and wake it up */ - TCB_t *a_tcb = RunPt->next; - while (a_tcb->blocked != sem) - { - a_tcb = a_tcb->next; - } - a_tcb->blocked = 0; - } - enable_irq(); -} diff --git a/software/system/os.h b/software/system/os.h deleted file mode 100644 index 7857e63..0000000 --- a/software/system/os.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef OS_H -#define OS_H - -void ost_tasker_init(); - -extern void ost_hal_panic(); - -/** - * Assert, or panic. - */ -#define assert_or_panic(expr) ((expr) ? (void)0U : ost_hal_panic()) - -/** - * The module os encapsulates the core functionality of the operating system and - * exposes the functions for interacting with it. - */ - -#include - -#define MAXNUMTHREADS 10 /* Maximum number of threads, allocated at compile time */ -#define STACKSIZE 100 /* Number of 32-bit words in each TCB's stack */ -#define THREADFREQ 1 /* Maximum time-slice, in Hz, before the scheduler is run */ - -#define OS_SCHEDL_PRIO_MIN 1 /* Lowest priority that can be assigned to a thread */ -#define OS_SCHEDL_PRIO_MAX UINT8_MAX /* Highest priority that can be assigned to a thread */ - -/** - * The type Semaphore_t abstracts the semaphore's counter. - * A value of type *Semaphore_t should only be updated through the fn OS_Semaphore_Wait - * and OS_Semaphore_Signal. - */ -typedef int32_t Semaphore_t; - -typedef void (*thread_func_t)(void *args); - -/** - * Function descriptions are provided in os.c - */ - -void OS_Init(uint32_t scheduler_frequency_hz); - -// void OS_Thread_CreateFirst(thread_func_t task, uint8_t priority, const char *name); - -void qor_create_thread(thread_func_t task, uint8_t priority, const char *name); - -void qor_switch_context(); - -void qor_start(void); - -void OS_Thread_Suspend(void); - -void OS_Thread_Sleep(uint32_t sleep_duration_ms); - -void OS_DecrementTCBsSleepDuration(void); - -void OS_Thread_Kill(void); - -void OS_Semaphore_Wait(Semaphore_t *sem); - -void OS_Semaphore_Signal(Semaphore_t *sem); - -#endif // OST_TASKER_H diff --git a/software/system/qor.c b/software/system/qor.c new file mode 100644 index 0000000..1394255 --- /dev/null +++ b/software/system/qor.c @@ -0,0 +1,451 @@ +/** + * @brief + * + * // Quite OK RTOS scheduler is a very simple real-time, pre-emptive, tickless tasker + * Design goals: + * - Easily portable (limited assembly) + * - Tick-less + * - Preemptive + * - Only one inter-thread resource: mailboxes (no mutex, semaphore...) + + */ + +#include "ost_hal.h" +#include "debug.h" +#include "qor.h" +#include + +#include "pico/critical_section.h" +#include "hardware/exception.h" + +// =========================================================================================================== +// ARM GENERIC +// =========================================================================================================== +inline static void enable_irq() +{ + __asm volatile("cpsie i"); +} + +inline static void disable_irq() +{ + __asm volatile("cpsid i"); +} + +void qor_sleep_ms(uint8_t svc, uint32_t ms) +{ + + __wfi; +} + +static inline void __set_PRIMASK(uint32_t priMask) +{ + __asm volatile("MSR primask, %0" + : + : "r"(priMask) + : "memory"); +} + +static inline uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __asm volatile("MRS %0, primask" + : "=r"(result)); + return (result); +} + +static inline uint32_t qor_enter_critical(void) +{ + uint32_t primask = __get_PRIMASK(); + disable_irq(); + return primask; +} + +void qor_exit_critical(uint32_t status) +{ + __set_PRIMASK(status); +} + +__attribute__((naked)) void PendSV_Handler() +{ + // __asm("bkpt 1"); + qor_switch_context(); +} + +__attribute__((optimize("O0"))) static void qor_svc_call(void) +{ + volatile uint32_t *icsr = (void *)0xE000ED04; + // Pend a PendSV exception using by writing 1 to PENDSVSET at bit 28 + *icsr = 0x1 << 28; + // flush pipeline to ensure exception takes effect before we + // return from this routine + __asm("isb"); +} + +static const bool qor_inside_interrupt(void) +{ + uint32_t ulCurrentInterrupt; + bool xReturn; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile("mrs %0, ipsr" + : "=r"(ulCurrentInterrupt)::"memory"); + + if (ulCurrentInterrupt == 0) + { + xReturn = false; + } + else + { + xReturn = true; + } + + return xReturn; +} + +// =========================================================================================================== +// GLOBAL AND STATIC VARIABLES +// =========================================================================================================== + +static uint32_t Stacks[MAXNUMTHREADS][STACKSIZE]; + +/* Pointer to the currently running thread */ +qor_tcb_t *RunPt = NULL; +static qor_tcb_t *TcbHead = NULL; + +/* The variable ActiveTCBsCount tracks the number of TCBs in use by the OS */ +static uint32_t ActiveTCBsCount = 0; + +// =========================================================================================================== +// Quite Ok RTOS private and public functions +// =========================================================================================================== +void OS_Init(uint32_t scheduler_frequency_hz) +{ + exception_set_exclusive_handler(PENDSV_EXCEPTION, PendSV_Handler); +} + +void OS_ExitLoop() +{ + for (;;) + ; +} + +extern void qor_go(); + +uint32_t *qor_initialize_stack(uint32_t *top_of_stack, thread_func_t task, void *args) +{ + // ARM Calling convention: the folowwing registers are automatically saved onto the stack by the processor (in this ordoer on the stack) + // DDI0419C_arm_architecture_v6m_reference_manual-1.pdf B1.5.6 Exception entry behavior + top_of_stack--; + /* From the "STM32 Cortex-M4 Programming Manual" on page 23: + * attempting to execute instructions when the T bit is 0 results in a fault or lockup */ + *top_of_stack = 0x01000000; /* Thumb Bit (PSR) */ + top_of_stack--; + *top_of_stack = (uint32_t)task; // PC Program Counter (R15) + top_of_stack--; + *top_of_stack = (uint32_t)OS_ExitLoop; /* (LR) Link Register (Return address) R14 */ + top_of_stack -= 5; // skip R12, R3, R2, R1 + *top_of_stack = (uint32_t)args; // R0 + top_of_stack -= 8; // R11 -> R4 + return top_of_stack; +} + +void qor_create_thread(qor_tcb_t *tcb, thread_func_t task, uint8_t priority, const char *name) +{ + assert_or_panic(ActiveTCBsCount >= 0 && ActiveTCBsCount < MAXNUMTHREADS); + disable_irq(); + + tcb->state = qor_tcb_state_active; + tcb->wait_time = 0; + tcb->state = qor_tcb_state_active; + tcb->priority = priority; + tcb->name = name; + tcb->next = NULL; + tcb->mbox = NULL; + tcb->message = NULL; + tcb->wait_next = NULL; + tcb->sp = qor_initialize_stack(&Stacks[ActiveTCBsCount][STACKSIZE], task, (void *)name); + + if (TcbHead == NULL) + { + TcbHead = tcb; + } + else + { + // Go to the end of the queue + qor_tcb_t *t = TcbHead; + while (t->next != NULL) + { + t = t->next; + } + // Add TCB to the end of the queue + t->next = tcb; + } + ActiveTCBsCount++; + + enable_irq(); +} + +void qor_start(void) +{ + assert_or_panic(ActiveTCBsCount > 0); + + // FIXME: use the scheduler to find the best first thread to start + RunPt = TcbHead; + /* Prevent the timer's ISR from firing before OSAsm_Start is called */ + disable_irq(); + + qor_go(); + + /* This statement should not be reached */ + ost_hal_panic(); +} + +void qor_scheduler(void) +{ + qor_tcb_t *t = TcbHead; + + /* Search for highest priority thread not sleeping or blocked */ + uint32_t max_priority = RunPt->priority; + qor_tcb_t *best_pt = RunPt; + while (t != NULL) + { + if ((t->priority > max_priority) && + (t->wait_time == 0) && + (t->state == qor_tcb_state_active)) + { + best_pt = t; + max_priority = t->priority; + } + t = t->next; + } + + RunPt = best_pt; +} + +void OS_Thread_Suspend(void) +{ + // SchedlTimer_ResetCounter(); +} + +void qor_sleep(uint32_t sleep_duration_ms) +{ + // RunPt->sleep = sleep_duration_ms; + // OS_Thread_Suspend(); +} + +// void OS_DecrementTCBsSleepDuration(void) +// { +// for (size_t tcb_idx = 0; tcb_idx < MAXNUMTHREADS; tcb_idx++) +// { +// if (TCBs[tcb_idx].sleep > 0) +// { +// TCBs[tcb_idx].sleep -= 1; +// } +// } +// } + +void OS_Thread_Kill(void) +{ + /* + assert_or_panic(ActiveTCBsCount > 1); + disable_irq(); + + qor_tcb_t *previous_tcb = RunPt; + while (1) + { + previous_tcb = previous_tcb->next; + if (previous_tcb->next == RunPt) + break; + } + qor_tcb_t *next_tcb = RunPt->next; + + previous_tcb->next = next_tcb; + RunPt->state = qor_tcb_state_free; + + ActiveTCBsCount--; + enable_irq(); + OS_Thread_Suspend(); + */ +} + +// =========================================================================================================== +// MAILBOX IMPLEMENTATION +// =========================================================================================================== + +#define qor_mbox_tSendNormal 0x0 +#define qor_mbox_tSendFront 0x1 + +void qor_mbox_init(qor_mbox_t *mbox, void **msgBuffer, uint32_t maxCount) +{ + mbox->msgBuffer = msgBuffer; + mbox->maxCount = maxCount; + mbox->read = 0; + mbox->read = 0; + mbox->head = NULL; +} + +uint32_t qor_mbox_wait(qor_mbox_t *mbox, void **msg, uint32_t waitTicks) +{ + uint32_t status = qor_enter_critical(); + + if (mbox->count == 0) + { + if (waitTicks > 0) + { + // No any data, block on that resource + RunPt->mbox = mbox; + RunPt->state = qor_tcb_state_wait_mbox; + RunPt->wait_time = waitTicks; + qor_exit_critical(status); + qor_svc_call(); // call scheduler + + status = qor_enter_critical(); + if ((RunPt->wait_time >= 0) && (RunPt->message != NULL)) + { + *msg = RunPt->message; + qor_exit_critical(status); + return QOR_MBOX_OK; + } + else + { + qor_exit_critical(status); + return QOR_MBOX_ERROR; + } + } + else + { + return QOR_MBOX_ERROR; + } + } + else + { + if ((RunPt->message != NULL)) + { + *msg = RunPt->message; + qor_exit_critical(status); + return QOR_MBOX_OK; + } + else + { + qor_exit_critical(status); + return QOR_MBOX_ERROR; + } + } +} + +/* +uint32_t qor_mbox_get(qor_mbox_t *mbox, void **msg) +{ + uint32_t status = qor_enter_critical(); + if (mbox->count > 0) + { + --mbox->count; + *msg = mbox->msgBuffer[mbox->read++]; + if (mbox->read >= mbox->maxCount) + { + mbox->read = 0; + } + qor_exit_critical(status); + return tErrorNoError; + } + else + { + qor_exit_critical(status); + return tErrorResourceUnavaliable; + } +} +*/ + +/* + +uint32_t qor_mbox_notify(qor_mbox_t *mbox, void *msg, uint32_t notifyOption) +{ + uint32_t status = qor_enter_critical(); + if (tEventWaitCount(&mbox->event) > 0) + { + tTask *task = tEventWakeUp(&mbox->event, (void *)msg, tErrorNoError); + if (task->prio < currentTask->prio) + { + tTaskSched(); + } + } + else + { + if (mbox->count >= mbox->maxCount) + { + qor_exit_critical(status); + return tErrorResourceFull; + } + if (notifyOption & qor_mbox_tSendFront) + { + if (mbox->read <= 0) + { + mbox->read = mbox->maxCount - 1; + } + else + { + --mbox->read; + } + mbox->msgBuffer[mbox->read] = msg; + } + else + { + mbox->msgBuffer[mbox->write++] = msg; + if (mbox->write >= mbox->maxCount) + { + mbox->write = 0; + } + } + mbox->count++; + } + qor_exit_critical(status); + return tErrorNoError; +} +*/ + +void qor_mbox_flush(qor_mbox_t *mbox) +{ + uint32_t status = qor_enter_critical(); + /* + if (tEventWaitCount(&mbox->event) == 0) + { + mbox->read = 0; + mbox->write = 0; + mbox->count = 0; + } + */ + qor_exit_critical(status); +} + +uint32_t mbox_destroy(qor_mbox_t *mbox) +{ + uint32_t status = qor_enter_critical(); + /* + uint32_t count = tEventRemoveAll(&mbox->event, (void *)0, tErrorDel); + qor_exit_critical(status); + if (count > 0) + { + tTaskSched(); + } + */ + return 0; // count; +} + +void qor_mbox_get_stats(qor_mbox_t *mbox, mbox_stats_t *info) +{ + uint32_t status = qor_enter_critical(); + + info->count = mbox->count; + info->maxCount = mbox->maxCount; + + info->taskCount = 0; + qor_tcb_t *head = mbox->head; + while (head != NULL) + { + info->taskCount++; + head = head->wait_next; + } + + qor_exit_critical(status); +} diff --git a/software/system/qor.h b/software/system/qor.h new file mode 100644 index 0000000..0141e67 --- /dev/null +++ b/software/system/qor.h @@ -0,0 +1,117 @@ +#ifndef QOR_H +#define QOR_H + +extern void ost_hal_panic(); + +/** + * Assert, or panic. + */ +#define assert_or_panic(expr) ((expr) ? (void)0U : ost_hal_panic()) + +/** + * The module os encapsulates the core functionality of the operating system and + * exposes the functions for interacting with it. + */ + +#include + +#define MAXNUMTHREADS 10 /* Maximum number of threads, allocated at compile time */ +#define STACKSIZE 100 /* Number of 32-bit words in each TCB's stack */ +#define THREADFREQ 1 /* Maximum time-slice, in Hz, before the scheduler is run */ + +#define OS_SCHEDL_PRIO_MIN 1 /* Lowest priority that can be assigned to a thread */ +#define OS_SCHEDL_PRIO_MAX UINT8_MAX /* Highest priority that can be assigned to a thread */ + +/** + * The type Semaphore_t abstracts the semaphore's counter. + * A value of type *Semaphore_t should only be updated through the fn OS_Semaphore_Wait + * and OS_Semaphore_Signal. + */ +typedef int32_t Semaphore_t; + +typedef void (*thread_func_t)(void *args); + +// =========================================================================================================== +// THREADS API +// =========================================================================================================== + +/** + * @brief RTOS Task state + * + */ +typedef enum +{ + qor_tcb_state_active, + qor_tcb_state_wait_mbox, + qor_tcb_state_sleep +} qor_tcb_state_t; + +/** + * @brief Thread Control Block + * + * IMPORTANT! keep the stack pointer on top, it is required by the task switch assembly + * + */ + +typedef struct qor_mbox_t qor_mbox_t; +typedef struct TCB +{ + uint32_t *sp; /* Stack pointer, valid for threads not running */ + struct TCB *next; /* Pointer to circular-linked-list of TCBs */ + struct TCB *wait_next; // Next TCB in waiting list + qor_tcb_state_t state; /* TCB active or free */ + uint32_t wait_time; //!< Timeout for mbox maiting or sleep + qor_mbox_t *mbox; /* Pointer to mailbox on which the thread is blocked, NULL if not blocked */ + void *message; //