• Nenhum resultado encontrado

O gestor de tarefas no FreeRTOS é responsável por manipular as listas de tarefas suspensas e bloqueadas, ficando a cargo do escalonador a manipulação da lista de tarefas prontas, e de modo a seguir a implementação do sistema operativo ficou então definido que as listas de tarefas suspensas e bloqueadas ficariam a cargo da entidade gestor de tarefas e a lista de tarefas prontas para execução a cargo

da entidade escalonador. Para ativar a funcionalidade de um sistema operativo híbrido, basta no ficheiro de configuração do freeRTOS ativar a macro criada para esse propósito, configHW_SCHEDULER, sendo que esta macro ativa o recurso de todos os serviços implementados em hardware. A API criada para o kernel do sistema operativo comunicar com este periférico é apresentada na Figura 5.10 e foi criada no módulo denominado mss_task_manager.c e mss_task_manager.h, sendo está API totalmente manipulada pelo kernel ficando assim abstraída do ambiente do utilizador.

Figura 5.10: API - Gestor de Tarefas

As funções desta API são bastante intuitivas e de fácil compreensão do seu funcio- namento prático, as únicos que podem deixar mais dúvida são a MSS _Task _MA-

NAGER _GetNextTaskToRun() e MSS _Task _MANAGER _GetTaskToRun()

dado à sua semelhança, mas o comportamento destas é distinto sendo que a pri- meira é utilizada para pedir ao escalonador a próxima tarefa para entrar em exe- cução, já a segunda é para pedir ao escalonador o endereço da tarefa que irá entrar em execução.

O componente gestor de tarefas como já mostrado na segunda arquitetura apresen- tada na secção 5.2 é o único componente conectado ao barramento principal sendo ele o responsável por fazer a ponte de comunicação com os restantes periféricos, deste modo é necessário um mecanismo para a comunicação com os restantes pe- riféricos, escalonador, temporizador e controlador de interrupções. A Figura 5.11 ilustra o módulo de topo desenhado do gestor de tarefas junto com as respetivas interfaces.

Listas

Como dito anteriormente o gestor de tarefas contém duas listas, lista de tarefas suspensas e lista de tarefas bloqueadas, mas estas listas terão de ser capazes de armazenar as informações relativas às tarefas para que quando estas forem pedidas

Tasks Manager

List Task Suspensed List Task Blocked

AHB-Lite

Control Logic Scheduler interface Timer interface In te rf ac e C o nt ro lle r Slave

Figura 5.11: Gestor de Tarefas

pelo kernel do sistema operativo este consiga pôr a tarefa em execução o mais rápido possível e toda a informação relacionada às tarefas está armazenada numa estrutura a que o FreeRTOS denomina de TCB, já referenciada na Capítulo 3, não havendo a necessidade de duplicar essa informação para as listas, então a única informação relevante de uma tarefa seria o endereço da estrutura TCB da tarefa para de uma forma mais rápida o kernel através desse endereço ter acesso ao TCB da tarefa e a prioridade da tarefa e identificador desta. A Figura 5.12 mostra um diagrama que representa a implementação da lista de tarefas suspensas.

Esta lista de tarefas foi implementada com recurso a uma memória RAM com 8 posições de memória de 32bits cada, em que para armazenar as informações re- ferentes às tarefas suspensas é necessário utilizar duas posições de memória para cada tarefa, fazendo com que seja possível apenas ter até 4 tarefas suspensas. Na primeira posição é guardado o endereço de memória correspondente ao TCB (Task

Control Block) da tarefa e na segunda posição é guarda a prioridade associada à

tarefa, quanto ao identificador da tarefa é armazenado num vetor de registos cria- dos à parte, sendo que estes registos foram criados para facilitar a implementação de inserção e remoção de tarefas na lista de tarefas suspensas, dado que as tarefas

Task_Addr Task_priority Task_Addr Task_priority Task_Addr Task_priority Task_Addr Task_priority Suspended_List write_pointer Task_id Task_id Task_id Task_id empty_suspend write_pointer = empty_suspend << 1; 0 1 2 3 Memory_RAM_32x8 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07

