-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathkernel_functions.c
More file actions
481 lines (414 loc) · 15.3 KB
/
kernel_functions.c
File metadata and controls
481 lines (414 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
#include "system_sam3x.h"
#include "at91sam3x8.h"
#include "kernel_functions.h"
#include "DoubleLinkedList.h"
#include "mail_functions.h"
// Pointers to the different lists in use in the system.
List *ReadyList = NULL;
List *WaitingList = NULL;
List *TimerList = NULL;
// Pointers to the previous and next running tasks in the system.
TCB *PreviousTask = NULL;
TCB *NextTask = NULL;
// The global system tick counter.
int Ticks;
// The current mode of the system.
int KernelMode;
/**
* Initializes the kernel and its data structures, including ReadyList, WaitingList, and TimerList, and leaves the kernel in start-up mode.
* This function must be called before any other call to the kernel.
* @return The function's status, either FAIL or OK (defined as constants in kernel.h).
* @throws An exception if there is an error initializing the kernel.
*/
exception init_kernel(void){
Ticks = 0;
ReadyList = Create_List();
if(ReadyList == NULL){
free(ReadyList);
return FAIL;
}
WaitingList = Create_List();
if(WaitingList == NULL){
free(ReadyList);
free(WaitingList);
return FAIL;
}
TimerList = Create_List();
if(TimerList == NULL){
free(ReadyList);
free(WaitingList);
free(TimerList);
return FAIL;
}
if(!create_task(Idle_Task, UINT_MAX ))
return FAIL;
KernelMode = INIT;
return OK;
}
/**
* The Idle_Task function represents an empty task that runs forever. This task is typically
* used as a placeholder for unused CPU cycles and helps prevent the system from stalling
* due to a lack of active tasks. The Idle_Task function does not perform any operations and
* simply enters an infinite loop, waiting for the next tick interrupt to occur.
* @param None
* @return None
*/
void Idle_Task(){
while(1); // Alles Gut!
}
/**
* Creates a task with the specified function pointer and deadline. If the call is made in start-up mode,
* only the necessary data structures will be created. However, if the call is made in running mode,
* it will lead to a rescheduling and possibly a context switch.
* @param task_body A pointer to the C function holding the code of the task.
* @param deadline The kernel will try to schedule the task so it will meet this deadline.
* @return The exception status of the function, i.e. FAIL or OK.
*/
exception create_task (void (*taskBody)(), uint deadline) {
TCB *new_tcb;
new_tcb = (TCB *) calloc(1, sizeof(TCB));
if (new_tcb == NULL) {
free(new_tcb);
return FAIL;
}
/* you must check if calloc was successful or not! */
new_tcb->PC = taskBody;
new_tcb->SPSR = 0x21000000;
new_tcb->Deadline = deadline;
new_tcb->StackSeg [STACK_SIZE - 2] = 0x21000000;
new_tcb->StackSeg [STACK_SIZE - 3] = (unsigned int) taskBody;
new_tcb->SP = &(new_tcb->StackSeg [STACK_SIZE - 9]);
// after the mandatory initialization you can implement the rest of the suggested pseudocode
if(KernelMode == INIT){
exception e = addAtFirstSmaller(ReadyList, createNode(new_tcb, 0, NULL));
if(e != OK)
return FAIL;
return OK;
}else{
isr_off();
PreviousTask = ReadyList->pHead->pTask;
exception e = addAtFirstSmaller(ReadyList, createNode(new_tcb, 0, NULL));
if(e != OK)
return FAIL;
NextTask = ReadyList->pHead->pTask;
SwitchContext();
}
return OK;
}
/**
* Starts the kernel and the system of created tasks.
* Control is passed to the task with the tightest deadline.
* This function must be placed last in the application initialization code.
*/
void run (void){
Ticks = 0;
KernelMode = RUNNING;
NextTask = ReadyList->pHead->pTask;
LoadContext_In_Run();
}
/**
* Stop the current task and remove all of its data. Before removing the data,
* it switches to the process stack of the task to be called.
* Then, it schedules another task to start running.
*/
void terminate (void){
isr_off();
//REMOVE and extract the removed node without freeing memory
Node *leavingObj = extract(ReadyList);
NextTask = ReadyList->pHead->pTask;
switch_to_stack_of_next_task();
//remove data structers of task being terminated
free(leavingObj->pTask);
free(leavingObj);
//Load context
LoadContext_In_Terminate();
}
/**
* Creates a mailbox with a specified maximum number of messages and data size for each message.
* The mailbox is used for asynchronous and synchronous communication between tasks.
* @param nMessages Maximum number of messages the mailbox can hold.
* @param nDataSize Size of one message in the mailbox.
* @return A pointer to the created mailbox or NULL if creation fails.
*/
mailbox* create_mailbox (uint nMessages, uint nDataSize) {
mailbox* mBox = (mailbox*) calloc(1, sizeof(mailbox));
// Memory Full
if (mBox == NULL){
free(mBox);
return NULL;
}
mBox->nMaxMessages = nMessages;
mBox->nDataSize = nDataSize;
mBox->pHead = NULL;
mBox->pTail = NULL;
return mBox;
}
/**
* Removes a mailbox if it is empty.
* @param mBox Pointer to the mailbox to be removed.
* @return OK if the mailbox was removed, NOT_EMPTY if the mailbox was not removed because it was not empty.
*/
exception remove_mailbox (mailbox* mBox){
// Empty mailbox
if (mBox->pHead == NULL){
free(mBox);
return OK;
}else
return NOT_EMPTY;
}
/**
* Sends a message to the specified mailbox. If there is a receiving task waiting for a message on the specified mailbox,
* the message will be delivered and the receiving task will be moved to the ReadyList. Otherwise, the sending task will be blocked.
* During the blocking period of the task, its deadline might be reached, in which case it will be resumed with the DEADLINE_REACHED exception.
* @param mBox Pointer to the specified mailbox.
* @param pData Pointer to a memory area where the data of the communicated message is residing.
* @return OK if the message was sent successfully,
* DEADLINE_REACHED if the sending task's deadline is reached while it is blocked by the send_wait call.
*/
exception send_wait(mailbox* mBox, void* pData){
if (mBox == NULL || pData == NULL)
return FAIL;
// #Disable interrupt
isr_off();
msg* receiving_task = mBox->pHead;
int dataSize = mBox->nDataSize;
// We are the Receiver of a message
if (receiving_task->Status == RECEIVER){
// #Copy sender's data to the data area of the receivers Message
memcpy(receiving_task->pData, pData, dataSize);
// #Remove receiving task's Message struct from the mailbox
deCoupleMessage(mBox, receiving_task);
// #Update PreviousTask
PreviousTask = ReadyList->pHead->pTask;
// #Insert receiving task in ReadyList
addAtFirstSmaller(ReadyList, deCoupler2(WaitingList, receiving_task->pBlock));
// Decrement number of blocked messages
(mBox->nBlockedMsg)--;
// #Update NextTask
NextTask = ReadyList->pHead->pTask;
// We are the Sender of a message
}else{
// #Allocate a Message structure Set data pointer
msg* message = (msg*) calloc(1, sizeof(msg));
// Memory Full check
if (message == NULL){
free(message);
return FAIL;
}
// Allocate memory for the block
message->pData = (char *) calloc(1, mBox->nDataSize);
// Memory Full check
if (message->pData == NULL){
free(message->pData);
free(message);
return FAIL;
}
// #Add Message to the mailbox
memcpy(message->pData, pData, mBox->nDataSize);
// Setup the message struct correctly
message->Status = SENDER;
message->pBlock = ReadyList->pHead;
// Add message to mailbox
addMessage(mBox, message);
// Increment number of blocked messages
(mBox->nBlockedMsg)++;
// #Update PreviousTask
PreviousTask = ReadyList->pHead->pTask;
// #Move sending task from ReadyList to WaitingList
addAtFirstSmaller(WaitingList, deCoupler2(ReadyList, message->pBlock));
// #Update NextTask
NextTask = ReadyList ->pHead ->pTask;
}
SwitchContext();
if (Ticks >= NextTask->Deadline){
isr_off();
// Remove SEND message
msg* e_message = extractMessage(mBox);
(mBox->nBlockedMsg)--;
// Free as we are now done
//free(e_message); **********************************
// Interrupts on again
isr_on();
return DEADLINE_REACHED;
}else
return OK;
}
/**
* Sends a Message to the specified mailbox. The sending task will continue execution after the call.
* When the mailbox is full, the oldest Message will be overwritten. The send_no_wait call will imply a
* new scheduling and possibly a context switch. Note: send_wait and send_no_waitMessages shall not be mixed
* in the same mailbox.
* @param mBox a pointer to the specified Mailbox.
* @param pData a pointer to a memory area where the data of the communicated Message is residing.
* @return Description of the function's status, i.e. FAIL/OK.
*/
exception send_no_wait(mailbox* mBox, void* pData){
if (mBox == NULL || pData == NULL)
return FAIL;
// #Disable interrupt
isr_off();
msg* receiving_task = mBox->pHead;
int dSize = mBox->nDataSize;
// We are the Receiver of a message
if (receiving_task->Status == RECEIVER){
// #Copy sender's data to the data area of the receivers Message
memcpy(receiving_task->pData, pData, dSize);
// #Remove receiving task's Message struct from the mailbox
deCoupleMessage(mBox, receiving_task);
// #Update PreviousTask
PreviousTask = ReadyList->pHead->pTask;
// #Insert receiving task in ReadyList
addAtFirstSmaller(ReadyList, deCoupler2(WaitingList, receiving_task->pBlock));
// #Update NextTask
NextTask = ReadyList->pHead->pTask;
// #Switch context
SwitchContext();
}else{
// #Allocate a Message structure Set data pointer
msg* message = (msg*) calloc(1, sizeof(msg));
// Memory Full check
if(message == NULL){
msg* e_message = extractMessage(mBox);
free(e_message->pData);
free(e_message);
msg* message = (msg*) calloc(1, sizeof(msg));
}
// Allocate memory for the block
message->pData = (char *) calloc(1, mBox->nDataSize);
// Memory Full check
if (message->pData == NULL){
free(message->pData);
free(message);
return FAIL;
}
// #Copy data to the Message
memcpy(message->pData, pData, dSize);
// Setup the message struct correctly
message->pBlock = NULL;
message->Status = SENDER;
if (mBox->nMessages >= mBox->nMaxMessages){
msg* e_message = extractMessage(mBox);
free(e_message->pData);
free(e_message);
}
addMessage(mBox, message);
// No SwitchContext function call so has to enable Interrupt manually again
isr_on();
}
return OK;
}
/**
* Attempts to receive a message from the specified mailbox. If there is a send_wait or a send_no_wait message waiting for a receive_wait or a
* receive_no_wait message on the specified mailbox, receive_wait will collect it. If the message was of send_wait type, the sending task
* will be moved to the ReadyList. Otherwise, if there is no send message waiting on the specified mailbox, the receiving task will be blocked.
* During the blocking period of the task, its deadline might be reached, in which case it will be resumed with the DEADLINE_REACHED exception.
* @param mBox Pointer to the specified mailbox.
* @param pData Pointer to a memory area where the data of the communicated message is to be stored.
* @return OK if the message was received successfully, DEADLINE_REACHED if the receiving task's deadline is reached while it is blocked by the receive_wait call.
*/
exception receive_wait(mailbox* mBox, void* pData){
if (mBox == NULL || pData == NULL)
return FAIL;
// #Disable interrupt
isr_off();
msg* sending_message = mBox->pHead;
int dSize = mBox->nDataSize;
if (sending_message->Status == SENDER){
// # Copy sender's data to receiving task's data area
memcpy(pData, sending_message->pData, dSize);
// Remove sending task's Message struct from the mailbox
deCoupleMessage(mBox, sending_message);
if (isMember(WaitingList, sending_message->pBlock)){
// #Update PreviousTask
PreviousTask = ReadyList->pHead->pTask;
// #Move sending task to ReadyList
addAtFirstSmaller(ReadyList, deCoupler2(WaitingList, sending_message->pBlock));
// #Update NextTask
NextTask = ReadyList ->pHead ->pTask;
}else{
// #Free senders data area
free(sending_message->pData);
//free(sending_message); ??
}
(mBox->nBlockedMsg)--;
}else{
// #Allocate a Message structure
msg* message = (msg*) calloc(1, sizeof(msg));
// Memory Full check
if (message == NULL){
free(message);
return FAIL;
}
// Allocate memory for the block
message->pData = (char *) calloc(1, mBox->nDataSize);
// Memory Full check
if (message->pData == NULL){
free(message->pData);
free(message);
return FAIL;
}
// #Add Message to the mailbox
message->pData = pData;
message->Status = RECEIVER;
message->pBlock = ReadyList->pHead;
ReadyList->pHead->pMessage = message;
addMessage(mBox, message);
// #Update PreviousTask
PreviousTask = ReadyList->pHead->pTask;
// #Move sending task from ReadyList to WaitingList
addAtFirstSmaller(WaitingList, deCoupler2(ReadyList, message->pBlock));
// Increment number of blocked messages
(mBox->nBlockedMsg)++;
// #Update NextTask
NextTask = ReadyList ->pHead ->pTask;
}
SwitchContext();
if (Ticks >= NextTask->Deadline){
isr_off();
// Remove SEND message
msg* e_message = deCoupleMessage(mBox, ReadyList->pHead->pMessage);
// Free as we are now done
//free(e_message); **********************************
isr_on();
return DEADLINE_REACHED;
}else
return OK;
}
/**
* Attempts to receive a Message from the specified mailbox. The calling task will continue execution after the call.
* When there is no sendMessage available, or if the mailbox is empty, the function will return FAIL. Otherwise, OK
* is returned. The call might imply a new scheduling and possibly a context switch.
* @param mBox a pointer to the specified mailbox.
* @param pData a pointer to the Message.
* @return Integer indicating whether a Message was received or not (OK/FAIL).
*/
exception receive_no_wait(mailbox* mBox, void* pData){
if (mBox == NULL || pData == NULL)
return FAIL;
msg* sending_message = mBox->pHead;
int dSize = mBox->nDataSize;
if (sending_message->Status == SENDER){
// #Disable interrupt
isr_off();
// # Copy sender's data to receiving task's data area
memcpy(pData, sending_message->pData, dSize);
// Remove sending task's Message struct from the mailbox
// Free the message struct
deCoupleMessage(mBox, sending_message);
if (isMember(WaitingList, sending_message->pBlock)){
// #Update PreviousTask
PreviousTask = ReadyList->pHead->pTask;
// #Move sending task to ReadyList
addAtFirstSmaller(ReadyList, deCoupler2(WaitingList, sending_message->pBlock));
mBox->nBlockedMsg--;
// #Update NextTask
NextTask = ReadyList ->pHead ->pTask;
SwitchContext();
}else{
// #Free senders data area
free(sending_message->pData);
}
}else
return FAIL;
return OK;
}