SRTOS
Loading...
Searching...
No Matches
task.c File Reference
#include "task.h"
Include dependency graph for task.c:

Go to the source code of this file.

Functions

uint32_t * initTaskStackFrame (uint32_t taskStack[], void(*taskFunc)(void))
 Initializes a task's stack frame.
STATUS createTask (uint32_t taskStack[], void(*taskFunc)(void), unsigned int priority, TCB *userAllocatedTCB, TaskNode *userAllocatedTaskNode)
 Add a task to the scheduler's ready list.
void SysTick_Handler ()
 This interrupt handler will be called every 1 ms, checking if any tasks need to be unblocked or blocked, and whether a context switch is needed.
void PendSV_Handler ()
 This interrupt will be pended when a context switch is needed.
void SVC_Handler ()
 This interrupt will be executed when the scheduler is started.
void startScheduler ()
 Start the scheduler.
void setPendSVPending ()
 This function will be called when a context-switch is needed.
void taskDelay (uint32_t ticksToDelay)
 This function will delay a task's execution for ticksToDelay ms.
void __attribute__ ((weak))
uint32_t getCurTaskWordsAvailable ()
 This function will return the minimum number of words left on the stack.

Variables

volatile uint32_t msTicks = 0
 msTicks contains the amount of ticks that have occured since the scheduler started.
TaskNodecurTask = NULL
TaskNodereadyTasksList [MAX_PRIORITIES] = { NULL }
 This is a list of linked lists that contain the ready tasks.

Function Documentation

◆ __attribute__()

void __attribute__ ( (weak) )

Definition at line 494 of file task.c.

496{
497 for (;;)
498 {
499 }
500}

◆ createTask()

STATUS createTask ( uint32_t taskStack[],
void(* taskFunc )(void),
unsigned int priority,
TCB * userAllocatedTCB,
TaskNode * userAllocatedTaskNode )

Add a task to the scheduler's ready list.

Parameters
taskStackThe user-defined array of size STACK_SIZE
taskAddressThe address of the task function
priorityThe task's priority which must be between 0 and MAX_PRIORITIES - 1, inclusive
userAllocatedTCBThe address of the task's TCB allocated by the user
userAllocatedTaskNodeThe address of the task's TaskNode allocated by the user
Returns
Returns either STATUS_SUCCESS or STATUS_FAILURE, depending on whether the task was successfully created or not.
Note
Must be called before the scheduler is started.

Definition at line 77 of file task.c.

80{
81 if (!taskFunc || !userAllocatedTCB || !userAllocatedTaskNode)
82 return STATUS_FAILURE;
83 if (priority >= MAX_PRIORITIES)
84 return STATUS_FAILURE;
85 if (STACK_SIZE < 18)
86 return STATUS_FAILURE;
87
88 userAllocatedTCB->sp = initTaskStackFrame (taskStack, taskFunc);
89 userAllocatedTCB->priority = priority;
90 userAllocatedTCB->id = prvCurTaskIDNum;
91 prvCurTaskIDNum++;
92 userAllocatedTCB->stackFrameLowerBoundAddr = &taskStack[0];
93
94 /* Insert at end of tasks linked list */
95 userAllocatedTaskNode->taskTCB = userAllocatedTCB;
96 userAllocatedTaskNode->next = NULL;
97
98 STATUS resStatus;
100 {
101 resStatus = prvAddTaskNodeToReadyList (userAllocatedTaskNode);
102 }
104
105 return resStatus;
106}
#define STACK_SIZE
Default stack size for each task, in 32-bit words.
#define MAX_PRIORITIES
Number of unique task priority levels supported by the scheduler.
uint32_t priority
Definition task.h:33
uint32_t * sp
Definition task.h:32
uint32_t id
Definition task.h:34
uint32_t * stackFrameLowerBoundAddr
Definition task.h:36
TCB * taskTCB
Definition task.h:46
TaskNode * next
Definition task.h:47
void systemEXIT_CRITICAL()
Exit a critical section by enabling all interrupts.
void systemENTER_CRITICAL()
Enter a critical section by disabling all interrupts.
uint32_t * initTaskStackFrame(uint32_t taskStack[], void(*taskFunc)(void))
Initializes a task's stack frame.
Definition task.c:46
STATUS
This enum is used to indicate whether an operation was sucessful or not.
Definition task.h:22
@ STATUS_FAILURE
Definition task.h:24
Here is the call graph for this function:
Here is the caller graph for this function:

◆ getCurTaskWordsAvailable()

uint32_t getCurTaskWordsAvailable ( )

This function will return the minimum number of words left on the stack.

Returns
This function returns the minimum amount of words left on the stack.
Note
A word is 32 bits in size on the ARM-Cortex M4/M4f.

Definition at line 503 of file task.c.

