182.704 Building Reliable Distributed Systems LU Matthias Fgger, - - PowerPoint PPT Presentation

182 704 building reliable distributed systems lu
SMART_READER_LITE
LIVE PREVIEW

182.704 Building Reliable Distributed Systems LU Matthias Fgger, - - PowerPoint PPT Presentation

182.704 Building Reliable Distributed Systems LU Matthias Fgger, Alexander Kler, Ulrich Schmid WS 2014 course Wireless Nodes, nesC, TinyOS The target systems Wireless motes cluster ATAVRRZ200 demonstration kit from Atmel >= 4 motes


slide-1
SLIDE 1

182.704 Building Reliable Distributed Systems LU

Matthias Függer, Alexander Kößler, Ulrich Schmid WS 2014 course

Wireless Nodes, nesC, TinyOS

slide-2
SLIDE 2

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

slide-3
SLIDE 3

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

slide-4
SLIDE 4

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

slide-5
SLIDE 5

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!

slide-6
SLIDE 6

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!

slide-7
SLIDE 7

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, …)

slide-8
SLIDE 8

Example Blink

BlinkC.nc

#include "Timer.h" module BlinkC { uses interface … provides interface … }

BlinkC

slide-9
SLIDE 9

Example Blink

BlinkC.nc

#include "Timer.h" module BlinkC { uses interface Timer<TMilli> as Timer0; uses interface Timer<TMilli> as Timer1; uses interface Timer<TMilli> as Timer2; } interface Timer<precision_tag> { command void startPeriodic(uint32_t dt); command void startOneShot(uint32_t dt); command void stop(); event void fired(); command bool isRunning(); 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(); command uint32_t gett0(); command uint32_t getdt(); }

interfaces can be typed

  • general interfaces
  • for compile-time wiring checking

more than 1 interface per module possible! BlinkC Timer0 command event

slide-10
SLIDE 10

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();

slide-11
SLIDE 11

Command/event model

Split phase (call back) a longer computation (1) (2)

f(x) call f(x) returns; y = f(x) start f(x) call f(x) signals f(x); y = f(x) returns f(x) done

slide-12
SLIDE 12

Command/event versus task

What to do and what not (deferred procedure calls) (1) (2)

f(x) call f(x); signal/call g(y); g(y) etc... f(x) call f(x); post g(); g(y)

slide-13
SLIDE 13

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 }

slide-14
SLIDE 14

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(); } }

slide-15
SLIDE 15

Command/event versus task

What to do and what not (deferred procedure calls)

in some module’s implementation that provides the interface Someone:

  • riginal implementation

task implementation now we can use:

... i:= 0; Someone.doThis(); ... event void Someone.done(uint8_t x) { // obtain x i++; if (i < 10) { Someone.doThis(); } } command error_t Someone.doThis() { // dot it and obtain x x = 1; signal Someone.done(x); } command error_t Someone.doThis() { // dot it and obtain x x = 1; return post Someone.doneTask; } task void doneTask() { signal Someone.done(x); }

slide-16
SLIDE 16

Example Blink

BlinkC.nc #include "Timer.h" module BlinkC { uses interface Timer<TMilli> as Timer0; uses interface Timer<TMilli> as Timer1; uses interface Timer<TMilli> as Timer2; uses interface Leds; uses interface Boot; } implementation { event void Boot.booted() { call Timer0.startPeriodic( 250 ); call Timer1.startPeriodic( 500 ); call Timer2.startPeriodic( 1000 ); } event void Timer0.fired() { call Leds.led0Toggle(); } event void Timer1.fired() { call Leds.led1Toggle(); } event void Timer2.fired() { call Leds.led2Toggle(); } }

BlinkC Timer0 Timer1 Timer2 Leds Boot uses

slide-17
SLIDE 17

Example Blink

BlinkC.nc

#include "Timer.h" module BlinkC { uses interface Timer<TMilli> as Timer0; uses interface Timer<TMilli> as Timer1; uses interface Timer<TMilli> as Timer2; uses interface Leds; uses interface Boot; }

BlinkC Timer0 Timer1 Timer2 Leds Boot

BlinkAppC.nc

configuration BlinkAppC { } implementation { components MainC, BlinkC, LedsC; components new TimerMilliC() as Timer0; components new TimerMilliC() as Timer1; components new TimerMilliC() as Timer2; BlinkC -> MainC.Boot; BlinkC.Timer0 -> Timer0; BlinkC.Timer1 -> Timer1; BlinkC.Timer2 -> Timer2; BlinkC.Leds -> LedsC; }

TimerMilliC TimerMilliC TimerMilliC MainC LedsC uses provides

  • > wiring

from user to provider

long version: BlinkC.Boot -> MainC.Boot; BlinkC.Timer0 -> Timer0.Timer; BlinkC.Timer1 -> Timer1.Timer; BlinkC.Timer2 -> Timer2.Timer; BlinkC.Leds -> LedsC.Leds;

slide-18
SLIDE 18

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; }