Figura 5.12: Lista de tarefas suspensas

suspensas não necessitam de estar ordenadas na memória e podem ser removi- das aleatoriamente. Cada registo destes fica associado a uma posição da memória RAM, por exemplo, o primeiro registo com índice 0, fica associado à posição 0 da memória RAM, o registo de índice 1 fica associado à posição 2 e o terceiro e quarto registo são associados às posições 4 e 6 da memória RAM, respetivamente. A va- riável empty_suspend assume sempre o índice do primeiro registo que tem valor zero, ficando assim sempre com a referência do primeiro registo que não contém nenhuma tarefa suspensa associada e como as tarefas são identificadas a partir do valor 1, o valor 0 nestes registos indica que não tem nenhuma tarefa associada e a associação do registo à posição da memória RAM é feita através da linha código:

write_pointer = empty_suspend « 1;.

Quando é feita uma inserção nesta lista, é preenchido o primeiro registo vazio com o identificador da tarefa, com o este registo preenchido fica-se a saber qual a posição da memória RAM para guardar a restante informação relativa à tarefa e para uma tarefa sair do estado de suspensa e passar para o estado de pronta para execução, o sistema operativo deverá informar ao gestor de tarefas o identificador da tarefa que deverá ser removida da lista das tarefas suspensas e passar para a lista das tarefas prontas e o gestor de tarefas com o identificador da tarefa basta-o comparar com os identificadores guardados nos registos para esse efeito e quando encontrado o registo com o mesmo valor que o identificador da tarefa, o gestor de tarefas fica a saber através desse registo qual a posição da memória RAM que contém o restante informação da tarefa armazenada, e com isto, é feita

a leitura desses dados da memória RAM, e o registo que continha o identificador da tarefa é colocado novamente a 0 para indicar que a tarefa foi removida da lista de tarefas suspensas, e com todos os dados da tarefa obtidos basta enviar-los para o escalonador de hardware, onde este irá posteriormente inserir-los na lista de tarefas prontas, dado à lista de tarefas prontas estarem implementadas dentro do escalonador.

Em relação à lista de tarefas bloqueadas a a Figura 5.13 retrata de uma forma simplista a implementação adotada.

Task_Addr Task_priority Task_Addr Task_priority Task_Addr Task_priority Task_Addr Task_priority Blocked_List write_pointer write_pointer = empty_blocked << 1; Memory_RAM_32x8 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 minor_item_value Task_id Task_id Task_id Task_id empty_blocked 0 1 2 3 Item_value Item_value Item_value Item_value 0 1 2 3 count_tick comparator blocked_to_ready

Figura 5.13: Lista de tarefas bloqueadas

Tal como as listas de tarefas suspensas, as listas de tarefas bloqueadas recorrem a uma RAM também com 8 posições de memória de 32bits cada e a composição do conteúdo desta memória é igual à utilizada pelas listas suspensas, onde são utilizadas duas posições de memória para armazenar a informação relativa a uma tarefa e também implementa o vetor de 4 registos que guarda o identificador da tarefa bloqueada. A parte diferenciadora em relação às lista de tarefas suspensas está no novo vetor também este de quatro registos para salvaguardar um valor, em que este valor corresponde ao número que a contagem do system tick deverá atingir. A variável minor_item_value assume o menor valor das tarefas que estão bloqueadas e este valor é comparado com o número de ticks que já ocorreram desde que o escalonador foi iniciado e quando o valor coincide com o valor de contagem de ticks, a variável blocked_to_ready assume o índice correspondente a esse item_value, já com o índice determinado sabe-se em qual a posição da RAM em que a tarefa tem os seus dados guardados e nesse momento é iniciado o processo para a remoção da tarefa da lista de tarefas bloqueadas e colocada na lista de tarefas prontas. Este processo é o mesmo realizado pela lista de tarefas

suspensas.

Interfaces de comunicação

