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