slide-19
SLIDE 19

Booting

tos/system/RealMainC

module RealMainP @safe() { provides interface Boot; uses interface Scheduler; uses interface Init as PlatformInit; uses interface Init as SoftwareInit; } implementation { int main() @C() @spontaneous() { atomic { 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() { } }

slide-20
SLIDE 20

TinyOS Scheduling Policies

  • Non-Preemptive FIFO
  • Small
  • Easy
  • Fast
  • Thread Based Priority Queues using Preemptive Jobs
  • Bigger
  • Not that easy
  • „Slow“
slide-21
SLIDE 21

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
slide-22
SLIDE 22

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

slide-23
SLIDE 23

Another Push Task Example

module BlinkTaskC { … } implementation { task void toggle() { call Leds.led0Toggle(); } event void Boot.booted(){ call Timer0.startPeriodic( 1000 ); } event void Timer0.fired(){ post toggle(); } }

slide-24
SLIDE 24

What Really Happens

command void Scheduler.init() { atomic { memset( (void *)m_next, NO_TASK, sizeof(m_next) ); m_head = NO_TASK; m_tail = NO_TASK; } } bool pushTask( uint8_t id ) { if( !isWaiting(id) ) { if( m_head == NO_TASK ) { m_head = id; m_tail = id; } else { m_next[m_tail] = id; m_tail = id; } return TRUE; } else { return FALSE; } } async command error_t TaskBasic.postTask[uint8_t id]() { atomic { return pushTask(id) ? SUCCESS : EBUSY; } } command void Scheduler.taskLoop() { for (;;) { uint8_t nextTask; atomic { while ((nextTask = popTask()) == NO_TASK) { call McuSleep.sleep(); } } signal TaskBasic.runTask[nextTask](); } }

slide-25
SLIDE 25

Example of Splitting Computation

task void computeTask() { uint32_t i; for (i = 0; i < 400001; i++) { … } } task void computeTask() { static uint32_t i; uint32_t start = i; for (;i < start + 10000 && i < 400001; i++) { … } if (i >= 400000) { i = 0; } else { post computeTask(); } }

slide-26
SLIDE 26

Non-Preemptive FIFO Scheduler

  • Every task is posted into the task queue
  • Queue is processed in FIFO ordering
  • Every task can only be posted once
  • Every task consumes only 1 byte in the task queue
  • Limited to 255 tasks
  • Task queue length is evaluated at compile time [ unique() ]
  • Every task runs „to-completion“
  • Dispatch long operations in multiple seperate tasks
  • What about context switches?
slide-27
SLIDE 27

Thread Model

  • ${TINYOSBASE}/tos/lib/tosthreads holds nearly the

complete tinyos structure again …

  • … but now with thread support
  • 2 Priorities:
  • High Priority: Tasks
  • Low Priority: Threads
  • Switching between done by timer interrupt every 5ms
slide-28
SLIDE 28

Thread Implementation

