2. nesC (network embedded system C)
• necC is a component-based C dialect.
• nesC application consists of one or more
components wired.
• Use a purely local namespace.
• There are two types of components:
– modules
• provide the implementation of one or more
interfaces.
– configurations
• used to wire other components together.
3. Contd.
• Components define two scopes.
– Specification (signature)
• names of interfaces it provides (implements) and names of
interfaces it uses (calls).
– Implementation
• implementation of commands and events.
module configuration
module
{
…..//provide and uses
interfaces
…
}
implementation{
…..//executable code
…
}
configuration
{
…..//provide and uses
interfaces
…
}
implementation{
…..//wire components
…
}
4. Modules and State
• Modules:
– are executable codes.
– must implement every command of interfaces it provides and
every event of interfaces it uses.
– can declare state variables. Any state of a components is
private.
– Ex: uint8_t counter = 0;
8 bits 16 bits 32 bits 64 bits
signed int8_t int16_t int32_t int64_t
unsigned uint8_t uint16_t uint32_t uint64_t
5. BilinkC.nc
module BilnkC
{
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 Timer1.startPeriodic(1000);
}
event void Timer0.fired(){
call Leds.led0Toggle();
}
event void Timer1.fired(){
call Leds.led1Toggle();
}
event void Timer2.fired()
{
call Leds.led2Toggle();
}
}
BlinkAppC.nc
configuration BlinkC
{
}
Implementation
{
components MainC, BlinkC, LedsC;
components new TimerMillic() as Timer0;
components new TimerMillic() as Timer0;
components new TimerMillic() as Timer0;
BlinkC ->MainC.Boot;
BlinkC.Timer0 -> Timer0;
BlinkC.Timer1 -> Timer1;
BlinkC.Timer2 -> Timer2;
BlinkC.Leds -> LedsC;
}
nesC uses arrows to bind interfaces to one another.
The right arrow (A->B) as "A wires to B"
6. Interfaces, Commands and Events
• if a component uses an interface, it can call the interface's
commands and must implement handlers for its events.
• Invoking an interface command requires the call keyword, and
invoking an interface event requires the signal keyword.
• Ex
Timer.nc:
interface Timer
{
// basic interface
command void startPeriodic( uint32_t dt );
command void startOneShot( uint32_t dt );
command void stop();
event void fired();
// extended interface omitted (all commands)
}
7. Internal
Functions
• Component's private
function for its own
internal use.
• Similar to C function.
• Can’t invoke directly.
• Can freely call
commands or signal
events.
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 {
void startTimers() {
call Timer0.startPeriodic( 250 );
call Timer1.startPeriodic( 500 );
call Timer2.startPeriodic( 1000 );
}
event void Boot.booted() {
startTimers();
}
.
.
.
}
•Ex:
8. Split-Phase Operations
• Hardware is almost always split-phase (non blocking/asynchronous).
• In split-phase completion of a request is a callback.
– ADC
• synchronous operation:
– Magnetometer.
– samples periodically.
– when queried gives a cached value. (it can return the result immediately)
• make everything synchronous through threads is not possible.
• Solution :
– Operations that are split-phase in hardware are split-phase in software.
– Introduce interfaces that are bidirectional
ADC
S/W
Start Sample
Interrupts
when
complete
Read the
value
10. Split-Phase Operations
(Contd.)
• Split-phase interfaces enable a TinyOS component to easily start several
operations at once and have them execute in parallel.
• split-phase operations can save memory.
• Ex: The command Timer.startOneShot is an example of a split-phase call.
The user of the Timer inteface calls the command, which returns
immediately. Some time later (specified by the argument), the component
providing Timer signals Timer.fired. In a system with blocking calls, a
program might use sleep():
11. Interfaces with Arguments
• Interfaces can take types as arguments.
• wiring providers and users of interfaces that have type
arguments, they types must match.
• used to enforce type checking.
13. Tasks
• Consider magnetometer/ADC example:
– depending on split-phase operations means that the
magnetometer driver has to issue a callback.
– it could just signal the event from within the call.
– signaling an event from within a command is
generally a bad idea.
• might cause a very long call loop.
• corrupt memory and crash the program.
– needs a way to schedule a function to be called later
(like an interrupt).
– can do this is with a task.
14. Tasks (Contd.)
• Task
– A module can post a task to the TinyOS scheduler.
– doesn’t take any parameters.
– A component posts a task to the TinyOS scheduler with the
post keyword: post
– only one task runs at any time. and TinyOS doesn’t interrupt
one task to run another.
– Implies that tasks should usually be reasonably short.
15. Tasks (Contd.)
• Ex:
event void Timer.fired() {
call Read.read();
}
event void RawRead.readDone(error_t err, uint16_t val) {
if (err == SUCCESS) {
lastVal = val;
filterVal *= 9;
filterVal /= 10;
filterVal += lastVal / 10;
}
}
command error_t Read.read() {
post readDoneTask();
return SUCCESS;
}
task void readDoneTask() {
signal Read.readDone(SUCCESS, filterVal);
}
• When Read.read is called, posts readDoneTask and returns immediately.
At some point later, TinyOS runs the task, which signals Read.readDone.
16. Tasks (Contd.)
• Why?
– Synchronous code runs in a single execution context
and does not have any kind of pre-emption.
– sync code runs for a long time.
– A component needs to be able to split a large
computation into smaller parts, which can be
executed one at a time.
• Task
– A task is function which a component tells TinyOS to
run later, rather than now.
17. Concurrency
• Tasks allow software components to emulate the split-
phase behavior of hardware.
• They also provide a mechanism to manage pre-emption
in the system.
• In nesC and TinyOS, functions that can run
preemptively labeled with the async keyword.
• async function can’t call a command or event that isn’t
async.
• By default, commands and events are sync.
• A task post is an async operation, while a task running
is sync.
18. Concurrency (Contd.)
• Can’t make everything
async because of race
condition.
• Ex: consider the
command, toggle, which
flips the state bit and
returns the new one:
• Solutions:
– Keep code synchronous
when you can.
– functionality through
atomic statements.
19. Concurrency (Contd.)
• The atomic block
promises that these
variables can be read
and written atomically
• this does not promise
that the atomic block
won’t be preempted
– with atomic blocks, two
code segments that do not
share any of the same
variables can preempt one
another
20. Concurrency (Contd.)
• An atomic block involves some kind of
execution (e.g.. disabling an interrupt), so
unnecessary atomics are a waste of CPU
cycles.
• an atomic block does have a CPU cost,
so you want to minimize how many you
have.
• shorter atomic blocks delay interrupts less
and so improve system concurrency.
21. Allocation
• the only way that components can share state is
through function calls.
• two basic ways that components can pass
parameters: by value and by reference (pointer).
• every pointer should have a clear owner, and
only the owner can modify the corresponding
memory.
• abstract data types (ADTs) in TinyOS are
usually represented one of two ways:
– Generic modules
– Through an interface with by-reference commands.
22. Allocation (An Example)
• Generic module, Ex
– many TinyOS components needs to maintain bit vectors, and so in tos/system there’s a
generic module BitVectorC that takes the number of bits as a parameter:
– This component allocates the bit vector internally and provides the BitVector
interface to access it:
23. Allocation (Contd.)
– in BitVector, it’s possible that, after a bit has been
fetched for get() but before it returns, an interrupt
fires whose handler calls set() on that same bit.
– you should call get() from within an atomic section.
• passing a parameter by reference,
– this is easy, as all of its commands are synchronous:
no code that can preempt the call (async).