Sistema operacional de tempo real: faça você mesmo (V)

Sem mais enrolação, vamos ao nosso escalonador. Para os programadores de coração mole e apegados a paixões humanas do tipo “nunca use goto” recomendo pegar um copo d´água antes. Lembre-se também que a rotina BRTOS_Scheduler() está pendurada na interrupção de watchdog, logo será executada em modo interrompido e o retorno dela será feito por uma instrução “reti“, que restaura automaticamente o SR e PC da pilha (leia novamente o post III caso esteja confuso). Outro detalhe é o “NAKED” presente na definição da função. Ele irá instruir o MSPGCC a criar uma função *SEM* nenhuma mudança na pilha ao ser executada (nada é salvo e variáveis não são criadas na pilha), deixando isto na responsabilidade do programador.

NAKED( BRTOS_Scheduler )
{
save_context_entry:

    SaveContext();
    SaveStackPointer();
    RestoreSchedStackPointer();
  	
dont_save_context_entry:

    /* Update timers */
    BRTOS_ProcessTimers();
    /* check sleeping tasks */
    BRTOS_SleepTasks();
    /* Get the next task to run */
    ucCurrentTask = BRTOS_RoundRobin(ucCurrentPriLevel);

    if(ucCurrentTask == BRTOS_NO_TASK_TO_RUN)
    {
        /* nothing to do: sleep */
        GoToLowPowerMode3();
        goto dont_save_context_entry;
    }

    /* save scheduler context */
    SaveSchedStackPointer();
    /* restore stack pointer */
    RestoreStackPointer();
    /* restore other registers */
    RestoreContext();	
    /* reti will pop PC and Status from stack (naked function) */
    EnableInterrupts();
    ReturnFromInterrupt();
}

A primeira ação é salvar o contexto com SaveContext(). Perceba que deve existir uma tarefa em execução e o ponteiro da pilha (SP) tem relação com esta tarefa. SaveContext() irá colocar na pilha desta tarefa os registros relacionados em SaveContext(), deixando a pilha da tarefa com a seguinte estrutura:

+------------------- +
|        SR          | -> Status
+--------------------+
|        PC          | -> Program counter
+--------------------+    da tarefa
|     Contexto:      |
|     registros      |
|    de R3 a R15     |
+--------------------+

SaveContext() é escrita em assembly e não vejo muito como fugir disso já que é preciso controle total neste momento:

#define SaveContext() __asm__ __volatile__ ("push R3\n"  \
                                            "push R4\n"  \
                                            "push R5\n"  \
                                            "push R6\n"  \
                                            "push R7\n"  \
                                            "push R8\n"  \
                                            "push R9\n"  \
                                            "push R10\n" \
                                            "push R11\n" \
                                            "push R12\n" \
                                            "push R13\n" \
                                            "push R14\n" \
                                            "push R15" )

Com o contexto salvo, falta agora salvar a posição do SP da tarefa no TCB dela, com a chamada SaveStackPointer(). A notação é do conjunto de ferramentas do GCC e, confesso, não me agrada muito mas é bem flexível.

#define SaveStackPointer() \
   asm("mov.w R1,%0"  : "=m" (asBrtosTasks[ucCurrentTask].pusStackPtr))

Neste ponto estamos na condição ideal para trocar o SP da tarefa pelo SP do escalonador, devidamente guardado na variável usSchStackPtr através da função RestoreSchedStackPointer(). O SP do escalonador vai nos permitir fazer chamadas em C, criar variáveis locais dentro do escalonador, etc. Quando discutirmos a inicialização do sistema vai ficar claro onde esta variável foi inicializada.

#define RestoreSchedStackPointer() \
   asm("mov.w %0,R1" :: "m" (usSchStackPtr))

Depois deste início bem dependente da plataforma e do compilador, duas tarefas de faxina são feitas. Uma delas é processar timers, ainda não implementada, e a outra verificar se as tarefas em modo “SLEEP” ainda precisam continuar dormindo ou não.

BRTOS_ProcessTimers();
BRTOS_SleepTasks();

O próximo passo é decidir quem ficará com o controle do processador, feito através da chamada BRTOS_RoundRobin(). Voltaremos nesta função num post futuro, por hora apenas aceite que ela irá devolver qual tarefa deve ter seu contexto restaurado.

ucCurrentTask = BRTOS_RoundRobin(ucCurrentPriLevel); 

Se não existe nenhuma tarefa pronta para entrar em execução, o escalonar coloca o processa em modo de espera (GoToLowPowerMode3(), totalmente dependente do MSP430) e a próxima interrupção do watchdog irá acordar o processador, voltando algumas linhas através de um goto e refazendo as tarefas de rotina. Este ponto pode ser melhorado já que se o processador sair do modo de espera por outro motivo, a contagem de ticks para tarefas em SLEEP não estará correta. Uma possibilidade é ter uma tarefa para cuidar disso, ao invés de fazer isto no processador, evitando problemas.

if(ucCurrentTask == BRTOS_NO_TASK_TO_RUN)
{
    /* nothing to do: sleep */
    GoToLowPowerMode3();
    goto dont_save_context_entry;
}

Uma vez com a tarefa que será executada definida, é hora de restaurar o contexto e devolver o processador para ela. As tarefas são relativamente simples: salvar o SP do escalonador, restaurar o SP da tarefa, restaurar os registros da tarefa e retornar da interrupção. Dê uma espiada na pilha desenhada no início do post e vai ver que a pilha vai ser consumida na ordem correta e a tarefa voltará ao seu estado anterior. Veja o código de cada chamada para entender melhor.

SaveSchedStackPointer();
RestoreStackPointer();
RestoreContext();	
EnableInterrupts();
ReturnFromInterrupt();

Vamos parar por aqui, mas já deu para perceber que a rotina apresentada pode ser melhorada e é pouco portável para outros processadores ou compiladores. Também tem pontos discutíveis e o objetivo é este mesmo: sensibilizar. Com certeza você vai tomar outras decisões ao implementar a sua rotina.

  1. Sistema operacional de tempo real: faço você mesmo (VI) « Jedizone
  2. Ajude a divulgar a série « Jedizone
  3. Blog do Je » Sistema operacional de tempo real: faça você mesmo
  4. TTMMHTW: multitouch, rtos, productivity, qml, … | coding
  5. BRTOS « Rot-13

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Sair / Alterar )

Imagem do Twitter

You are commenting using your Twitter account. Sair / Alterar )

Foto do Facebook

You are commenting using your Facebook account. Sair / Alterar )

Connecting to %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.

Join 417 other followers