Para a comunicação com os componentes, escalonador, temporizador e controlador de interrupções, foi desenvolvida uma interface dedicada para cada periférico em que são geridas pelo controlador de interface presente no gestor de tarefas e o controlador lógico é responsável tanto por gerir os vários registos internos assim como pela gestão as listas de tarefas bloqueadas e suspensas que estão integradas no gestor de tarefas.

Para o desenvolvimento das interfaces foi definido um conjunto de sinais de forma a que a comunicação fosse viável, ao invés de ser criada uma interface genérica, foi criada uma interface personalizada a cada componente, assim desta forma, a interface criada necessita de menos lógica adicional o que reflete numa percentagem menor de recursos utilizados pelo FPGA.

Interface Escalonador

A Tabela 5.1 discrimina os sinais utilizados na interface desenvolvida para comu- nicar com o escalonador.

O sinal scheduler_En tem a finalidade de ativar ou suspender o escalonador. Quando é pretendido inserir ou remover uma tarefa da lista de tarefas prontas para execução existem os sinais Enqueue Task e Dequeue Task respetivamente, sendo que para sinalizar o escalonador estes sinais deverão estar a nível lógico alto por pelo menos um ciclo de relógio. Para fazer um inserção de uma tarefa no escalonador será necessário as informações da tarefa a ser inserida, para este efeito existem os sinaisTask Addr, Task Id e Task Priority que como o nome deles indicam têm o endereço da tarefa o seu identificador e a sua prioridade por esta ordem. Quando o escalonar seleciona uma tarefa as informações desta ficam disponíveis nos sinais Next Task Addr, Next Task Id e Task Priority, assim quando processador perguntar ao gestor de tarefas pela próxima tarefa para ser executada, o gestor de tarefas só tem ler estes sinais e responder ao processador. Interface Temporizador

A interface desenvolvida para a comunicação com o temporizador é muito simplista como a Tabela 5.2 demonstra.

Tabela 5.1: Interface com o escalonador

Sinal Entrada/Saída Descrição

scheduler_En Saída Indica se o escalonador esta ativo ’1’

ou suspenso ’0’

Enqueue Task Saída Indica uma operação para inserir uma

tarefa na lista de tarefas prontas

Dequeue Task Saída Indica uma operação para remover a

última tarefa seleciona pelo escalonador

Task Addr[31:0] Saída Vetor para o endereço da tarefa

a inserir

Task Id[7:0] Saída Vetor com o identificador da tarefa

a inserir

Task Priority[7:0] Saída Vetor com a prioridade da tarefa

a inserir

Next Task Addr[31:0] Entrada Vetor com o endereço da tarefa

selecionada pelo escalonador

Next Task Id[7:0] Entrada Vetor com o identificador da tarefa

selecionada pelo escalonador

Next Task Priority[7:0] Entrada Vetor com a prioridade da tarefa

selecionada pelo escalonador

Read_done Saída Sinal para indicar que a tarefa

selecionada foi lida e o escalonador poderá fazer uma nova seleção

‘1’ ativa o temporizador e este inicia a contagem, quando o nível lógico é ‘0’ este encontra-se desativo. O tick_value que é um sinal de 32bits permite definir o valor de overflow do temporizador, para que este valor seja definido será necessá- rio indicar uma operação de escrita ao temporizador (write_en). Quando o valor de overflow é atingido pelo valor de contagem, é gerado um sinal que está ligado ao escalonador, onde este sinal serve de tick para indicar ao escalonador que é necessário realizar a troca da tarefa em execução e assim fica a cargo do escalo- nador selecionar a nova tarefa para execução e gerar o sinal de preempção que irá interromper a execução do processador. Quando o processador é interrompido, este pergunta ao gestor de tarefas qual a próxima tarefa a ser executada e inicia o processo de troca de contexto da tarefa que estava a ser executada pela próxima tarefa mais prioritária e por fim indica ao gestor de tarefas que poderá limpar a interrupção gerada pelo temporizador. O aviso ao temporizador é feito através do sinal clear_timer_interrupt.

Tabela 5.2: Interface com o temporizador

Sinal Entrada/Saída Descrição

