29.01.2015 Views

Embedded Software for SoC - Grupo de Mecatrônica EESC/USP

Embedded Software for SoC - Grupo de Mecatrônica EESC/USP

Embedded Software for SoC - Grupo de Mecatrônica EESC/USP

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

32 Chapter 3<br />

4.3. CPU idle loop<br />

When no tasks are RUNNABLE‚ the CPU runs some kind of idle loop. Current<br />

processors could benefit from this to enter a low power state. However‚ waking<br />

up from such a state is in the or<strong>de</strong>r of 100 ms [8] and its use would there<strong>for</strong>e<br />

be very application <strong>de</strong>pen<strong>de</strong>nt.<br />

An other‚ lower latency solution‚ would be to launch an idle thread whose<br />

role is to infinitely call the sched_yield function. There must be one such<br />

thread per CPU‚ because it is possible that all CPUs are waiting <strong>for</strong> some<br />

coprocessor to complete there work. These threads should not be ma<strong>de</strong> RUN<br />

as long as other threads exist in the RUNNABLE list. This strategy is elegant<br />

in theory‚ but it uses as many threads resources as processors and needs a<br />

specific scheduling policy <strong>for</strong> them.<br />

Our current choice is to use a more ad-hoc solution‚ in the which all idle<br />

CPUs enter the same idle loop‚ that is <strong>de</strong>scribed in Algorithm 3-2. This routine<br />

is called only once the scheduler lock has been acquired and the interruptions<br />

globally masked. We use the save_context function to save the current<br />

thread context‚ and update the register that contains the function return address<br />

to have it point to the end of the function. This action is necessary to avoid<br />

going through the idle loop again when the restore_context function is called.<br />

Once done‚ the current thread may be woken up again on an other processor‚<br />

and there<strong>for</strong>e we may not continue to use the thread stack‚ since this would<br />

modify the local data area of the thread. This justifies the use of a <strong>de</strong>dicated<br />

stack (one <strong>for</strong> all processors in centralized sceduling and one per processor<br />

<strong>for</strong> distributed scheduling). The registers of the CPU can be modified‚ since<br />

they are not anymore belonging to the thread context.<br />

The wakeup variable is a volatile field of the scheduler. It is nee<strong>de</strong>d to<br />

in<strong>for</strong>m this idle loop that a thread has been ma<strong>de</strong> RUNNABLE. Each function<br />

that awakes (or creates) a thread <strong>de</strong>crements the variable. The go local variable<br />

enable each CPU to register <strong>for</strong> getting out of this loop. When a pthread<br />

function releases a mutex‚ signals a condition or posts a semaphore‚ the CPU<br />

with the correct go value is allowed to run the awaken thread. This takes places<br />

after having released the semaphore lock to allow the other functions to change<br />

the threads states‚ and also after having enable the interruptions in or<strong>de</strong>r <strong>for</strong><br />

the hardware to notify the end of a computation.<br />

Algorithm 3-2. CPU Idle Loop.<br />

if current_thread exists<br />

save_context (current_thread)<br />

current.return_addr_register end of function address<br />

local_stack scheduler stack<br />

repeat<br />

go wakeup++<br />

spin_unlock(lock)‚ global_interrupt_unmask<br />

while wakeup > go endwhile

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!