504{
505 uint32_t *curTaskStackFrameLowerBound;
507 {
508 curTaskStackFrameLowerBound = curTask->taskTCB->stackFrameLowerBoundAddr;
509 }
511
512 curTaskStackFrameLowerBound
513 += 2; /* Skip the 2 canary values (assumes no stack overflow) */
514
515 uint32_t amtWordsAvailable = 0;
516
517 while (*(curTaskStackFrameLowerBound) == STACK_USAGE_WATERMARK)
518 {
519 curTaskStackFrameLowerBound++;
520 amtWordsAvailable++;
521 }
522
523 return amtWordsAvailable;
524}
#define STACK_USAGE_WATERMARK
Definition mcu_macros.h:27
TaskNode * curTask
Definition task.c:11
Here is the call graph for this function:

◆ initTaskStackFrame()

uint32_t * initTaskStackFrame ( uint32_t taskStack[],
void(* taskFunc )(void) )

Initializes a task's stack frame.

This will populate the stack with a dummy xPSR and LR value to allow the task to begin execution. These values will be overwritten once the task starts executing. Also, this will fill the bottom of the stack with canary values so that stack overflows can be detected. The stack is also populated with usage watermarks.

Parameters
taskStackThe array created by the user
taskFuncThe address of the task function
Warning
This function should not be called by user code.
Returns
Returns a pointer to the top of the stack.

Definition at line 46 of file task.c.

47{
48 for (int i = 0; i < STACK_SIZE; ++i)
49 {
50 taskStack[i] = STACK_USAGE_WATERMARK;
51 }
52
53 taskStack[0] = STACK_OVERFLOW_CANARY_VALUE;
54 taskStack[1] = STACK_OVERFLOW_CANARY_VALUE;
55
56 taskStack[STACK_SIZE - 1] = 0x01000000; /* xPSR */
57 taskStack[STACK_SIZE - 2] = ((uint32_t)taskFunc) | 0x1; /* PC */
58 taskStack[STACK_SIZE - 3] = 0xFFFFFFFD; /* LR */
59 taskStack[STACK_SIZE - 4] = 0x00000000; /* R12 */
60 taskStack[STACK_SIZE - 5] = 0x00000000; /* R3 */
61 taskStack[STACK_SIZE - 6] = 0x00000000; /* R2 */
62 taskStack[STACK_SIZE - 7] = 0x00000000; /* R1 */
63 taskStack[STACK_SIZE - 8] = 0x00000000; /* R0 */
64 taskStack[STACK_SIZE - 9] = 0x00000000; /* R11 */
65 taskStack[STACK_SIZE - 10] = 0x00000000; /* R10 */
66 taskStack[STACK_SIZE - 11] = 0x00000000; /* R9 */
67 taskStack[STACK_SIZE - 12] = 0x00000000; /* R8 */
68 taskStack[STACK_SIZE - 13] = 0x00000000; /* R7 */
69 taskStack[STACK_SIZE - 14] = 0x00000000; /* R6 */
70 taskStack[STACK_SIZE - 15] = 0x00000000; /* R5 */
71 taskStack[STACK_SIZE - 16] = 0x00000000; /* R4 */
72
73 return &taskStack[STACK_SIZE - 16];
74}
#define STACK_OVERFLOW_CANARY_VALUE
Definition mcu_macros.h:26
Here is the caller graph for this function:

◆ PendSV_Handler()

void PendSV_Handler ( )

This interrupt will be pended when a context switch is needed.

Note
This interrupt will run only after all other pending interupts have finished executing.
prvNextTask will be set when this interrupt is pended.
Warning
This function should never be called by user code.

Definition at line 166 of file task.c.

167{
168 prvCheckCurTaskForStackOverflow ();
169
170 uint32_t spToSave;
171 __asm volatile ("mrs r0, PSP\n"
172 "stmdb r0!, {r4-r11}\n"
173 "mov %[spToSave], r0\n"
174 : [spToSave] "=r"(spToSave));
175
176 curTask->taskTCB->sp = (uint32_t *)spToSave;
177
178 uint32_t nextSP;
180 {
181 nextSP = (uint32_t)prvNextTask->taskTCB->sp;
182 curTask = prvNextTask;
183 }
185
186 __asm volatile ("mov r2, %[nextSP]\n"
187 "ldmia r2!, {r4-r11}\n"
188 "msr PSP, r2\n"
189 :
190 : [nextSP] "r"(nextSP));
191
192 __asm volatile ("ldr lr, =0xFFFFFFFD\n"
193 "bx lr\n");
194}
Here is the call graph for this function:

◆ setPendSVPending()

void setPendSVPending ( )

This function will be called when a context-switch is needed.

Warning
This function should not be called from user code.

Definition at line 231 of file task.c.

232{
233 ICSR |= (1 << 28);
234}
#define ICSR
Definition mcu_macros.h:22
Here is the caller graph for this function:

◆ startScheduler()

void startScheduler ( )

Start the scheduler.

Note
No tasks should be created after this function is called.
There should be no code directly under this function call that is expected to execute. This function will initiate the transfer of execution to the first task.

Definition at line 218 of file task.c.

219{
220 prvIdleTask = createIdleTask ();
221 curTask = prvGetHighestTaskReadyToExecute ();
222 __asm volatile ("svc #0");
223}
Here is the caller graph for this function:

