Sistemas Digitales FreeRTOS en ATMEGA32 Fundamentos e Implementaci´on on
Nicol´ as as Ilich Samus Profe Profeso sor: r: Jos´ Jos´e Ju´ Juarez ´arez
Universidad Nacional de Quilmes 10 de Febrero de 2012
´ Indice general
Sobre el presente traba jo
2
I
3
FreeR reeRTOS: TOS: Siste Sistema ma Opera Operati tivo vo de de Tiempo Tiempo Real Real para para Mic Microco rocont ntrol rolado adores res
1. Introduc Introducci´ ci´ on on
4
1.1. FreeRTOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Fundamentos
2.1. Las tareas . . . . . . . . . . . . . . . . . 2.1.1. Estados . . . . . . . . . . . . . . 2.1.2. Prioridades . . . . . . . . . . . . 2.1.3. Implementando una tarea . . . 2.1.4. La tarea Idle . . . . . . . . . . . 2.2. Sistema multitarea . . . . . . . . . . . . 2.3. Scheduler: El El pl planificador de de ta tareas . . 2.4. Cambio de contexto . . . . . . . . . . . 2.5. Aplicaciones de tiempo real . . . . . . . 2.5.1. Ejemplo . . . . . . . . . . . . . . 2.6. Planificaci Planificaci´´on de tareas en tiempo real 2.7. Comunica Comunicaci´ ci´ on entre tareas . . . . . . . 2.7.1. Colas . . . . . . . . . . . . . . . 2.7. 2.7.2. 2. Sem´ Sem´ aforos binarios . . . . . . . 2.7. 2.7.3. 3. Sem´ Sem´ aforos contadores . . . . . . 2.7.4. Mutex . . . . . . . . . . . . . . .
4 5
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . .. . . . . . . . . .. . . . . . . . . . . .. . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . .. . . . . . . . . .. . . . . . . . . . . .. . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
3. Implemen Implementaci´ taci´ on
5 5 5 6 6 6 7 7 8 8 9 10 10 10 11 11 13
3.1. Temporizaci´ emporizaci´ on: El tick del sistema . . . . . . . . . . . . 3.1.1. Atributo signal de de GCC . . . . . . . . . . . . . . 3.1.2. Atributo naked de GCC . . . . . . . . . . . . . . 3.1. 3.1.3. 3. El c´ odigo de la RSI del Tick . . . . . . . . . . . 3.2. .2. El conte ontext xto o een n lo los mic micrroco ocontrol trolaadore doress AVR AVR . . . . . . . 3.2.1. Guardando el contexto . . . . . . . . . . . . . . . 3.2.2. Restaurando el contexto . . . . . . . . . . . . . . 3.3. .3. Eje Ejemplo mplo comp comple leto to de un cam ambi bioo de con context textoo . . . . . . 3.3.1. 3.3.1. Previo Previo a la interru interrupci´ pci´ on del Tick . . . . . . . . . 3.3.2. 3.3.2. Durante Durante la la inter interrupci´ rupci´ on del Tick . . . . . . . . . 3.3.3. La RSI del Tick . . . . . . . . . . . . . . . . . . . 3.3. 3.3.44. Incr Increm emeentand tando o el el con conta tado dorr de de Tic Ticks ks . . . . . . . 3.3.5. 3.3 .5. Recuper Recuperaci aci´´on o n del del pun punte terro de de pil pila a de de la la Tar Tareea B 3.3.6. 3.3.6. Restauraci Restauraci´´on del contexto de la Tarea B . . . . 3.3.7. Retorno de la RSI de del Tick . . . . . . . . . . . .
Bibliograf´ıa
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
13 13 13 14 14 15 16 16 17 17 17 18 18 18 19
20
1
Sobre el presente trabajo
Este trabajo no pretende ser una traducci´on sino mi interpretaci´on de lo que se encuentra en el sitio http://www.freertos.org.
2
Parte I
FreeRTOS: Sistema Operativo de Tiempo Real para Microcontroladores
3
Cap´ıtulo 1 Introducci´ on
1.1.
FreeRTOS
FreeRTOS es un Kernel de tiempo real open source. Est´a desarrollado y soportado por High Integrity Systems Ltd. y por una amplia comunidad de desarrolladores, siendo uno de los sistemas operativos de tiempo real m´as usado de su categor´ıa y ha sido portado a m´ ultiples arquitecturas y herramientas de desarrollo. Es del tipo monol´ıtico ya que el kernel en s´ı y las aplicaciones se albergan en el mismo binario. A su vez, posee caracter´ısticas propias de los del tipo microkernel ya que s´olo cuenta con un planificador y una API de comunicaci´on entre procesos. No obstante, proporciona mecanismos suficientes para poder implementar drivers de dispositivos, que en el caso de FreeRTOS no son m´as que una tarea convencional dedicada a tal fin y las rutinas de interrupci´on vinculadas al hardware a abstraer. FreeRTOS est´a dise˜ nado para ser usado en sistemas embebidos basados en microcontroladores con recursos limitados, en los que otras opciones no son viables. A˜nade un overhead de procesamiento m´ınimo, as´ı como un consumo de memoria muy bajo; el kernel solo requiere entre 4 y 9 KB seg´un la configuraci´on. El planificador puede configurarse para funcionar de forma apropiativa, cooperativa e h´ıbrida; permite el uso de tareas, corrutinas o ambos mecanismos a la vez. Tambi´en proporciona mecanismos de traza de ejecuci´on y optimizaci´on.
4
Cap´ıtulo 2 Fundamentos
2.1.
Las tareas
2.1.1.
Estados
Una tarea puede estar en uno y s´olo uno de los siguientes estados en el sistema: Running: Si una tarea se est´a ejecutando o corriendo, es decir, usando tiempo de procesador y con su contexto cargado, se encuentra en este estado. Ready: Cuando una tarea est´a habilitada para ser ejecutada pero no se encuentra en el estado running , debido a que otra tarea de prioridad mayor o igual lo esta haciendo, se encuentra en este estado. Blocked: Una tarea se encuentra bloqueada blocked si est´a esperando un evento temporal o externo. Por ejemplo, una tarea llamada vTaskDelay() estar´a bloqueada hasta que expire el periodo de retardo. Las tareas tambi´en pueden estar bloqueadas esperando eventos de colas o sem´aforos. Siempre tienen un periodo de timeout luego del cual son desbloqueadas. Las tareas bloqueadas no se pueden programar para su ejecuci´on. Suspended: Las tareas en este estado no pueden programarse para ser ejecutadas, s´olo pueden entrar y salir usando las funciones de la API de forma expl´ıcita, vTaskSuspend() y xTaskResume() respectivamente. No se puede especificar un timeout .
Figura 2.1: Transiciones de estado v´alidas.
2.1.2.
Prioridades
A cada tarea se le asigna una prioridad entre 0 y (configMAX PRIORITIES - 1). configMAX PRIORITIES se define en FreeRTOSConfig.h . Cuanto mayor sea el valor de configMAX PRIORITIES mayor ser´a el consumo de memoria RAM por parte del kernel. Los valores m´as bajos indican menor prioridad, estando definida la prioridad de la tarea idle (tskIDLE PRIORITY) igual a cero.
5
El scheduler se asegura de brindar tiempo de procesamiento a aquellas tareas que se encuentren en los estados de running o ready por sobre aquellas que tengan una prioridad menor y tambi´en est´en en el estado ready .
2.1.3.
Implementando una tarea
Prototipo de una tarea: void vATaskFunction( void * p v P a r a me t e r s ) { fo r ( ; ; ) { / / - - C od ig o d e a pl ic ac io n d e l a t ar ea . - } }
2.1.4.
La tarea Idle
La tarea idle es creada autom´aticamente por el kernel cuando se inicia el scheduler . Esta tarea es la encargada de liberar la memoria asignada por el kernel a las tareas que han sido eliminadas. Por esto es importante que aquellas aplicaciones que hagan uso de la funci´on vTaskDelete() reserven tiempo de procesamiento a la tarea idle . Se pueden crear tareas que tengan la misma prioridad que idle , tskIDLE PRIORITY . Aprovechando el tiempo ocioso del procesador
Se puede el aprovechar el tiempo en el cual el procesador no est´a corriendo ninguna tarea definida por el usuario, es decir el tiempo en cual est´a corriendo la tarea idle. Esto se puede hacer de dos formas: Implementando un hook: Definir configUSE IDLE HOOK igual a 1 en FreeRTOSConfig.h. Definir una funci´on con el siguiente prototipo: void vApplicationIdleHook( void ) ;
Implementando una tarea con la misma prioridad que la tarea idle (prioridad cero): Este m´etodo es m´ as flexible y menos peligroso pero demanda un mayor consumo de memoria RAM. Un uso t´ıpico del hook es poner al procesador en el modo de ahorro de energ´ıa.
2.2.
Sistema multitarea
Para que en un sistema realmente se pueda ejecutar m´as de una tarea a la vez es necesario disponer de m´ as de una CPU. Si se dispone de una u ´ nica CPU se puede utilizar un kernel como FreeRTOS para simular la ejecuci´on de varias tareas de forma concurrente realizando una partici´on de tiempo y otorgando acceso a la CPU por un periodo acotado a cada tarea.
Figura 2.2: Simulaci´on de concurrencia con una sola CPU. 6
2.3.
Scheduler: El planificador de tareas
El planificador de tareas del kernel es el responsable de decidir qu´e tarea debe ser ejecutada. El kernel puede suspender y luego continuar la ejecuci´on de una tarea varias veces durante el tiempo de vida de la misma. La pol´ıtica de planificaci´on es el algor´ıtmo utilizado para determinar qu´e tarea debe ser ejecutada. Adem´ as las tareas pueden suspenderse a s´ı mismas independientemente del kernel cuando, por ejemplo, requieren un retraso por un tiempo determinado o esperar (permanecer bloqueadas) a que un recurso est´ e disponible u ocurra un evento. Una tarea bloqueada o en estado de sleep no est´a habilitada para ser ejecutada y no se le asigna tiempo de procesamiento.
Figura 2.3: El planificador de tareas en acci´on. 1. La tarea 1 se esta ejecutando. 2. El kernel suspende la tarea 1 . 3. El kernel contin´ ua la tarea 2. 4. La tarea 2 bloquea un perif´erico para su uso exclusivo. 5. El kernel suspende la tarea 2 . 6. El kernel contin´ ua la tarea 3. 7. La tarea 3 intenta acceder al mismo perif´erico y lo encuentra bloqueado por lo que se suspende a s´ı misma. 8. El kernel contin´ ua la tarea 1. 9. Durante la siguiente ejecuci´ on de la tarea 2 esta termina de utilizar el perif´erico y lo desbloquea 10. La pr´ oxima vez que se ejecuta la tarea 3 intenta acceder al perif´erico y al encontrarlo disponible accede al mismo. Contin´ ua su ejecuci´on hasta que el kernel la suspenda.
2.4.
Cambio de contexto
Mientras una tarea se encuentra en ejecuci´on utiliza el procesador, los registros y accede a la RAM y la ROM. A estos recursos se los conoce como el contexto. Una tarea no sabe cu´ando ser´a suspendida o cu´ando fue reanudada por u ´ ltima vez por el kernel. Tampoco sabe si alguno de estos eventos ha ocurrido. Cuando una tarea es suspendida y se da lugar a la ejecuci´on de otras tareas, estas pueden modificar los valores almacenados en los registros. Al reanudarse no puede saber que los valores en los registros fueron alterados y, al utilizarlos, el resultado de la tarea ser´a err´oneo. Para prevenir estos errores es esencial que antes de reanudarse una tarea disponga del contexto exactamente igual al que dispon´ıa justo antes de la suspensi´ on. El kernel es el responsable de asegurar esto y lo hace guardando el contexto cuando la tarea es suspendida y restaur´andolo cuando la tarea es reanudada.
7
Figura 2.4: Contexto de ejecuci´on luego de la suspensi´on.
2.5.
Aplicaciones de tiempo real
Los sistemas operativos de tiempo real utilizan los mismos principios de procesamiento multitarea que aquellos que no lo son. La diferencia radica en la pol´ıtica de la planificaci´on de tareas. Los sistemas de tiempo real o embebidos est´an dise˜ nados para dar una respuesta puntual o precisa en tiempo a eventos del mundo real. Estos eventos pueden tener un plazo en el cual el sistema de tiempo real debe responder, es por esto que la pol´ıtica de planificaci´ on de tareas debe asegurar que estos plazos se cumplan.
2.5.1.
Ejemplo
En este ejemplo se tiene un sistema de tiempo real que incluye un teclado y un display LCD. El usuario debe poder ver en el LCD la tecla que presion´o en un periodo de tiempo razonable. Cualquier respuesta entre 0 y 100 ms ser´a aceptable. Esta funcionalidad se puede implementar con una tarea aut´onoma como la que se muestra a continuaci´on: void v K e y H a nd l e r T as k ( void * p v P a r am e t e r s ) { / / E l m an ej o d el t ec la do e s u n p or ce so c on ti nu o , e s p or e st o q ue l a t ar e a e s i m pl e me n ta d a c om o u n b u cl e i n fi n it o . for ( ;; ) { / / [ M an t en e rs e s u sp e nd i da e s pe r an d o q ue s e p r es i on e u na t ec l a ]
/ / [ P ro c es a r l a t ec l a p r es i on a da ] } }
Ahora tambi´en tengamos en cuenta que el sistema de tiempo real est´a ejecutando una funci´on de control basada en una entrada filtrada digitalmente. La entrada debe ser muestreada, filtrada y el lazo de control debe ser ejecutado cada 2ms. Para que el filtro funcione correctamente el muestreo debe realizarse cada 0,5ms. Esta funcionalidad se puede implementar con una tarea aut´onoma como la que se muestra a continuaci´on: void v C o n t ro l T a sk ( void * p v P a r am e t e r s ) { for ( ;; ) { / / [ M an t en e rs e s u sp e nd i da p or 2 m s d es d e e l c o mi e nz o d el c ic l o anterior] / / [ M u e s tr e a r l a e n t ra d a ] / / [ F il t ra r l a e n tr a da m u es t re a da ]
8
// [ Ej ec ut ar el a l g o r t m o de c on tr ol ] / / [ D e v o lv e r r e s u lt a d o s ] } }
Se deben asignar las prioridades como se muestra a continuaci´on: El plazo de la tarea de control es m´as estricto que el de la tarea de manejo del teclado. La consecuencia del no cumplimiento del plazo de la tarea control es m´as grave que en el caso de la tarea de manejo del teclado.
2.6.
Planificaci´ on de tareas en tiempo real
La figura a continuaci´ on muestra como las tareas definidas anteriormente son programadas por un sistema operativo de tiempo real. Adem´as de estas, FreeRTOS ha creado una tarea especial llamada idle task que se ejecutar´ a siempre y cuando no haya otra tarea habilitada para hacerlo. Esta tarea siempre esta disponible para ser ejecutada.
Figura 2.5: Planificaci´on de tareas en tiempo real. Al comenzar, ninguna de las dos tareas est´a habilitada para ser ejecutada; vControlTask est´ a esperando el momento correcto para iniciar un nuevo ciclo de control y vKeyHandlerTask est´ a esperando que se presione alguna tecla. Es por esto que el kernel le da lugar a idle task . En t1: Se presiona una tecla. vKeyHandlerTask est´a lista para ser ejecutada. Como tiene mayor prioridad que la idle task , el kernel le concede tiempo de procesador. En t2: La tarea vKeyHandlerTask termin´ o de procesar la entrada en el teclado y actualiz´o el LCD. Como no se presion´o otra tecla, la tarea se suspende a s´ı misma y el kernel reanuda la ejecuci´on de la idle task . En t3: Un evento del timer indica que es momento de ejecutar un nuevo ciclo de control. La tarea vControlTask pasa a estar lista para ser ejecutada y, como tiene la prioridad m´as alta, el kernel la programa para ser ejecutada inmediatamente. Entre t3 y t4: Mientras se ejecuta la tarea vControlTask se presiona una tecla. Entonces el kernel cambia el estado de la tarea vKeyHandler y la habilita para ser ejecutada, pero como su prioridad es menor que vControlTask no se la pasa a ejecuci´on. En t4: La tarea vControlTask termina de procesar el ciclo de control y como no puede volverse a ejecutar hasta el pr´oximo evento de timer se suspende a si misma. vKeyHandlerTask es ahora la tarea con mayor prioridad, as´ı que el kernel la ejecuta para procesar la tecla presionada anteriormente. En t5: vKeyHandlerTask se suspende a s´ı misma ya que termin´ o con el procesamiento de la tecla presionada y, como ninguna de las dos tareas est´a habilitada para ser ejecutada, el kernel pone en ejecuci´ on la idle task . Entre t5 y t6: Se procesa un evento del timer y no se presiona ninguna tecla. En t6: Se presiona una tecla y antes de que vKeyHandlerTask se termine de ejecutar se dispara un evento del timer. Ahora ambas tareas est´an disponibles para su ejecuci´on pero como vControlTask tiene mayor prioridad que vKeyHandlerTask esta es suspendida antes de terminar el procesamiento del teclado y vControlTask comienza su ejecuci´on. 9
En t8: vControlTask termina de procesar el ciclo de control y se suspende a s´ı misma. vKeyHandlerTask vuelve a tener la mayor prioridad as´ı que el kernel la pasa a tiempo de ejecuci´on.
2.7. 2.7.1.
Comunicaci´ on entre tareas Colas
El uso de colas es la forma est´andar de comunicaci´ on entre tareas. Pueden ser usadas para enviar mensajes entre tareas y entre interrupciones y tareas. En general se usan como buffers FIFO (First In FIrst Out) pero tambi´en se pueden usar de modo tal que los nuevos elementos se agreguen al principio de la cola. Las colas pueden contener elementos o items de tama˜ no fijo y una cantidad m´axima de ellos. Los items son pasados a la cola por copia, no por referencia. De esta forma la cola se encarga de que dos tareas no puedan acceder al mismo dato de forma simult´anea. Todas las cuestiones referentes a la exclusi´on mutua son manejadas por la cola. Las funciones de la API permiten establecer un periodo m´aximo (Cantidad de Ticks) en el cual las tareas pueden permanecer en estado bloqueado para acceder a una cola, ya sea en el caso de una tarea que debe escribir en una cola que se encuentra completa, o bien una tarea que debe leer de una cola que est´a vac´ıa. Si m´ as de una tarea se encuentra bloqueada en la misma cola, la de mayor prioridad ser´a la primera en desbloquearse.
(a) La cola est´ a vac´ıa.
(b) La Tarea A env´ıa el item 1.
(c) El item 1 se ubica al final de la cola.
(d) La Tarea A env´ıa el item 2.
(e) El item 2 se ubica al final de la cola.
(f) La Tarea A env´ıa el item 3.
(g) El item 3 se ubica al final de la cola.
(h) La Tarea B lee el item 1.
(i) El item 2 se ubica al inicio de la cola.
(j) La Tarea B lee el item 2.
(k) El item 3 se ubica al inicio de la cola.
(l) La Tarea B lee el item 3.
Figura 2.6: Comunicaci´on entre tareas: Cola.
2.7.2.
Sem´ aforos binarios
Los sem´ aforos binarios se usan tanto en exclusiones mutuas como en sincronizaci´ on. Los sem´ aforos binarios y los mutex son muy parecidos, s´olo se diferencian en que los Mutex incluyen un mecanismo de herencia de prioridad. Es por esto que los sem´aforos binarios son elegidos para implementar sincronizaciones y los Mutex para exclusiones mutuas simples. Las funciones de la API, al igual que en el manejo de colas, permiten establecer el tiempo m´aximo en el cual una tarea debe permanecer en el estado Bloqueado intentando tomar take el sem´aforo. De igual manera, si m´as de una tarea se encuentra bloqueada en el mismo sem´aforo, la de mayor prioridad ser´a la primera en desbloquearse cuando el sem´aforo se encuentre disponible. Se puede pensar al sem´aforo binario como una cola que puede albergar un solo ´ıtem, es decir, solo puede estar llena o vac´ıa. A las tareas que utilizan este tipo de colas no les importa qu´e valores contiene la cola, sino si esta se encuentra llena o vac´ıa. Este mecanismo es muy ´util en la sincronizaci´on, por ejemplo, entre una tarea y una interrupci´on.
10
(a) El sem´ aforo no esta disponible. La tarea espera bloqueada a que se libere.
(b) Ocurre una interrupci´ on.
(c) La interrupci´ on devuelve el sem´ aforo.
(d) ...que desbloquea a la tarea.
(e) La tarea toma el sem´ aforo.
(f) La tarea ahora puede continuar y al terminar devolver el sem´ aforo.
Figura 2.7: Comunicaci´on entre tareas: Sem´aforos binarios.
2.7.3.
Sem´ aforos contadores
As´ı como los sem´aforos binarios se pueden pensar como colas de longitud uno, los sem´aforos contadores se pueden pensar como colas de longitud mayor a uno. Al igual que en los sem´aforos binarios, no es relevante qu´e valores se almacenan sino si la cola se encuentra vac´ıa o no. Conteo de eventos no procesados
En este caso un manejador de eventos incrementa el contador (devuelve el sem´aforo) cada vez que el evento ocurre y una tarea encargada de procesarlo lo decrementa (toma el sem´aforo) cada vez que lo procesa. El valor del contador ser´a entonces la diferencia entre la cantidad de eventos que ocurrieron y los que han sido procesados. Administraci´ on de recursos
En este caso el contador indica la cantidad disponible de un recurso determinado. Para ganar el control sobre un recurso, una tarea primero debe obtener un sem´aforo decrementando el valor del contador. Cuando el contador llegue a cero indicar´a que no hay m´as recursos libres. Cuando una tarea termine de utilizar un recurso devolver´a el sem´aforo, incrementando el valor del contador.
2.7.4.
Mutex
Los Mutexs son sem´aforos binarios que incluyen un mecanismo de herencia de prioridad. Son los m´as apropiados para implementar exclusiones mutuas (MUTtual EXclusions). Cuando son usados para exclusiones mutuas, los mutex son como llaves de acceso a un recurso. Cuando una tarea desea acceder al recurso primero debe obtener (take) la llave. Al terminar, debe devolverla (give) para que otra tarea pueda solicitarla y acceder al recurso. Los Mutexs usan la misma API que los sem´aforos por lo que tambi´en permiten especificar el tiempo m´ aximo de bloqueo (cantidad de ticks) para las tareas que los solicitan, pero a diferencia de estos usan herencia de prioridad. Esto quiere decir que si una tarea de prioridad alta se bloquea cuando intenta obtener (take) un Mutex que ha sido tomado por una tarea de prioridad menor, entonces la prioridad de la tarea que tiene el Mutex se incrementar´a temporalmente al mismo valor que la de alta prioridad. Este mecanismo permite minimizar la ¨ınversi´on de prioridades”
11
(a) Para acceder al recurso, la tarea primero debe obtener (take) el mutex.
(b) La tarea A intenta obtener el mutex.
(c) La tarea A obtuvo el mutex. Ahora puede acceder al (d) La tarea B intenta acceder al recurso, para esto recurso. primero debe obtener el mutex.
(e) ...Pero no est´ a disponible (fue tomado por la tarea (f) Cuando la tarea A termina de usar el recurso deA), por lo que la tarea B se bloquea. vuelve el mutex.
(g) La tarea B se desbloquea.
(h) La tarea B obtiene el mutex y accede al recurso.
(i) Al terminar, la tarea B devuelve el mutex.
Figura 2.8: Comunicaci´on entre tareas: Mutex.
12
Cap´ıtulo 3 Implementaci´ on
3.1.
Temporizaci´ on: El tick del sistema
La unidad de tiempo que utiliza el sistema se llama Tick . El kernel de FreeRTOS utiliza una variable para contar los ticks y as´ı llevar cuenta del tiempo de ejecuci´ on. Cada vez que un tick se completa el sistema revisa si hay alguna tarea habilitada de mayor prioridad que la que se encuentra en ejecuci´ on y, si este es el caso, suspende la tarea en ejecuci´on, realiza el cambio de contexto e inicia/reanuda la de mayor prioridad. Para implementar este mecanismo FreeRTOS utiliza un timer que genera una interrupci´o n en la que se incrementa la variable mencionada. La frecuencia de este timer es configurable y permite ajustar con precisi´on el ∆t del sistema.
Figura 3.1: Disparo de interrupci´on del Tick RTOS. En 1: la idle task se est´a ejecutando. En 2: ocurre un tick y se salta a la Rutina de Servicio de Interrupci´on del tick. La RSI del tick habilita para su ejecuci´on a la vControlTask y como esta tiene mayor prioridad que la idle task realiza el cambio de contexto correspondiente. Como el contexto de ejecuci´on es ahora el de vControlTask al salir de la RSI (4) se pasa el control a vControlTask , la cual inicia/reanuda su ejecuci´on.
3.1.1.
Atributo signal de GCC
El atributo signal en un prototipo de funci´on le notifica al compilador que esa funci´on es una Rutina de Servicio de Interrupci´on (RSI). Esto genera dos cambios importantes a la salida del compilador. 1. Como el compilador no puede saber en qu´ e momento se disparar´ a la interrupci´on, no puede guardar en la pila s´olo aquellos registros que se estaban utilizando en ese momento y ser´an modificados por la RSI. Es por esto que el atributo signal guarda en la pila todos los registros que pueden ser modificados por la RSI. 2. Adem´ as el atributo signal fuerza a que se utilice la funci´on “return from interrupt” (RETI) en vez de “return” (RET). Los microcontroladores de la l´ınea AVR deshabilitan las interrupciones al entrar en una RSI y la funci´on RETI es necesaria para rehabilitarlas al salir.
3.1.2.
Atributo naked de GCC
Como para realizar un cambio de contexto, adem´as de los registros guardados por el compilador por uso del atributo “signal” es necesario guardar el resto de los registros m´as el puntero de pila, se pueden guardar 13
expl´ıcitamente todos los registros al entrar en la RSI pero con esta pr´actica se estar´ıan guardando algunos registros dos veces, una por el c´odigo generado por el compilador y otra por el c´odigo de la RSI. Para que esto no suceda se puede usar el atributo “naked” adem´as del “signal”. El atributo “naked” le indica al compilador que no genere el c´odigo necesario para guardar los registros utilizados por la RSI y que no termine la funci´on con la instrucci´on RETI.
3.1.3.
El c´ odigo de la RSI del Tick
En esta secci´on se describe como est´a implementada la RSI disparada por el comparador del timer 1. Se realiza un llamado a la funci´on vPortYieldFromTick() debido a la implementaci´on de cambios de contextos realizados por las mismas tareas (cuando una tarea se bloquea a s´ı misma). void SIG_OUTPUT_COMPARE1A( void ) _ _a tt ri bu te __ ( ( s ig na l , n ak ed ) ) ; void vPortYieldFromTick( void ) _ _a tt ri bu te __ ( ( n ak ed ) ) ; / * R ut in a d e S er vi ci o d e I nt er ru pc io n d el T ic k . * / void SIG_OUTPUT_COMPARE1A( void ) { /* L La ma do a la f u n c i n del Tick */ vPortYieldFromTick(); / * R et or no d e l a i nt er ru pc io n . S i o cu rr io u n c am bi o d e c on te xt o s e v ol ve ra a u na t ar ea d is ti nt a a la q ue se e nc on tr ab a en e j e c u c i n . */ as m volatile ( " r e t i " );
} /*--------------------------------------------------*/ void vPortYieldFromTick( void ) { /* G ua rd ar el c on te xt o e x p l c i t a m e n t e , ya q ue se n a ke d " * / portSAVE_CONTEXT();
us
el a tr ib ut o "
/ * In cr em en ta r e l c on ta do r d e t ic ks y c he qu ea r s i e l n ue vo v al or d e t ic k c au so qu e un r et ar do e xp ir e . E st a l la ma da a f u n c i n p ue de h a bi l it a r u na t ar e a p ar a s u e j ec u ci o n */ vTaskIncrementTick();
/ * C he q ue a r s i u n c a mb i o d e c o nt e xt o e s n e ce s ar i o . C a mb i ar a l c o nt e xt o d e l a t a re a h a bi l it a da p or v T as k I nc r em e n tT i ck ( ) s i t i en e mayor prioridad que la tarea interrumpida */ vTaskSwitchContext();
/ * Si o c ur r io u n c a mb io d e c on te xt o , e st a f u nc i on r e st a ur a ra e l c o nt e xt o d e l a f u nc i on q ue e st a s i en d o r e an u nd a da * / portRESTORE_CONTEXT();
/ * R et or na r de l a f un ci on " n ak ed " * / as m volatile ( " r e t " ) ; } /*--------------------------------------------------*/
3.2.
El contexto en los microcontroladores AVR
En los microcontroladores AVR como el ATMEGA32 el contexto consiste en lo siguiente: 32 registros de prop´osito general. El registro de estado: Este registro afecta la ejecuci´on de las instrucciones ya que contiene los bits de carry, zero, overflow, etc. El contador de programa: Al reanudarse, una tarea debe continuar su ejecuci´on en la instrucci´on que estaba por ejecutarse antes de la suspensi´on. 14
Puntero de pila: Los dos registros que contienen este puntero.
Figura 3.2: Contexto en un microcontrolador AVR.
3.2.1.
Guardando el contexto
Cada tarea dispone de su propia secci´ on en la pila de memoria. Es por esto que para guardar el contexto se debe realizar un push de los registros en la pila de tareas. Para implementar esta funci´on se usa c´odigo assembler ya que este procedimiento depende de la arquitectura. # define portSAVE _CONTEXT () asm volatile ( " push r0 " in r0 , __SREG__ " cli " push r0 " push r1 " clr r1 " push r2 " push r3 " push r4 " push r5
\n \t " \n \t " \n \t " \n \t " \n \t " \n \t " \n \t " \n \t " \n \t " \n \t "
\ \ \ \ \ \ \ \ \ \ \ \
( 1) ( 2) ( 3) ( 4) ( 5) ( 6) ( 7)
: : : " push " push " lds " lds " in " st " in " st
r30 \n \t " \ r31 \n \t " \ r26 , pxCurrentTCB \n \t " \ r27 , p xC ur re nt TC B + 1 \n \t " \ r0 , __SP_L__ \n \t " \ x +, r0 \n \t " \ r0 , __SP_H__ \n \t " \ x +, r0 \n \t " \
( 8) ( 9) ( 10 ) ( 11 ) ( 12 ) ( 13 )
);
1. R0 se guarda primero ya que ser´ a necesario sobreescribirlo para guardar el registro de estado. 2. Se mueve el registro de estado a R0 15
3. Se deshabilita el llamado a interrupciones. Esto es necesario ya que la funci´ on portSAVE CONTEXT(), no solo es llamada desde la RSI del tick, sino que tambi´ en es llamada por ejemplo, por una tarea cuando se suspende a s´ı misma. 4. Se guarda el registro de estado en la pila. 5. Se guarda R1. 6. Como el compilador asume que el valor original de R1 es cero se limpia R1. 7. Se guardan los registros restantes: De R2 a R31. 8. Se carga el registro XL con la direcci´on en la que se guardar´a el low byte del puntero de pila. 9. Idem con el high byte. 10. Se guarda el low byte del puntero de pila. 11. Idem con el high byte.
3.2.2.
Restaurando el contexto
portRESTORE CONTEXT() es la funci´on inversa de portSAVE CONTEXT(). El contexto que se va a restaurar fue guardado previamente en la pila de tarea. # define port RESTOR E_CONTE XT () asm volatile ( " lds r26 , pxCurrentTCB " lds r27 , p x Cu rr en tT CB + 1 " ld r28 , x+ " out __SP_L__ , r28 " ld r29 , x+ " out __SP_H__ , r29 " pop r31 " pop r30
\ \n \t " \ n\ t" \n \t " \n \t " \n \t " \n \t " \n \t " \n \t "
\ \ \ \ \ \ \ \
( 1) ( 2)
\n \t " \n \t " \n \t " \n \t "
\ \ ( 5) \ ( 6) \ ( 7)
( 3) ( 4)
: : : " pop " pop " out " pop
r1 r0 __SREG__ , r0 r0
);
1. pxCurrentTCB almacena la direcci´on de donde se encuentra el puntero de la pila de la tarea. Se carga en el registro XL. 2. Idem con el high byte, se carga en XH. 3. El puntero de pila de la tarea que se est´ a reanudando se carga en el registro SPL del microcontrolador. 4. Idem con el high byte, se carga en SPH. 5. Se realiza un pop de los registros almacenados en la pila en orden num´erico reverso hasta R1. 6. Como el registro de estado se hab´ıa almacenado en la pila entre RO y R1 se lo restaura. 7. Se restaura R0.
3.3.
Ejemplo completo de un cambio de contexto
En el siguiente ejemplo se detallan cada una de las etapas en el proceso de suspender una tarea y reanudar otra, conocido como cambio de contexto.
16
3.3.1.
Previo a la interrupci´ on del Tick
Antes de que se dispare la interrupci´on del tick, la tarea TaskA se encuentra en estado running y la tarea TaskB est´a suspendida.
Figura 3.3: Contexto: Task A en ejecuci´on.
3.3.2.
Durante la interrupci´ on del Tick
La interrupci´on ocurre justo cuando la Task A est´ a por ejecutar la instrucci´on LDI . Cuando ocurre la interrupci´ on el microcontrolador guarda autom´aticamente el Program Counter (PC) en la pila, antes de entrar en la RSI. Es importante notar que el PC es almacenado por el microcontrolador y no por el kernel en portSAVE CONTEXT()
Figura 3.4: El PC se almacena en la pila de Task A por la interrupci´on.
3.3.3.
La RSI del Tick
Como se describi´o en la Secci´on: 3.1.3, la funci´on SIG OUTPUT COMPARE1A definida con el atributo ”naked”para que no guarde parcialmente el contexto, llama a la funci´on vPortYieldFromTick(). Esta funci´on llama a portSAVE CONTEXT que se encarga de empujar el contexto de ejecuci´on entero dentro de la pila de la Tarea A como se muestra en la Figura: 3.5. Adem´as portSAVE CONTEXT guarda una copia del puntero de pila de la Tarea A para permitir su posterior reanudaci´on.
17
Figura 3.5: El contexto de la Tarea A se almacena en la pila de la Tarea A.
3.3.4.
Incrementando el contador de Ticks
Luego de portSAVE CONTEXT se ejecuta vTaskIncrementTick(). Al incrementar el tick, la Tarea B pasa al estado Ready y como posee mayor prioridad que la Tarea A, vTaskSwitchContext() selecciona a la Tarea B como la pr´oxima a reanudar al finalizar la RSI.
3.3.5.
Recuperaci´ on del puntero de pila de la Tarea B
Para restaurar el contexto de la Tarea B lo primero que debe hacer portRESTORE CONTEXT() es recuperar el puntero de la pila de la Tarea B de la copia guardada cuando se suspendi´o. Una vez recuperado, se carga este puntero en el registro SP del procesador y ahora la pila del microcontrolador apunta al contexto de la Tarea B.
Figura 3.6: El registro SP apunta al contexto de la Tarea B.
3.3.6.
Restauraci´ on del contexto de la Tarea B
portRESTORE CONTEXT restaura el contexto de la Tarea B. S´olo el PC queda en la pila.
18
Figura 3.7: Contexto de la Tarea B restaurado.
3.3.7.
Retorno de la RSI del Tick
vPortYieldFromTick() retorna a SIG OUTPUT COMPARE1A() cuya u ´ ltima instrucci´ on es return from interrupt”(RETI). Esta instrucci´on asume que el pr´oximo valor en la pila es la direcci´o n a la cual debe retornar para reanudar el curso normal de ejecuci´on previo a la interrupci´on. Pero como la RSI al realizar el cambio de contexto modific´o la pila ahora la interrupci´on no regresar´a al punto en el que fue llamada (en Tarea A), sino que regresar´a al punto en el que fue suspendida la Tarea B.
Figura 3.8: La Tarea B se ejecutar´a al salir de la interrupci´on.
19
Bibliograf´ıa
[1] Richard Barry , FreeRTOS Fundamentals, Real Time Engineers Ltd, Inglaterra, 2012. http://www.freertos.org/implementation/index.html?http://www.freertos.org/ implementation/a00003.html
[2] Richard Barry , FreeRTOS Implementation, Real Time Engineers Ltd, Inglaterra, 2012. http://www.freertos.org/implementation/index.html
[3] Richard Barry , Atmel AVR (MegaAVR) / WinAVR Port, Real Time Engineers Ltd, Inglaterra, 2012. http://www.freertos.org/index.html?http://www.freertos.org/a00098.html
[4] ATMEL, AVR084: Replacing ATmega323 by ATmega32. http://www.atmel.com/Images/doc2518.pdf
[5] Antonio Jimenez Bellido, Herramientas libres para la implementaci´on de sistemas de control en tiempo real con microcontroladores ARM7, Escuela T´ecnica Superior de Ingenier´ıa de Telecomunicaci´on, Universidad de M´alaga, Espa˜ na, M´alaga, 2010. http://es.scribd.com/doc/68622187/67/INTRODUCCION-A-FREERTOS
[6] N´ ucleo Monol´ıtico Wikipedia, 2012. http://es.wikipedia.org/wiki/Ncleo_monoltico
[7] Micron´ ucleo Wikipedia, 2012. http://es.wikipedia.org/wiki/Microncleo
[8] Ing. Marcelo Lorenzati , Desarrollo de drivers y aplicaciones para FreeRtos, SASE 2010, Argentina, Buenos Aires, Marzo 2010. [9] Jos´e Daniel Mu˜ noz Fr´ıas , Sistemas Empotrados en Tiempo Real. Una introducci´on basada en FreeRTOS y en el microcontrolador ColdFire MCF5282, Espa˜na, Febrero 2009. http://books.google.com.ar/books?id=og3fCjuh27kC
20