182.694 Microcontroller VU Martin Perner SS 2017 Featuring Today: TinyOS Part 2

Weekly Training Objective

Already done 3.4.1 Input capture 3.6.3 SPI ∗ 3.7.2 Watchdog ∗ This week 4.2.1 UART to GLCD † 4.2.2 Keypad 4.2.3 Debounced Buttons ∗

Martin Perner

TinyOS Part 2

May 22, 2017

2

TinyOS – Recap

TinyOS is a small operating system that is designed to run on low-power wireless sensor nodes, and networked embedded systems. provides a set of important services and abstractions. defines a concurrent execution model. is written in nesC, which is a C dialect. can be seen as a mixture of OOP and hardware wiring.

Martin Perner

TinyOS Part 2

May 22, 2017

3

TinyOS first steps Flash an example application in apps/Blink/ execute $ make bigAVR6 1280 with connected board execute $ make bigAVR6 1280 install A binary is generated and downloaded.

Hint $ make bigAVR6 1280 install,id Sets TOS NODE ID to the value of id. This can be used, e.g., for defining the IP address of the node, or allow for different functionality (master/slave). Martin Perner

TinyOS Part 2

May 22, 2017

4

Interfaces

Enabling reusability Multiple interfaces can be used/provided. Interfaces are bidirectional. command, implemented by provider of the interface, called by user. event, implemented by user of the interface, signaled by provider.

Bidirectionality is the basis for split-phase.

Martin Perner

TinyOS Part 2

May 22, 2017

5

Example Interface

A generic timer interface – Timer.nc i n t e r f a c e Timer { command v o i d s t a r t P e r i o d i c ( u i n t 3 2 t d t ) ; command v o i d s t a r t O n e S h o t ( u i n t 3 2 t d t ) ; command v o i d s t o p ( ) ; event void f i r e d ( ) ; ... }

Interface, and thus timer-usage, independent of microcontroller used.

Martin Perner

TinyOS Part 2

May 22, 2017

6

Configurations

Wiring TinyOS only allows to wire interfaces. This wiring is done in configurations. Connecting interfaces of components via -> resp. DemoP . Magic ; }

Martin Perner

TinyOS Part 2

May 22, 2017

8

Example Configuration Boot

Configuration DemoC c o n f i g u r a t i o n DemoC { u s e s i n t e r f a c e Boot ; p r o v i d e s i n t e r f a c e MoreMagic ; }

MoreMagic

implementation { components DemoP , MakeMagicC ; Boot = DemoP . Boot ; MoreMagic = MakeMagicC . MoreMagic ;

Boot

Magic

DemoP

MakeMagicC

Magic

MoreMagic

MakeMagicC . Magic −> DemoP . Magic ; }

Martin Perner

TinyOS Part 2

May 22, 2017

8

Example Configuration Boot

Configuration DemoC c o n f i g u r a t i o n DemoC { u s e s i n t e r f a c e Boot ; p r o v i d e s i n t e r f a c e MoreMagic ; }

Magic

implementation { components DemoP , MakeMagicC ; Boot = DemoP . Boot ; MoreMagic = MakeMagicC . MoreMagic ;

DemoP

MakeMagicC

Magic

MoreMagic

MakeMagicC . Magic −> DemoP . Magic ; }

Martin Perner

TinyOS Part 2

May 22, 2017

8

Example Configuration

Boot

Configuration Boot

c o n f i g u r a t i o n DemoC { u s e s i n t e r f a c e Boot ; p r o v i d e s i n t e r f a c e MoreMagic ; }

DemoC

implementation { components DemoP , MakeMagicC ;

MakeMagicC

Boot = DemoP . Boot ; MoreMagic = MakeMagicC . MoreMagic ;

MoreMagic

MakeMagicC . Magic −> DemoP . Magic ;

MoreMagic

}

Martin Perner

DemoP

TinyOS Part 2

May 22, 2017

8

Example Module

Modules – Where code is placed module DemoP { u s e s i n t e r f a c e Boot ; p r o v i d e s i n t e r f a c e Magic ; }

