nesC: A Programming Language for Motes David Gay, Phil Levis, Eric Brewer, Rob von Behren, Nikita Borisov, Mike Chen, David Culler Intel Research, UC Berkeley
The State of the Motes TinyOS programs hard to write TinyOS programs hard to write No compiler: No compiler: verbose, unwieldy syntax verbose, unwieldy syntax many compile-time checks missing many compile-time checks missing No support for common idioms: No support for common idioms: state-machine-like programs state-machine-like programs split-phase operations split-phase operations atomicity guarantees, etc atomicity guarantees, etc
A Long-term Solution We are working on an improved language We are working on an improved language Component-based Component-based Better support for common idioms Better support for common idioms split-phase ops, state-machines, etc split-phase ops, state-machines, etc Clean atomicity model Clean atomicity model Very much in the design/investigation stage Very much in the design/investigation stage
A Short-term Solution: nesC A cleaned up language for TinyOS A cleaned up language for TinyOS Goals: Goals: Easier to use Easier to use Allow whole-program optimisations Allow whole-program optimisations Base for language experimentation Base for language experimentation Pronounced “nestlé” Pronounced “nestlé”
nesC: Easier to Use Nicer syntax Nicer syntax Check errors Check errors Missing connections Missing connections Type errors in connections Type errors in connections Cycles, incorrect directions Cycles, incorrect directions Interfaces: Interfaces: Group related functionality (caller, callee): Group related functionality (caller, callee): sendMsg: sendMsg: users: call send(m), implement sendDone(m) users: call send(m), implement sendDone(m) implementer: implement send(m), call sendDone(m) implementer: implement send(m), call sendDone(m) Concise wiring Concise wiring
The Old Model Components Components.comp: specification.comp: specification.C: behaviour.C: behaviour.desc: select and wire.desc: select and wire specification: specification: accepts commands accepts commands uses commands uses commands signals events signals events handles events handles events comp1: C code comp3 comp4 comp2:.desc application:.desc
The Old Model, continued 4 kinds of components: 4 kinds of components:.desc only (ex: cnt_to_leds):.desc only (ex: cnt_to_leds): top-level components and wiring for an application top-level components and wiring for an application.C and.comp (ex: CLOCK):.C and.comp (ex: CLOCK): a component implemented in C a component implemented in C.desc and.comp (ex: GENERIC_COMM):.desc and.comp (ex: GENERIC_COMM): a component implemented by connecting other components a component implemented by connecting other components.C,.comp and.desc (ex: INT_TO_RFM):.C,.comp and.desc (ex: INT_TO_RFM): a component implemented in C with predefined wiring to other components a component implemented in C with predefined wiring to other components
The nesC Model Components: 1 file Components: 1 file specification specification implementation implementation module: C behaviour module: C behaviour configuration: select and wire configuration: select and wire specification specification provides interface provides interface requires interface requires interface application: configuration comp1: module comp3 comp4 comp2: configuration
The nesC Model, continued 2 kinds of components 2 kinds of components module: was.C and.comp module: was.C and.comp configuration: was.desc and.comp configuration: was.desc and.comp What happened to the other 2 kinds of components ? What happened to the other 2 kinds of components ?.desc only (ex: cnt_to_leds):.desc only (ex: cnt_to_leds): use a configuration with empty specification { } use a configuration with empty specification { }.C,.comp and.desc (ex: INT_TO_RFM):.C,.comp and.desc (ex: INT_TO_RFM): use a configuration and module (2 files) use a configuration and module (2 files) naming convention: IntToRfm (configuration), IntToRfmM (module) naming convention: IntToRfm (configuration), IntToRfmM (module) allows module to be re-used with different wiring allows module to be re-used with different wiring
Interfaces Group related functionality, e.g.: Group related functionality, e.g.: split-phase operation (send, sendDone) split-phase operation (send, sendDone) standard control interface: init, power standard control interface: init, power Specifies bi-directional calls (defines, uses): Specifies bi-directional calls (defines, uses): interface SendMsg { defines command result_t send(uint16_t to, TOS_MsgPtr msg); defines command result_t send(uint16_t to, TOS_MsgPtr msg); uses event result_t sendDone(TOS_MsgPtr msg, result_t ok); uses event result_t sendDone(TOS_MsgPtr msg, result_t ok);} if component C with interface I has function f: if component C with interface I has function f: if I is required and f is uses, C must define f if I is required and f is uses, C must define f if I is provided and f is defines, C must define f if I is provided and f is defines, C must define f
Some Interfaces interface Leds { defines { defines { command result_t init(); command result_t init(); command result_t redOn(); command result_t redOn(); command result_t redOff(); command result_t redOff(); command result_t redToggle(); command result_t redToggle(); }} interface Clock { defines command result_t setRate(char interval, char scale); uses event result_t fire(); } interface StdControl { defines { command result_t init(); command result_t power(char mode); }
Some More Interfaces interface SendMsg { defines command result_t send(uint16_t to, TOS_MsgPtr msg); uses event result_t sendDone(TOS_MsgPtr msg, result_t ok); } interface ReceiveMsg { uses event TOS_MsgPtr receive(TOS_MsgPtr m); } interface ADC { defines command result_t getData(); defines command result_t getContinuousData(); uses event result_t dataReady(uint16_t data); } But where did the Active Message type go ? But where did the Active Message type go ? But where did the ADC port go ? But where did the ADC port go ?
Parameterised Interfaces Specify a numeric parameter for an interface in a component: Specify a numeric parameter for an interface in a component: argument value specified in configurations argument value specified in configurations dynamic dispatch (receive, sendDone), implicit argument (send) dynamic dispatch (receive, sendDone), implicit argument (send) Like the current scheme for receiving active messages Like the current scheme for receiving active messages Used also for ADC ports, exclusive EEPROM access Used also for ADC ports, exclusive EEPROM access configuration MyApp... implementation { uses MyMain, GenericComm; MyMain.Msg1 -> GenericComm.Send[12]; MyMain.Msg2 -> GenericComm.Receive[99]; } module MyMain { requires interface Send Msg1; requires interface Receive Msg2; } implementation { event Msg2.receive(MsgPtr m) { call Msg1.send(myMsg); } interface Receive { uses event receive(MsgPtr m); uses event receive(MsgPtr m);} interface Send { defines command send(MsgPtr m); defines command send(MsgPtr m); uses event sendDone(MsgPtr m); uses event sendDone(MsgPtr m);} configuration GenericComm { provides interface Send[char id]; provides interface Send[char id]; provides interface Receive[char id]; provides interface Receive[char id];} implementation {... }
Miscellaneous Features default events and commands (cf: _NULL_FUNC) default events and commands (cf: _NULL_FUNC) some commands, events not always connected some commands, events not always connected specify default behaviour in the absence of connection, e.g.: specify default behaviour in the absence of connection, e.g.: default event TOS_MsgPtr receive(TOS_MsgPtr msg) { return msg; } default event TOS_MsgPtr receive(TOS_MsgPtr msg) { return msg; } includes is used to include C types/constants/etc for use in an interface or component includes is used to include C types/constants/etc for use in an interface or component these types are accessible to all components that use the interface or component these types are accessible to all components that use the interface or component use, e.g., to share packet format specification across components use, e.g., to share packet format specification across components nesC source code is not preprocessed nesC source code is not preprocessed
Implementation nesc1 takes a “closed” component (no interfaces) and: nesc1 takes a “closed” component (no interfaces) and: Checks nesC errors Checks nesC errors Unknown/multiply-defined interfaces, components, etc Unknown/multiply-defined interfaces, components, etc Connections (missing, cyclical, type-incorrect, wrong direction) Connections (missing, cyclical, type-incorrect, wrong direction) Checks (most) C errors Checks (most) C errors Generates one C-file for the application Generates one C-file for the application Includes only reachable code (prunes out unreachable functions) Includes only reachable code (prunes out unreachable functions) Generated in reverse-topological-call-order Generated in reverse-topological-call-order Allows gcc’s inliner to do a much better inlining job Allows gcc’s inliner to do a much better inlining job avr-gcc compiles nesc1 output avr-gcc compiles nesc1 output Generated code has #line directives, so clean error messages Generated code has #line directives, so clean error messages
Status Compiler complete (some bugs expected) Compiler complete (some bugs expected) Basic infrastructure and components for Mica Basic infrastructure and components for Mica (old) radio stack (old) radio stack ADC, Clock, Leds, EEPROM, serial port ADC, Clock, Leds, EEPROM, serial port Apps ported: Apps ported: Blink, CntTo{Leds,Rfm,LedsAndRfm}, RfmToLeds Blink, CntTo{Leds,Rfm,LedsAndRfm}, RfmToLeds SenseToLeds, Chirp, Oscilloscope, EEPROMTest SenseToLeds, Chirp, Oscilloscope, EEPROMTest GenericBase GenericBase
Status: Code Size Application C (TinyOS) nesCSavings Blink % CntToLeds % SenseToLeds % Oscilloscope % RfmToLeds % GenericBase % CntToRfm % CntToLedsAndRfm % Chirp % EEPROMTest %
Plans Goal: Switch all TinyOS C code to nesC Goal: Switch all TinyOS C code to nesC Stage 1: convert all “standard” components & apps Stage 1: convert all “standard” components & apps Who: The TinyOS team Who: The TinyOS team Support: Mica, Rene2, all sensor boards Support: Mica, Rene2, all sensor boards Stage 1a: Phil Levis will “port” TOSSIM to nesC Stage 1a: Phil Levis will “port” TOSSIM to nesC Stage 1b: Eric Brewer will write porting guide, nesC tutorial Stage 1b: Eric Brewer will write porting guide, nesC tutorial Stage 2: distribute nesC in next TinyOS release Stage 2: distribute nesC in next TinyOS release Stage 3: discontinue support for macros/perl scripts Stage 3: discontinue support for macros/perl scripts Goal: Improve nesC Goal: Improve nesC See Eric’s talk See Eric’s talk
Open Issues Naming Convention (Java-inspired) Naming Convention (Java-inspired) Interface design Interface design Revisit some component designs Revisit some component designs Directory structure Directory structure
Conclusions nesC much nicer to use than TinyOS nesC much nicer to use than TinyOS No more obscure C compiler warnings/errors No more obscure C compiler warnings/errors No more obscure bugs due to typos No more obscure bugs due to typos No more obscure infinite loops due to wiring restrictions No more obscure infinite loops due to wiring restrictions nesC produces smaller code than TinyOS nesC produces smaller code than TinyOS Prunes unreachable code Prunes unreachable code Inlining across component boundaries Inlining across component boundaries The best is yet to come! The best is yet to come! Short-term: multiple instantiation, better inlining selection, packet format specification Short-term: multiple instantiation, better inlining selection, packet format specification Long-term: language improvements for bug prevention Long-term: language improvements for bug prevention
Packet Format Specification Write a C file with the packet type: Write a C file with the packet type: // IntMsg.th: The message type for IntToRfm/RfmToInt typedef struct { char val; char val; int src; int src; } IntMsg; enum { AM_INTMSG = 4 AM_INTMSG = 4}; Use it in nesC components via includes Use it in nesC components via includes Automatic generation of corresponding Java type and marshaller/unmarshaller Automatic generation of corresponding Java type and marshaller/unmarshaller
Components components components module C1 { requires interface triangle; requires interface triangle; } implementation {... } module C2 { provides interface triangle in; provides interface triangle in; requires { requires { interface triangle out; interface triangle out; interface rectangle side; } interface rectangle side; } } implementation {... } configuration C3 { provides interface triangle; provides interface triangle; provides interface rectangle; provides interface rectangle; } implementation {... } C1 C2 C3
Configurations Connect components Connect components configuration app { } implementation { uses c1, c2, c3; c1 -> c2; // implicit interface sel. c2.out -> c3.triangle; c3 <- c2.side; } Partial configurations: Partial configurations: component c2c3 { provides interface triangle t1; provides interface triangle t1;} implementation { uses c2, c3; uses c2, c3; t1 -> c2.in; c2.out -> c3.triangle; c3 <- c2.side; } C1 C2 C3 C2 C3
Modules “C” implementation of components “C” implementation of components all top-level declarations private, except those specified in the components interface all top-level declarations private, except those specified in the components interface module C2 { provides interface send in; requires interface send out; } implementation { enum { ready, busy } state; command in.send(Message m) { if (state == busy) return 0; state = busy; call out.send(m); return 1; } event out.send_done(Message m) { state = ready; signal in.send_done(m); }... }