Skip to content

Commit

Permalink
sched: create an array of task descriptors to hold state of all tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
roemvaar committed Apr 5, 2024
1 parent ba8ad52 commit 38fa6db
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 72 deletions.
112 changes: 87 additions & 25 deletions include/sched.h
Original file line number Diff line number Diff line change
@@ -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_ */
2 changes: 1 addition & 1 deletion include/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
49 changes: 20 additions & 29 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -48,18 +37,16 @@ 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
* (such as the system timer interrupt).
*/
sched_init();

/* Run first user space application
*/
/* Run first user space application */
// int first_task = task_create(1, &user_task);
}

Expand All @@ -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);
Expand All @@ -91,5 +78,9 @@ int kmain(void)
}
}

// while (1) {
// schedule();
// }

return 0;
}
5 changes: 3 additions & 2 deletions src/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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};
88 changes: 73 additions & 15 deletions src/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,115 @@

#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;
} else if (get_num_tasks() >= MAX_NUM_TASKS) {
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;
}

0 comments on commit 38fa6db

Please sign in to comment.