Boot DemoP

implementation { e v e n t v o i d Boot . b o o t e d ( ) { ... }

Magic

command i n t Magic . g e t ( ) { ... } }

Martin Perner

TinyOS Part 2

May 22, 2017

9

Function Examples Required by Interface Command function: ( a s y n c ) command u i n t 8 t f ( u i n t 8 t x ) ; y = call f (x) ;

Event function: ( async ) event v o i d am ready ( u i n t 8 t x ) ; s i g n a l am ready ( x ) ;

To be used inside a module Task: task void f () ; post f () ;

Plain function: uint8 t f ( uint8 t x) ; y = f (x) ;

Martin Perner

TinyOS Part 2

May 22, 2017

10

Split-Phase

Classic Approach call f (x)

return; y = f (x)

f (x) f (x) access some hardware, and has to wait for a signal by the hardware. After calling f (x) the exeuction blocks until completion (busy waiting . . . )

Martin Perner

TinyOS Part 2

May 22, 2017

11

Split-Phase

The Split-Phase Approach call f (x)

start f (x)

signals f done(y)

return

y = f (x) is done

After calling f (x), the execution is started in the background. The caller receives a callback (event) upon completion of f (x).

Martin Perner

TinyOS Part 2

May 22, 2017

12

Deferred Task Execution Function call call f (x)

f (x)

cont. f (x) signal/call g(z) g(z) etc.

Assume that the return value and the side-effects of g(·) are not required by f (·). Why should we wait? Martin Perner

TinyOS Part 2

May 22, 2017

13

Deferred Task Execution Post a Task call f (x)

f (x) post g() g()

Task have no parameters. Parameter passing needs to be done with state-variables in the module. A task can only be posted once onto the tasklist. Martin Perner

TinyOS Part 2

May 22, 2017

14

Booting

How is TinyOS booted? We have seen the interface Boot of MainC being used for initialization. It provides an event booted, which is called after start-up of the system.

Martin Perner

TinyOS Part 2

May 22, 2017

15

Booting tos/system/MainC.nc #i n c l u d e ” h a r d w a r e . h” c o n f i g u r a t i o n MainC { p r o v i d e s i n t e r f a c e Boot ; uses i n t e r f a c e I n i t as S o f t w a r e I n i t ; } implementation { components P l a t f o r m C , RealMainP , T i n y S c h e d u l e r C ; RealMainP . S c h e d u l e r −> T i n y S c h e d u l e r C ; RealMainP . P l a t f o r m I n i t −> P l a t f o r m C ; // E x p o r t t h e S o f t w a r e I n i t and Booted f o r a p p l i c a t i o n s S o f t w a r e I n i t = RealMainP . S o f t w a r e I n i t ; Boot = RealMainP ; }

Martin Perner

TinyOS Part 2

May 22, 2017

16

Booting tos/system/RealMainP.nc 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 (){} }

Martin Perner

TinyOS Part 2

May 22, 2017

17

How many instances of a Component are there? Do we care? If we need some boot-up initialization, we use MainC and the Boot interface Do we get a new component every time we use it? No, components are “singletons” (there is only one) What can we do if we want two instances of a stateful component? (e.g., Queue)

Generic Components Add generic in front of signature of configuration/module Instantiate with new keyword We can pass parameters, and even types, at instantiation.

Martin Perner

TinyOS Part 2

May 22, 2017

18

Generic Components – Examples QueueC.nc g e n e r i c module QueueC ( t y p e d e f q u e u e t , u i n t 8 t q u e u e S i z e ) { p r o v i d e s i n t e r f a c e Queue; } ...

SomeThing g e n e r i c c o n f i g u r a t i o n SomeThing ( ) { ... } implementation { components new QueueC ( u i n t 8 t , 5 ) a s Queue ; ... }

Martin Perner

TinyOS Part 2

May 22, 2017

19

Generic Component

Limitation Contrary to, e.g., C++, the type checking happens on declaration At this point, the used type is unknown Does x + 1 compile for very type used for x? The attribute @integer() can be applied to the declaration for some type assumptions.

