diff --git a/include/sched.h b/include/sched.h index e874f76..d61e08e 100644 --- a/include/sched.h +++ b/include/sched.h @@ -1,52 +1,114 @@ #ifndef SCHED_H_ #define SCHED_H_ +/* Scheduling - first iteration: + * + * Only one priority (0) is supported. + * The maximum number of tasks (max_num_tasks) will be one hundred. + * A fixed array of task descriptors, size of max_num_tasks. + * A fized array of process stacks. Each process will have an array. + * Use round robin algorithm (FIFO). + */ + #define MAX_NUM_TASKS 100 +#define PRIORITY_LEVELS 1 + +typedef struct _task_descriptor TaskDescriptor_t; +typedef void (*entry_point)(void); -extern struct TaskDescriptor *current; -extern struct TaskDescriptor *task_descriptors_array[MAX_NUM_TASKS]; +// extern TaskDescriptor_t *init_task; +// extern TaskDescriptor_t *current_task; +// extern TaskDescriptor_t *task_descriptors_array[MAX_NUM_TASKS]; extern int num_tasks; /* TaskState_t + * * A task is in one of the following run states. + * + * ACTIVE - The task that has just run, is running, or is about to run. On a single processor only one task can be active at a time + * READY - The task is ready to be activated + * EXITED - The task will never again run. It may still retain some resources + * SEND_BLOCKED - The task has executed Receive(), and is waiting for a task to sent to it + * RECEIVE_BLOCKED - The task has executed Send(), and is waiting for the message to be received + * REPLY_BLOCKED - The task has executed Send() and its message has been received, but it has not received a reply + * EVENT_BLOCKED - The task has executed AwaitEvent(), but the event on which it is waiting has not occurred */ typedef enum { - ACTIVE = 0, /* The task that has just run, is running, or is about to run. Scheduling, which happens near the end of kernel processing, changes the active task. On a single processor only one task can be active at a time. */ - READY = 1, /* The task is ready to be activated. */ - EXITED = 2, /* The task will never again run. It may still retain some resources if resource re-use is not fully implemented. */ - SEND_BLOCKED = 3, /* The task has executed Receive(), and is waiting for a task to sent to it. */ - RECEIVE_BLOCKED = 4, /* The task has executed Send(), and is waiting for the message to be received. */ - REPLY_BLOCKED = 5, /* The task has executed Send() and its message has been received, but it has not received a reply. */ - EVENT_BLOCKED = 6 /* The task has executed AwaitEvent(), but the event on which it is waiting has not occurred. */ + ACTIVE = 0, + READY = 1, + EXITED = 2, + SEND_BLOCKED = 3, + RECEIVE_BLOCKED = 4, + REPLY_BLOCKED = 5, + EVENT_BLOCKED = 6 } TaskState_t; -/* CPU Context +/* CPUContext_t * */ -struct CPUContext -{ - unsigned int SPSR; -}; +// typedef struct _cpu_context +// { +// unsigned long x0; +// unsigned long x1; +// unsigned long x2; +// unsigned long x3; +// unsigned long x4; +// unsigned long x5; +// unsigned long x6; +// unsigned long x7; +// unsigned long x8; +// unsigned long x9; +// unsigned long x10; +// unsigned long x11; +// unsigned long x12; +// unsigned long x13; +// unsigned long x14; +// unsigned long x15; +// unsigned long x16; +// unsigned long x17; +// unsigned long x18; +// unsigned long x19; +// unsigned long x20; +// unsigned long x21; +// unsigned long x22; +// unsigned long x23; +// unsigned long x24; +// unsigned long x25; +// unsigned long x26; +// unsigned long x27; +// unsigned long x28; +// unsigned long x29; +// unsigned long x30; +// unsigned long elr_el1; +// unsigned long spsr_el1; +// unsigned long sp_el0; +// } CPUContext_t; /* TaskDescriptor_t */ - -struct TaskDescriptor +typedef struct _task_descriptor { int tid; /* Task identifier (tid), which is unique among all active tasks */ int priority; /* The task's priority */ - struct TaskDescriptor *parent_td; /* A pointer to the TaskDescriptor of the task that created it, its parent */ - // struct TaskDescriptor *next_task_ready_queue; /* A pointer to the TaskDescriptor of the next task in the task's ready queue */ - // struct TaskDescriptor *next_task_send_queue; /* A pointer to the TaskDescriptor of the next task in the task's send queue */ - TaskState_t state; /* The task's current run state */ - /* TODO */ /* The task's current stack pointer */ - /* TODO */ /* CPU Context */ -}; + TaskDescriptor_t *parent_td; /* A pointer to the TaskDescriptor of the task that created it, its parent */ + // TaskDescriptor_t *next_task_ready_queue; /* Pointer to TaskDescriptor of the next ready task (schedule) */ + // TaskDescriptor_t *next_task_send_queue; /* Pointer to TaskDescriptor of the next ready task (send queue) */ + TaskState_t state; /* The task's current run state */ + /* TODO */ /* The task's current stack pointer */ + /* TODO - CPU Context CPUContext_t *context; This is the context, includes the tasks's SPSR */ + entry_point function; +} TaskDescriptor_t; void sched_init(void); -int get_current_task_tid(void); +void schedule(void); +// void switch_to(TaskDescriptor_t *next); +int get_current_task_tid(void); // int get_tid(void); +TaskDescriptor_t *get_current_task(void); int get_num_tasks(void); -struct TaskDescriptor *get_free_task_descriptor(void); +TaskDescriptor_t *get_free_task_descriptor(void); +void add_task_to_ready_queue(TaskDescriptor_t *task); +// int find_first_empty(struct task_descriptor td[]); +// TaskDescriptor_t *get_current_task(void); #endif /* SCHED_H_ */ diff --git a/include/task.h b/include/task.h index d0b7f4c..75e0c7e 100644 --- a/include/task.h +++ b/include/task.h @@ -3,7 +3,7 @@ #include "sched.h" -int task_create(int priority, void (*function)()); +int task_create(int priority, entry_point function); int task_tid(void); int task_parent_tid(void); void task_yield(void); diff --git a/src/main.c b/src/main.c index 511c91c..9db4f9e 100644 --- a/src/main.c +++ b/src/main.c @@ -13,28 +13,17 @@ void user_task(void) uart_printf(CONSOLE, "first_task (user): %d\r\n", i); } } - - // char input; - // uint32_t count; - - // while (1) { - // input = uart_getc(CONSOLE); - - // if (input == '$') { - // count = sys_timer_get_count(); - // uart_printf(CONSOLE, "Timer count: %u\r\n", count); - // } else if (input == '\n' || input == '\r') { - // uart_printf(CONSOLE, "\r\n"); - // } else { - // uart_putc(CONSOLE, input); - // } - // } } +/* start_kernel + * + * The kernel needs to initialize important features, such as the + * exception vector, kernel data structures, set up and run the + * init_task + */ void start_kernel(void) { - /* Configure GPIO and UART for console output. - */ + /* Configure GPIO and UART for console output. */ uart_init(); /* Initialize exception vector for handling exceptions @@ -48,9 +37,8 @@ void start_kernel(void) sys_timer_init(); // timer_init(); - /* Initialize interrupt controller - */ - //interrupt_controller_enable(); + /* Initialize interrupt controller */ + // interrupt_controller_enable(); irq_enable(); /* Set up the scheduler prior starting any interrupts @@ -58,8 +46,7 @@ void start_kernel(void) */ sched_init(); - /* Run first user space application - */ + /* Run first user space application */ // int first_task = task_create(1, &user_task); } @@ -69,18 +56,18 @@ int kmain(void) uart_printf(CONSOLE, "RTOS by roemvaar (Feb, 2024).\r\n"); - int first_task = task_create(0, &user_task); - if (first_task < 0) { - uart_printf(CONSOLE, "Error creating first task: %d\r\n", first_task); - return first_task; - } + // int first_task = task_create(0, &user_task); + // if (first_task < 0) { + // uart_printf(CONSOLE, "Error creating first task: %d\r\n", first_task); + // return first_task; + // } char input; uint32_t count; while (1) { input = uart_getc(CONSOLE); - + if (input == '$') { count = sys_timer_get_count(); uart_printf(CONSOLE, "Timer count: %u\r\n", count); @@ -91,5 +78,9 @@ int kmain(void) } } + // while (1) { + // schedule(); + // } + return 0; } diff --git a/src/sys.c b/src/sys.c index 9566407..6eae5d4 100644 --- a/src/sys.c +++ b/src/sys.c @@ -34,7 +34,7 @@ void sys_task_exit(void) // For most purposes this is enough to tell the program that this // entry can be overwritten - //schedule(); + // schedule(); uart_printf(CONSOLE, "Syscall: sys_task_exit\r\n"); // exit_task(); call from scheduler } @@ -53,4 +53,5 @@ void sys_clock_delay(int tid, int ticks) -// void *const sys_call_table[] = {sys_task_create, sys_task_tid, sys_task_parent_tid, sys_task_exit, sys_clock_time, sys_clock_delay}; +// void *const sys_call_table[] = {sys_task_create, sys_task_tid, sys_task_parent_tid, +// sys_task_exit, sys_clock_time, sys_clock_delay}; diff --git a/src/task.c b/src/task.c index 4e02c08..47eca71 100644 --- a/src/task.c +++ b/src/task.c @@ -2,15 +2,24 @@ #include "sched.h" -/* - * return: +/* task_create + * + * Allocates and initializes a task descriptor, using the given priority, and + * the given function pointer as a pointer to the entry point of executable + * code, essentially a function with no arguments and no return value. When + * 'create' returns, the task descriptor has all the state needed to run the + * task, the task’s stack has been suitably initialized, and the task has been + * entered into its ready queue so that it will run the next time it is scheduled. + * + * return: * tid - success creating new task * -1 - invalid priority * -2 - kernel is out of task descriptors */ -int task_create(int priority, void (*function)()) +int task_create(int priority, entry_point function) { - (void)function; // TODO: Where should I store the entry point? + // // Get task id from scheduler + // int tid = get_tid(); if (priority != 0) { return -1; @@ -18,41 +27,90 @@ int task_create(int priority, void (*function)()) return -2; } - struct TaskDescriptor *new_task; + TaskDescriptor_t *new_task; + TaskDescriptor_t *current_task = get_current_task(); + /* Get an empty slot on the task descriptor's array and fill the structure */ new_task = get_free_task_descriptor(); new_task->priority = priority; new_task->state = ACTIVE; - int tid = num_tasks; - new_task->tid = tid; - new_task->parent_td = current; - task_descriptors_array[tid] = new_task; + new_task->tid = num_tasks; + // new_td->tid = tid; + new_task->parent_td = current_task; + new_task->function = function; - return tid; + /* Add new task into ready_queue */ + add_task_to_ready_queue(new_task); + + return new_task->tid; } -/* - * return: +/* task_tid + * + * Returns the task id of the calling task. + * + * return: * tid - the task id of the task that is currently running */ int task_tid(void) { - int tid = get_current_task_tid(); + TaskDescriptor_t *current_task = get_current_task(); - return tid; + return current_task->tid; } +/* task_parent_tid + * + * Returns the task id of the task that created the calling task. This will be + * problematic only if the parent task has exited or been destroyed, in which + * case the return value is an error. + * + * return: + * tid - the task id of the task that created the caller + * 0 - if the current task is init_task + * -1 - if parent has been destroyed + */ int task_parent_tid(void) { - return 0; + int parent_tid; + + TaskDescriptor_t *current_task = get_current_task(); + + // Check if the current task is init_task + if (current_task->tid == 0) { + parent_tid = 0; + } else { + TaskDescriptor_t *parent = current_task->parent_td; + + if (parent->state == EXITED) { + parent_tid = -1; + } else { + parent_tid = parent->tid; + } + } + + return parent_tid; } +/* task_yield + * + * Causes a task to pause executing. The task is moved to the end of its priority + * queue, and will resume executing when next scheduled. + */ void task_yield(void) { return; } +/* task_exit + * + * Causes a task to cease execution permanently. It is removed from all priority + * queues, send queues, receive queues and event queues. Resources owned by the + * task, primarily its memory and task descriptor, may be reclaimed. + */ void task_exit(void) { + /* The task will never again run. It may still retain some resources if resource re-use is not fully implemented. */ + return; }