◆ SVC_Handler()

void SVC_Handler ( )

This interrupt will be executed when the scheduler is started.

Note
This is where the first task is started.
Warning
This function should not be called from user code.

Definition at line 203 of file task.c.

204{
205 TCB *tcbToStart = curTask->taskTCB;
206 uint32_t spToStart = (uint32_t)tcbToStart->sp;
207
208 __asm volatile ("ldr r0, %[sp]\n"
209 "ldmia r0!, {r4-r11}\n"
210 "msr PSP, r0\n"
211 "ldr lr, =0xFFFFFFFD\n"
212 "bx lr\n"
213 :
214 : [sp] "m"(spToStart));
215}
This struct is the Task Control Block (TCB), which is what stores a task's properties.
Definition task.h:31

◆ SysTick_Handler()

void SysTick_Handler ( )

This interrupt handler will be called every 1 ms, checking if any tasks need to be unblocked or blocked, and whether a context switch is needed.

Warning
This function should not be called from user code.

Definition at line 115 of file task.c.

116{
117 msTicks++;
118
119 prvUnblockDelayedTasksReadyToUnblock ();
120
121 if (curTask == NULL)
122 {
123 return;
124 }
125
126 uint32_t curExecutingPriority = curTask->taskTCB->priority;
127
128 TaskNode *highestPriorityPossibleExecute
129 = prvGetHighestTaskReadyToExecute ();
130
131 /* Check if a higher priority task is ready to execute */
132 if (curExecutingPriority < highestPriorityPossibleExecute->taskTCB->priority)
133 {
134 prvNextTask = highestPriorityPossibleExecute;
136 return;
137 }
138
139 if (curTask->next == NULL)
140 {
141 if (highestPriorityPossibleExecute->taskTCB->id != curTask->taskTCB->id)
142 {
143 /* There is another task of equal priority, time to switch. */
144 prvNextTask = highestPriorityPossibleExecute;
146 return;
147 }
148 }
149 else
150 {
151 /* There is another task of equal priority, time to switch. */
152 prvNextTask = curTask->next;
154 return;
155 }
156}
volatile uint32_t msTicks
msTicks contains the amount of ticks that have occured since the scheduler started.
Definition task.c:10
void setPendSVPending()
This function will be called when a context-switch is needed.
Definition task.c:231
Here is the call graph for this function:

◆ taskDelay()

void taskDelay ( uint32_t ticksToDelay)

This function will delay a task's execution for ticksToDelay ms.

This function will move the current task to the blocked list for ticksToDelay ms. If there are no other tasks available to execute, the idleTask will execute.

Parameters
ticksToDelayThe number of milliseconds to delay a task's execution.

Definition at line 237 of file task.c.

238{
240 {
241 uint32_t curTaskID = curTask->taskTCB->id;
242 uint32_t curTaskPriority = curTask->taskTCB->priority;
243
244 curTask->taskTCB->delayedUntil = msTicks + ticksToDelay;
245
246 /* Remove the task from the ready list */
247 TaskNode *cur = readyTasksList[curTaskPriority];
248 TaskNode *prev = NULL;
249
250 if (cur->next == NULL)
251 {
252 /* This is the only task for this priority, and it must be curTask */
253 readyTasksList[curTaskPriority] = NULL;
254 prvNextTask = prvGetHighestTaskReadyToExecute ();
255 prvAddTaskToBlockedList (curTask);
258 return;
259 }
260
261 /* Check if curTask is the head of the priority */
262 if (cur->taskTCB->id == curTaskID)
263 {
264 readyTasksList[curTaskPriority] = curTask->next;
265
266 prvNextTask = prvGetHighestTaskReadyToExecute ();
267 prvAddTaskToBlockedList (curTask);
270 return;
271 }
272
273 /* There is more than one task for the current priority */
274 while (cur->taskTCB->id != curTaskID)
275 {
276 prev = cur;
277 cur = cur->next;
278 }
279
280 TaskNode *afterCur = cur->next;
281 prev->next = afterCur;
282
283 prvNextTask = prvGetHighestTaskReadyToExecute ();
284 prvAddTaskToBlockedList (curTask);
285 }
288}
TaskNode * readyTasksList[MAX_PRIORITIES]
This is a list of linked lists that contain the ready tasks.
Definition task.c:12
Here is the call graph for this function:

Variable Documentation

◆ curTask

TaskNode* curTask = NULL

Definition at line 11 of file task.c.

◆ msTicks

volatile uint32_t msTicks = 0

msTicks contains the amount of ticks that have occured since the scheduler started.

Warning
User code should rarely, if ever, access this variable.

Definition at line 10 of file task.c.

◆ readyTasksList

TaskNode* readyTasksList[MAX_PRIORITIES] = { NULL }

This is a list of linked lists that contain the ready tasks.

Note
Each index represents the linked list of ready tasks for that priority.
Warning
This variable should never be accessed in user code.

Definition at line 12 of file task.c.

12{ NULL };