Sunday, February 19, 2012

Notes about task execution and scheduler

Before talking about task scheduler, I am gonna inspect how the task is executed first.

How the task progresses?

First, you need to initialize the system by calling  nrk_init() where an idle task is created. The idle task is used in task scheduler for making tasks sleep deep (I will talk about it later in the scheduler section). And then you should create the task by calling nrk_create_taskset(), where task period, priority, CPU reserved time etc. are defined and especially the user-defined task is pushed into the task stack.

And then, nrk_start() starts executing all the tasks in the task stack. In nrk_start(), you should pay attention to two methods -- nrk_target_start() and nrk_start_high_ready_task()nrk_target_start() (defined in nrk_cpu.c) initializes timers (very important to the system because all the tasks are executed based on the timer) and nrk_start_high_ready_task() starts the ready task with the highest priority though it is defined hardware specific assembly file. Initially, all the tasks are ready so the task with the highest priority would be executed first.

Since the task is started, the processor would process the task. There are two points required high attention. 1. Task should not exceed the period defined before. 2. At the end of the task, nrk_wait_until_next_period() (defined in nrk_task.cmust be called. This method would bring you into the task scheduler.

How the scheduler works?

In nrk_wait_until_next_period(), the task scheduler would be called 2ms after the task finishes its job.
Code segment:

      ......
      timer += TIME_PAD; // TIMER_PAD = 2 and timer = nrk_os_timer_get();
      _nrk_prev_timer_val = timer;
      _nrk_set_next_wakeup (timer);
      ......
      _nrk_wait_for_scheduler ();
      ......
_nrk_prev_timer_val records task execution time plus 2ms and this value would be used in the scheduler to calculate next wake-up time. (we will talk about the computation in next blog) _nrk_set_next_wakeup (timer) would make the OS timer generate an interrupt 2ms later. _nrk_wait_for_scheduler () would make the task into the sleep mode. It should be noticed that the sleep mode is not the most power-saved. The deep sleep which indeed saves power would be made by the task scheduler.

However, the scheduler is hardware, namely timer, triggered. Looking into nrk_task.c, you would find that _nrk_scheduler() is called in the _nrk_timer_tick() which is called by the interrupt caused by the timer. That is to say, it is not the _nrk_wait_for_scheduler () trigger the scheduler but instead the _nrk_set_next_wakeup (timer).

Once the scheduler is on work, the idle task defined in  nrk_init() is used for making the process into deep sleep. Figure 1 shows it.
Fig 1
The yellow line indicates the task execution time. The purple line indicates the scheduler execution time. And the green line indicates the sleep mode time. It is obvious that the scheduler is called three times between two periods of the task. Actually, the processor calls the idle task at the first. The scheduler determines to start the task again at the second time since the scope shows that the processor enters the sleep mode again which is executed in the user-defined task. And the scheduler finally decides to start the task immediately since the task should start its new period. And also, you can see that during the first two callings the processor is not in the sleep mode. Actually, at that time, processor is in deep sleep. 

There would be two questions about the scheduler.
1. How the scheduler knows or computes the next wake-up time for the idle and non-idle task?
2. How the scheduler tell the processor to wake up the non-idle task from the sleep mode?

No comments:

Post a Comment