void arch_sched(Thread *prev) { if (prev) { context_switch(&prev->arch_data.context, sched_context); } else { context_restore(sched_context); panic("schould not return here"); } }
error_t sched_do() { current_task++; if(current_task>=num_tasks) current_task = 0; if(!tasks[current_task].pid || !tasks[current_task].stack) return E_INVALID; tasks[current_task].stack = context_restore(tasks[current_task].stack); return E_NONE; }
/* * This is the wrapper for the task that executes rendundantly on both cores * There is one VERY important thing to note. When the critical task begins executing * the value of the stack pointer MUST be the same on both cores. This means that * the wrapper must have the same number of variables declared within its scope (i.e. * onto its stack) before calling the critical task (pt() in this example) */ void preemption_task(void* pdata){ int done = 0; int first = 0; int t_os; CriticalFunctionPointers* cp = (CriticalFunctionPointers*) SHARED_MEMORY_BASE; pt = cp->task[1]; while(1){ // Get initial time, then wait for 2 ticks t_os = OSTimeGet(); OSTimeDly(2 - t_os); //This is a crude way of synchronizing the beginning of the task //on both cores while (done == 0) { altera_avalon_mutex_lock(mutex, 1); //Acquire the hardware mutex { if(first == 0){ cp->checkout[1] = 1; first = 1; } if( cp->checkout[0] == 1){ cp->checkout[0] = 0; done = 1; } } altera_avalon_mutex_unlock(mutex); } // Set default block size for fingerprinting fprint_set_block_size(cp->blocksize[1]); //Context switch is necessary to clear the callee saved registers long registers[8]; context_switch(registers); //Set the global pointer in case of compilation issues related //to global variables set_gp(); //call the critical task pt(cp->args[1]); //restore the original global pointer restore_gp(); //Restore the callee saved registers context_restore(registers); //Get the end time alt_u64 t = alt_timestamp(); //store the end time cp->core_time[1] = t; } }
/* fig_begin task_start */ void task_start(int task_id) { /* a pointer to a TCB */ task_control_block *tcb_ref; /* get a pointer to the TCB associated with task identity task_id */ tcb_ref = tcb_storage_get_tcb_ref(task_id); /* set Task_Id_Running to task id of new task */ Task_Id_Running = task_id; /* restore context for the task with this TCB */ context_restore(tcb_ref->stack_pointer); }
/** * Restore environment previously stored by setjmp. * * This function never returns. * * @param env Variable with the environment previously stored by call * to setjmp. * @param val Value to fake when returning from setjmp (0 is transformed to 1). */ void longjmp(jmp_buf env, int val) { env[0].return_value = (val == 0) ? 1 : val; context_restore(&env[0].context); __builtin_unreachable(); }
/** * @brief The PENDSV interrupt is used for context switching * * This interrupt saves the context, runs the scheduler and restores the context of the thread * that is run next. */ __attribute__((naked)) void isr_pendsv(void) { context_save(); sched_run(); context_restore(); }
/** * @brief The SVC interrupt is used for dispatching a thread if no context exists. * * Starting a thread from non-existing context is needed in two situations: * 1) after system initialization for running the main thread * 2) after exiting from a thread */ __attribute__((naked)) void isr_svc(void) { sched_run(); context_restore(); }