├── .gitignore ├── bld └── Makefile.config ├── README ├── Makefile ├── src ├── include │ └── libchain │ │ ├── repeat.h │ │ └── chain.h └── chain.c ├── README.md └── GUIDE.md /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.swo 3 | *.o 4 | *.out 5 | *.a 6 | *.map 7 | *.d 8 | *.S 9 | *.bc 10 | -------------------------------------------------------------------------------- /bld/Makefile.config: -------------------------------------------------------------------------------- 1 | ifeq ($(LIBCHAIN_ENABLE_DIAGNOSTICS),1) 2 | LOCAL_CFLAGS += -DLIBCHAIN_ENABLE_DIAGNOSTICS 3 | endif 4 | 5 | override CFLAGS += $(LOCAL_CFLAGS) 6 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Intermittent execution using channels 2 | 3 | To show communication activity over channels using printf, 4 | define the following flag when compiling libchain *and* 5 | the application: 6 | 7 | make LIBCHAIN_ENABLE_DIAGNOSTICS=1 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIB = libchain 2 | 3 | OBJECTS = \ 4 | chain.o \ 5 | 6 | DEPS += \ 7 | libmsp \ 8 | 9 | override SRC_ROOT = ../../src 10 | 11 | override CFLAGS += \ 12 | -I$(SRC_ROOT)/include/$(LIB) \ 13 | 14 | include $(MAKER_ROOT)/Makefile.$(TOOLCHAIN) 15 | -------------------------------------------------------------------------------- /src/include/libchain/repeat.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBCHAIN_REPEAT_H 2 | #define LIBCHAIN_REPEAT_H 3 | 4 | #define REPEAT0(x) 5 | #define REPEAT1(x) x 6 | #define REPEAT2(x) REPEAT1(x),REPEAT1(x) 7 | #define REPEAT4(x) REPEAT2(x),REPEAT2(x) 8 | #define REPEAT8(x) REPEAT4(x),REPEAT4(x) 9 | #define REPEAT16(x) REPEAT8(x),REPEAT8(x) 10 | #define REPEAT32(x) REPEAT16(x),REPEAT16(x) 11 | #define REPEAT64(x) REPEAT32(x),REPEAT32(x) 12 | #define REPEAT128(x) REPEAT64(x),REPEAT64(x) 13 | #define REPEAT256(x) REPEAT128(x),REPEAT128(x) 14 | #define REPEAT512(x) REPEAT256(x),REPEAT256(x) 15 | #define REPEAT1024(x) REPEAT512(x),REPEAT512(x) 16 | #define REPEAT2048(x) REPEAT1024(x),REPEAT1024(x) 17 | #define REPEAT4096(x) REPEAT2048(x),REPEAT2048(x) 18 | 19 | #define REPEAT_INNER(count, x) REPEAT ## count(x) 20 | #define REPEAT(count, x) REPEAT_INNER(count, x) 21 | 22 | #endif // LIBCHAIN_REPEAT_H 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Chain: Task-based Programming Model for Intermittent Software 2 | ============================================================= 3 | 4 | Chain ([OOPSLA\'16](http://dl.acm.org/citation.cfm?id=2983995)) is a 5 | programming model for the intermittent execution model, that arises on 6 | intermittently-powered energy-harvesting devices. The `libchain` library 7 | implements this programming model with a set of extensions to the C language 8 | and a runtime. To write a program using Chain, the application source must 9 | include the header and link against the library. 10 | 11 | To get started quickly writing, building and deploying applications with Chain 12 | on real energy-harvesting devices, see the guide in the [release 13 | documentation](https://github.com/CMUAbstract/releases/blob/master/Chain.md). 14 | 15 | Table of Contents 16 | ----------------- 17 | 18 | * [Overview](#overview) 19 | * [Programming Interface](#chain-programming-interface) 20 | * [Diagnostics](#diagnostics) 21 | * [Dependencies](#dependencies) 22 | 23 | Overview 24 | -------- 25 | 26 | In the Chain programming model, applications are expressed as a graph of 27 | statically-defined tasks with explicit control flow statements that transfer 28 | control from one task to the next. Tasks store *local* data in variables 29 | allocated on the (volatile) stack and *shared* data in persisted channels 30 | statically allocated in non-volatile memory. The programming interface 31 | provided by `libchain` includes macros for declaring tasks and channels, 32 | and for accessing channels from task code. 33 | 34 | Programming Interface 35 | --------------------- 36 | 37 | The programmer\'s interface provided by `libchain`, and defined in 38 | `libchain/src/include/libchain/libchain.h`, consists of a set of macros for 39 | declaring tasks and channels, passing data through channels, and transferring 40 | control flow between tasks. 41 | 42 | A guiding principle followed in this implementation is to keep as much work as 43 | possible static, avoiding introducing any kind of runtime state that is 44 | fundamentally statically-determined. This approach often sacrifices conciseness 45 | in return for performance and extensibility for eventually creating a compiler 46 | that would compile a nicer syntax into the above primitives. 47 | 48 | Declare and define a task: 49 | 50 | TASK(index, task_body_func) 51 | 52 | void task_body_func() { 53 | ... 54 | } 55 | 56 | The *index* parameter must be a distinct number for each task. Throughout 57 | the API, tasks are identified by the name of their body function. 58 | 59 | Declare a channel, which declares the typed fields that hold the data exchanged 60 | between tasks: 61 | 62 | struct msg_type { 63 | CHAN_FIELD(type, name); 64 | ... 65 | }; 66 | 67 | CHANNEL(task_name_from, task_name_to, msg_type); 68 | 69 | Self-channels require a dedicated macro for its fields and an initializer: 70 | 71 | struct msg_self_type { 72 | SELF_CHAN_FIELD(type, name); 73 | ... 74 | }; 75 | #define FIELD_INIT_msg_self_type \ 76 | SELF_FIELD_INITIALIZER \ 77 | ... 78 | } 79 | 80 | SELF_CHANNEL(task_name, msg_self_type); 81 | 82 | Multicast channels are declared using a dedicated macro that accepts a 83 | unique name for the channel and a list of destination tasks: 84 | 85 | MULTICAST_CHANNEL(msg_type, ch_name, task_name_from, task_name_to_1, task_name_to_2, ...); 86 | 87 | In the task code, channels are identified by their endpoints. Channel identifiers 88 | for different channel types are constructed using the following macros. 89 | 90 | Regular channels are identified using the `CH()` macro, which takes the source 91 | task and the destination task: 92 | 93 | CH(task_name_from, task_name_to) 94 | 95 | Multicast channels are identified by the `MC_OUT_CH()`/`MC_IN_CH()` macros 96 | (when writing to and reading from the channel, respectively), which take the 97 | unique name of the multicast channel, as well as the source, and all 98 | destination tasks: 99 | 100 | MC_OUT_CH(ch_name, task_name_from, task_name_to_1, task_name_to_2, ...) 101 | MC_IN_CH(ch_name, task_name_from, task_name_to) 102 | 103 | Self-channels are identified by the `SELF_IN_CH()/SELF_OUT_CH()`, macro which 104 | takes the channel name: 105 | 106 | SELF_CH(task_name) 107 | 108 | To write data from local variable `var` of type `type` to the field `field` in 109 | one or more (*n*) channels, each identified by one of the above 110 | channel-identifier macros: 111 | 112 | CHAN_OUTn(type, var, field, CH(...), CH(...), MC_OUT_CH(...), SELF_CH(...), ...) 113 | 114 | To read data into local variable `var` of type `type` from the field `field` from 115 | the channel where the field was most-recently updated out of a set of *n* 116 | potential channels all of which contain this field: 117 | 118 | type var = *CHAN_INn(type, field, CH(...), SELF_IN_CH(...), CH(...), ...) 119 | 120 | To transition control between tasks, task code may invoke the transition statement 121 | at any point: 122 | 123 | TRANSITION_TO(task_destination) 124 | 125 | Diagnostics 126 | ----------- 127 | 128 | To have `libchain` produce verbose console output on each operation, e.g. 129 | channel access, set the [Maker](https://github.com/CMUAbstract/maker) build 130 | configuration variable `LIBCHAIN_ENABLE_DIAGNOSTICS`, by adding the following 131 | statement to the `bld/Makefile` of the application: 132 | 133 | export LIBCHAIN_ENABLE_DIAGNOSTICS = 1 134 | 135 | Dependencies 136 | ------------ 137 | 138 | Note that `libchain` is packaged for [Maker](https://github.com/CMUAbstract/maker), 139 | which means that the application's dependencies (including all transitive dependencies) 140 | are declared in `bld/Makefile`, included into the application as submodules in 141 | `ext/`, and are automatically built along with the application. 142 | 143 | Chain depends on the following libraries: 144 | 145 | * [libmsp](https://github.com/CMUAbstract/libmsp) : basic lowest-level 146 | common "drivers" for MSP430 microcontrollers 147 | 148 | `libmsp` is required for macros for declaring persistent variables. 149 | 150 | Optional dependencies for [diagnostic builds](#diagnostics) for a console: 151 | 152 | * [libio](https://github.com/CMUAbstract/libio) and its transient dependencies 153 | -------------------------------------------------------------------------------- /src/chain.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef LIBCHAIN_ENABLE_DIAGNOSTICS 5 | #define LIBCHAIN_PRINTF(...) 6 | #else 7 | #include 8 | #define LIBCHAIN_PRINTF printf 9 | #endif 10 | 11 | #include "chain.h" 12 | 13 | /* Dummy types for offset calculations */ 14 | struct _void_type_t { 15 | void * x; 16 | }; 17 | typedef struct _void_type_t void_type_t; 18 | 19 | __nv chain_time_t volatile curtime = 0; 20 | 21 | /* To update the context, fill-in the unused one and flip the pointer to it */ 22 | __nv context_t context_1 = {0}; 23 | __nv context_t context_0 = { 24 | .task = TASK_REF(_entry_task), 25 | .time = 0, 26 | .next_ctx = &context_1, 27 | }; 28 | 29 | __nv context_t * volatile curctx = &context_0; 30 | 31 | // for internal instrumentation purposes 32 | __nv volatile unsigned _numBoots = 0; 33 | 34 | /** 35 | * @brief Function to be invoked at the beginning of every task 36 | */ 37 | void task_prologue() 38 | { 39 | task_t *curtask = curctx->task; 40 | 41 | // Swaps of the self-channel buffer happen on transitions, not restarts. 42 | // We detect transitions by comparing the current time with a timestamp. 43 | if (curctx->time != curtask->last_execute_time) { 44 | 45 | // Minimize FRAM reads 46 | self_field_meta_t **dirty_self_fields = curtask->dirty_self_fields; 47 | 48 | int i; 49 | 50 | // It is safe to repeat the loop for the same element, because the swap 51 | // operation clears the dirty bit. We only need to be a little bit careful 52 | // to decrement the count strictly after the swap. 53 | while ((i = curtask->num_dirty_self_fields) > 0) { 54 | self_field_meta_t *self_field = dirty_self_fields[--i]; 55 | 56 | if (self_field->idx_pair & SELF_CHAN_IDX_BIT_DIRTY_CURRENT) { 57 | // Atomically: swap AND clear the dirty bit (by "moving" it over to MSB) 58 | __asm__ volatile ( 59 | "SWPB %[idx_pair]\n" 60 | : [idx_pair] "=m" (self_field->idx_pair) 61 | ); 62 | } 63 | 64 | // Trade-off: either we do one FRAM write after each element, or 65 | // we do only one write at the end (set to 0) but also not make 66 | // forward progress if we reboot in the middle of this loop. 67 | // We opt for making progress. 68 | curtask->num_dirty_self_fields = i; 69 | } 70 | 71 | curtask->last_execute_time = curctx->time; 72 | } else { 73 | // In this case, swapping that needed to take place after the last 74 | // transition has run to completion (even if it was restarted) [because 75 | // the last_execute_time was set]. We get into this clause only 76 | // because of a restart. We must clear any state that the incomplete 77 | // execution of the task might have changed. 78 | curtask->num_dirty_self_fields = 0; 79 | } 80 | } 81 | 82 | /** 83 | * @brief Transfer control to the given task 84 | * @details Finalize the current task and jump to the given task. 85 | * This function does not return. 86 | * 87 | * TODO: mark this function as bare (i.e. no prologue) for efficiency 88 | */ 89 | void transition_to(task_t *next_task) 90 | { 91 | context_t *next_ctx; // this should be in a register for efficiency 92 | // (if we really care, write this func in asm) 93 | 94 | // reset stack pointer 95 | // update current task pointer 96 | // tick logical time 97 | // jump to next task 98 | 99 | // NOTE: the order of these does not seem to matter, a reboot 100 | // at any point in this sequence seems to be harmless. 101 | // 102 | // NOTE: It might be a bit cleaner to reset the stack and 103 | // set the current task pointer in the function *prologue* -- 104 | // would need compiler support for this. 105 | // 106 | // NOTE: It is harmless to increment the time even if we fail before 107 | // transitioning to the next task. The reverse, i.e. failure to increment 108 | // time while having transitioned to the task, would break the 109 | // semantics of CHAN_IN (aka. sync), which should get the most recently 110 | // updated value. 111 | // 112 | // NOTE: Storing two pointers (one to next and one to current context) 113 | // does not seem acceptable, because the two would need to be kept 114 | // consistent in the face of intermittence. But, could keep one pointer 115 | // to current context and a pointer to next context inside the context 116 | // structure. The only reason to do that is if it is more efficient -- 117 | // i.e. avoids XORing the index and getting the actual pointer. 118 | 119 | // TODO: handle overflow of timestamp. Some very raw ideas: 120 | // * limit the age of values 121 | // * a maintainance task that fixes up stored timestamps 122 | // * extra bit to mark timestamps as pre/post overflow 123 | 124 | // TODO: re-use the top-of-stack address used in entry point, instead 125 | // of hardcoding the address. 126 | // 127 | // Probably need to write a custom entry point in asm, and 128 | // use it instead of the C runtime one. 129 | 130 | next_ctx = curctx->next_ctx; 131 | next_ctx->task = next_task; 132 | next_ctx->time = curctx->time + 1; 133 | 134 | next_ctx->next_ctx = curctx; 135 | curctx = next_ctx; 136 | 137 | task_prologue(); 138 | 139 | __asm__ volatile ( // volatile because output operands unused by C 140 | "mov #0x2400, r1\n" 141 | "br %[ntask]\n" 142 | : 143 | : [ntask] "r" (next_task->func) 144 | ); 145 | 146 | // Alternative: 147 | // task-function prologue: 148 | // mov pc, curtask 149 | // mov #0x2400, sp 150 | // 151 | // transition_to(next_task->func): 152 | // br next_task 153 | } 154 | 155 | /** @brief Sync: return the most recently updated value of a given field 156 | * @param field_name string name of the field, used for diagnostics 157 | * @param var_size size of the 'variable' type (var_meta_t + value type) 158 | * @param count number of channels to sync 159 | * @param ... channel ptr, field offset in corresponding message type 160 | * @return Pointer to the value, ready to be cast to final value type pointer 161 | */ 162 | void *chan_in(const char *field_name, size_t var_size, int count, ...) 163 | { 164 | va_list ap; 165 | unsigned i; 166 | unsigned latest_update = 0; 167 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 168 | unsigned latest_chan_idx = 0; 169 | #endif 170 | 171 | var_meta_t *var; 172 | var_meta_t *latest_var = NULL; 173 | 174 | LIBCHAIN_PRINTF("[%u] %s: in: '%s':", curctx->time, 175 | curctx->task->name, field_name); 176 | 177 | va_start(ap, count); 178 | 179 | for (i = 0; i < count; ++i) { 180 | uint8_t *chan = va_arg(ap, uint8_t *); 181 | size_t field_offset = va_arg(ap, unsigned); 182 | 183 | uint8_t *chan_data = chan + offsetof(CH_TYPE(_sa, _da, _void_type_t), data); 184 | chan_meta_t *chan_meta = (chan_meta_t *)(chan + 185 | offsetof(CH_TYPE(_sb, _db, _void_type_t), meta)); 186 | uint8_t *field = chan_data + field_offset; 187 | 188 | switch (chan_meta->type) { 189 | case CHAN_TYPE_SELF: { 190 | self_field_meta_t *self_field = (self_field_meta_t *)field; 191 | 192 | unsigned var_offset = 193 | (self_field->idx_pair & SELF_CHAN_IDX_BIT_CURRENT) ? var_size : 0; 194 | 195 | var = (var_meta_t *)(field + 196 | offsetof(SELF_FIELD_TYPE(void_type_t), var) + var_offset); 197 | 198 | break; 199 | } 200 | default: 201 | var = (var_meta_t *)(field + 202 | offsetof(FIELD_TYPE(void_type_t), var)); 203 | } 204 | 205 | LIBCHAIN_PRINTF(" {%u} %s->%s c%04x:off%u:v%04x [%u],", i, 206 | chan_meta->diag.source_name, chan_meta->diag.dest_name, 207 | (uint16_t)chan, field_offset, 208 | (uint16_t)var, var->timestamp); 209 | 210 | if (var->timestamp > latest_update) { 211 | latest_update = var->timestamp; 212 | latest_var = var; 213 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 214 | latest_chan_idx = i; 215 | #endif 216 | } 217 | } 218 | va_end(ap); 219 | 220 | LIBCHAIN_PRINTF(": {latest %u}: ", latest_chan_idx); 221 | 222 | uint8_t *value = (uint8_t *)latest_var + offsetof(VAR_TYPE(void_type_t), value); 223 | 224 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 225 | for (int i = 0; i < var_size - sizeof(var_meta_t); ++i) 226 | LIBCHAIN_PRINTF("%02x ", value[i]); 227 | LIBCHAIN_PRINTF("\r\n"); 228 | #endif 229 | 230 | // TODO: No two timestamps compared above can be equal. 231 | // How can two different sources (i.e. different tasks) write to 232 | // this destination at the same logical time? Pretty sure, this is 233 | // impossible. 234 | // ASSERT(latest_field != NULL); 235 | 236 | return (void *)value; 237 | } 238 | 239 | /** @brief Write a value to a field in a channel 240 | * @param field_name string name of the field, used for diagnostics 241 | * @param value pointer to value data 242 | * @param var_size size of the 'variable' type (var_meta_t + value type) 243 | * @param count number of output channels 244 | * @param ... channel ptr, field offset in corresponding message type 245 | */ 246 | void chan_out(const char *field_name, const void *value, 247 | size_t var_size, int count, ...) 248 | { 249 | va_list ap; 250 | int i; 251 | var_meta_t *var; 252 | 253 | va_start(ap, count); 254 | 255 | for (i = 0; i < count; ++i) { 256 | uint8_t *chan = va_arg(ap, uint8_t *); 257 | size_t field_offset = va_arg(ap, unsigned); 258 | 259 | uint8_t *chan_data = chan + offsetof(CH_TYPE(_sa, _da, _void_type_t), data); 260 | chan_meta_t *chan_meta = (chan_meta_t *)(chan + 261 | offsetof(CH_TYPE(_sb, _db, _void_type_t), meta)); 262 | uint8_t *field = chan_data + field_offset; 263 | 264 | switch (chan_meta->type) { 265 | case CHAN_TYPE_SELF: { 266 | self_field_meta_t *self_field = (self_field_meta_t *)field; 267 | task_t *curtask = curctx->task; 268 | 269 | unsigned var_offset = 270 | (self_field->idx_pair & SELF_CHAN_IDX_BIT_NEXT) ? var_size : 0; 271 | 272 | var = (var_meta_t *)(field + 273 | offsetof(SELF_FIELD_TYPE(void_type_t), var) + var_offset); 274 | 275 | // "Enqueue" the buffer index to be flipped on next transition: 276 | // (1) initialize the dirty bit for next swap, or, in other words, 277 | // "finalize" clearing of the dirty bit from the previous 278 | // swap, since the swap "clears" the dirty bit by moving 279 | // it over from LSB to MSB. 280 | // (2) mark the index dirty, which enques the swap 281 | // (3) add the field to the list of dirty fields 282 | // 283 | // NOTE: these do not have to be atomic, and can be repeated any 284 | // number of times (idempotent). Counter of the dirty list is 285 | // reset on in task prologue. 286 | self_field->idx_pair &= ~(SELF_CHAN_IDX_BIT_DIRTY_NEXT); 287 | self_field->idx_pair |= SELF_CHAN_IDX_BIT_DIRTY_CURRENT; 288 | curtask->dirty_self_fields[curtask->num_dirty_self_fields++] = self_field; 289 | 290 | break; 291 | } 292 | default: 293 | var = (var_meta_t *)(field + 294 | offsetof(FIELD_TYPE(void_type_t), var)); 295 | } 296 | 297 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 298 | LIBCHAIN_PRINTF("[%u] %s: out: '%s': %s -> %s c%04x:off%u:v%04x: ", 299 | curctx->time, curctx->task->name, field_name, 300 | chan_meta->diag.source_name, chan_meta->diag.dest_name, 301 | (uint16_t)chan, field_offset, (uint16_t)var); 302 | 303 | for (int i = 0; i < var_size - sizeof(var_meta_t); ++i) 304 | LIBCHAIN_PRINTF("%02x ", *((uint8_t *)value + i)); 305 | LIBCHAIN_PRINTF("\r\n"); 306 | #endif 307 | 308 | var->timestamp = curctx->time; 309 | void *var_value = (uint8_t *)var + offsetof(VAR_TYPE(void_type_t), value); 310 | memcpy(var_value, value, var_size - sizeof(var_meta_t)); 311 | } 312 | 313 | va_end(ap); 314 | } 315 | 316 | /** @brief Entry point upon reboot */ 317 | int chain_main() { 318 | _numBoots++; 319 | 320 | // Resume execution at the last task that started but did not finish 321 | 322 | // TODO: using the raw transtion would be possible once the 323 | // prologue discussed in chain.h is implemented (requires compiler 324 | // support) 325 | // transition_to(curtask); 326 | 327 | task_prologue(); 328 | 329 | __asm__ volatile ( // volatile because output operands unused by C 330 | "br %[nt]\n" 331 | : /* no outputs */ 332 | : [nt] "r" (curctx->task->func) 333 | ); 334 | 335 | return 0; // TODO: write our own entry point and get rid of this 336 | } 337 | -------------------------------------------------------------------------------- /src/include/libchain/chain.h: -------------------------------------------------------------------------------- 1 | #ifndef CHAIN_H 2 | #define CHAIN_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "repeat.h" 10 | 11 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 12 | #define TASK_NAME_SIZE 32 13 | #define CHAN_NAME_SIZE 32 14 | #endif // LIBCHAIN_ENABLE_DIAGNOSTICS 15 | 16 | #define MAX_DIRTY_SELF_FIELDS 4 17 | 18 | typedef void (task_func_t)(void); 19 | typedef unsigned chain_time_t; 20 | typedef uint32_t task_mask_t; 21 | typedef uint16_t field_mask_t; 22 | typedef unsigned task_idx_t; 23 | 24 | typedef enum { 25 | CHAN_TYPE_T2T, 26 | CHAN_TYPE_SELF, 27 | CHAN_TYPE_MULTICAST, 28 | CHAN_TYPE_CALL, 29 | CHAN_TYPE_RETURN, 30 | } chan_type_t; 31 | 32 | // TODO: include diag fields only when diagnostics are enabled 33 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 34 | typedef struct _chan_diag_t { 35 | char source_name[CHAN_NAME_SIZE]; 36 | char dest_name[CHAN_NAME_SIZE]; 37 | } chan_diag_t; 38 | #endif // LIBCHAIN_ENABLE_DIAGNOSTICS 39 | 40 | typedef struct _chan_meta_t { 41 | chan_type_t type; 42 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 43 | chan_diag_t diag; 44 | #endif // LIBCHAIN_ENABLE_DIAGNOSTICS 45 | } chan_meta_t; 46 | 47 | typedef struct _var_meta_t { 48 | chain_time_t timestamp; 49 | } var_meta_t; 50 | 51 | typedef struct _self_field_meta_t { 52 | // Single word (two bytes) value that contains 53 | // * bit 0: dirty bit (i.e. swap needed) 54 | // * bit 1: index of the current var buffer from the double buffer pair 55 | // * bit 5: index of the next var buffer from the double buffer pair 56 | // This layout is so that we can swap the bytes to flip between buffers and 57 | // at the same time (atomically) clear the dirty bit. The dirty bit must 58 | // be reset in bit 4 before the next swap. 59 | unsigned idx_pair; 60 | } self_field_meta_t; 61 | 62 | typedef struct { 63 | task_func_t *func; 64 | task_mask_t mask; 65 | task_idx_t idx; 66 | 67 | // Dirty self channel fields are ones to which there had been a 68 | // chan_out. The out value is "staged" in the alternate buffer of 69 | // the self-channel double-buffer pair for each field. On transition, 70 | // the buffer index is flipped for dirty fields. 71 | self_field_meta_t *dirty_self_fields[MAX_DIRTY_SELF_FIELDS]; 72 | volatile unsigned num_dirty_self_fields; 73 | 74 | volatile chain_time_t last_execute_time; // to execute prologue only once 75 | 76 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 77 | char name[TASK_NAME_SIZE]; 78 | #endif // LIBCHAIN_ENABLE_DIAGNOSTICS 79 | } task_t; 80 | 81 | #define SELF_CHAN_IDX_BIT_DIRTY_CURRENT 0x0001U 82 | #define SELF_CHAN_IDX_BIT_DIRTY_NEXT 0x0100U 83 | #define SELF_CHAN_IDX_BIT_CURRENT 0x0002U 84 | #define SELF_CHAN_IDX_BIT_NEXT 0x0200U 85 | 86 | #define VAR_TYPE(type) \ 87 | struct { \ 88 | var_meta_t meta; \ 89 | type value; \ 90 | } \ 91 | 92 | #define FIELD_TYPE(type) \ 93 | struct { \ 94 | VAR_TYPE(type) var; \ 95 | } 96 | 97 | #define SELF_FIELD_TYPE(type) \ 98 | struct { \ 99 | self_field_meta_t meta; \ 100 | VAR_TYPE(type) var[2]; \ 101 | } 102 | 103 | #define CH_TYPE(src, dest, type) \ 104 | struct _ch_type_ ## src ## _ ## dest ## _ ## type { \ 105 | chan_meta_t meta; \ 106 | struct type data; \ 107 | } 108 | 109 | /** @brief Declare a value transmittable over a channel 110 | * @param type Type of the field value 111 | * @param name Name of the field, include [] suffix to declare an array 112 | * @details Metadata field must be the first, so that these 113 | * fields can be upcast to a generic type. 114 | * NOTE: To make compiler error messages more useful, instead of making 115 | * the struct anonymous, we can name it with '_chan_field_ ## name'. 116 | * But, this imposes the restriction that field names should be unique 117 | * across unrelated channels. Adding the channel name to each CHAN_FIELD 118 | * macro is too verbose, so compromising on error msgs. 119 | * 120 | * TODO: could CHAN_FIELD be a special case of CHAN_FIELD_ARRARY with size = 1? 121 | */ 122 | #define CHAN_FIELD(type, name) FIELD_TYPE(type) name 123 | #define CHAN_FIELD_ARRAY(type, name, size) FIELD_TYPE(type) name[size] 124 | #define SELF_CHAN_FIELD(type, name) SELF_FIELD_TYPE(type) name 125 | #define SELF_CHAN_FIELD_ARRAY(type, name, size) SELF_FIELD_TYPE(type) name[size] 126 | 127 | /** @brief Execution context */ 128 | typedef struct _context_t { 129 | /** @brief Pointer to the most recently started but not finished task */ 130 | task_t *task; 131 | 132 | /** @brief Logical time, ticks at task boundaries */ 133 | chain_time_t time; 134 | 135 | // TODO: move this to top, just feels cleaner 136 | struct _context_t *next_ctx; 137 | } context_t; 138 | 139 | extern context_t * volatile curctx; 140 | 141 | /** @brief Internal macro for constructing name of task symbol */ 142 | #define TASK_SYM_NAME(func) _task_ ## func 143 | 144 | #define TASK_REF(func) &TASK_SYM_NAME(func) 145 | 146 | #ifdef LIBCHAIN_ENABLE_DIAGNOSTICS 147 | #define TASK_DIAG_FIELDS(func) , #func 148 | #define CHAN_DIAG_FIELDS(src, prefix, dest) , { #src, prefix #dest } 149 | #else // !LIBCHAIN_ENABLE_DIAGNOSTICS 150 | #define TASK_DIAG_FIELDS(func) 151 | #define CHAN_DIAG_FIELDS(src, prefix, dest) 152 | #endif // !LIBCHAIN_ENABLE_DIAGNOSTICS 153 | 154 | /** @brief Declare a task 155 | * 156 | * @param idx Global task index, zero-based 157 | * @param func Pointer to task function 158 | * 159 | * TODO: These do not need to be stored in memory, could be resolved 160 | * into literal values and encoded directly in the code instructions. 161 | * But, it's not obvious how to implement that with macros (would 162 | * need "define inside a define"), so for now create symbols. 163 | * The compiler should actually optimize these away. 164 | * 165 | * TODO: Consider creating a table in form of struct and store 166 | * for each task: mask, index, name. That way we can 167 | * have access to task name for diagnostic output. 168 | */ 169 | #define TASK(idx, func) \ 170 | void func(); \ 171 | __nv task_t TASK_SYM_NAME(func) = { func, (1UL << idx), idx, {0}, 0, 0, TASK_DIAG_FIELDS(func) }; \ 172 | 173 | #define TASK_REF(func) &TASK_SYM_NAME(func) 174 | 175 | /** @brief Function called on every reboot 176 | * @details This function usually initializes hardware, such as GPIO 177 | * direction. The application must define this function. 178 | */ 179 | extern void init(); 180 | 181 | /** @brief First task to run when the application starts 182 | * @details Symbol is defined by the ENTRY_TASK macro. 183 | * This is not wrapped into a delaration macro, because applications 184 | * are not meant to declare tasks -- internal only. 185 | * 186 | * TODO: An alternative would be to have a macro that defines 187 | * the curtask symbol and initializes it to the entry task. The 188 | * application would be required to have a definition using that macro. 189 | * An advantage is that the names of the tasks in the application are 190 | * not constrained, and the whole thing is less magical when reading app 191 | * code, but slightly more verbose. 192 | */ 193 | extern task_t TASK_SYM_NAME(_entry_task); 194 | 195 | /** @brief Declare the first task of the application 196 | * @details This macro defines a function with a special name that is 197 | * used to initialize the current task pointer. 198 | * 199 | * This does incur the penalty of an extra task transition, but it 200 | * happens only once in application lifetime. 201 | * 202 | * The alternatives are to force the user to define functions 203 | * with a special name or to define a task pointer symbol outside 204 | * of the library. 205 | */ 206 | #define ENTRY_TASK(task) \ 207 | TASK(0, _entry_task) \ 208 | void _entry_task() { TRANSITION_TO(task); } 209 | 210 | /** @brief Call this in the last statement in main to transfer control to the task chain 211 | * @details This function does not return. 212 | */ 213 | int chain_main(); 214 | 215 | void task_prologue(); 216 | void transition_to(task_t *task); 217 | void *chan_in(const char *field_name, size_t var_size, int count, ...); 218 | void chan_out(const char *field_name, const void *value, 219 | size_t var_size, int count, ...); 220 | 221 | #define FIELD_COUNT_INNER(type) NUM_FIELDS_ ## type 222 | #define FIELD_COUNT(type) FIELD_COUNT_INNER(type) 223 | 224 | /** @brief Initializers for the fields in a channel 225 | * @details The user defines a FILED_INIT_ macro that, 226 | * in braces contains comma separated list of initializers 227 | * one for each field, in order of the declaration of the fields. 228 | * Each initializer is either 229 | * * SELF_FIELD_INITIALIZER, or 230 | * * SELF_FIELD_ARRAY_INITIALIZER(count) [only count=2^n supported] 231 | */ 232 | 233 | #define SELF_FIELD_META_INITIALIZER { (SELF_CHAN_IDX_BIT_NEXT) } 234 | #define SELF_FIELD_INITIALIZER { SELF_FIELD_META_INITIALIZER } 235 | 236 | #define SELF_FIELD_ARRAY_INITIALIZER(count) { REPEAT(count, SELF_FIELD_INITIALIZER) } 237 | 238 | #define SELF_FIELDS_INITIALIZER_INNER(type) FIELD_INIT_ ## type 239 | #define SELF_FIELDS_INITIALIZER(type) SELF_FIELDS_INITIALIZER_INNER(type) 240 | 241 | #define CHANNEL(src, dest, type) \ 242 | __nv CH_TYPE(src, dest, type) _ch_ ## src ## _ ## dest = \ 243 | { { CHAN_TYPE_T2T CHAN_DIAG_FIELDS(src, "", dest) } } 244 | 245 | #define SELF_CHANNEL(task, type) \ 246 | __nv CH_TYPE(task, task, type) _ch_ ## task ## _ ## task = \ 247 | { { CHAN_TYPE_SELF CHAN_DIAG_FIELDS(task, "", task) }, SELF_FIELDS_INITIALIZER(type) } 248 | 249 | /** @brief Declare a channel for passing arguments to a callable task 250 | * @details Callers would output values into this channels before 251 | * transitioning to the callable task. 252 | * 253 | * TODO: should this be associated with the callee task? i.e. the 254 | * 'callee' argument would be a task name? The concern is 255 | * that a callable task might need to be a special type 256 | * of a task composed of multiple other tasks (a 'hyper-task'). 257 | * */ 258 | #define CALL_CHANNEL(callee, type) \ 259 | __nv CH_TYPE(caller, callee, type) _ch_call_ ## callee = \ 260 | { { CHAN_TYPE_CALL CHAN_DIAG_FIELDS(callee, "call:", callee) } } 261 | #define RET_CHANNEL(callee, type) \ 262 | __nv CH_TYPE(caller, callee, type) _ch_ret_ ## callee = \ 263 | { { CHAN_TYPE_RETURN CHAN_DIAG_FIELDS(callee, "ret:", callee) } } 264 | 265 | /** @brief Delcare a channel for receiving results from a callable task 266 | * @details Callable tasks output values into this channel, and a 267 | * result-processing task would collect the result. The 268 | * result-processing task does not need to be dedicated 269 | * to this purpose, but the results need to be collected 270 | * before the next call to the same task is made. 271 | */ 272 | #define RETURN_CHANNEL(callee, type) \ 273 | __nv CH_TYPE(caller, callee, type) _ch_ret_ ## callee = \ 274 | { { CHAN_TYPE_RETURN CHAN_DIAG_FIELDS(callee, "ret:", callee) } } 275 | 276 | /** @brief Declare a multicast channel: one source many destinations 277 | * @params name short name used to refer to the channels from source and destinations 278 | * @details Conceptually, the channel is between the specified source and 279 | * destinations only. The arbitrary name exists only to simplify referring 280 | * to the channel: to avoid having to list all sources and destinations 281 | * every time. However, access control is not currently enforced. 282 | * Declarations of sources and destinations is necessary for perform 283 | * compile-time checks planned for the future. 284 | */ 285 | #define MULTICAST_CHANNEL(type, name, src, dest, ...) \ 286 | __nv CH_TYPE(src, name, type) _ch_mc_ ## src ## _ ## name = \ 287 | { { CHAN_TYPE_MULTICAST CHAN_DIAG_FIELDS(src, "mc:", name) } } 288 | 289 | #define CH(src, dest) (&_ch_ ## src ## _ ## dest) 290 | #define SELF_CH(tsk) CH(tsk, tsk) 291 | 292 | /* For compatibility */ 293 | #define SELF_IN_CH(tsk) CH(tsk, tsk) 294 | #define SELF_OUT_CH(tsk) CH(tsk, tsk) 295 | 296 | /** @brief Reference to a channel used to pass "arguments" to a callable task 297 | * @details Each callable task that takes arguments would have one of these. 298 | * */ 299 | #define CALL_CH(callee) (&_ch_call_ ## callee) 300 | 301 | /** @brief Reference to a channel used to receive results from a callable task */ 302 | #define RET_CH(callee) (&_ch_ret_ ## callee) 303 | 304 | /** @brief Multicast channel reference 305 | * @details Require the source for consistency with other channel types. 306 | * In IN, require the one destination that's doing the read for consistency. 307 | * In OUT, require the list of destinations for consistency and for 308 | * code legibility. 309 | * Require name only because of implementation constraints. 310 | * 311 | * NOTE: The name is not pure syntactic sugar, because currently 312 | * refering to the multicast channel from the destination by source 313 | * alone is not sufficient: there may be more than one channels that 314 | * have overlapping destination sets. TODO: disallow this overlap? 315 | * The curent implementation resolves this ambiguity using the name. 316 | * 317 | * A separate and more immediate reason for the name is purely 318 | * implementation: if if we disallow the overlap ambiguity, this 319 | * macro needs to resolve among all the channels from the source 320 | * (incl. non-overlapping) -- either we use a name, or we force the 321 | * each destination to specify the complete destination list. The 322 | * latter is not good since eventually we want the type of the 323 | * channel (task-to-task/self/multicast) be transparent to the 324 | * application. type nature be transparent , or we use a name. 325 | */ 326 | #define MC_IN_CH(name, src, dest) (&_ch_mc_ ## src ## _ ## name) 327 | #define MC_OUT_CH(name, src, dest, ...) (&_ch_mc_ ## src ## _ ## name) 328 | 329 | /** @brief Internal macro for counting channel arguments to a variadic macro */ 330 | #define NUM_CHANS(...) (sizeof((void *[]){__VA_ARGS__})/sizeof(void *)) 331 | 332 | /** @brief Read the most recently modified value from one of the given channels 333 | * @details This macro retuns a pointer to the most recently modified value 334 | * of the requested field. 335 | * 336 | * NOTE: We pass the channel pointer instead of the field pointer 337 | * to have access to diagnostic info. The logic in chain_in 338 | * only strictly needs the fields, not the channels. 339 | */ 340 | // #define CHAN_IN1(field, chan0) (&(chan0->data.field.value)) 341 | #define CHAN_IN1(type, field, chan0) \ 342 | ((type*)((unsigned char *)chan_in(#field, sizeof(VAR_TYPE(type)), 1, \ 343 | chan0, offsetof(__typeof__(chan0->data), field)))) 344 | #define CHAN_IN2(type, field, chan0, chan1) \ 345 | ((type*)((unsigned char *)chan_in(#field, sizeof(VAR_TYPE(type)), 2, \ 346 | chan0, offsetof(__typeof__(chan0->data), field), \ 347 | chan1, offsetof(__typeof__(chan1->data), field)))) 348 | #define CHAN_IN3(type, field, chan0, chan1, chan2) \ 349 | ((type*)((unsigned char *)chan_in(#field, sizeof(VAR_TYPE(type)), 3, \ 350 | chan0, offsetof(__typeof__(chan0->data), field), \ 351 | chan1, offsetof(__typeof__(chan1->data), field), \ 352 | chan2, offsetof(__typeof__(chan2->data), field)))) 353 | #define CHAN_IN4(type, field, chan0, chan1, chan2, chan3) \ 354 | ((type*)((unsigned char *)chan_in(#field, sizeof(VAR_TYPE(type)), 4, \ 355 | chan0, offsetof(__typeof__(chan0->data), field), \ 356 | chan1, offsetof(__typeof__(chan1->data), field), \ 357 | chan2, offsetof(__typeof__(chan2->data), field), \ 358 | chan3, offsetof(__typeof__(chan3->data), field)))) 359 | #define CHAN_IN5(type, field, chan0, chan1, chan2, chan3, chan4) \ 360 | ((type*)((unsigned char *)chan_in(#field, sizeof(VAR_TYPE(type)), 5, \ 361 | chan0, offsetof(__typeof__(chan0->data), field), \ 362 | chan1, offsetof(__typeof__(chan1->data), field), \ 363 | chan2, offsetof(__typeof__(chan2->data), field), \ 364 | chan3, offsetof(__typeof__(chan3->data), field), \ 365 | chan4, offsetof(__typeof__(chan4->data), field)))) 366 | 367 | /** @brief Write a value into a channel 368 | * @details Note: the list of arguments here is a list of 369 | * channels, not of multicast destinations (tasks). A 370 | * multicast channel would show up as one argument here. 371 | */ 372 | #define CHAN_OUT1(type, field, val, chan0) \ 373 | chan_out(#field, &val, sizeof(VAR_TYPE(type)), 1, \ 374 | chan0, offsetof(__typeof__(chan0->data), field)) 375 | #define CHAN_OUT2(type, field, val, chan0, chan1) \ 376 | chan_out(#field, &val, sizeof(VAR_TYPE(type)), 2, \ 377 | chan0, offsetof(__typeof__(chan0->data), field), \ 378 | chan1, offsetof(__typeof__(chan1->data), field)) 379 | #define CHAN_OUT3(type, field, val, chan0, chan1, chan2) \ 380 | chan_out(#field, &val, sizeof(VAR_TYPE(type)), 3, \ 381 | chan0, offsetof(__typeof__(chan0->data), field), \ 382 | chan1, offsetof(__typeof__(chan1->data), field), \ 383 | chan2, offsetof(__typeof__(chan2->data), field)) 384 | #define CHAN_OUT4(type, field, val, chan0, chan1, chan2, chan3) \ 385 | chan_out(#field, &val, sizeof(VAR_TYPE(type)), 4, \ 386 | chan0, offsetof(__typeof__(chan0->data), field), \ 387 | chan1, offsetof(__typeof__(chan1->data), field), \ 388 | chan2, offsetof(__typeof__(chan2->data), field), \ 389 | chan3, offsetof(__typeof__(chan3->data), field)) 390 | #define CHAN_OUT5(type, field, val, chan0, chan1, chan2, chan3, chan4) \ 391 | chan_out(#field, &val, sizeof(VAR_TYPE(type)), 5, \ 392 | chan0, offsetof(__typeof__(chan0->data), field), \ 393 | chan1, offsetof(__typeof__(chan1->data), field), \ 394 | chan2, offsetof(__typeof__(chan2->data), field), \ 395 | chan3, offsetof(__typeof__(chan3->data), field), \ 396 | chan4, offsetof(__typeof__(chan4->data), field)) 397 | 398 | /** @brief Transfer control to the given task 399 | * @param task Name of the task function 400 | * */ 401 | #define TRANSITION_TO(task) transition_to(TASK_REF(task)) 402 | 403 | #endif // CHAIN_H 404 | -------------------------------------------------------------------------------- /GUIDE.md: -------------------------------------------------------------------------------- 1 | This document is a companion guide to the artifacts for OOSPLA 2016 paper #28: 2 | "Chain: Tasks and Channels for Reliable Intermittent Programs." 3 | 4 | **NOTE:** This library is packaged for 5 | [Maker](https://github.com/CMUAbstract/maker) build system, and is built 6 | automatically together with the dependent application. Use the [LED 7 | blinker](https://github.com/CMUAbstract/app-blinker-chain) example application 8 | as a template to get started quickly. 9 | 10 | **NOTE:** The following guide is outdated. 11 | 12 | Table of Contents 13 | ================= 14 | 15 | * [Synopsis](#synopsis) 16 | * [Virtual Machine Image](#virtual-machine-image) 17 | * [Dependencies](#dependencies) 18 | * [MSP430 Toolchain](#msp430-toolchain) 19 | * [Maker](#maker) 20 | * ['''OPTIONAL''': LLVM/Clang](#optional-llvmclang) 21 | * [Environment](#environment) 22 | * [Libraries](#libraries) 23 | * [WISP base firmware library](#wisp-base-firmware-library) 24 | * [Energy-Interference-free Debugger (EDB) and libedb](#energy-interference-free-debugger-edb-and-libedb) 25 | * [Auxiliary libraries](#auxiliary-libraries) 26 | * [libmsp](#libmsp) 27 | * [libmspprintf](#libmspprintf) 28 | * [libmspconsole](#libmspconsole) 29 | * [libio](#libio) 30 | * [libmspmath](#libmspmath) 31 | * [libmspbuiltins](#libmspbuiltins) 32 | * [Chain Runtime](#chain-runtime) 33 | * [Prior work (OPTIONAL)](#prior-work-optional) 34 | * [Mementos](#mementos) 35 | * [Runtime libs](#runtime-libs) 36 | * [LLVM passes](#llvm-passes) 37 | * [DINO](#dino) 38 | * [Applications](#applications) 39 | * [LED Blinker / Template for Custom Applications](#led-blinker--template-for-custom-applications) 40 | * [Cuckoo Filter](#cuckoo-filter) 41 | * [Cold-Chain Equipment Monitoring](#cold-chain-equipment-monitoring) 42 | * [AR](#ar) 43 | * [RSA](#rsa) 44 | 45 | Synopsis 46 | ======== 47 | 48 | This document is a companion guide to the artifacts for OOSPLA 2016 paper #28: 49 | "Chain: Tasks and Channels for Reliable Intermittent Programs." 50 | 51 | The artifact is a complete, deployment-ready development environment for our 52 | language Chain, which targets intermittently-powered energy-harvesting embedded 53 | systems. This guide precisely documents how to set up the 54 | development environment from scratch and how to build the applications 55 | evaluated in the paper. 56 | 57 | The main Chain language implementation is a self-contained library called 58 | `libchain`. This guide describes how to build `libchain` and then build all 59 | applications to refer to `libchain` headers and link to the built `libchain` 60 | library. This guide also describes how to build build-system components and 61 | supporting libraries that are required by applications and by `libchain`. 62 | This guide also includes a "Hello, World!" (LED blinking) application that 63 | makes a handy starting point for quickly creating custom Chain applications to 64 | run on real hardware. 65 | 66 | In the interest of completeness of the evaluation of our artifact, we also 67 | include a complete, deployment-ready development environment for DINO and 68 | Mementos, the systems that were the basis of comparison for Chain in the 69 | paper. That environment also includes DINO- and Mementos-ready versions of the 70 | applications, too, allowing an artifact reviewer to build Chain, DINO, and 71 | Mementos versions side-by-side. Our custom build system manages the complexity of 72 | targeting these multiple build variants. 73 | 74 | Chain targets real, energy-harvesting hardware, namely the Wireless 75 | Identification and Sensing Platform (WISP) v5, which is based on the TI 76 | MSP430FR5969 microcontroller. Our custom build system manages the complexity 77 | of cross-compiling for this hardware. 78 | 79 | The workflow in this guide consists of (1) installing third-party dependencies, 80 | (2) building auxiliary platform-support libraries, (3) building the Chain 81 | runtime, and (4) building the applications. This guide describes exactly what 82 | it takes to set up a Chain development environment on any reasonable Linux 83 | machine (tested on Arch Linux and users reported success on Ubuntu). 84 | 85 | To save the reviewer time, we already followed all the steps in this guide on a 86 | virtual machine (VM) and included the VM image with the artifact submission. 87 | If you are using the VM, the Chain development environment is already built, 88 | set up, and ready to roll: you can skip directly to the section on building and 89 | re-building the applications, or perhaps, create your own, new Chain 90 | application based on the LED blinking application. 91 | 92 | The URLs of the source code repositories have been hidden in this guide, to 93 | keep the review blind. The URLs are still present in the VM image, because it 94 | is impractical to purge that information, as VCS is part of our workflow to 95 | keep the code modular. To keep the review blind, we trust the reviewer to not 96 | inspect the Git history. 97 | 98 | To demonstrate the results illustrated in the paper directly would put an 99 | unreasonable burden on an artifact evaluator, requiring them to obtain and 100 | configure an RF energy-harvesting power source, a WISP, and a hardware WISP 101 | programmer. Instead, (as per discussion with the AEC chairs) we included a 102 | demonstration video for evaluation, alongside our artifact VM. The video 103 | demonstrates the application binaries running on the target hardware, 104 | illustrating the expected execution behavior under intermittent power, 105 | validating the results from the paper. The videos are located in `experiments/` 106 | directory: 107 | 108 | * `blinker-experiments.ogv` : a demonstration how Chain maintains 109 | application correctness across power failures, using a basic 110 | "Hello, World!" application 111 | * Screencast demonstrations of each implementation variant of an application 112 | using Chain and using state-of-the-art (Mem-NV, Mem-V, and DINO), running on 113 | the real WISP device, first powered by a wireless intermittent energy source, 114 | and then -- for validation -- powered by a wired continuously available energy 115 | source: 116 | * `cuckoo-filter/` : Cuckoo Filtering application 117 | * `cem/` : Cold-Chain Equipment Monitor application 118 | * `rsa/` : RSA encryption application 119 | * *Experiments with Activity Recognition app could not be repeated because 120 | the experiment requires wiring setup for training the model that could 121 | not be re-constructed in the alloted time. The code for this application can be 122 | inspected and built for all systems. 123 | 124 | *NOTE*: The WISP device used for the original experiments is no longer 125 | available in it's past configuration, since it had to be modified (e.g. the 126 | capacitor had to be decreased from 47uF to 10uF). The RF environment also 127 | necesserily changed. As a result, the magnitudes of the quantative performance 128 | results in the video will differ from the original experiments. Naturally, all 129 | correctness claims about consistency of program state in non-volatile memory 130 | hold in the old and the new experiments alike. 131 | 132 | Virtual Machine Image 133 | --------------------- 134 | 135 | We include a virtual machine appliance image in OVA format. It was created with 136 | VirtualBox v5, but it might also be possible to use the appliance in VMware. 137 | 138 | Prior to importing the virtual appliance into VirtualBox, create a host-only 139 | network interface to create a network connection to the VM from the host. The 140 | virtual appliance has two network interfaces, one NAT, once host-only. The 141 | point of creating a host-only network connection is to allow you to SSH to the 142 | VM from your host machine; the terminal in the VM is ugly and using your native 143 | terminal via SSH will be much nicer. The VM automatically starts an SSH 144 | daemon, allowing you to SSH from your host to the VM. If you are unsure of the 145 | IP address of the VM, you can check by logging in directly through the VirtualBox GUI 146 | and executing `ip addr`. The IP address will probably be something like 147 | `192.168.56.101` and its subnet should correspond to the subnet of the 148 | host-only network adaptor on your host machine. 149 | 150 | The virtual machine should have two disks attached to the SATA controller. 151 | Both disks mount automatically on boot. 152 | 153 | The operating system is a minimal installation of Arch Linux. 154 | 155 | *Credentials* are: user `reviewer` password `review`. 156 | 157 | If you are using the VM, the build environment is already installed, and all 158 | source code is already fetched and built -- *it is not necessary to build 159 | everything again unless you want to*. You can follow the next steps to set up 160 | the development environment again from scratch, either as an exercise, or 161 | to ensure that our tutorial is correct. Alternatively, you can skip to the 162 | section called `Applications` below for instructions on how to build, re-build, 163 | or extend our applications. 164 | 165 | Dependencies 166 | ============ 167 | 168 | ## MSP430 Toolchain 169 | 170 | TI MSP430 GCC Toolchain is the cross-compiler used to build executables for the 171 | MSP430 platform. 172 | 173 | Version: Only v3.05 is supported; changes in v4.00 appear to not be backward compatible. 174 | 175 | Upstream: http://www.ti.com/tool/msp430-gcc-opensource 176 | 177 | Arch Linux package (from AUR): `mspgcc-ti`, installed to `/opt/ti/mspgcc` 178 | 179 | 180 | ## Maker 181 | 182 | The Maker tool is a custom package manager and builder for C code written in 183 | GNU Make. Maker makefiles are high level instructions that can specify 184 | dependent libraries. 185 | 186 | This artifact uses an older version of Maker, in which libraries need to be 187 | built manually. 188 | 189 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/maker 190 | 191 | Edit path to TI MSP430 GCC Toolchain in `maker/Makefile.env`: 192 | 193 | TOOLCHAIN_ROOT ?= $(TI_ROOT)/mspgcc 194 | 195 | 196 | ## '''OPTIONAL''': LLVM/Clang 197 | 198 | LLVM/Clang is a compiler with a MSP430 assembly backend, that relies on MSP430 199 | GCC Toolchain (see above) to compile the assembly into native object code. 200 | 201 | The LLVM/Clang toolchain is NOT needed to build Chain applications. It is only 202 | needed to build applications with competing systems: Mementos and DINO. 203 | 204 | Version: v3.8 with a patch that fixes a compatibility issue in MSP430 backend. 205 | 206 | Upstream: http://llvm.org/ 207 | 208 | Build instructions: http://llvm.org/docs/CMake.html 209 | 210 | Brief synopsis: 211 | 212 | mkdir llvm-install && mkdir llvm-build && cd llvm-build 213 | cmake -G "Unix Makefiles" -DLLVM_OPTIMIZED_TABLEGEN=1 -DLLVM_TARGETS_TO_BUILD="MSP430;X86" \ 214 | -DCMAKE_LINKER=/usr/bin/ld.gold -DCMAKE_INSTALL_PREFIX=../llvm-install ../llvm-src 215 | 216 | In the virtual machine, LLVM was built from source at `/opt/llvm/llvm-src` and 217 | installed to `/opt/llvm/llvm-install`. The build directory (`/opt/llvm/llvm-build`) was 218 | removed to save space. 219 | 220 | The patch for MSP430 backend is commit 81386d4b4fdd80f038fd4ebddc59613770ea236c. 221 | 222 | Environment 223 | ----------- 224 | 225 | Create a workspace directory: `mkdir ~/src && cd ~/src`. 226 | 227 | The build framework relies on the following environment variables to be set: 228 | 229 | export DEV_ROOT=$HOME/src 230 | export MAKER_ROOT=$HOME/src/maker 231 | export TOOLCHAIN_ROOT=/opt/ti/mspgcc 232 | export LLVM_ROOT=/opt/llvm/llvm-install 233 | 234 | Libraries 235 | --------- 236 | 237 | ## WISP base firmware library 238 | 239 | The `wisp-base` library provides low-level functionality for setting up the 240 | microcontroller and using the peripherals on the WISP platform. 241 | 242 | We use a forked version with bug fixes and other build-related patches. 243 | 244 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/wisp5.git 245 | cd wisp5/CCS/wisp-base/gcc 246 | make 247 | 248 | The above command will create `libwisp-base.a` in the above directory. 249 | 250 | ## Energy-Interference-free Debugger (EDB) and libedb 251 | 252 | EDB is a hardware/software debugger for energy-harvesting devices previously 253 | developed and [published in prior 254 | work](http://dl.acm.org/citation.cfm?id=2872409). EDB is a hardware printed 255 | circuit board, a library that links into the target application (`libedb`), and 256 | host-side software. Our target hardware (i.e., WISP) has no simple way to 257 | produce human-readable output. We use EDB's intermittence-safe `PRINTF` to 258 | forward output to the host workstation from the target, while it runs on 259 | intermittent (RF) energy. 260 | 261 | cd ~/src 262 | git clone -b oopsla16-artifact git@github.com:CMUAbstract/libedb.git 263 | cd libedb/gcc 264 | make 265 | 266 | The above command will create `libedb.a` in the above folder. 267 | 268 | ## Auxiliary libraries 269 | 270 | In order to run on the target hardware platform, the applications rely on 271 | several re-usable platform support libraries: 272 | 273 | * libmsp: MCU clock setup, peripherals (complementary to `libwisp-base`) 274 | * libmspprintf: a lean `printf` implementation 275 | * libmspconsole: an output "backend" for `libio` using HW UART 276 | * libio: wrapper around different output "backends" (e.g. HW UART, SW UART, EDB PRINTF) 277 | * libmspmath: division, multiplications, sqrt 278 | * libmspbuiltins: builtin functions (e.g. `__delay_cycles`) not provided by LLVM/Clang's runtime library 279 | 280 | Each of these libraries must be built either with the same compiler as the 281 | application or unconditionally with GCC (even if linked into an LLVM-built 282 | application). The following steps obtain the source and produce all the 283 | necessary builds of the libraries. 284 | 285 | ### libmsp 286 | 287 | cd ~/src 288 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/libmsp.git 289 | 290 | cd bld/gcc && make 291 | 292 | export LLVM_ROOT=/opt/llvm/llvm-install 293 | cd bld/clang && make 294 | 295 | ### libmspprintf 296 | 297 | cd ~/src 298 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/libmspprintf.git 299 | cd bld/gcc && make 300 | 301 | ### libmspconsole 302 | 303 | cd ~/src 304 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/libmspconsole.git 305 | cd bld/gcc && make 306 | 307 | ### libio 308 | 309 | The `libio` library only contains headers, so there is nothing to build. 310 | 311 | cd ~/src 312 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/libio.git 313 | 314 | ### libmspmath 315 | 316 | cd ~/src 317 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/libmspmath.git 318 | cd bld/gcc && make 319 | 320 | ### libmspbuiltins 321 | 322 | Note that `libmspbuiltins` is always built with GCC, but included in LLVM/Clang 323 | builds of the application. (In GCC application builds, this library may be still 324 | included for uniformity, but in that case it is effectively empty.) 325 | 326 | cd ~/src 327 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/libmspbuiltins.git 328 | cd bld/gcc && make 329 | 330 | Chain Runtime 331 | ============= 332 | 333 | The Chain runtime consists of one library: `libchain`. In this section we build 334 | the library. Any application that wishes to use the Chain abstractions needs to 335 | include the headers from this library and link against the library binary. 336 | 337 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/libchain.git 338 | cd bld/gcc 339 | make 340 | 341 | The above command will create `libchain.a` in the above directory. 342 | 343 | To show communication activity over channels using printf, define the following 344 | flag when compiling libchain *and* the application: 345 | 346 | make LIBCHAIN_ENABLE_DIAGNOSTICS=1 347 | 348 | 349 | Prior work (OPTIONAL) 350 | ===================== 351 | 352 | Mementos 353 | -------- 354 | 355 | Mementos is one of the previously proposed systems for intermittent computing 356 | that we evaluated. It consists of a runtime library and LLVM passes that 357 | must be run on the application binaries. Once both parts of Mementos have been 358 | built, building applications with Mementos is handled by the Maker tool. 359 | The application developer needs to create only a high-level Maker makefile in a 360 | dedicated build directory (`bld/mementos`). The applications in this artifact 361 | all contain this makefile. 362 | 363 | We use a fork of Mementos that differs from the original only in packaging 364 | details to make it easier to re-use across applications. Part of this 365 | re-packaging are patches for compatibility with a newer version of GCC and 366 | LLVM/Clang. 367 | 368 | The following steps in this section fetch and build Mementos. This needs to be 369 | done only once -- multiple applications use the artifacts built here. 370 | 371 | cd ~/src 372 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/mementos.git 373 | 374 | ### Runtime libs 375 | 376 | cd mementos/autoreconf && ./AutoRegen.sh && cd .. 377 | ./configure --with-gcc=/opt/ti/mspgcc --with-llvm=/opt/llvm/llvm-install 378 | make FRAM=1 379 | 380 | The above command builds several variants of the Mementos runtime library 381 | (`mementos+*.bc`) with LLVM/Clang. This library is linked into the application 382 | when the application is built with Mementos. 383 | 384 | We use the best performing variant, `timer+latch`, which inserts checkpoints at 385 | loop backedges and throttles checkpointing frequency with a timer. 386 | 387 | ### LLVM passes 388 | 389 | cd llvm && mkdir build && cd build 390 | cmake -DCMAKE_MODULE_PATH=/opt/llvm/llvm-install/share/llvm/cmake \ 391 | -DCMAKE_PREFIX_PATH=/opt/llvm/llvm-install \ 392 | -DCMAKE_INSTALL_PREFIX=/opt/llvm/llvm-install \ 393 | -DCMAKE_CXX_FLAGS="-std=c++11" .. 394 | cd .. 395 | make && make install 396 | 397 | The above command installs `Mementos.so` shared object to the LLVM installation path. 398 | This shared object will be loaded by the LLVM `opt` tool as part of each application 399 | Mementos build. 400 | 401 | DINO 402 | ---- 403 | 404 | DINO is one of the previously proposed systems for intermittent computing that 405 | we evaluated. Like Mementos, it consists of a runtime library and LLVM passes. 406 | We avoid building the LLVM passes by instrumenting our applications manually. 407 | Maker handles the complexity of building an application with DINO. 408 | 409 | Similarly to our re-packaging of Mementos, we fork and re-package DINO, 410 | patching it as necessary for compatibility with a newer version of GCC and 411 | LLVM/Clang, and ease of re-use by applications. 412 | 413 | The following steps fetch and build DINO. This needs to be done only once -- 414 | multiple applications use the artifacts built here. 415 | 416 | cd ~/src 417 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/dino.git 418 | 419 | cd ~/dino/DinoRuntime 420 | export LLVM_ROOT=/opt/llvm/llvm-install 421 | export GCC_ROOT=/opt/ti/mspgcc 422 | export MEMENTOS_ROOT=~/src/mementos 423 | make -f Makefile.clang 424 | 425 | The above command will create `dino.a.bc` in the above directory. Note 426 | 427 | Applications 428 | ============ 429 | 430 | In this section we build five applications with Chain as well as with Mementos 431 | and DINO. The first application is a "Hello, World!" (LED blinking) template 432 | skeleton that can be copied and used to quickly create a custom application, 433 | build and run on the target hardware. The remaining four applications are those 434 | evaluated in the paper: 435 | 436 | * CF: Approximate set membership using a cuckoo filtering 437 | * CEM: Cold-Chain Equipment Monitoring with LZW compression 438 | * RSA: Data Encryption using RSA 439 | * AR: Activity Recognition using an accelerometer 440 | 441 | [Several additional application 442 | examples](https://github.com/amjadmajid/chain_apps) in Chain have been 443 | generously contributed by [Amjad Majid](http://www.st.ewi.tudelft.nl/~amjad/) 444 | at Embedded Software group at [TU Delft](http://www.es.ewi.tudelft.nl). 445 | 446 | Each application in the above list has a dedicated repository for its 447 | implementation in Chain and a separate repository with its implementation in C. 448 | The latter is optionally compilable with checkpointing-based systems, Mementos 449 | or DINO. The names of the pairs of repositories for each application follow the 450 | patterns, `app-*-chain` and `app-*-chkpt`, respectively. 451 | 452 | In the virtual machine image applications have been cloned and built 453 | in `~/src/apps/*` directories. 454 | 455 | The source tree layout for all application repositories follows the following 456 | structure: 457 | 458 | README 459 | + src 460 | + bld 461 | 462 | Application source code is in `src`. Subdirectories of `bld` contain Maker 463 | makefiles and built application binaries. Each of these build directories 464 | contains a high-level Maker makefile specific to that build type. 465 | Builds can co-exist without interference. To build a particular build type run 466 | `make` in the corresponding directory. 467 | 468 | For *Chain implementations*, the relevant build sub-directory is always 469 | `bld/gcc`. For example, to build CF application implemented in Chain: 470 | 471 | cd ~/src/apps/app-cuckoo-chain 472 | cd bld/gcc 473 | make 474 | 475 | For *checkpointing-based implementations* the build sub-directories are: 476 | 477 | + bld 478 | + gcc 479 | + clang 480 | + mementos 481 | + dino 482 | 483 | For exampe, to build a particular build type run `make` in the corresponding directory. 484 | For example, to build the CF application with DINO: 485 | 486 | cd ~/src/apps/app-cuckoo-chkpt 487 | cd bld/dino 488 | make 489 | 490 | The Mem-V and Mem-NV variants share the same build directory. The Mem-V variant 491 | is the default. To build the Mem-NV variant run `make MEMENTOS_VARIANT=NV`. 492 | 493 | **NOTE**: Some applications may have a further hierarchy split at the top-level into 494 | `bin` and `lib`. In this case `lib` is an auxiliary library that contains 495 | source files that must be excluded from the checkpointing instrumentation (e.g. 496 | printf-related code). The application in `bin` has the same structure as 497 | described in the diagram above, and simply links in the auxiliary library from 498 | `lib`. The library and the binary must be built separately in order. The 499 | necessary commands are given below. 500 | 501 | **NOTE**: Before building any applications, make sure that the environment 502 | variables listed in the "Environment" section of this document are set. 503 | 504 | ## LED Blinker / Template for Custom Applications 505 | 506 | The LED Blinker is a simple application that demonstrates basic use of tasks 507 | and channels. The source tree of this application code can be copied and 508 | modified to quickly create a custom application using Chain. 509 | 510 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-blinker-chain.git 511 | cd bld/gcc && make 512 | 513 | ## Cuckoo Filter 514 | 515 | The Cuckoo Filter application populates a cuckoo filter data structure with 516 | pseudo-random keys and quieries it to determine appoximate set membership. 517 | 518 | cd ~/src/apps 519 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-cuckoo-chain.git 520 | cd app-cuckoo-chain && cd bld/gcc && make 521 | 522 | cd ~/src/apps 523 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-cuckoo-chkpt.git 524 | cd app-cuckoo-chkpt/lib/bld 525 | cd gcc && make && cd .. 526 | cd clang && make && cd .. 527 | cd ../.. 528 | 529 | cd bin/bld 530 | cd gcc && make && cd .. 531 | cd clang && make && cd .. 532 | cd mementos && make && cd .. 533 | cd dino && make && cd .. 534 | 535 | The above commands will create binaries `cuckoo.out` in each build subdirectory, 536 | that are ready to be flashed onto the target device. 537 | 538 | 539 | ## Cold-Chain Equipment Monitoring 540 | 541 | The CEM application reads temperature samples from a sensor, compresses the 542 | data stream with LZW and outputs the compressed stream to EDB console. 543 | 544 | cd ~/src/apps 545 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-temp-log-chain.git 546 | cd app-temp-log-chain && cd bld/gcc && make 547 | 548 | cd ~/src/apps 549 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-temp-log-chkpt.git 550 | cd app-temp-log-chkpt/lib/bld 551 | cd gcc && make && cd .. 552 | cd clang && make && cd .. 553 | cd ../.. 554 | 555 | cd bin/bld 556 | cd gcc && make && cd .. 557 | cd clang && make && cd .. 558 | cd mementos && make && cd .. 559 | cd dino && make && cd .. 560 | 561 | The above commands will create binaries `temp-log.out` in each build subdirectory, 562 | that are ready to be flashed onto the target device. 563 | 564 | ## AR 565 | 566 | Activity Recognition application reads sensor data from an accelerometer and 567 | classifies into two classes -- "stationary" and "moving" -- based on a 568 | pre-trained model. At the end of set number of samples, aggregate statistics 569 | about the classes are reported via the EDB console. 570 | 571 | cd ~/src/apps 572 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-activity-chain.git 573 | cd app-activity-chain && cd bld/gcc && make 574 | 575 | cd ~/src/apps 576 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-activity-chkpt.git 577 | cd app-activity-chkpt/bld 578 | cd gcc && make && cd .. 579 | cd clang && make && cd .. 580 | cd mementos && make && cd .. 581 | cd dino && make && cd .. 582 | 583 | The above commands will create binaries `ar.out` in each build subdirectory, 584 | that are ready to be flashed onto the target device. 585 | 586 | 587 | ## RSA 588 | 589 | The RSA application encrypts plain text stored in non-volatile memory and 590 | stores the cyphertext also in non-volatile memory. After encryption is 591 | completed, the cyphertex is output in hex via EDB console. Sample plaintext 592 | inputs are available in `data/` subdirectory. 593 | 594 | **IMPORTANT NOTE**: RSA was the first application implemented prior to 595 | (syntactic) improvements to `libchain` API, and therefore is compatible only 596 | with an earlier version of `libchain` (v0.1). To fetch and build this version 597 | of `libchain` into `~/src/libchain-v0.1` (already fetched and built in the VM 598 | image): 599 | 600 | cd ~/src 601 | git clone -b oopsla16-artifact-v0.1 https://github.com/CMUAbstract/libchain.git libchain-v0.1 602 | cd libchain-v0.1 bld/gcc 603 | make 604 | 605 | To build the applicaion against the earlier `libchain` version, set 606 | 607 | cd ~/src/apps 608 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-rsa-chain.git 609 | cd app-rsa-chain/bld/gcc 610 | make LIBCHAIN_ROOT=$DEV_ROOT/libchain-v0.1 611 | 612 | cd ~/src/apps 613 | git clone -b oopsla16-artifact https://github.com/CMUAbstract/app-rsa-chkpt.git 614 | cd app-rsa-chkpt/bld 615 | cd gcc && make && cd .. 616 | cd clang && make && cd .. 617 | cd mementos && make && cd .. 618 | cd dino && make && cd .. 619 | 620 | The above commands will create binaries `rsa.out` in each build subdirectory, 621 | that are ready to be flashed onto the target device. 622 | --------------------------------------------------------------------------------