]> rtime.felk.cvut.cz Git - arc.git/blob - system/kernel/task.c
Renamed USE_DEBUG to USE_DEBUG_PRINT, for now. Updates to kernel testsystem and kernel.
[arc.git] / system / kernel / task.c
1 /* -------------------------------- Arctic Core ------------------------------
2  * Arctic Core - the open source AUTOSAR platform http://arccore.com
3  *
4  * Copyright (C) 2009  ArcCore AB <contact@arccore.com>
5  *
6  * This source code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 as published by the
8  * Free Software Foundation; See <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * for more details.
14  * -------------------------------- Arctic Core ------------------------------*/
15
16 #include <stdlib.h>\r
17 #include "Os.h"
18 #include "internal.h"
19 #include "arc.h"
20 #include "arch.h"
21
22 /** @req OS067 */
23
24 _Bool os_pcb_pid_valid( OsPcbType *restrict pcb ) {
25         return ( pcb->pid > Oil_GetTaskCnt() ) ? 0 : 1;
26 }
27 /**
28  * Start an extended task.
29  * Tasks done:
30  * - Grab the internal resource for the process
31  * - Set it running state.
32  * - Start to execute the process
33  *
34  */
35 void Os_TaskStartExtended( void ) {
36         OsPcbType *pcb;
37
38         PRETASKHOOK();
39
40         pcb = Os_TaskGetCurrent();
41         Os_ResourceGetInternal();
42         Os_TaskMakeRunning(pcb);
43
44         Os_ArchFirstCall();
45
46         /** @req OS239 */
47         Irq_Disable();
48         if( Os_IrqAnyDisabled() ) {
49                 Os_IrqClearAll();
50         }
51
52 #warning Dont I have to check this at terminate task also?
53
54         /** @req OS069 */
55         ERRORHOOK(E_OS_MISSINGEND);
56
57         /** @req OS052 */
58         TerminateTask();
59 }
60
61 /**
62  * Start an basic task.
63  * See extended task.
64  */
65
66 void Os_TaskStartBasic( void ) {
67         OsPcbType *pcb;
68
69         PRETASKHOOK();
70
71         pcb = Os_TaskGetCurrent();
72         Os_ResourceGetInternal();
73         Os_TaskMakeRunning(pcb);
74         Os_ArchFirstCall();
75
76
77         /** @req OS239 */
78         Irq_Disable();
79         if( Os_IrqAnyDisabled() ) {
80                 Os_IrqClearAll();
81         }
82
83         /** @req OS069 */
84         ERRORHOOK(E_OS_MISSINGEND);
85
86         /** @req OS052 */
87         TerminateTask();
88 }
89
90
91 static void Os_StackSetup( OsPcbType *pcbPtr ) {
92         uint8_t *bottom;
93
94         /* Find bottom of the stack so that we can place the
95          * context there.
96          *
97          * stack bottom = high address. stack top = low address
98          */
99         bottom = (uint8_t *)pcbPtr->stack.top + pcbPtr->stack.size;
100         pcbPtr->stack.curr = bottom;
101         // TODO: alignments here..
102         // TODO :use function os_arch_get_call_size() ??
103
104         // Make some space for back-chain.
105         bottom -= 16;
106         // Set the current stack so that it points to the context
107         pcbPtr->stack.curr = bottom - Os_ArchGetScSize();
108
109         Os_StackSetEndmark(pcbPtr);
110 }
111
112 /**
113  * Fill the stack with a predefined pattern
114  *
115  * @param pcbPtr Pointer to the pcb to fill with pattern
116  */
117 static void Os_StackFill(OsPcbType *pcbPtr) {
118         uint8_t *p = pcbPtr->stack.curr;
119
120         assert(pcbPtr->stack.curr > pcbPtr->stack.top);
121
122         while (p > (uint8_t *) pcbPtr->stack.top) {
123                 --p;
124                 *p = STACK_PATTERN;
125         }
126 }
127
128 #if 0
129 /**
130  *
131  * @param pcbPtr
132  */
133 static void Os_TaskSetEntry(OsPcbType *pcbPtr ) {
134
135 }
136 #endif
137
138
139 /**
140  * Setup the context for a pcb. The context differs for different arch's
141  * so we call the arch dependent functions also.
142  * The context at setup is always a small context.
143  *
144  * @param pcb Ptr to the pcb to setup context for.
145  */
146 void Os_ContextInit( OsPcbType *pcb ) {
147
148         if( pcb->autostart ) {
149                 Os_TaskMakeReady(pcb);
150         } else {
151                 pcb->state = ST_SUSPENDED;
152         }
153
154         Os_StackSetup(pcb);
155         Os_StackFill(pcb);
156         OsArch_SetTaskEntry(pcb);
157
158         Os_ArchSetupContext(pcb);
159 }
160
161 /**
162  *
163  * @param pcb
164  */
165 void Os_ContextReInit( OsPcbType *pcbPtr ) {
166         Os_StackSetup(pcbPtr);
167 }
168
169 /**
170  * Search for a specific task in the pcb list.
171  *
172  * @param tid The task id to search for
173  * @return Ptr to the found pcb or NULL
174  */
175 OsPcbType *os_find_task( TaskType tid ) {
176         OsPcbType *i_pcb;
177
178         /* TODO: Implement this as an array */
179         TAILQ_FOREACH(i_pcb,& os_sys.pcb_head,pcb_list) {
180                 if(i_pcb->pid == tid ) {
181                         return i_pcb;
182                 }
183         }
184         assert(0);
185         return NULL;
186 }
187
188 /**
189  * Adds a pcb to the list of pcb's
190  * @param pcb
191  */
192 TaskType Os_AddTask( OsPcbType *pcb ) {
193         long msr;
194
195         Irq_Save(msr);  // Save irq status and disable interrupts
196
197         pcb->pid = os_sys.task_cnt;
198         // Add to list of PCB's
199         TAILQ_INSERT_TAIL(& os_sys.pcb_head,pcb,pcb_list);
200         os_sys.task_cnt++;
201
202         Irq_Restore(msr);  // Restore interrupts
203         return pcb->pid;
204 }
205
206
207 #define PRIO_ILLEGAL    -100
208 // TODO: we can't have O(n) search here.. hash on prio instead
209
210 /**
211  * Find the top priority task. Even the running task is included.
212  *
213  * @return
214  */
215
216 OsPcbType *Os_TaskGetTop( void ){
217         OsPcbType *i_pcb;
218         OsPcbType *top_prio_pcb = NULL;
219         OsPriorityType top_prio = PRIO_ILLEGAL;
220
221         os_isr_printf(D_TASK,"os_find_top_prio_proc\n");
222
223         TAILQ_FOREACH(i_pcb,& os_sys.ready_head,ready_list) {
224                 // all ready task are canidates
225                 if( i_pcb->state & (ST_READY|ST_RUNNING)) {
226                         if( top_prio != PRIO_ILLEGAL ) {
227                                 if( i_pcb->prio > top_prio ) {
228                                         top_prio = i_pcb->prio;
229                                         top_prio_pcb = i_pcb;
230                                 }
231                         } else {
232                                 top_prio = i_pcb->prio;
233                                 top_prio_pcb = i_pcb;
234                         }
235                 } else {
236                         assert(0);
237                 }
238         }
239
240         assert(top_prio_pcb!=NULL);
241
242         os_isr_printf(D_TASK,"Found %s\n",top_prio_pcb->name);
243
244         return top_prio_pcb;
245 }
246
247
248 #define USE_DEBUG_PRINT
249 #include "Trace.h"
250
251 // we come here from
252 // - WaitEvent()
253 //   old_pcb -> WAITING
254 //   new_pcb -> READY(RUNNING)
255 // - Schedule(),
256 //   old_pcb -> READY
257 //   new_pcb -> READY/RUNNING
258
259 /*
260  * two strategies
261  * 1. When running ->
262  *    - remove from ready queue
263  *    - set state == ST_RUNNING
264  *
265  * 2. When running ->
266  *    * leave in ready queue
267  *    * set state == ST_RUNNING
268  *    - ready queue and ST_READY not the same
269  *    + No need to remove the running process from ready queue
270  */
271
272 OsPcbType *Os_FindTopPrioTask( void ) {
273
274
275         return NULL;
276 }
277
278 /**
279  * Tries to Dispatch.
280  *
281  * Used by:
282  *   ActivateTask()
283  *   WaitEvent()
284  *   TerminateTask()
285  *
286  * @param force Force a re-scheduling
287  *
288  */
289 void Os_Dispatch( _Bool force ) {
290         OsPcbType *pcbPtr;
291         OsPcbType *currPcbPtr;
292
293         assert(os_sys.int_nest_cnt == 0);
294         assert(os_sys.scheduler_lock == 0 );
295
296         pcbPtr = Os_TaskGetTop();
297         currPcbPtr = Os_TaskGetCurrent();
298         /* Swap if we found any process or are forced (multiple activations)*/
299         if( pcbPtr != currPcbPtr ) {
300                 /* Add us to the ready list */
301                 if( currPcbPtr->state & ST_RUNNING ) {
302                         Os_TaskRunningToReady(currPcbPtr);
303                 }
304
305                 /*
306                  * Swap context
307                  */
308                 /** @req OS052 */
309                 POSTTASKHOOK();
310                 assert(pcbPtr!=NULL);
311
312                 Os_ResourceReleaseInternal();
313
314 #if (OS_STACK_MONITORING == 1)
315                 if( !Os_StackIsEndmarkOk(currPcbPtr) ) {
316 #if (  OS_SC1 == 1) || (  OS_SC2 == 1)
317                         /** @req OS068 */
318                         ShutdownOS(E_OS_STACKFAULT);
319 #else
320 #error SC3 or SC4 not supported. Protection hook should be called here
321 #endif
322                 }
323 #endif
324
325 #if 0
326                 // Make a simple stack check for prio procs...
327                 // See OS068, Autosar SWS
328                 {
329                         uint32_t stackp = (uint32_t)Os_ArchGetStackPtr();
330                         uint32_t smallc_size = Os_ArchGetScSize();
331
332                         // enough size to place a small context on the stack
333                         // top( low address ) + small context > current stackpointer
334                         if( (uint32_t)(currPcbPtr->stack.top + smallc_size) > stackp ) {
335                                 ShutdownOS(E_OS_STACKFAULT);
336                         }
337                 }
338 #endif
339
340                 /* Cases:
341                  * 1. Re-Activate the same basic task again -> Os_ArchSwapContextToW()
342                  * 2. Swap out a terminated task -> Os_ArchSwapContextToW()
343                  * 3. Normal swap -> Os_ArchSwapContext()
344                  */
345
346 #if 0
347                 /* Force is ONLY used from TerminateTask() */
348                 if( force ) {
349                         Os_StackSetup(pcbPtr);
350                         OsArch_SetTaskEntry(pcbPtr);
351                         // TODO: This really need to be done
352                         Os_ArchSetupContext(pcbPtr);
353                         // Os_ArchSetSpAndCall(pcbPtr->stack.curr,Os_TaskStartBasic);
354                 }
355 #endif
356
357                 Os_ArchSwapContext(currPcbPtr,pcbPtr);
358
359                 pcbPtr = Os_TaskGetCurrent();
360                 Os_TaskMakeRunning(pcbPtr);
361
362                 Os_ResourceGetInternal();
363
364                 PRETASKHOOK();
365
366         } else {
367                 Os_StackSetup(pcbPtr);
368                 Os_ArchSetSpAndCall(pcbPtr->stack.curr,Os_TaskStartBasic);
369 #if 0
370                 /* We haven't removed ourselves from the ready list? */
371                 assert(currPcbPtr->state != ST_WAITING);
372                 /* We have terminated and found us in the ready list? */
373                 assert(currPcbPtr->state != ST_SUSPENDED);
374 #endif
375         }
376 }
377
378 // We come here from
379 // - os_init
380
381 /**
382  * Called when a task is to be run for the first time.
383  */
384 void Os_TaskSwapContextTo(OsPcbType *old_pcb, OsPcbType *new_pcb ) {
385
386
387         Os_ArchSwapContextTo(old_pcb,new_pcb);
388         /* TODO: When do we return here ?? */
389 }
390
391
392 void Os_Arc_GetStackInfo( TaskType task, StackInfoType *s) {
393         OsPcbType *pcb  = os_get_pcb(task);
394
395         s->curr         = Os_ArchGetStackPtr();
396         s->top          = pcb->stack.top;
397         s->at_swap      = pcb->stack.curr;
398         s->size         = pcb->stack.size;
399         s->usage        = (void *)Os_StackGetUsage(pcb);
400 }
401
402
403 #if 0
404 OsPcbType *os_find_higher_priority_task( OsPriorityType prio ) {
405         OsPcbType *i_pcb;
406         OsPcbType *h_prio_pcb = NULL;
407         OsPriorityType t_prio = prio;
408
409         TAILQ_FOREACH(i_pcb,& os_sys.ready_head,ready_list) {
410                 if( i_pcb->prio > t_prio ) {
411                         t_prio = i_pcb->prio;
412                         h_prio_pcb = i_pcb;
413                 }
414         }
415         return h_prio_pcb;
416 }
417 #endif
418
419 \r
420 StatusType GetTaskState(TaskType TaskId, TaskStateRefType State) {\r
421         state_t curr_state = os_pcb_get_state(os_get_pcb(TaskId));\r
422         StatusType rv = E_OK;\r
423 \r
424         // TODO: Lazy impl. for now */\r
425         switch(curr_state) {\r
426         case ST_RUNNING:        *State = TASK_STATE_RUNNING;    break;\r
427         case ST_WAITING:        *State = TASK_STATE_WAITING;    break;\r
428         case ST_SUSPENDED:      *State = TASK_STATE_SUSPENDED;  break;\r
429         case ST_READY:          *State = TASK_STATE_READY;      break;\r
430         }\r
431 \r
432         // Prevent label warning. Remove when proper error handling is implemented.\r
433         if (0) goto err;\r
434 \r
435         OS_STD_END_2(OSServiceId_GetTaskState,TaskId, State);\r
436 }\r
437 \r
438 StatusType GetTaskID( TaskRefType task_id ) {\r
439         *task_id = os_sys.curr_pcb->pid;\r
440         return E_OK;\r
441 }\r
442
443 ISRType GetISRID( void ) {
444
445         /** @req OS264 */
446         if(os_sys.int_nest_cnt == 0 ) {
447                 return INVALID_ISR;
448         }
449
450         /** @req OS263 */
451         return (ISRType)Os_TaskGetCurrent()->pid;
452 }
453
454 #define TASK_CHECK_ID(x)                                \
455         if( (x) > Oil_GetTaskCnt()) { \
456                 rv = E_OS_ID;                                   \
457                 goto err;                                               \
458         }
459
460 \r
461 /**\r
462  * The task <TaskID> is transferred from the suspended state into\r
463  * the  ready state. The operating system ensures that the task\r
464  * code is being executed from the first statement.\r
465  *\r
466  * The service may be called from interrupt  level and from task\r
467  * level (see Figure 12-1).\r
468  * Rescheduling after the call to  ActivateTask depends on the\r
469  * place it is called from (ISR, non preemptable task, preemptable\r
470  * task).\r
471  *\r
472  * If E_OS_LIMIT is returned the activation is ignored.\r
473  * When an extended task is transferred from suspended state\r
474  * into ready state all its events are cleared.\r
475  *
476  * Note!
477  * ActivateTask will not immediately change the state of the task
478  * in case of multiple activation requests. If the task is not
479  * suspended, the activation will only be recorded and performed later.
480  *
481  * @param pid
482  * @return
483  */\r
484 \r
485 StatusType ActivateTask( TaskType TaskID ) {\r
486         long msr;\r
487         OsPcbType *pcb = os_get_pcb(TaskID);\r
488         StatusType rv = E_OK;\r
489 \r
490         os_isr_printf(D_TASK,"ActivateTask %s\n",pcb->name);\r
491
492 #if (OS_STATUS_EXTENDED == STD_ON )
493         TASK_CHECK_ID(TaskID);
494
495         /* @req OS093 ActivateTask */
496         if( Os_IrqAnyDisabled() ) {
497                 rv = E_OS_DISABLEDINT;
498                 goto err;
499         }
500 #endif
501
502         Irq_Save(msr);
503
504         if( os_pcb_get_state(pcb) == ST_SUSPENDED ) {
505                 pcb->activations++;
506                 if (pcb->proc_type == PROC_EXTENDED) {
507                         /** @req OSEK ActivateTask Cleanup events
508                          * OSEK,ActivateTask, When an extended task is transferred from suspended
509                          * state into ready state all its events are cleared.*/
510                         pcb->ev_set = 0;
511                         pcb->ev_wait = 0;
512                 }
513                 Os_StackSetup(pcb);
514                 OsArch_SetTaskEntry(pcb);
515                 Os_ArchSetupContext(pcb);
516                 Os_TaskMakeReady(pcb);
517         } else {
518
519                 if( pcb->proc_type == PROC_EXTENDED ) {
520                         /** @req OSEK Activate task.
521                          * An extended task be activated once. See Chapter 4.3 in OSEK
522                          */
523                         rv = E_OS_LIMIT;
524                         goto err;
525                 }
526
527                 /** @req OSEK_? Too many task activations */
528                 if( pcb->activations == pcb->activationLimit ) {
529                         rv=E_OS_LIMIT;
530                         goto err;
531                 } else {
532                         pcb->activations++;
533                 }
534         }
535
536         Irq_Restore(msr);\r
537
538         /* Preempt only if higher prio than us */\r
539         if(     (pcb->scheduling == FULL) &&
540                 (os_sys.int_nest_cnt == 0) && (pcb->prio > Os_TaskGetCurrent()->prio) )
541         {\r
542                 Os_Dispatch(0);\r
543         }\r
544 \r
545         OS_STD_END_1(OSServiceId_ActivateTask,TaskID);\r
546 }\r
547
548 /**
549  * This  service  causes  the  termination  of  the  calling  task.  The
550  * calling  task  is  transferred  from  the  running  state  into  the
551  * suspended state.
552  *
553  * An internal resource assigned to the calling task is automatically
554  * released. Other resources occupied by the task shall have been
555  * released before the call to TerminateTask. If a resource is still
556  * occupied in standard status the behaviour is undefined.
557  *
558  *   If the call was successful, TerminateTask does not return to the
559  * call level and the status can not be evaluated.
560  *
561  *   If the version with extended status is used, the service returns
562  * in case of error, and provides a status which can be evaluated
563  * in the application.
564  *
565  *   If the service TerminateTask is called successfully, it enforces a
566  * rescheduling.
567  *
568  *  [ Ending   a   task   function   without   call   to   TerminateTask
569  *    or ChainTask is strictly forbidden and may leave the system in an
570  *    undefined state. ]
571  *
572  * [] is an OSEK requirement and is overridden by OS052
573  *
574  * @return
575  */
576 \r
577 StatusType TerminateTask( void ) {\r
578         OsPcbType *curr_pcb = Os_TaskGetCurrent();\r
579         StatusType rv = E_OK;
580         uint32_t flags;\r
581 \r
582         os_std_printf(D_TASK,"TerminateTask %s\n",curr_pcb->name);\r
583
584 #if (OS_STATUS_EXTENDED == STD_ON )
585
586
587         if( os_sys.int_nest_cnt != 0 ) {
588                 rv =  E_OS_CALLEVEL;
589                 goto err;
590         }
591
592         /** @req OS070 */
593         if( Os_ResourceCheckAndRelease(curr_pcb) == 1 ) {
594                 rv =  E_OS_RESOURCE;
595                 goto err;
596
597         }
598
599
600 #endif
601
602
603         Irq_Save(flags);
604
605         --curr_pcb->activations;
606
607
608 //      assert(curr_pcb->activations>=0);
609
610         /*@req OSEK TerminateTask
611          * In case of tasks with multiple activation requests,
612          * terminating the current instance of the task automatically puts the next
613          * instance of the same task into the ready state
614          */
615         if( curr_pcb->activations <= 0 ) {
616                 curr_pcb->activations = 0;
617                 Os_TaskMakeSuspended(curr_pcb);
618         } else {
619                 /* We need to add ourselves to the ready list again,
620                  * with a startup context. */
621         }
622
623 //      Os_ContextReInit(curr_pcb);
624
625         /* Force the dispatcher to find something, even if its us */
626         Os_Dispatch(1);
627 \r
628         Irq_Restore(flags);\r
629         // It must find something here...otherwise something is very wrong..\r
630         assert(0);\r
631 \r
632         rv = E_NOT_OK;\r
633         goto err;\r
634 \r
635 \r
636         OS_STD_END(OSServiceId_TerminateTask);\r
637 }\r
638 \r
639 StatusType ChainTask( TaskType TaskId ) {\r
640         StatusType rv;
641         uint32_t flags;
642
643 #if (OS_STATUS_EXTENDED == STD_ON )
644         TASK_CHECK_ID(TaskId);
645
646         if( os_sys.int_nest_cnt != 0 ) {
647                 rv =  E_OS_CALLEVEL;
648                 goto err;
649         }
650 #endif
651 \r
652         Irq_Save(flags);\r
653         rv = ActivateTask(TaskId);\r
654         /* TODO: more more here..*/\r
655         TerminateTask();\r
656         Irq_Restore(flags);\r
657 \r
658         if (rv != E_OK) goto err;\r
659 \r
660         OS_STD_END_1(OSServiceId_ChainTask,TaskId);\r
661 }\r
662 \r
663 /**\r
664  * If a higher-priority task is ready, the internal resource of the task\r
665  * is released, the current task is put into the  ready state, its\r
666  * context is saved and the higher-priority task is executed.\r
667  * Otherwise the calling task is continued.\r
668  *\r
669  * TODO: The OSEK spec says a lot of strange things under "particulareties"\r
670  * that I don't understand
671  *
672  * See OSEK/VDX 13.2.3.4\r
673  *
674  */\r
675 StatusType Schedule( void ) {\r
676 //      OsPcbType *pcb;\r
677 //      OsPcbType *curr_pcb = get_curr_pcb();\r
678         StatusType rv = E_OK;
679         uint32_t flags;\r
680
681         /* We need to figure out if we have an internal resource,
682          * otherwise no re-scheduling.
683          * NON  - Have internal resource prio OS_RES_SCHEDULER_PRIO (32+)
684          * FULL - Assigned internal resource OR
685          *        No assigned internal resource.
686          * */
687         if( Os_TaskGetCurrent()->scheduling != NON ) {
688                 return E_OK;
689         }
690
691 #if 0
692         if( os_get_resource_int_p() == NULL ) {
693                 /* We do nothing */
694                 return E_OK;
695         }
696 #endif
697
698         /* Check that we are not calling from interrupt context */
699         if( os_sys.int_nest_cnt != 0 ) {
700                 rv =  E_OS_CALLEVEL;
701                 goto err;
702         }
703 \r
704         Irq_Save(flags);
705         Os_Dispatch(0);\r
706         Irq_Restore(flags);\r
707 \r
708         // Prevent label warning. Remove this when proper error handling is implemented.\r
709         if (0) goto err;\r
710 \r
711         OS_STD_END(OSServiceId_Schedule);\r
712 }\r
713 \r