  • Platform independent part:
  • Thread queue (implemented as linked list)
  • Threads
  • Thread context (GPR, SP, SREG)

struct thread { //***** next_thread must be at first position in struct for casting purposes ******* volatile struct thread* next_thread; //Pointer to next thread for use in queues when blocked thread_id_t id; //id of this thread for use by the thread scheduler init_block_t* init_block;

//Pointer to an initialization block from which this thread was spawned

stack_ptr_t stack_ptr;

//Pointer to this threads stack

volatile uint8_t state;

//Current state the thread is in

volatile uint8_t mutex_count;

//A reference count of the number of mutexes held by this thread

void (*start_ptr)(void*);

//Pointer to the start function of this thread

void* start_arg_ptr;

//Pointer to the argument passed as a parameter to the start function of this thread

syscall_t* syscall;

//Pointer to an instance of a system call

thread_regs_t regs;

//Contents of the GPRs stored when doing a context switch

};

slide-29
SLIDE 29

Thread Implementation (2)

Platform dependent part (the „real“ context switching)

//Save stack pointer #define SAVE_STACK_PTR(t) \ __asm__("in %A0, __SP_L__\n\t„ \ "in %B0, __SP_H__\n\t" \ :"=r"((t)->stack_ptr) : ); //Save status register #define SAVE_STATUS(t) \ __asm__("in %0,__SREG__ \n\t" : "=r" ((t)->regs.status) : ); #define SAVE_TCB(t) \ SAVE_GPR(t); \ SAVE_STATUS(t); \ SAVE_STACK_PTR(t) #define RESTORE_TCB(t) \ RESTORE_STACK_PTR(t); \ RESTORE_STATUS(t); \ RESTORE_GPR(t) #define SWITCH_CONTEXTS(from, to) \ SAVE_TCB(from); \ RESTORE_TCB(to) //Save General Purpose Registers #define SAVE_GPR(t) \ __asm__("mov %0,r0 \n\t" : "=r" ((t)->regs.r0) : ); \ __asm__("mov %0,r1 \n\t" : "=r" ((t)->regs.r1) : ); \ __asm__("mov %0,r2 \n\t" : "=r" ((t)->regs.r2) : ); \ __asm__("mov %0,r3 \n\t" : "=r" ((t)->regs.r3) : ); \ __asm__("mov %0,r4 \n\t" : "=r" ((t)->regs.r4) : ); \ __asm__("mov %0,r5 \n\t" : "=r" ((t)->regs.r5) : ); \ __asm__("mov %0,r6 \n\t" : "=r" ((t)->regs.r6) : ); \ __asm__("mov %0,r7 \n\t" : "=r" ((t)->regs.r7) : ); \ __asm__("mov %0,r8 \n\t" : "=r" ((t)->regs.r8) : ); \ __asm__("mov %0,r9 \n\t" : "=r" ((t)->regs.r9) : ); \ __asm__("mov %0,r10 \n\t" : "=r" ((t)->regs.r10) : ); \ __asm__("mov %0,r11 \n\t" : "=r" ((t)->regs.r11) : ); \ __asm__("mov %0,r12 \n\t" : "=r" ((t)->regs.r12) : ); \ __asm__("mov %0,r13 \n\t" : "=r" ((t)->regs.r13) : ); \ __asm__("mov %0,r14 \n\t" : "=r" ((t)->regs.r14) : ); \ __asm__("mov %0,r15 \n\t" : "=r" ((t)->regs.r15) : ); \ __asm__("mov %0,r16 \n\t" : "=r" ((t)->regs.r16) : ); \ __asm__("mov %0,r17 \n\t" : "=r" ((t)->regs.r17) : ); \ __asm__("mov %0,r18 \n\t" : "=r" ((t)->regs.r18) : ); \ __asm__("mov %0,r19 \n\t" : "=r" ((t)->regs.r19) : ); \ __asm__("mov %0,r20 \n\t" : "=r" ((t)->regs.r20) : ); \ __asm__("mov %0,r21 \n\t" : "=r" ((t)->regs.r21) : ); \ __asm__("mov %0,r22 \n\t" : "=r" ((t)->regs.r22) : ); \ __asm__("mov %0,r23 \n\t" : "=r" ((t)->regs.r23) : ); \ __asm__("mov %0,r24 \n\t" : "=r" ((t)->regs.r24) : ); \ __asm__("mov %0,r25 \n\t" : "=r" ((t)->regs.r25) : ); \ __asm__("mov %0,r26 \n\t" : "=r" ((t)->regs.r26) : ); \ __asm__("mov %0,r27 \n\t" : "=r" ((t)->regs.r27) : ); \ __asm__("mov %0,r28 \n\t" : "=r" ((t)->regs.r28) : ); \ __asm__("mov %0,r29 \n\t" : "=r" ((t)->regs.r29) : ); \

slide-30
SLIDE 30

wireless communication IEEE 802.15.4 (ZigBee)

“Wireless Medium Access Control (MAC) and Physical Layer (PHY) Specifications for Low-Rate Wireless Personal Area Networks (WPANs)” Data rates 250, 100, 40 and 20 kb/s Communication graph star (PAN coordianator) p2p (also PAN coordinator, arb. comm.) Device types FFD = fully functional device RFD = reduced functional device (can talk to FFD only)

slide-31
SLIDE 31

wireless communication IEEE 802.15.4 (ZigBee)

PHY and MAC layer specified PHY: In unlicensed 2.4 GHz band and others modulation depends, in 2.4 GHz band: O-QPSK MAC: Provides services to application:

