Delay Timer

Delay timer is a simple and very useful way of controlling when a task runs.

To implement the timer we first need to declare one a delay counter for each task and initalize them to 0.

// Global timer variables. One for each task.
int taskOneDelayCount;
int taskTwoDelayCount;

void main(void)
{
    // Initialize tasks

    // Initialize timers to 0.
    taskOneDelayCount = 0;
    taskTwoDelayCount = 0;

    Switch_To_Current();
}

Next we need to modify the scheduler so that it checks the delay counters. And we need to create a function that will set the delay for a task whenever its called.

void DelayTimer(int numOfTicks);

Also want to force a context switch whenever a task wants a delay. When the task calls for a delay it should be stopped immediately. That is the delay begins when the task calls the delay function. The only way to do that is to force a context switch right away.

void scheduler(int sp)
{
    WRITEREG32(T0IR, 0xFF);            // Reset timer

    // Check counters
    if (taskOneDelayCount > 0)
        taskOneDelayCount--;

    if (taskTwoDelayCount > 0)
        taskTwoDelayCount--;

    if (taskID == 1)
    {
        // Only switch to a task if it's delay count is at zero
        if (taskTwoDelayCount == 0)
        {
                    stackOneSP = sp;               // Store stack pointer for task 1
                    taskID = 2;                    // Set the taskID to task 2
                    currentSP = stackTwoSP;        // Set the current stack pointer to task 2's stack pointer
            }
    }
    else
    {
        // Only switch to a task if it's delay count is at zero
        if (taskOneDelayCount == 0)
        {
                    stackTwoSP = sp;               // Store stack pointer for task 2
                    taskID = 1;                    // Set the taskID to task 1
                    currentSP = stackOneSP;        // Set the current stack pointer to task 1's stack pointer
            }
    }
}
int currentTaskId;    // The ID of the current task
int currentSP;        // The value of the current task's stack pointer

struct TaskBlock
{
    int stackPointer;
    int delayCount;
}

TaskBlock taskBlocks[NUMBER_OF_TASKS];

// Allocate space for stacks
int stackOne[STACKSIZE];
int stackTwo[STACKSIZE];
int stackThree[STACKSIZE];

void CreateTask(int id, int* stack, void* task_address)
{
    taskBlocks[id].stackPointer = initialize_stack(stack, task_address);
    taskBlocks[id].delayCount = 0;
}

void StartOS()
{
    currentTaskId = 1;
    currentSP = taskBlocks[currentTaskId].stackPointer;

    // Run the current task
    switch_to_current();         // This call never returns to this function.
}

void main(void)
{
    // Create tasks
    CreateTask(1, stackOne, (void*)taskOne);
    CreateTask(2, stackTwo, (void*)taskTwo);
    CreateTask(3, stackIdle, (void*)taskIdle);

    StartOS();
}

Scheduler

Changes

Timer interrupt should call a timer handler that will acknowledge the VIC then call the context switching code. That will let us call the scheduler whenever we want independently of the timer.

void DelayTask(int numOfTicks)
{
    taskBlocks[currentTaskId].delayCount = numOfTicks;
    Scheduler();
}

Table Of Contents

Previous topic

Critical Sections

Next topic

Adding Semaphores Part One

This Page