176 lines
6.7 KiB
C
176 lines
6.7 KiB
C
#pragma once
|
|
|
|
#include <rt/assert.h>
|
|
#include <rt/context.h>
|
|
#include <rt/cycle.h>
|
|
#include <rt/list.h>
|
|
#include <rt/mpu.h>
|
|
#include <rt/stack.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#ifndef RT_TASK_READY_CTZ_ENABLE
|
|
|
|
#if ((defined(__arm__) || defined(__aarch64__)) && \
|
|
defined(__ARM_FEATURE_CLZ)) || \
|
|
defined(__x86_64__)
|
|
#define RT_TASK_READY_CTZ_ENABLE 1
|
|
#else
|
|
#define RT_TASK_READY_CTZ_ENABLE 0
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#define RT_TASK_PRIORITY_MIN UINT32_C(0)
|
|
#define RT_TASK_PRIORITY_MAX ((sizeof(uint32_t) * CHAR_BIT) - 1)
|
|
#define RT_TASK_PRIORITY_IDLE RT_TASK_PRIORITY_MAX
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifndef RT_TASK_CYCLE_ENABLE
|
|
#define RT_TASK_CYCLE_ENABLE 0
|
|
#endif
|
|
|
|
#if RT_TASK_CYCLE_ENABLE && !RT_CYCLE_ENABLE
|
|
#error "To use task cycle counts, the cycle counter must be enabled."
|
|
#endif
|
|
|
|
/* Yield the core to another task of the same priority. If the current task is
|
|
* still the highest priority, it will continue executing. */
|
|
void rt_task_yield(void);
|
|
|
|
// Sleep the current task for a given number of ticks.
|
|
void rt_task_sleep(unsigned long ticks);
|
|
|
|
/* Sleep the current task until *last_wake_tick + period. *last_wake_tick will
|
|
* be set to the next wakeup tick. */
|
|
void rt_task_sleep_periodic(unsigned long *last_wake_tick,
|
|
unsigned long period);
|
|
|
|
// Exit from the current task. Returning from a task function also exits.
|
|
__attribute__((noreturn)) void rt_task_exit(void);
|
|
|
|
// Get the name of the current task.
|
|
const char *rt_task_name(void);
|
|
|
|
// Get the current task.
|
|
struct rt_task *rt_task_self(void);
|
|
|
|
// Make the current task unprivileged. (This is a no-op on some architectures.)
|
|
void rt_task_drop_privilege(void);
|
|
|
|
enum rt_task_state
|
|
{
|
|
RT_TASK_STATE_INIT,
|
|
RT_TASK_STATE_RUNNING,
|
|
RT_TASK_STATE_READY,
|
|
RT_TASK_STATE_BLOCKED,
|
|
RT_TASK_STATE_BLOCKED_TIMEOUT,
|
|
RT_TASK_STATE_ASLEEP,
|
|
RT_TASK_STATE_EXITED,
|
|
};
|
|
|
|
struct rt_task
|
|
{
|
|
struct rt_list list;
|
|
struct rt_list sleep_list;
|
|
void *ctx;
|
|
uint32_t priority;
|
|
uint32_t base_priority;
|
|
enum rt_task_state state;
|
|
unsigned long wake_tick;
|
|
#if RT_MPU_ENABLE
|
|
struct rt_mpu_config mpu_config;
|
|
#endif
|
|
struct rt_list *wait_list_head;
|
|
struct rt_mutex *blocking_mutex;
|
|
union
|
|
{
|
|
struct rt_sem **sem;
|
|
struct rt_mutex **mutex;
|
|
} timeout_ptr;
|
|
struct rt_list mutex_list;
|
|
#if RT_TASK_CYCLE_ENABLE
|
|
uint64_t total_cycles;
|
|
uint32_t start_cycle;
|
|
#endif
|
|
const char *name;
|
|
};
|
|
|
|
/* Add a task to the ready list. This function may only be called in the system
|
|
* call handler or before rt_start. */
|
|
void rt_task_ready(struct rt_task *task);
|
|
|
|
#define RT_TASK_INIT(name_, name_str, priority_, ...) \
|
|
{ \
|
|
.list = RT_LIST_INIT(name_.list), \
|
|
.sleep_list = RT_LIST_INIT(name_.sleep_list), .priority = (priority_), \
|
|
.base_priority = (priority_), .state = RT_TASK_STATE_INIT, \
|
|
.mutex_list = RT_LIST_INIT(name_.mutex_list), .name = (name_str), \
|
|
}
|
|
|
|
#define RT_CAT_(a, b) a##b
|
|
#define RT_CAT(a, b) RT_CAT_(a, b)
|
|
|
|
#define RT_TASK_CONSTRUCTOR_PRIORITY 1000
|
|
#define RT_POST_TASK_CONSTRUCTOR_PRIORITY 60000
|
|
#define RT_TASK_CONSTRUCTOR_PRIORITY_MOD \
|
|
(RT_POST_TASK_CONSTRUCTOR_PRIORITY - RT_TASK_CONSTRUCTOR_PRIORITY)
|
|
|
|
#define RT_TASK_CONSTRUCTOR(counter) \
|
|
__attribute__((constructor(RT_TASK_CONSTRUCTOR_PRIORITY + \
|
|
(counter % RT_TASK_CONSTRUCTOR_PRIORITY_MOD))))
|
|
#define RT_POST_TASK_CONSTRUCTOR \
|
|
__attribute__((constructor(RT_POST_TASK_CONSTRUCTOR_PRIORITY)))
|
|
|
|
/* Use a constructor with a priority based on __COUNTER__ so that tasks created
|
|
* in the same file with equal priority are added to the ready list in the
|
|
* order they appear in the file. Use % 60000 to ensure that the overall
|
|
* constructor priority does not wrap around at 2^16. */
|
|
|
|
#define RT_TASK_COMMON(fn, stack_size, priority, name, ctx_init, ...) \
|
|
RT_TASK_COMMON_(fn, stack_size, priority, name, ctx_init, __COUNTER__, \
|
|
__VA_ARGS__)
|
|
|
|
#define RT_TASK_COMMON_(fn, stack_size, priority, name, ctx_init, counter, \
|
|
...) \
|
|
RT_TASK_CONSTRUCTOR(counter) \
|
|
__attribute__((used)) static void RT_CAT(fn##_task_init_, counter)(void) \
|
|
{ \
|
|
static RT_STACK(fn##_task_stack, stack_size); \
|
|
RT_MPU_PRIV_DATA(fn##_task) \
|
|
static struct rt_task fn##_task = \
|
|
RT_TASK_INIT(fn##_task, name, priority); \
|
|
RT_MPU_CONFIG_INIT(&fn##_task.mpu_config, \
|
|
RT_MPU_REGION(fn##_task_stack, \
|
|
sizeof fn##_task_stack, \
|
|
RT_MPU_ATTR_STACK), \
|
|
__VA_ARGS__); \
|
|
fn##_task.ctx = ctx_init; \
|
|
rt_task_ready(&fn##_task); \
|
|
} \
|
|
rt_static_assert((priority) <= RT_TASK_PRIORITY_MAX, \
|
|
"the priority of task \"" name "\", " #priority \
|
|
", exceeds the maximum value")
|
|
|
|
/* Create a task that runs fn on a stack of at least stack_size, with the given
|
|
* priority. Additional arguments are MPU regions that will be active while the
|
|
* task is running. */
|
|
#define RT_TASK(fn, stack_size, priority, ...) \
|
|
RT_TASK_COMMON(fn, stack_size, priority, #fn, \
|
|
rt_context_init((fn), fn##_task_stack, \
|
|
sizeof fn##_task_stack), \
|
|
__VA_ARGS__)
|
|
|
|
#define RT_TASK_ARG(fn, arg, stack_size, priority, ...) \
|
|
RT_TASK_COMMON(fn, stack_size, priority, #fn "(" #arg ")", \
|
|
rt_context_init_arg((fn), (arg), fn##_task_stack, \
|
|
sizeof fn##_task_stack), \
|
|
__VA_ARGS__)
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|