TinyOS Dhanshree Nimje Smita Khartad. TinyOS - Design Design.
TinyOS Modelo de RTOS SISEM 2009. Temas a tratar Tareas y eventos de hardware Scheduller de tareas...
-
Upload
flavio-ledo -
Category
Documents
-
view
213 -
download
0
Transcript of TinyOS Modelo de RTOS SISEM 2009. Temas a tratar Tareas y eventos de hardware Scheduller de tareas...
TinyOSModelo de RTOS
SISEM 2009
Temas a tratar• Tareas y eventos de hardware
• Scheduller de tareas
• Tareas en TinyOS 1.x
• Estructura e implementacion de tareas y schedulers en TinyOS 2.x.
• Comandos y eventos asíncronos
• Atomic
• Stack de radio
• Formato de mensajes
Arquitectura TinyOS
Sensing Comms Other Libraries
Application
Main (scheduler)
Hardware Abstractions (ADC, CLOCK, I2C, LEDS, PHOTO, UART, SPI)
TinyOS
nesC
GCC
Compilación
Eventos y Tareas
TinyOS tiene dos abstracciones computacionales básicas:
– eventos asíncronos– tareas.
También existen comandos y eventos sync
TinyOS Contexto de Ejecución
• Eventos generados por interrupciones expropian ejecución a las tareas
• Tareas no expropian tareas
• Ambos procesos esenciales en las transiciones entre los estados
Hardware
Interrupts
eve
nts
commands
Tasks
Tareas
• Las Tasks son funciones cuya ejecución se difiere para mas adelante.
• Una vez que ha sido agendada, corre hasta completar.
• No hay expropiación de una con otra.
Eventos asíncronos
• Generados por las interrupciones de hardware.
• Expropian la ejecución a las tareas permitiendo la atención inmediata.
• Lamentablemente, como veremos no todo puede ser asíncrono
Hardware event handlers
Se ejecutan en respuesta a una interrupción de hardware y también corren hasta completar, pero pueden expropiar la ejecución de una tarea o de otro hardware event handler.
Necesidad de uso de una tarea
event void Timer.fired(){
call Read.read();
}
event void Read.readDone(error_t err, uint16_t val){if (err==SUCCESS){… call Read.read()}
}
command error_t Read.read(){
signal Read.readDone(SUCCESS, filterVal);
}
• Señalizar un evento (sync) desde un comando (sync) no es buena idea, ya que puede dar lugar a loops de llamadas.
• Por ejemplo cuando se quiere muestrear un sensor muchas veces en forma rápida, lo cual se puede hacer llamando a Read.read en el Read.readDone handler
• El stack crecería en forma sinificativa
Para evitar el loop
• Esto da lugar a la necesidad de posponer la ejecución de una función y usar para ello un scheduller.
• Es lo que hace una tarea, es decir un procedimiento con llamada diferida.
Código modificado
command error_t Read.read(){
post readDoneTask();
return SUCCESS;
}
task void readDoneTask(){
signal Read.readDone(SUCCESS, filterVal);
}
Reglas de las tareas
– Las tareas no toman ni retornan argumentos.– Tarea no puede expropiar otra tarea.– La Tarea corre hasta completar.– Las Tareas son sincrónicas
• Las Interrupciones son asíncronas
– Todas las tareas tienen la misma prioridad.– No se puede postear/encolar la misma tarea
dos veces, pero una tarea se puede postear a si misma.
TinyOS 1.x
• La cola de tareas en TinyOS 1.x se implementa como un buffer circular de tamaño fijo de punteros a funciones. Al “postear” una tarea se coloca el puntero de tareas en la próxima posición libre del buffer.
• Si no hay lugares libres el ``post`` retorna fail.
Task Scheduler
command void Scheduler.taskLoop() { for (;;) { uint8_t nextTask;
atomic { while ((nextTask = popTask()) == NO_TASK) { call McuSleep.sleep(); } } signal TaskBasic.runTask[nextTask](); }}
Finalmente,cuando postear una tarea?
1. Cuando hay mucho procesamiento que tome mucho tiempo ejecutar.
2. Cuando el código pueda formar un loop recursivo de ejecución.
3. Cuando se quiera usar algo que pueda estar ocupado en esos momentos (p.e. la radio o la flash).
4. Cuando se haya recibido una interrupción y se necesite salir del contexto asíncrono (capa de implementación de hardware HIL)
ConcurrenciaLa atomicidad de las tareas evitan el problema de corrupción de los datos.
En cambio las interrupciones pueden expropiar la ejecución a las tareas, con el consiguiente peligro de corromper los datos
Palabra clave async
• Las funciones que pueden expropiar la ejecucíon a las tareas se etiquetan con la palabra async.
• Corren en forma asíncrona respecto a las tareas.
• Como regla, estas funciones sólo llaman a otras funciones también asíncronas
Funciones sincrónicas
• Si no se marcan con la etiqueta async las funciones son sincrónicas (comandos y eventos)
interface Send {
command error_t send(message_t* msg, uint8_t len);
event void sendDone(message_t* msg, error_t error);
command error_t cancel(message_t* msg);
command void* getPayload(message_t* msg);
command uint8_t maxPayloadLength(message_t* msg);
}
Interfaz asíncrona
interface Leds {
async command void led0On();
async command void led0Off();
async command void led0Toggle();
async command uint8_t get();
async command void set(uint8_t val);
}
Interrupciones
• Los interrupts handlers son async, por lo cual no pueden incluir llamadas a funciones sincrónicas.
• La única forma de que un interrupt handler pueda ejecutar una función sync es postear una tarea.– Un post a una tarea es una op async– Una tarea en ejecución es sync
Ejemplo
• A packet layer on top of a UART. When the UART receives a byte, it signals an interrupt.
• In the interrupt handler, software reads the byte out of the data register and puts it in a buffer.
• When the last byte of a packet is received, the software needs to signal packet reception.
• But the receive event of the Receive interface is sync. So in the interrupt handler of the final byte, the component posts a task to signal packet reception.
UART
Packet Layer
Interrupt
byte
Int Hand
Byte
Buffer
Signal packet receptiontarea event Receive.receive (sync)
post
Toma alrededor de 80 ciclos de reloj el post y poner a correr una tarea. Generalmente una tarea corre a lo sumo unos pocos milisegundos. Una tarea que dure mucho, o muchas tareas no tan largas, pueden introducir una latencia siginificativa (decenas de ms) entre el post y su ejecución.
En componentes de bajo nivel, tales como el stack de radio, la tasa de recepción de paquetes puede verse limitada por la cantidad de tareas que pueda encolar para para señalar la recepción. Una latencia de 10ms limitará el sistema a 100 paq/seg.
El procesador corre a f=8MHz. Consideremos dos casos que tienen 5 componentes que procesan tareas que se repostean a si mismas y una radio que recibe paquetes.
1) Las tareas duran 5ms f * 5ms = 40.000 ciclos total = 5 * 5 = 25 msOverhead = 80/40000 = 0,05%2) Las tareas duran 500us f*0,5ms = 4000 ciclos total =0.5 * 5 = 2,5 msOverhead = 80/4000 = 0,5%
TinyOS 2.x
• La semántica de tareas en TinyOS 2.x se basa en las limitaciones y errores en tiempo de ejecución que introduce el modelo 1.x.
• En TinyOS 2.x, post básico fallará SyS la tarea ya ha sido posteada y su ejecución no ha comenzado.
• Una tarea puede correr siempre, pero solo puede tener un post principal en un momento dado.
byte de estado
• Esto se consigue disponiendo de un byte de estado por tarea (se supone Nº tareas < 255).
• Si un componente necesita postear una tarea varias veces , la propia tarea puede repostearse a si misma cuando sea necesario.
Ejemplo
post processTask(); ... task void processTask() { // do work if (moreToProcess) { post processTask(); } }
ventajas
Esta semántica previene varios problemas tales como la imposibilidad de señalizar completitud en eventos split-phase por:– La cola de tareas está llena, – Overflow de la cola de tareas en la
inicializacion, – Asignación injusta de tareas por
componentes que postean muchas veces una misma tarea.
modelo KISS
• Se debe mantener la sencillez del uso básico de tareas, pero al mismo tiempo debe ser posible introducir nuevos tipos de tareas que vayan mas allá del caso básico.
• TinyOS 2.x logra esto manteniendo “post” y “task” para el caso básico, e introduciendo “task interfaces” para las adicionales.
Task interfaces
• Estas interfaces permiten extender la sintaxis y la semántica de las tareas.
• Por lo general una task interface tiene: – async command post – event run.
• La “signature” de estas funciones dependen de la interface.
Ejemplo
Una task interface que permite a una tarea tomar un parametro entero puede ser:
interface TaskParameter {
async error_t command postTask (uint16_t param);
event void runTask(uint16_t param); }
• Usando esta task interface, un componente puede hacer un post a una tarea con un parámetro “uint16_t”.
• Cuando el scheduler corre la tarea, señalizará el evento “runTask” con el parámetro pasado, el cual contiene un puntero a la lógica de la tarea.
Nota
• Observar que esto no no ahorra RAM: el scheduler debe tener RAM reservada para el parametro.
• Además, como solo debe haber una copia de una tarea corriendo por vez se puede almacenar la variable en el componente.
The Scheduler in TinyOS 2.x
• The scheduler is a TinyOS component.• Every scheduler MUST support nesC
tasks. • It MAY also support any number of
additional task interfaces. • The scheduler component is resonsible for
the policy of reconciling different task types (e.g., earliest deadline first tasks vs. priority tasks).
Aplicacion tipica de uso de tareas
• Adquisición de datos dirigida por eventos• Se encola la tarea para realizar cálculos.
event result_t sensor.dataReady(uint16_t data) {
putdata(data);
post processData();
return SUCCESS;
}
task void processData() {
int16_t i, sum=0;
for (i=0; i ‹ maxdata; i++)
sum += (rdata[i] ›› 7);
display(sum ›› shiftdata);
}
Task Preemption en TinyOS-2.x
• El lab MISL de la UCC y el InfoLab21 de la Universidad de Lancaster desarrollaron un nuevo scheduler para TinyOS-2.x
• Al agregar “task preemption” el nuevo scheduler puede interrumpir tareas no-criticas y pasar a procesar tareas de mayor prioridad imediatamente. Facilitando el cumplimiento de restricciones de tiempo.
• Permite definir tareas con prioridad específica. • Define 5 niveles de prioridad uno de los cuales soporta
la tarea básica de TinyOS.
async keyword
• Comandos y eventos que se ejecutan como parte de un “hardware event handler” deben ser declarados con la palabra clave async.
• En razón de que tanto las tasks como los “hardware event handlers” pueden ser preempted por otro código asíncrono, los programas nesC son suceptibles a ciertas “race conditions”.
atomic
• Las Races son evitadas o bien accediendo a los datos compartidos exclusivamente dentro de las tasks, o realizando todos los accesos dentro de sentencias atomic.
• El compilador nesC reporta al programador en tiempo de compilación las “data races” potenciales.
• El compilador puede reportar un falso positivo. Si el programador advierte este caso, puede declarar a la variable que crea el problema con la palabra clave norace, pero este recurso debe ser usado con precausión.
Radio Stacks
Radio Hardware
Transmit / Receive / Init
CSMA / Acknowledgements
ActiveMessage
Message Queue
Your Application
ReceiveSplitControlAMSend
Main Radio Interfaces
• SplitControl– Provided by ActiveMessageC
• AMSend– Provided by AMSenderC
• Receive– Provided by AMReceiverC
Main Serial Interfaces
• SplitControl– Provided by SerialActiveMessageC
• AMSend– Provided by SerialAMSenderC
• Receive– Provided by SerialAMReceiverC
Setting up the Radio: Configuration
configuration MyAppC {}
implementation { components MyAppP, MainC, ActiveMessageC, new AMSenderC(0), // send an AM type 0 message new AMReceiverC(0); // receive an AM type 0 message
MyAppP.Boot -> MainC; MyAppP.SplitControl -> ActiveMessageC; MyAppP.AMSend -> AMSenderC; MyAppP.Receiver -> AMReceiverC;}
Setting up the Radio: Modulemodule MyAppP { uses { interface Boot; interface SplitControl; interface AMSend; interface Receive; }}
implementation { …}
Turn on the Radio
event void Boot.booted() { call SplitControl.start(); }
event void SplitControl.startDone(error_t error) { post sendMsg(); }
event void SplitControl.stopDone(error_t error) { }
Send Messagesmessage_t myMsg;
task void sendMsg() { if(call AMSend.send(AM_BROADCAST_ADDR, &myMsg, 0) != SUCCESS) { post sendMsg(); }}
event void AMSend.sendDone(message_t *msg, error_t error) { post sendMsg();}
Receive a Message
event message_t *Receive.receive(message_t *msg, void
*payload, uint8_t length) {
call Leds.led0Toggle();
return msg;
}
Payloads
• A message consists of:– Header– Payload– Optional Footer
message_t
typedef nx_struct message_t {nx_uint8_t header[sizeof(message_header_t)];nx_uint8_t data[TOSH_DATA_LENGTH];nx_uint8_t footer[sizeof(message_footer_t)];
nx_uint8_t metadata[sizeof(message_metadata_t)];} message_t;
Payloads : Use Network Types(MyPayload.h)
#ifndef MYPAYLOAD_H#define MYPAYLOAD_H
typedef nx_struct MyPayload { nx_uint8_t count;} MyPayload;
enum { AM_MYPAYLOAD = 0x50,};
#endif
Example: Filling out a Payloadvoid createMsg() {
MyPayload *payload = (MyPayload *) call AMSend.getPayload(&myMsg);
payload->count = (myCount++);
post sendMsg();
}
Example: Receiving a Payloadevent void Receive.receive(message_t *msg, void *payload, uint8_t len) {
MyPayload *payload = (MyPayload *) payload;
call Leds.set(payload->count);
signal RemoteCount.receivedCount(payload->count);
return msg;
}
Donde encontrar información
Tutorial
• TinyOS Tutorial
• http://docs.tinyos.net/index.php/TinyOS_Tutorials
TinyOS Enhancement Proposals (TEPs)
• http://www.tinyos.net/tinyos-2.x/doc/• http://docs.tinyos.net/index.php/TEPs