timer_en Saída Ativar/Desativa o temporizador

write_en Saída Indica operação de escrita

tick_value[31:0] Saída Valor de overflow do temporizador

clear_timer_interrupt Saída Limpar o sinal de interrupção

A Tabela 5.3 descrimina os sinais utilizados na interface desenvolvida para o con- trolador de interrupções.

Tabela 5.3: Interface com o controlador de interrupções

Sinal Entrada/Saída Descrição

Int_Cont_en Saída Ativa o Controlador de Interrupções

Interrupt_en [7:0] Saída Habilita as interrupções individualmente

Edge_Trigger Saída Sinal que indica o tipo de Trigger

da interrupção

Interrupt_n [7:0] Saída Vetor que indica a interrupção a ser

configurada

Task_Id [7:0] Saída Vetor que indica o identificador da tarefa

associada à interrupção

IRQ Entrada Sinal que indica um pedido de

interrupção

IRQ_Task_Id [7:0] Entrada Vetor que indica o identificador da tarefa

associado à interrupção a ser atendida

A interface desenvolvida é uma interface simplista que possui apenas seis sinais. O primeiro sinal descrito na tabela, Int_Cont_en tem a função de ativar e desativar o controlador de interrupções. O Interrupt_en é um sinal de 8bits responsável por habilitar as interrupções, onde cada um destes bits tem associado uma interrupção externa. No caso do sinal Edge_Trigger, este tem a função de definir o tipo de trigger que irá disputar a interrupção, podendo ser o trigger definido para transição positiva quando o sinal apresenta nível lógico ’1’ ou negativa quando o sinal apresenta o nível lógico ’0’. O Interrupt_n indica qual a interrupção externa que irá ser configurada. O sinal Task_Id dá a informação do identificador da tarefa associada à interrupção. Para indicar que interrupção foi gerada, o controlador de interrupções recorre ao sinal IRQ para indicar ao gestor de tarefas o pedido de interrupção. O último sinal presente nesta interface é o IRQ_Task_Id, este sinal indica ao gestor de tarefas o identificador da tarefa que deverá ser desbloqueada e transitar para as tarefas prontas para execução.

Tabela 5.4: Comandos do gestor de tarefas

Macro do comandos Leitura/

Escrita Descrição

Task_Addr E Recebe o endereço da tarefa

Task_Id E Recebe o identificador da tarefa

Task_Priority E Recebe a prioridade da tarefa

Insert_Suspended_List E Insere os dados recebidos da tarefa

na lista de tarefas suspensas

Remove_Suspended_List E

Através do identificador da tarefa remove a tarefa da lista das tarefas suspensas

Insert_Blocked_List E Insere os dados recebidos da tarefa

na lista das tarefas bloqueadas

Remove_Blocked_List E

Através do identificador da tarefa remove a tarefa da lista das tarefas bloqueadas

Get_Task_Addr L Obtém o endereço da tarefa

Get_Task_Id L Obtém o identificador da tarefa

Get_Task_Priority L Obtém a prioridade da tarefa

FreeRTOS Acelerado em Hardware

Para o sistema operativo que é executado em software poder utilizar as suas exten- sões em hardware, deverá existir uma forma de comunicação com estes componen- tes e o gestor de tarefas fornece uma interface que possibilita a comunicação com todas as extensões de hardware. A Tabela 5.4 contém os comandos que são utiliza- dos apenas para comunicação com o gestor de tarefas e os restantes comandos, do escalonador e temporizador, serão abordados nas secções 5.6 e 5.8, respetivamente.

São apresentados 10 comandos diferentes e todos eles permitem operações de lei- tura e escrita, estando referido na segunda coluna representado por ‘E’ as operações de Escrita e ‘L’ as de leitura. Dado os modos de operação serem codificados pelo barramento de forma implícita, estes dois modos de operações podem apresentar o mesmo espaço de endereços, por exemplo, as Task_Addr e Get_Task_Addr são definidas com o mesmo valor, mas estas são distinguidas através das operações de escrita e leitura, respetivamente.

Documentos relacionados