ISIX-RTOS - small operating system for ARM microcontrollers 1.2
|
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(¤t_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 }