WeirdC g e n e r i c module WeirdC ( t y p e d e f f o o t @ i n t e g e r ( ) ) { ... }

Martin Perner

TinyOS Part 2

May 22, 2017

20

Abstract Data Type (ADT)

Does a type argument to a component helps? How can we use it in an interface? Use a typed interface!

QueueC.nc i n t e r f a c e Queue { ... command t head ( ) ; ... }

Martin Perner

TinyOS Part 2

May 22, 2017

21

Recap – Fan-In and Fan-Out

What happens if an interface is connected to multiple component If there are multiple modules connected to the same interface, then All connected providers of a command receive the call. All connected users of an event are signaled. The order is not defined! Generally, there is no possibility to determine whose signal caused an event.

The last item can be circumvented by using parameterized interfaces.

Martin Perner

TinyOS Part 2

May 22, 2017

22

Parameterized Interfaces

Adding caller/callee information to an interface Dirty hack: Additional parameter in command/event plus generic component to pass the parameter. Better: use parameterized interfaces Adds “implicit” parameter to function call. Interface definition does not need to be changed use/provide clauses are extended. Parameters are assigned in configuration.

Martin Perner

TinyOS Part 2

May 22, 2017

23

Parameterized Interface

Default Cases! Implementationwise, parameterized interfaces are more or less a switch. There is no compile-time check if every called parameter is wired (could be data dependent!) The caller/callee must provide default implementations for parameterized calls.

Martin Perner

TinyOS Part 2

May 22, 2017

24

Example Parameterized Interface 1

Demo Application Count towards a generic value Increment after a timer has fired Output to a port interface

Martin Perner

TinyOS Part 2

May 22, 2017

25

Example Parameterized Interface 1 CounterC #i n c l u d e ” Timer . h” g e n e r i c module CounterC ( u i n t 8 t t o p ) { u s e s i n t e r f a c e Boot ; u s e s i n t e r f a c e Timer a s Timer ; uses i n t e r f a c e Port as CounterPort ; } implementation { uint8 t counter ; task void increment () { c o u n t e r ++; i f ( counter > top ) { counter = 0; } c a l l CounterPort . set ( counter ) ; } e v e n t v o i d Boot . b o o t e d ( ) { counter = 0; c a l l C o u n t e r P o r t . makeOutput ( ) ; c a l l Timer . s t a r t P e r i o d i c ( 5 0 0 ) ; } e v e n t v o i d Timer . f i r e d ( ) { post increment ( ) ; } } Martin Perner

TinyOS Part 2

May 22, 2017

26

Example Parameterized Interface 1 – Port

Port i n t e r f a c e Port { command v o i d makeOutput ( ) ; command v o i d s e t ( u i n t 8 t ) ; }

Martin Perner

TinyOS Part 2

May 22, 2017

27

Example Parameterized Interface 1 – CounterAppC

CounterAppC c o n f i g u r a t i o n CounterAppC { } implementation { components MainC ; components new CounterC ( 6 ) a s C o u n t e r ; components new RawPortC ( ( u i n t 8 t ) &PORTA, ( u i n t 8 t ) &DDRA) a s PortA ; components new RawPortC ( ( u i n t 8 t ) &PORTB, ( u i n t 8 t ) &DDRB) a s PortB ; components new T i m e r M i l l i C ( ) a s Timer ; C o u n t e r . Boot −> MainC . Boot ; C o u n t e r . Timer −> Timer ; C o u n t e r . C o u n t e r P o r t −> PortA . P o r t [ 3 ] ; C o u n t e r . C o u n t e r P o r t −> PortB . P o r t [ 4 ] ; }

Martin Perner

TinyOS Part 2

May 22, 2017

28

