182.704 Building Reliable Distributed Systems LU Matthias Függer, Alexander Kößler, Ulrich Schmid WS 2014 course Wireless Nodes, nesC, TinyOS
The target systems Wireless motes cluster ATAVRRZ200 demonstration kit from Atmel >= 4 motes a single mote AT86RF230 radio transceiver 2450 MHz band ATmega 1281V MC
First steps 1. get your accounts 2. login to a PC in ECS lab and get a copy of TinyOS git clone ssh://trac/ecs/repo/git/lva/brds/tinyos 3. set environment variables (.bashrc) export TOSROOT="/homes/koe/tinyos" export TOSDIR="$TOSROOT/tos" export MAKERULES="$TOSROOT/support/make/Makerules" 4. in apps/Blink/ execute make rz200 5. connect mote via programmer and execute make rz200 install Binary is generated and downloaded via avrdude
ncc – nesc compiler for tinyos > make iris ncc -o build/iris/main.exe -Os -Wall -Wshadow -Wnesc-all -target=iris -fnesc-cfile=build/iris/app.c -board=micasb -DDEFINED_TOS_AM_GROUP=0x22 --param max-inline-insns-single=100000 -DIDENT_APPNAME=\"BlinkAppC\" -DIDENT_USERNAME=\"fuegger\" -DIDENT_HOSTNAME=\"ecslab6\ „ -DIDENT_USERHASH=0x894ea284L -DIDENT_TIMESTAMP=0x4bc706feL -DIDENT_UIDHASH=0xee40a21fL -fnesc-dump=wiring -fnesc-dump='interfaces(!abstract())' -fnesc-dump='referenced(interfacedefs, components)' -fnesc-dumpfile=build/iris/wiring-check.xml BlinkAppC.nc -lm compiled BlinkAppC to build/iris/main.exe 2268 bytes in ROM 51 bytes in RAM avr-objcopy --output-target=srec build/iris/main.exe build/iris/main.srec avr-objcopy --output-target=ihex build/iris/main.exe build/iris/main.ihex writing TOS image ncc calls nescc, which calls gcc
Memory requirements ATmega1281/V usage: Flash: 128KB EEPROM: 4KB RAM: 8KB 2268 bytes in ROM = 2.3 KB (1.7%) [.text segment size] 51 bytes in RAM = 0.05 KB (0.4%) [.bss segment size] This is quite bad for a blinking application, but good for an operating system!
A threaded version in apps/tosthreads/apps/Blink/ execute > make rz200 threads tells nesc to use thread lib instead 5358 bytes in ROM = 5.3 KB (4.1%) 993 bytes in RAM= 1.0 KB (12.1%) The latter is problematic (only static variables, no dynamic varibles yet). Context switching needs quite some memory!
Where to find code? apps/ example applications apps/tosthreads/apps/ example TOSthreads applications tos/interfaces all the interfaces tos/system/ major hardware independent code, implementing interfaces tos/chips + tos/platforms hardware dependent code tos/lib/ TinyOS add-ons ( timer , radio transceiver, TOSthreads, …)
Example Blink BlinkC.nc #include "Timer.h" module BlinkC { uses interface … provides interface … } BlinkC
Example Blink BlinkC.nc interface Timer<precision_tag> { #include "Timer.h" command void startPeriodic (uint32_t dt); command void startOneShot(uint32_t dt); module BlinkC command void stop(); { event void fired (); uses interface Timer<TMilli> as Timer0; uses interface Timer<TMilli> as Timer1; command bool isRunning(); uses interface Timer<TMilli> as Timer2; command bool isOneShot(); } command void startPeriodicAt(uint32_t t0, uint32_t dt); command void startOneShotAt(uint32_t t0, uint32_t dt); command uint32_t getNow(); BlinkC command uint32_t gett0(); command uint32_t getdt(); } event command interfaces can be typed general interfaces - Timer0 for compile-time wiring checking - more than 1 interface per module possible!
Command/event model Command/event model functions, commands, events, tasks function (inside Module) uint8_t myFunction(uint8_t x) {…} y = myFunction(x); command (between Modules) (async) command uint8_t myInterface.myCommand(uint8_t x) {…} y = call myInterface.myCommand(x); event (between Modules) (async) event void myInterface.mySignal(uint8_t x) {…} signal myInterface.mySignal(x); task (inside Module – main execution primitive) task void myTask() {…} post myTask();
Command/event model Split phase (call back) a longer computation (1) call f(x) returns; y = f(x) f(x) call f(x) returns signals f(x); y = f(x) (2) start f(x) f(x) done
Command/event versus task What to do and what not (deferred procedure calls) (1) call f(x); f(x) signal/call g(y); g(y) etc... call f(x); (2) f(x) post g(); g(y)
Command/event versus task What to do and what not (deferred procedure calls) in some module’s implementation that provides the interface Someone: x:= 0; // in implementation scope command error_t Someone.doThis() { // do it and obtain x x := f(.); signal Someone.done(x); } some other module which uses Someone to repeatedly do this: .... for (uint8_t i=0; i++; i < 10) { Someone.doThis(); } .... event void Someone.done(uint8_t x) { // obtain x }
Command/event versus task What to do and what not (deferred procedure calls) in some module’s implementation that provides the interface Someone: command error_t Someone.doThis() { // dot it and obtain x x = 1; signal Someone.done(x); } a “clever” solution: ... i:= 0; Someone.doThis(); ... event void Someone.done(uint8_t x) { // obtain x i++; if (i < 10) { Someone.doThis(); } }
Command/event versus task What to do and what not (deferred procedure calls) in some module’s implementation that provides the interface Someone: original implementation task implementation command error_t Someone.doThis() { command error_t Someone.doThis() { // dot it and obtain x // dot it and obtain x x = 1; x = 1; signal Someone.done(x); return post Someone.doneTask; } } task void doneTask() { signal Someone.done(x); now we can use: } ... i:= 0; Someone.doThis(); ... event void Someone.done(uint8_t x) { // obtain x i++; if (i < 10) { Someone.doThis(); } }
Example Blink BlinkC.nc implementation { event void Boot.booted() #include "Timer.h" { call Timer0.startPeriodic( 250 ); module BlinkC call Timer1.startPeriodic( 500 ); { call Timer2.startPeriodic( 1000 ); uses interface Timer<TMilli> as Timer0; } uses interface Timer<TMilli> as Timer1; event void Timer0.fired() uses interface Timer<TMilli> as Timer2; { uses interface Leds; call Leds.led0Toggle(); uses interface Boot; } } BlinkC event void Timer1.fired() { uses call Leds.led1Toggle(); } Timer0 Timer1 Timer2 Leds Boot event void Timer2.fired() { call Leds.led2Toggle(); } }
Example Blink BlinkC.nc BlinkAppC.nc -> wiring from user to provider #include "Timer.h" configuration BlinkAppC { module BlinkC } { implementation uses interface Timer<TMilli> as Timer0; { uses interface Timer<TMilli> as Timer1; components MainC, BlinkC, LedsC; uses interface Timer<TMilli> as Timer2; components new TimerMilliC() as Timer0; uses interface Leds; components new TimerMilliC() as Timer1; uses interface Boot; components new TimerMilliC() as Timer2; } long version: BlinkC BlinkC.Boot -> MainC.Boot; BlinkC -> MainC.Boot; BlinkC.Timer0 -> Timer0.Timer; BlinkC.Timer0 -> Timer0; uses BlinkC.Timer1 -> Timer1.Timer; BlinkC.Timer1 -> Timer1; BlinkC.Timer2 -> Timer2.Timer; BlinkC.Timer2 -> Timer2; BlinkC.Leds -> LedsC.Leds; BlinkC.Leds -> LedsC; Timer0 Timer1 Timer2 Leds Boot } provides TimerMilliC TimerMilliC TimerMilliC LedsC MainC
Booting tos/system/MainC #include "hardware.h" configuration MainC { provides interface Boot; uses interface Init as SoftwareInit; } implementation { components PlatformC, RealMainP , TinySchedulerC; RealMainP.Scheduler -> TinySchedulerC; RealMainP.PlatformInit -> PlatformC; // Export the SoftwareInit and Booted for applications SoftwareInit = RealMainP.SoftwareInit; Boot = RealMainP; }
Booting tos/system/RealMainC module RealMainP @safe() { implementation { provides interface Boot; int main() @C() @spontaneous() { uses interface Scheduler; atomic uses interface Init as PlatformInit; { uses interface Init as SoftwareInit; platform_bootstrap(); } call Scheduler.init(); call PlatformInit.init(); while (call Scheduler.runNextTask()); call SoftwareInit.init(); while (call Scheduler.runNextTask()); } __nesc_enable_interrupt(); signal Boot.booted(); call Scheduler.taskLoop(); return -1; } default command error_t PlatformInit.init() { return SUCCESS; } default command error_t SoftwareInit.init() { return SUCCESS; } default event void Boot.booted() { } }
TinyOS Scheduling Policies Non-Preemptive FIFO • Small • Easy • Fast Thread Based Priority Queues using Preemptive Jobs • Bigger • Not that easy • „Slow“
Concurrency in nesC Execution model in nesC is „ run-to-completion “ tasks • No preemption • Atomic with respect to other tasks • Not atomic with respect to interrupt handlers Code divided into two parts: • Syncronous Code: functions, commands, events, tasks that are only reachable from tasks • Asyncronous Code: used in interrupt handlers (must be marked async) Race conditions: • No race conditions between tasks • Avoid race conditions by protection through an atomic statement • Calls to functions are only protected if every call is protected • Compiler detects race conditions
nesC Tasks in TinyOS 2.x post processTask(); … task void processTask() { //do work if (moreToProcess){ post processTask(); } } post will only fail iff the task is already posted but execution has not started yet
Recommend
More recommend