1
Tomás Sánchez López
July 9, 2004
Real-time & Embedded Systems Laboratory
[email protected]://tomassanchez.doesntexist.com
TinyOS … in deep
2
Outline
Overview Design Implementatio
n Overview Example Tasks Main Scheduler
Conclusions
Network Stack Sources
Compilation
3
Overview
TinyOs has been being developed by the university of Berkeley for more than 3 years
As other projects, they develop its own hardware platform Use an event model and state machine Use their own C language extension called Nesc
Need to learn, not only the design but also how to implement it
Its module based Components that interact and create a graph with its
relations Layered based where the hardware details are hidden
through several levels of components Two level of scheduling
Tasks that are scheduled through a FIFO queue Events that are caused by hardware interrupts
4
Design
Architecture of TinyOS consist of interacting components and a task scheduler
3 entities exist that allow components to execute code and communicate among them:
Commands and Command Handlers Events and Event Handlers Tasks
Also a fixed-size and statically allocated frame exist per each component, representing its internal state and being operated by the 3 previous entities
Components declare commands used and events signalled, creating a layers of components, where upper layers issue commands to lower layers and handle events coming from them
5
Design In TinyOS is talked then about bidirectional
interface, referring communication between components through events and commands
6
Design
Perform the primary computation work Atomic with respect to other tasks, and run to
completion, but can be preempted by events Allow the OS to allocate a single stack assigned
to the currently executing task Call lower level commands Signal higher level events Schedule other tasks within a component Simulate concurrency using events Are queued in FIFO task scheduler
TASKS
7
Design
COMMANDS
Non-blocking requests to lower level components
Deposit request parameters into a component’s frame, and post a task for later execution
Can also invoke lower level commands, but cannot block
Return status to the caller
8
Design
EVENTS
Event handlers deal with hardware events (interrupts) directly or by lower layer components
Deposit information into a frame and post tasks to the scheduler (similar to commands)
Signal higher level events Call lower level commands
9
Implementation - Overview
Proprietary language based on C language called NesC
Provides macros which will be translated by the NesC compiler into native C code and compiled later by the gcc compiler
Two types of components in NesC: Modules: Application code, implementing one or more
interfaces Configurations: Wires components and, on a top level,
assembles applications to produce and executable All files have *.nc extension with a naming convention
File layout distinguish modules and configurations Old versions were not like this
10
Implementation - ExampleIn Practice: Blink Application Simple application that
causes the red LED on the mote to turn on and off at 1Hz
Two components. A module called BlinkM.nc and a configuration called Blink.nc, plus:
Timer components Led access components
Main is the first component executed in any TinyOS application and must be indicated in its configuration file
Hardware
11
Implementation - ExampleIn Practice: Blink Application
configuration Blink {}implementation { components Main, BlinkM, SingleTimer, LedsC;
Main.StdControl -> BlinkM.StdControl; Main.StdControl -> SingleTimer.StdControl; BlinkM.Timer -> SingleTimer.Timer; BlinkM.Leds -> LedsC;}
Configuration: Blink.nc
uses provides
12
Implementation - ExampleIn Practice: Blink Application Module: BlinkM.nc
module BlinkM { provides { interface StdControl; }
uses { interface Timer; interface Leds; }}
implementation { command result_t StdControl.init() { call Leds.init(); return SUCCESS; }
command result_t StdControl.start() { return call Timer.start(TIMER_REPEAT, 1000); }
command result_t StdControl.stop() { return call Timer.stop(); }
event result_t Timer.fired() { call Leds.redToggle(); return SUCCESS; } } Main.StdControl ->
BlinkM.StdControl;
BlinkM.Timer -> SingleTimer.Timer;BlinkM.Leds -> LedsC;
configuration Main {
uses interface StdControl; }
….
interface StdControl { command result_t init(); command result_t start(); command result_t stop(); }
13
Implementation - ExampleIn Practice: Blink Application Module: BlinkM.nc
module BlinkM { provides { interface StdControl; }
uses { interface Timer; interface Leds; }}
implementation { command result_t StdControl.init() { call Leds.init(); return SUCCESS; }
command result_t StdControl.start() { return call Timer.start(TIMER_REPEAT, 1000); }
command result_t StdControl.stop() { return call Timer.stop(); }
event result_t Timer.fired() { call Leds.redToggle(); return SUCCESS; } } Main.StdControl ->
BlinkM.StdControl;
BlinkM.Timer -> SingleTimer.Timer;BlinkM.Leds -> LedsC;
async command result_t Leds.init() { atomic { ledsOn = 0; TOSH_SET_RED_LED_PIN(); TOSH_SET_YELLOW_LED_PIN(); TOSH_SET_GREEN_LED_PIN(); } return SUCCESS; } async command result_t Leds.redToggle() { result_t rval;atomic { if (ledsOn & RED_BIT) rval = call Leds.redOff(); else rval = call Leds.redOn(); }return rval; }
14
Implementation - ExampleIn Practice: Blink Application Module: BlinkM.nc
module BlinkM { provides { interface StdControl; }
uses { interface Timer; interface Leds; }}
implementation { command result_t StdControl.init() { call Leds.init(); return SUCCESS; }
command result_t StdControl.start() { return call Timer.start(TIMER_REPEAT, 1000); }
command result_t StdControl.stop() { return call Timer.stop(); }
event result_t Timer.fired() { call Leds.redToggle(); return SUCCESS; } } Main.StdControl ->
BlinkM.StdControl;
BlinkM.Timer -> SingleTimer.Timer;BlinkM.Leds -> LedsC;
async command result_t Leds.init() { atomic { ledsOn = 0; TOSH_SET_RED_LED_PIN(); TOSH_SET_YELLOW_LED_PIN(); TOSH_SET_GREEN_LED_PIN(); } return SUCCESS; } async command result_t Leds.redToggle() { result_t rval;atomic { if (ledsOn & RED_BIT) rval = call Leds.redOff(); else rval = call Leds.redOn(); }return rval; }
15
Implementation - ExampleIn Practice: Blink Application Module: BlinkM.nc
For a component to call the commands of an interface, it must implement the events signaled by itAn event can call commands
The async reserved word is used by commands and event that are executed as part of a hardware event handler. The code section within the atomic curly braces will execute without preemption
16
Implementation - ExampleIn Practice: Blink Application
17
Implementation - Tasks
What about the tasks? They are declared and implemented using the syntax:
task void taskname() { ... } Can be posted for execution within a command, event
or another task: post taskname();
Tasks posted go to the FIFO queue
Example: SenseTask application Gather a serial of sensor reading and full a cyclic
buffer Post a task to process the sensor readings Show the readings in the leds
18
Implementation - Tasks Example: SenseTask application
The Main module call StdControl, which goes down in the hierarchy and inits (among other things) the Timer module
The timer module fires a signal each 500ms (as requested by our application calling a Timer module command), which event handler is implemented by the application.
The handler calls a command of the ADC to get the sensor reading, which in turn signals an event when the data is ready
Te application handles this event and fills the buffer and posts the task
The task analyses the buffer and calls commands that will light the desired leds
Note that the buffer, used by the task and the putting data function, is always protected from data races with the atomic keyword
19
Implementation - Main
How does everything really start?module RealMain { uses { command result_t hardwareInit(); interface StdControl; interface Pot; } } implementation { int main() __attribute__ ((C, spontaneous)) { call hardwareInit(); call Pot.init(10); TOSH_sched_init();
call StdControl.init(); call StdControl.start(); __nesc_enable_interrupt();
while(1) { TOSH_run_task(); } } }
The component RealMain.nc, which starts the hardware, the StdControl chain, the Scheduler and enables the interrupts, is converted into the main(void) function by the NesC compiler.
configuration Main { uses interface StdControl; } implementation { components RealMain, PotC, HPLInit; StdControl = RealMain.StdControl; RealMain.hardwareInit -> HPLInit; RealMain.Pot -> PotC; }
The Main.nc is just a configuration for quick changes in interface wiring
20
Implementation - Scheduler
Tasks are posted to a FIFO queue (which actually a cyclic array buffer)
The entries in the queue are pointers to the task function, and a maximum of 8 positions (7 tasks) is defined
Scheduling next task just checks there is a task to pick, removes it from the head and executes it
Post a task puts the task pointer into the next free slot
The scheduler runs as long as there are tasks to pick or, otherwise, sends the system to sleep
The scheduler can be found in sched.c
21
Implementation - Network
Same procedure as the rest of the modules is used for the network stack.
AMStandard.nc implements the AM (radio and serial) functionality: Initializations of radio,
UART and timers Management of
received packets: Defines event handlers for lower layer events reporting a received message
Accepts messages and forwards them to lower layer components
Handles the signals of “message sent” to upper layer.
22
Implementation - Network
Radio stack example to the lowest layer
• Control power of the radio
• Set radio modes (Rx and Tx)
• Control the gathering of bytes
• Set up back off random times if channel busy
23
Implementation - Network
AM.h contains the data structure regarding to the network packets
typedef struct TOS_Msg{ uint16_t addr; uint8_t type; uint8_t group; uint8_t length; int8_t data[TOSH_DATA_LENGTH]; uint16_t crc; uint16_t strength; uint8_t ack; uint16_t time; uint8_t sendSecurityMode; uint8_t receiveSecurityMode; } TOS_Msg;
2 bytes1 byte1 byte1 byte29 bytes2 bytes
36 bytes
Not acually sent. Required for internal accounting and by the AM interface
Other packet structures for security are included (TinySec)
24
Implementation - Stack
Due to that tasks are run to completion, it is possible to allocate a single stack that is assigned to the currently executing task
The stack in not allocated of fixed size at compile time, but it grows over the physical memory Should it grow too much, we could have
problems of overriding frame variables Normally is not a problem, but is
unpredictable So what happens when a task is preempted by
an event? It stores its return information in the stack
25
Implementation - Stack
And what happens if the event handler is preempted by another event? As non two entities can run at the same
time, we can still use the same stack for nested interrupts
However, nested callings to commands or events are often reorganized by the nesC compiler so they will disappear in actual assembler.
26
Implementation - Sources
Ncc (extension of gcc) + gdb + java This Unix tool support is ported to Windows by using
the Cygwin Linux emulation layer and applications
TinyOS root
apps contribdoc tos tools
…
nesdoc
…
micamica2
…
Blink
…
micamica2
build
interfaces
libs
platforms
sensorboardssystem
types
micamica2…
27
Implementation - Sources
Apps One directory for each application with the source One build directory with subdirectories for different platform
compilations Doc
All applications can be doc-compiled for an specific supported platform
The documentation system includes a complete source of components, interfaces and application component graphs
Contrib Other parties contributions such as routing or MAC layer
protocols Tools
Different tools such as matlab or java utilities
28
Implementation - Sources
Tos Contains the sources of TinyOS Interfaces: Defines interfaces that might be used when
building and wiring applications Libs: Contains components that can be used to offer certain
additional functionalities, such as security (TinySec) or SQL-like queries (TinyDB)
Platform: Contains platform specific components, which will be used depending on compiling options
Sensorboards: The same but with sensor boards and not motes
System: TinyOS core with the main components and configurations
Types: Contain data structures and related and related functions, such as the AM message frame.
29
Implementation - Compilation
Compilation is automated though Makefiles which will include flags and platform specific paths for compiling the given application
A Makerules file is included in the root apps directory
A local Makefile should be included in application’s root directory which will include the Makerules file and override its parameters if necessary
An *.exe is provided for the specified platform. Another make command will transform the binary and upload it to the mote if connected to the PC.
30
Implementation - Compilation
NesC language, first, transforms the code of all the components into ANSI C and merges it into a single app.c file on the build directory of each application
Events and commands are transformed into regular C functions
Due to that the same command or event call may exist with the same name on several components, nesC overloads the naming to specify the component.
component Example:event result_t Event.handler() { int foo = 2; call Command.go(foo); return SUCCESS;}
result_t Example$Event$handler() { int foo = 2; Example$Command$go(foo); return SUCCESS;}
31
Conclusions
TinyOS follows an event model that forces an specific structure of its source code The choice is intended for low memory usage
while allowing acceptable concurrency Obliges a language extension Obscures the programming and
understanding , mainly of the operating system
Its advantageous in modularity Achieves its performance goals, but makes the
development difficult