Example Parameterized Interface 1 – RawPortC RawPortC g e n e r i c module RawPortC ( u i n t 8 t p o r t a d d r , u i n t 8 t d d r a d d r ) { p r o v i d e s i n t e r f a c e Port [ u i n t 8 t i d ] ; } implementation { #d e f i n e PORT (∗TCAST( v o l a t i l e u i n t 8 t ∗ ONE, p o r t a d d r ) ) #d e f i n e DDR (∗TCAST( v o l a t i l e u i n t 8 t ∗ ONE, d d r a d d r ) ) i n l i n e command v o i d P o r t . makeOutput [ u i n t 8 t i d ] ( ) { DDR = 0 x f f ; } i n l i n e command v o i d P o r t . s e t [ u i n t 8 t i d ] ( u i n t 8 t v a l u e ) { i f ( i d != v a l u e ) { PORT = v a l u e ; } else { PORT = 0 x f f ; } } }

Martin Perner

TinyOS Part 2

May 22, 2017

29

Example Parameterized Interface 1 – Discussion

What have we achieved? Caller still unaware of the callees, as before Callees got a parameter passed. Not very impressive. Generic Components are better fitted for this scenario!

Martin Perner

TinyOS Part 2

May 22, 2017

30

Example Parameterized Interface 2

Weirder Demo Application Count towards a generic value Set current value to one port, on overflow set all ports to 0xff

selected.h #i f n d e f #d e f i n e

SELECTED H SELECTED H

#d e f i n e UNIQUE PORT ” u n i q u e p o r t ” #e n d i f

Martin Perner

TinyOS Part 2

May 22, 2017

31

Example Parameterized Interface 2

SelectedAppC #i n c l u d e ” s e l e c t e d . h” c o n f i g u r a t i o n SelectedAppC { } implementation { components MainC ; components new CounterC ( 6 ) a s C o u n t e r ; components new RawPortC ( ( u i n t 8 t ) &PORTA, ( u i n t 8 t ) &DDRA) a s PortA ; components new RawPortC ( ( u i n t 8 t ) &PORTB, ( u i n t 8 t ) &DDRB) a s PortB ; components new T i m e r M i l l i C ( ) a s Timer ; C o u n t e r . Boot −> MainC . Boot ; C o u n t e r . Timer −> Timer ; C o u n t e r . C o u n t e r P o r t [ u n i q u e (UNIQUE PORT ) ] −> PortA . P o r t ; C o u n t e r . C o u n t e r P o r t [ u n i q u e (UNIQUE PORT ) ] −> PortB . P o r t ; }

Martin Perner

TinyOS Part 2

May 22, 2017

32

Example Parameterized Interface 2 CounterC #i n c l u d e ” Timer . h” #i n c l u d e ” s e l e c t e d . h” generic uses uses uses }

module CounterC ( u i n t 8 t t o p ) { i n t e r f a c e Boot ; i n t e r f a c e Timer a s Timer ; i n t e r f a c e Port as CounterPort [ u i n t 8 t i d ] ;

implementation { u i n t 8 t counter ,

i =0;

task void increment () { c o u n t e r ++; i f ( counter > top ) { f o r ( i =0; i < u n i q u e C o u n t (UNIQUE PORT ) ; c a l l CounterPort . set [ i ](0 x f f ) ; } counter = 0; } c a l l CounterPort . set [ counter ] ( counter ) ; } ...

Martin Perner

i ++) {

TinyOS Part 2

May 22, 2017

33

Example Parameterized Interface 2 CounterC cont. ... e v e n t v o i d Boot . b o o t e d ( ) { counter = 0; f o r ( i =0; i < u n i q u e C o u n t (UNIQUE PORT ) ; c a l l C o u n t e r P o r t . makeOutput [ i ] ( ) ; } c a l l Timer . s t a r t P e r i o d i c ( 5 0 0 ) ; }

i ++) {

e v e n t v o i d Timer . f i r e d ( ) { post increment ( ) ; } d e f a u l t command v o i d C o u n t e r P o r t . makeOutput [ u i n t 8 t i d ] ( ) { DDRF = 0 xFF ; } d e f a u l t command v o i d C o u n t e r P o r t . s e t [ u i n t 8 t i d ] ( u i n t 8 t v a l u e ) { PINF = 0 xFF ; } }

Martin Perner

TinyOS Part 2

May 22, 2017

34

Example Parameterized Interface 2 – Discussion

Interface Port and module RawPortC as before.

What have we achieved? As the top-value of the counter is larger than 2, PORTF is toggled. Caller is aware of the callees. Caller needs to provide default implementation to prevent out-of-bound behavior. Callees unaware of the caller.

Martin Perner

TinyOS Part 2

May 22, 2017

35

Unique Why are we using unique? Numbers to parameterized interface need to be unique. Counting per hand is hard, ugly, and a path to hell . . . If we need to pass the number of attached interfaces, we would need to pass a parameter to the component. Instead we can use uniqueCount. There are cases we non-consecutive numbering for the parameterized interface is used, e.g., port numbers.

Requirements Counting happens based on a unique string (use a define in a header!) unique returns a unique id. Numeric from 0 to n − 1 uniqueCount returns n Martin Perner

TinyOS Part 2

May 22, 2017

36

Parameterized Interface at caller and callee

What needs to be changed? Add parameter to both sides in wiring. Add parameter to interface declaration. Add parameter at interface function declaration and usage. Note: if the same value, generated by unique, is used multiple times: use an enum! ... enum { COUNTER PORT1 = u n i q u e (UNIQUE PORT) }; C o u n t e r . C o u n t e r P o r t [ COUNTER PORT1 ] −> PortA . P o r t [ COUNTER PORT1 ] ; ...

Martin Perner

TinyOS Part 2

May 22, 2017

37

Execution Model

The Execution Model of TinyOS TinyOS has two basic execution modes: synchronous asynchronous

Martin Perner

TinyOS Part 2

May 22, 2017

38

Execution Model Tasks in TinyOS Every synchronous execution in TinyOS is a task. As there is no preemptive scheduler (per-default), a task runs until it is finished. This is problematic for long running computations ⇒ defer computation by posting another task. Can be interrupted by asynchronous event: interrupt.

Interrupts Interrupts are handled in TinyOS as asynchronous executions. As usual, they can happen at any time, given interrupts are enabled. Do the usual synchronization problems also arise?

Martin Perner

TinyOS Part 2

May 22, 2017

39

Execution Model

Mixing Asynchronous/Synchronous Executions TinyOS has checks to prevent obvious mistakes, but the programmer still has to take care. Variables used in asynchronous context must be accessed in atomic sections. Functions that can be called from asynchronous contexts must be declared async. To get from asynchronous to synchronous execution a task has to be posted.

Martin Perner

TinyOS Part 2

May 22, 2017

40

Default Task Scheduling Policy Non-Preemptive FIFO Small, Easy, Fast 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 Every task runs until completion Dispatch long operations in multiple separate tasks What about context switches? Martin Perner

TinyOS Part 2

May 22, 2017

41

Concurrency 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: Synchronous Code: functions, commands, events, tasks that are only reachable from tasks Asynchronous 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

Martin Perner

TinyOS Part 2

May 22, 2017

42

Tasks in TinyOS 2.x How to use a task command v o i d t h i n g y ( ) { ... post processTask ( ) ; ... } ... task void processTask () { // do work ... i f ( moreToProcess ) { post processTask ( ) ; } }

The post will only fail iff the task is already posted in task queue and its execution has not started yet.

Martin Perner

TinyOS Part 2

May 22, 2017

43

Task Example

Blink with Tasks module B l i n k T a s k C { ... } implementation { task void toggle () { c a l l Leds . led0Toggle ( ) ; } e v e n t v o i d Boot . b o o t e d ( ) { c a l l Timer0 . s t a r t P e r i o d i c ( 1 0 0 0 ) ; } e v e n t v o i d Timer0 . f i r e d ( ) { post toggle ( ) ; } }

Martin Perner

TinyOS Part 2

May 22, 2017

44

Splitting Computation via Tasks Break-up long running computations for reduced latency Instead of t a s k v o i d computeTask ( ) { uint32 t i ; f o r ( i =0; i