  • beacon management
  • channel access (CSMA-CA)
  • ACKed messages
  • CRC
slide-32
SLIDE 32

wireless communication (optional) superframe structure

Frame beacons sent by PAN coordinator

from [4]

slide-33
SLIDE 33

wireless communication Non beacon mode

CSMA-CA random backoff time

from [4]

slide-34
SLIDE 34

wireless communication

PHY & MAC packet

from [5]

slide-35
SLIDE 35

wireless communication

IEEE 802.15.4 (ZigBee) In RZ200: Atmel AT86RF230

from [4]

slide-36
SLIDE 36

wireless communication

The controller issuing commands

from [5]

slide-37
SLIDE 37

wireless communication

The controller accessed in tos/chips/rf230/RF230LayerP.nc

from [5]

slide-38
SLIDE 38

wireless communication

Typical Communication Scenario

slide-39
SLIDE 39

wireless communication

Typical Communication Scenario (with ACKs enabled) Retransmission wait for timeout, retransmit if no ACK received, and not max count exceeded.

slide-40
SLIDE 40

wireless communication

In TinyOS uses only the basic features of RF230

  • no ACKed messages
  • no beacon/slotted transfer

ACKs are implemented in software, e.g. in

tos/chips/rf230/SoftwareAckLayerP

slide-41
SLIDE 41

wireless communication

In TinyOS

initially

call ActiveMessageC.start();

sending a message

message_t packet; if (locked) { return; } else { msg_t* msg; msg = (msg_t*)call Packet.getPayload(&packet, sizeof(msg_t)); if (msg == NULL) { return; } msg->data = 42; if (call AMSend.send(AM_BROADCAST_ADDR, &packet, sizeof(msg_t)) == SUCCESS) { locked = TRUE; } }

slide-42
SLIDE 42

TinyOS and Ethernet/WLAN

bigAVR6 Platform Only (Networking already stable) See UDP Example () Uses TOS_NODE_ID as part of MAC address and IP address make bigAVR6_1280 install,<id> (no space between , and the id!) Alternative Tasks possible (maybe?) Implementation of am_message interface on top of ip/udp/… Zigbee Transceiver available (driver missing – lib compatible to rz200?)

slide-43
SLIDE 43

Resources

[1] NesC reference manual nescc.sourceforge.net/papers/nesc-ref.pdf [2] TinyOS (lots of tutorials!) www.tinyos.net/ [3] TinyOS programming manual www.tinyos.net/tinyos-2.x/doc/pdf/tinyos-programming.pdf [4] IEEE 802.15.4-2006 standard [5] Atmel AT86RF230 manual