ISIX-RTOS - small operating system for ARM microcontrollers 1.2

scheduler.c

Go to the documentation of this file.
00001 #include <isix/config.h>
00002 #include <isix/printk.h>
00003 #include <isix/types.h>
00004 #include <prv/scheduler.h>
00005 #include <isix/task.h>
00006 #include <isix/memory.h>
00007 #include <prv/list.h>
00008 #include <prv/semaphore.h>
00009 #include <prv/irqtimers.h>
00010 #include <prv/multiple_objects.h>
00011 
00012 #ifndef ISIX_DEBUG_SCHEDULER
00013 #define ISIX_DEBUG_SCHEDULER ISIX_DBG_OFF
00014 #endif
00015 
00016 
00017 /*-----------------------------------------------------------------------*/
00018 //Current task pointer
00019 volatile bool isix_scheduler_running;
00020 
00021 /*-----------------------------------------------------------------------*/
00022 //Current task pointer
00023 task_t * volatile isix_current_task = NULL;
00024 
00025 /*-----------------------------------------------------------------------*/
00026 //Sched lock counter
00027 static volatile unsigned short critical_count = 0;
00028 
00029 /*-----------------------------------------------------------------------*/
00030 //Binary tree of task ready to execute
00031 static list_entry_t ready_task;
00032 
00033 /*-----------------------------------------------------------------------*/
00034 //Task waiting for event
00035 static list_entry_t wait_tasks[2];
00036 
00037 //Pointer to current list overflow list
00038 static list_entry_t* p_waiting_task;
00039 static list_entry_t* pov_waiting_task;
00040 
00041 /*-----------------------------------------------------------------------*/
00042 //Task waiting for event
00043 static list_entry_t dead_task;
00044 
00045 /*-----------------------------------------------------------------------*/
00046 //Free priority innodes
00047 static list_entry_t free_prio_elem;
00048 
00049 /*-----------------------------------------------------------------------*/
00050 //Global jiffies var
00051 static volatile tick_t jiffies;
00052 
00053 //Number of deleted task
00054 static volatile unsigned number_of_task_deleted;
00055 
00056 //Number of priorities
00057 
00058 static volatile prio_t number_of_priorities;
00059 
00060 /*-----------------------------------------------------------------------*/
00061 //Isix bug report when isix_printk is defined
00062 void isix_bug(void)
00063 {
00064     //Go to critical sections forever
00065         isixp_enter_critical();
00066         isix_printk("OOPS: Please reset board");
00067     task_ready_t *i;
00068     task_t *j;
00069     //TODO: Add interrupt blocking
00070     isix_printk("Ready tasks");
00071     list_for_each_entry(&ready_task,i,inode)
00072     {
00073          isix_printk("\t* List inode %08x prio %d",(unsigned int)i,i->prio);
00074          list_for_each_entry(&i->task_list,j,inode)
00075          {
00076               isix_printk("\t\t-> task %08x prio %d state %d",j,j->prio,j->state);
00077          }
00078     }
00079     isix_printk("Sleeping tasks\n");
00080     list_for_each_entry(p_waiting_task,j,inode)
00081     {
00082         isix_printk("\t->Task: %08x prio: %d state %d jiffies %d",j,j->prio,j->state,j->jiffies);
00083     }
00084     while(1);
00085 }
00086 /*-----------------------------------------------------------------------*/
00087 //After isix_bug function disable isix_printk
00088 #if ISIX_DEBUG_SCHEDULER == ISIX_DBG_OFF
00089 #undef isix_printk
00090 #define isix_printk(...)
00091 #endif
00092 
00093 
00094 /*-----------------------------------------------------------------------*/
00095 //Lock scheduler
00096 void isixp_enter_critical(void)
00097 {
00098         if(critical_count==0)
00099         {
00100                 port_set_interrupt_mask();
00101         }
00102     critical_count++;
00103 }
00104 
00105 /*-----------------------------------------------------------------------*/
00106 //Unlock scheduler
00107 void isixp_exit_critical(void)
00108 {
00109         critical_count--;
00110         if(critical_count == 0 )
00111     {
00112                 port_clear_interrupt_mask();
00113     }
00114 }
00115 
00116 /*-----------------------------------------------------------------------*/
00117 //Scheduler is called in switch context
00118 void isixp_schedule(void)
00119 {
00120 
00121     //Enter to the critical section
00122         isixp_enter_critical();
00123 
00124     //Remove executed task and add at end
00125     if(isix_current_task->state & TASK_READY)
00126     {
00127         isix_current_task->state &= ~TASK_RUNNING;
00128         list_delete(&isix_current_task->inode);
00129         list_insert_end(&isix_current_task->prio_elem->task_list,&isix_current_task->inode);
00130     }
00131     task_ready_t * current_prio;
00132     //Get first ready prio
00133     current_prio = list_get_first(&ready_task,inode,task_ready_t);
00134     isix_printk("Scheduler: actual prio %d prio list %08x\n",current_prio->prio,current_prio);
00135     //Get first ready task
00136     isix_printk("Scheduler: prev task %08x\n",isix_current_task);
00137     isix_current_task = list_get_first(&current_prio->task_list,inode,task_t);
00138     isix_current_task->state |= TASK_RUNNING;
00139     if(isix_current_task->prio != current_prio->prio)
00140     {
00141         isix_bug();
00142     }
00143     isix_printk("Scheduler: new task %08x\n",isix_current_task);
00144     isixp_exit_critical();
00145 }
00146 
00147 /*-----------------------------------------------------------------------*/
00148 //Time call from isr
00149 void isixp_schedule_time(void)
00150 {
00151 
00152         //Increment sys tick
00153         jiffies++;
00154         if(!isix_scheduler_running)
00155         {
00156                 return;
00157         }
00158         if(jiffies == 0)
00159         {
00160            list_entry_t *tmp = p_waiting_task;
00161            p_waiting_task = pov_waiting_task;
00162            pov_waiting_task = tmp;
00163         }
00164 
00165     task_t *task_c;
00166 
00167     while( !list_isempty(p_waiting_task) &&
00168                 jiffies>=(task_c = list_get_first(p_waiting_task,inode,task_t))->jiffies
00169       )
00170     {
00171         isix_printk("SchedulerTime: sched_time %d task_time %d\n",jiffies,task_c->jiffies);
00172         task_c->state &= ~TASK_SLEEPING;
00173         task_c->state |= TASK_READY;
00174         list_delete(&task_c->inode);
00175         if(task_c->state & TASK_WAITING)
00176         {
00177             if(!task_c->sem)
00178             {
00179                 isix_printk("OOPS task waiting when not assigned to sem");
00180                 isix_bug();
00181             }
00182                 task_c->state &= ~TASK_SEM_WKUP;
00183             task_c->sem = NULL;
00184             task_c->state &= ~TASK_WAITING;
00185             list_delete(&task_c->inode_sem);
00186             isix_printk("SchedulerTime: Timeout delete from sem list\n");
00187         }
00188 #ifdef ISIX_CONFIG_USE_MULTIOBJECTS
00189         if(task_c->state & TASK_WAITING_MULTIPLE)
00190         {
00191                 task_c->state &= ~(TASK_WAITING_MULTIPLE|TASK_MULTIPLE_WKUP);
00192         }
00193 #endif
00194         if(isixp_add_task_to_ready_list(task_c)<0)
00195         {
00196             isix_printk("SchedulerTime: Error in add task to ready list\n");
00197             isix_bug();
00198         }
00199     }
00200     //Handle time from vtimers
00201     isixp_vtimer_handle_time( jiffies );
00202 }
00203 /*-----------------------------------------------------------------------*/
00204 //Try get task ready from free list if is not exist allocate memory
00205 static task_ready_t *alloc_task_ready_t(void)
00206 {
00207    task_ready_t *prio = NULL;
00208    if(list_isempty(&free_prio_elem)==true)
00209    {
00210        isix_bug();
00211    }
00212    else
00213    {
00214         //Get element from list
00215         prio = list_get_first(&free_prio_elem,inode,task_ready_t);
00216         list_delete(&prio->inode);
00217         prio->prio = 0;
00218         isix_printk("alloc_task_ready_t: get from list node 0x%08x\n",prio);
00219    }
00220    return prio;
00221 }
00222 
00223 /*-----------------------------------------------------------------------*/
00224 //Try get task ready from free list if is not exist allocate memory
00225 static inline void free_task_ready_t(task_ready_t *prio)
00226 {
00227     list_insert_end(&free_prio_elem,&prio->inode);
00228     isix_printk("free_task_ready_t move 0x%08x to unused list\n",prio);
00229 }
00230 
00231 /*-----------------------------------------------------------------------*/
00232 //Add assigned task to ready list
00233 int isixp_add_task_to_ready_list(task_t *task)
00234 {
00235     if(task->prio > number_of_priorities)
00236         return ISIX_ENOPRIO;
00237         //Scheduler lock
00238     isixp_enter_critical();
00239     //Find task equal entry
00240     task_ready_t *prio_i;
00241     list_for_each_entry(&ready_task,prio_i,inode)
00242     {
00243         //If task equal entry is found add this task to end list
00244         if(prio_i->prio==task->prio)
00245         {
00246             isix_printk("AddTaskToReadyList: found prio %d equal node %08x\n",prio_i->prio,prio_i);
00247             //Set pointer to priority struct
00248             task->prio_elem = prio_i;
00249             //Add task at end of ready list
00250             list_insert_end(&prio_i->task_list,&task->inode);
00251             //Unlock scheduler
00252             isixp_exit_critical();
00253             return 0;
00254         }
00255         else if(task->prio < prio_i->prio)
00256         {
00257            isix_printk("AddTaskToReadyList: Insert %d node %08x\n",prio_i->prio,prio_i);
00258            break;
00259         }
00260     }
00261     //Priority not found allocate priority node
00262     task_ready_t *prio_n = alloc_task_ready_t();
00263     //If malloc return NULL then failed
00264     if(prio_n==NULL) return ISIX_ENOMEM;
00265     //Assign priority
00266     prio_n->prio = task->prio;
00267     //Set pointer to priority struct
00268     task->prio_elem = prio_n;
00269     //Initialize and add at end of list
00270     list_init(&prio_n->task_list);
00271     list_insert_end(&prio_n->task_list,&task->inode);
00272     list_insert_before(&prio_i->inode,&prio_n->inode);
00273     isix_printk("AddTaskToReadyList: Add new node %08x with prio %d\n",prio_n,prio_n->prio);
00274     isixp_exit_critical();
00275     return ISIX_EOK;
00276 }
00277 
00278 /*-----------------------------------------------------------------------*/
00279 //Delete task from ready list
00280 void isixp_delete_task_from_ready_list(task_t *task)
00281 {
00282     //Scheduler lock
00283    isixp_enter_critical();
00284    list_delete(&task->inode);
00285    //Check for task on priority structure
00286    if(list_isempty(&task->prio_elem->task_list)==true)
00287    {
00288         //Task list is empty remove element
00289         isix_printk("DeleteTskFromRdyLst: Remove prio list elem\n");
00290         list_delete(&task->prio_elem->inode);
00291         free_task_ready_t(task->prio_elem);
00292    }
00293    //Scheduler unlock
00294    isixp_exit_critical();
00295 }
00296 
00297 /*-----------------------------------------------------------------------*/
00298 //Move selected task to waiting list
00299 void isixp_add_task_to_waiting_list(task_t *task, tick_t timeout)
00300 {
00301     //Scheduler lock
00302     isixp_enter_critical();
00303     task->jiffies = jiffies + timeout;
00304     if(task->jiffies < jiffies)
00305     {
00306         //Insert on overflow waiting list in time order
00307         task_t *waitl;
00308         list_for_each_entry(pov_waiting_task,waitl,inode)
00309         {
00310            if(task->jiffies<waitl->jiffies) break;
00311         }
00312         isix_printk("MoveTaskToWaiting: OVERFLOW insert in time list at %08x\n",&waitl->inode);
00313         list_insert_before(&waitl->inode,&task->inode);
00314     }
00315     else
00316     {
00317         //Insert on waiting list in time order no overflow
00318         task_t *waitl;
00319         list_for_each_entry(p_waiting_task,waitl,inode)
00320         {
00321             if(task->jiffies<waitl->jiffies) break;
00322         }
00323         isix_printk("MoveTaskToWaiting: NO overflow insert in time list at %08x\n",&waitl->inode);
00324         list_insert_before(&waitl->inode,&task->inode);
00325     }
00326     //Scheduler unlock
00327     isixp_exit_critical();
00328 }
00329 
00330 /*--------------------------------------------------------------*/
00331 //Add task to semaphore list
00332 void isixp_add_task_to_sem_list(list_entry_t *sem_list,task_t *task)
00333 {
00334     //Scheduler lock
00335     isixp_enter_critical();
00336     //Insert on waiting list in time order
00337     task_t *taskl;
00338     list_for_each_entry(sem_list,taskl,inode_sem)
00339     {
00340         if(task->prio<taskl->prio) break;
00341     }
00342     isix_printk("MoveTaskToSem: insert in time list at %08x\n",taskl);
00343     list_insert_before(&taskl->inode_sem,&task->inode_sem);
00344 
00345     //Scheduler unlock
00346     isixp_exit_critical();
00347 
00348 }
00349 /*-----------------------------------------------------------------------*/
00350 //Add task list to delete
00351 void isixp_add_task_to_delete_list(task_t *task)
00352 {
00353     //lock scheduler
00354     isixp_enter_critical();
00355     list_insert_end(&dead_task,&task->inode);
00356     number_of_task_deleted++;
00357     isixp_exit_critical();
00358 }
00359 
00360 /*-----------------------------------------------------------------------*/
00361 //Dead task are clean by this procedure called from idle task
00362 //One idle call clean one dead tasks
00363 static inline void cleanup_tasks(void)
00364 {
00365     if( number_of_task_deleted > 0 )
00366     {
00367         isixp_enter_critical();
00368         if(!list_isempty(&dead_task))
00369         {
00370                 task_t *task_del = list_get_first(&dead_task,inode,task_t);
00371                 list_delete(&task_del->inode);
00372                 isix_printk("Task to delete: %08x(SP %08x) PRIO: %d",task_del,task_del->init_stack,task_del->prio);
00373                 port_cleanup_task(task_del->top_stack);
00374                 isix_free(task_del->init_stack);
00375                 isix_free(task_del);
00376                 number_of_task_deleted--;
00377         }
00378         isixp_exit_critical();
00379     }
00380 }
00381 
00382 /*-----------------------------------------------------------------------*/
00383 //Idle task function do nothing and lower priority
00384 ISIX_TASK_FUNC(idle_task,p)
00385 {
00386    (void)p;
00387         while(1)
00388     {
00389         //Cleanup free tasks
00390         cleanup_tasks();
00391         //Call port specific idle
00392         port_idle_cpu();
00393 #ifndef  ISIX_CONFIG_USE_PREEMPTION
00394         isix_yield();
00395 #endif
00396     }
00397 }
00398 
00399 /*-----------------------------------------------------------------------*/
00400 //Get currrent jiffies
00401 tick_t isix_get_jiffies(void)
00402 {
00403     tick_t t1,t2;
00404     do
00405     {
00406         t1 = jiffies;
00407         t2 = jiffies;
00408     }
00409     while(t1!=t2);
00410     return t2;
00411 }
00412 
00413 /*-----------------------------------------------------------------------*/
00414 /* Number of priorites assigned when OS start */
00415 void isix_init(prio_t num_priorities)
00416 {
00417         //Copy priority
00418         number_of_priorities = num_priorities;
00419         //Init heap
00420         isix_alloc_init();
00421         //Initialize ready task list
00422     list_init(&ready_task);
00423     //Initialize waiting list
00424     list_init(&wait_tasks[0]);
00425     list_init(&wait_tasks[1]);
00426     //Initialize overflow waiting list
00427     p_waiting_task = &wait_tasks[0];
00428     pov_waiting_task = &wait_tasks[1];
00429     //Initialize dead task
00430     list_init(&dead_task);
00431     //Initialize free prio elem list
00432     list_init(&free_prio_elem);
00433     //This memory never will be freed
00434     task_ready_t *prio = isix_alloc(sizeof(task_ready_t)*(num_priorities+1));
00435     for(int i=0; i<num_priorities+1; i++)
00436     {
00437         list_insert_end(&free_prio_elem,&prio[i].inode);
00438     }
00439     //Lower priority is the idle task
00440     isix_task_create(idle_task,NULL,ISIX_PORT_SCHED_MIN_STACK_DEPTH,num_priorities);
00441     //Initialize virtual timers infrastructure
00442     isixp_vtimer_init();
00443     //Initialize multiple objects infrastructure
00444     ixixp_multiple_objects_init();
00445 }
00446 
00447 /*-----------------------------------------------------------------------*/
00448 /* This function start scheduler after main function */
00449 void isix_start_scheduler(void) __attribute__((noreturn));
00450 void isix_start_scheduler(void)
00451 {
00452    jiffies = 0;         //Zero jiffies if it was previously run
00453    isix_scheduler_running = true;
00454    //Restore context and run OS
00455    port_start_first_task();
00456    while(1);    //Prevent compiler warning
00457 }
00458 
00459 /*-----------------------------------------------------------------------*/
00460 //Get maxium available priority
00461 prio_t isix_get_min_priority(void)
00462 {
00463         return number_of_priorities;
00464 }
00465 /*-----------------------------------------------------------------------*/
00466 //Return scheduler active
00467 bool isix_is_scheduler_active(void)
00468 {
00469     return isix_scheduler_running;
00470 }
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines