├── .gitignore ├── LICENSE ├── README.md ├── doc └── images │ └── pump-youtube-thumbnail.jpg ├── examples ├── pump │ ├── Makefile │ ├── pump.c │ └── pump_tasks.c └── test │ ├── Makefile │ ├── factor_tasks.c │ ├── main.c │ ├── simple_tasks.c │ ├── toplevel_tasks.c │ └── types.h ├── src ├── delay.h ├── dtask.c └── dtask.h ├── third_party ├── README.md ├── __init__.py └── toposort.py └── tools └── generate_task_header.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *_tasks.h 4 | *.pyc 5 | /examples/test/test 6 | /examples/pump/pump.elf 7 | .tramp_history 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DTask 2 | ===== 3 | 4 | DTask is a scheduler for statically dependent tasks. 5 | 6 | 7 | Many embedded sensor applications can be structured as a set of dependent computations starting from an interrupt. Intermediate computations may be shared, such as filtered data. This is a type of data flow programming. DTask is designed to make this easy to express, and yet require minimal resources. DTask could replace an RTOS in some situations, and is much less complicated. 8 | 9 | DTask uses annotations in the source code to calculate task dependencies during compilation, and then sorts them topologically. At runtime, the task are processed in waves, so that results can be passed atomically without locking. Tasks can be enabled and disabled at runtime, which will automatically enable/disable other related tasks. 10 | 11 | DTask allows definition of many small modular tasks that will be easier to test, and yet can be inlined for performance. 12 | 13 | This is not an official Google product. 14 | 15 | Usage 16 | ===== 17 | 18 | Declare a task like this: 19 | 20 | ``` 21 | DTASK(task_name, result_type) 22 | { 23 | bool z_is_valid; 24 | result_type z; 25 | 26 | int x = *DREF(an_integer); 27 | int y = *DREF(another_integer); 28 | // compute z from x and y, set z_is_valid if the computation yields a (new/valid) result 29 | if(z_is_valid) { 30 | task_name = z; 31 | } 32 | return z_is_valid; 33 | } 34 | ``` 35 | 36 | Code can be added to run when the task is enabled or disabled: 37 | 38 | ``` 39 | DTASK_ENABLE(task_name) 40 | { 41 | // run when task_name is enabled 42 | } 43 | 44 | DTASK_DISABLE(task_name) 45 | { 46 | // run when task_name is disabled 47 | } 48 | ``` 49 | 50 | Tasks are declared in groups. A task group is declared with: 51 | 52 | ``` 53 | DTASK_GROUP(group_name) 54 | ``` 55 | 56 | The following tasks are part of this group. This will create a struct type that contains the state named `group_name_state`, which contains a field for every task. 57 | 58 | API 59 | === 60 | 61 | Outside of a task (or from another task group, see below): 62 | 63 | - `dtask_enable(state, TASK1 | TASK2)`: Enable TASK1 and TASK2 (and dependencies) 64 | - `dtask_disable(state, TASK1 | TASK2)`: Disable TASK1 and TASK2 (and dependents) 65 | - `dtask_clear(state, TASK1 | TASK2)`: Only enable TASK1 and TASK2 if enabled tasks depend on them 66 | - `dtask_switch(state, TASK1 | TASK2)`: Enable only TASK1 and TASK2 and depencencies, disable all others 67 | - `dtask_select(state)`: commit the changes from the above calls, and run DTASK_ENABLE/DTASK_DISABLE code for tasks that are enabled/disabled. 68 | - `dtask_run(state, INITIAL_TASK)`: run tasks, running the task named `initial` unconditionally. 69 | 70 | And within a task: 71 | 72 | - to get a value: `x = *DREF(task_name);` 73 | - to set the result of a task: `*DREF(task_name) = x;` 74 | - `task_name` should be the name of the current task only. 75 | 76 | See `main.c`, and `*_tasks.c` for an example. Run `make && ./test` to build and run the example. 77 | 78 | Delays 79 | ====== 80 | 81 | Delays can be used to look at the history of a value, useful to implement FIR filters for example. 82 | 83 | Use `DELAY(type, length)` as a task type to implement a delay. Then use the following API: 84 | 85 | - `DELAY_READ(delay, type, len, i)`: returns a pointer to the value `i` waves ago 86 | - `DELAY_WRITE(delay, type, len, value)`: writes the value for the current wave 87 | - `DELAY_FILL(delay, type, len, value)`: fills the delay with a value 88 | 89 | The delay must be declared with `DECLARE_DELAY(type, length)`. 90 | 91 | See `factor_tasks.c` for an example. 92 | 93 | Nesting 94 | ======= 95 | 96 | Task groups can be nested by calling `dtask_run()` from a task in another task group. 97 | 98 | See `toplevel_tasks.c` for an example. 99 | 100 | MSP430 Lauchpad Pump Demo 101 | ========================= 102 | 103 | Click below for a video of the pump controller demo in operation: 104 | 105 | [![Pump Demo Video](https://github.com/google/dtask/blob/master/doc/images/pump-youtube-thumbnail.jpg)](https://youtu.be/qKPn5xx2aHA) 106 | -------------------------------------------------------------------------------- /doc/images/pump-youtube-thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/dtask/9e39f3956bb4eb646bbaa1b49f8fa98ad78df058/doc/images/pump-youtube-thumbnail.jpg -------------------------------------------------------------------------------- /examples/pump/Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DIR := build 2 | DTASK_ROOT := ../.. 3 | DTASK_SRC := $(DTASK_ROOT)/src 4 | DTASK_TOOLS := $(DTASK_ROOT)/tools 5 | 6 | ARCH = MSP430 7 | MCU = msp430g2231 8 | CFLAGS = -std=gnu99 -D__$(ARCH)__ -g -Os -mmcu=$(MCU) -DNO_CLZ -DNO_CTZ 9 | CC = msp430-gcc 10 | MCU_INCLUDE = /usr/msp430/include 11 | 12 | SRC := $(wildcard *.c) 13 | DTASK_TARGETS := pump_tasks 14 | DTASK_GENERATED_HEADERS := $(patsubst %, $(BUILD_DIR)/%.h, $(DTASK_TARGETS)) 15 | HEADERS := $(wildcard *.h) $(DTASK_GENERATED_HEADERS) 16 | OBJS := $(patsubst %.c, $(BUILD_DIR)/%.o, $(SRC)) $(BUILD_DIR)/dtask.o 17 | CFLAGS += -I $(DTASK_SRC) -I $(BUILD_DIR) 18 | 19 | .PHONY: all 20 | all: pump.elf 21 | 22 | print-%: 23 | @echo $($*) 24 | 25 | pump.elf: $(OBJS) 26 | $(CC) $(CFLAGS) $^ -o $@ 27 | 28 | $(DTASK_GENERATED_HEADERS): $(BUILD_DIR)/%.h : $(SRC) 29 | @mkdir -p $(BUILD_DIR) 30 | PYTHONPATH=$(DTASK_ROOT) python2 $(DTASK_TOOLS)/generate_task_header.py -I $(DTASK_SRC) -I $(MCU_INCLUDE) -b 16 --target $* $(SRC) 31 | mv $*.h $(BUILD_DIR) 32 | 33 | $(BUILD_DIR)/%.o: %.c $(HEADERS) 34 | @mkdir -p $(BUILD_DIR) 35 | $(CC) $(CFLAGS) -c $< -o $@ 36 | 37 | $(BUILD_DIR)/dtask.o: $(DTASK_SRC)/dtask.c $(DTASK_SRC)/*.h 38 | @mkdir -p $(BUILD_DIR) 39 | $(CC) $(CFLAGS) -c $< -o $@ 40 | 41 | .PHONY: debug 42 | debug: 43 | msp430-gdb pump.elf --eval-command="target remote localhost:2000" 44 | 45 | .PHONY: clean 46 | clean: 47 | rm -rf $(BUILD_DIR) 48 | rm -f pump.elf 49 | -------------------------------------------------------------------------------- /examples/pump/pump.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #include 16 | #include "dtask.h" 17 | 18 | #ifndef DTASK_GEN 19 | #include "pump_tasks.h" 20 | #endif 21 | 22 | // state for all pump tasks 23 | static pump_tasks_state_t state = DTASK_STATE(pump_tasks, 0, 0); 24 | 25 | // bit mask of initial tasks to run (triggered externally e.g. interrupt) 26 | static dtask_set_t initial = 0; 27 | 28 | static uint16_t clock = 0; 29 | 30 | // GPIO lines 31 | #define LED1 0x01 32 | #define LED2 0x40 33 | #define SWITCH 0x08 34 | #define PUMP_GPIO 0x80 35 | 36 | void set(uint8_t x, bool on) { 37 | P1OUT = (P1OUT & ~x) | (on ? x : 0); 38 | } 39 | 40 | // interrupts 41 | __attribute__((interrupt(TIMERA0_VECTOR))) 42 | void timer_isr() { 43 | P1IES = (P1IES & ~SWITCH) | (P1IN & SWITCH); // make sure button interrupts on the right edge 44 | clock++; 45 | initial |= TIMER; // mark timer task to run 46 | LPM0_EXIT; // exit low power mode 47 | } 48 | 49 | __attribute__((interrupt(PORT1_VECTOR))) 50 | void button_isr() { 51 | P1IES ^= SWITCH; // catch the other edge 52 | P1IFG &= ~SWITCH; // clear the interrupt flag 53 | initial |= GPIO_BUTTON; // mark button_gpio task to run 54 | LPM0_EXIT; // exit low power mode 55 | } 56 | 57 | // initialization 58 | void init() { 59 | 60 | // disable watchdog 61 | WDTCTL = WDTPW | WDTHOLD; 62 | 63 | // LEDs, switch, and pump 64 | P1DIR = LED1 | LED2 | PUMP_GPIO; 65 | P1OUT = SWITCH | PUMP_GPIO; 66 | P1REN = ~P1DIR; 67 | P1IES = SWITCH; 68 | P1IE = SWITCH; 69 | P1IFG = 0; 70 | 71 | // timer, ~8.5Hz, ~512 ticks per minute 72 | TACCTL0 = CCIE; 73 | TACTL = MC_1 | ID_3 | TASSEL_2; 74 | TACCR0 = 16845; /* DCO = ~1.15Mhz */ 75 | 76 | __eint(); // enable interrupts 77 | } 78 | 79 | void main() { 80 | init(); 81 | 82 | // enable the outputs 83 | dtask_enable((dtask_state_t *)&state, PUMP | STATUS_LED | UI); 84 | 85 | for(;;) { 86 | 87 | // atomically get the initial set 88 | __dint(); 89 | dtask_set_t run_initial = initial; 90 | initial = 0; 91 | __eint(); 92 | 93 | if(run_initial == 0) { 94 | 95 | // enter low power mode 96 | LPM0; 97 | 98 | } else { 99 | 100 | // external inputs 101 | state.gpio_button = !(P1IN & SWITCH); 102 | state.timer = clock; 103 | 104 | // run tasks 105 | dtask_run((dtask_state_t *)&state, run_initial); 106 | 107 | // external outputs 108 | set(PUMP_GPIO, state.pump); 109 | set(LED2, state.status_led); 110 | set(LED1, state.ui.led); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /examples/pump/pump_tasks.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #ifndef DTASK_GEN 16 | #include "pump_tasks.h" 17 | #endif 18 | 19 | #define BUTTON_RESET_TIME 2 20 | #define LONG_PRESS_TIME 16 21 | #define PUMP_PERIOD 5 22 | #define ENABLED_TIME 64 23 | #define MAX_ANIM 64 24 | 25 | DTASK_GROUP(pump_tasks) 26 | 27 | // button event types 28 | enum button_event { 29 | BTN_PRESS = 0, // normal press 30 | BTN_RELEASE, // release after press 31 | BTN_LONG_PRESS, // long press 32 | BTN_LONG_RELEASE // release after long press 33 | }; 34 | 35 | // pass though external button GPIO state 36 | DTASK(gpio_button, bool) { 37 | return true; 38 | } 39 | 40 | // pass through external timer 41 | DTASK(timer, uint16_t) { 42 | return true; 43 | } 44 | 45 | // converts raw GPIO to button events 46 | DTASK(button, struct { int event; uint16_t press_time; } ) { 47 | 48 | // dependencies 49 | bool gpio = *DREF(gpio_button); 50 | uint16_t time = *DREF(timer); 51 | int event = DREF(button)->event; 52 | 53 | // on GPIO change 54 | if(DTASK_AND(GPIO_BUTTON)) { 55 | // button presses are rate limited 56 | if(gpio && time - DREF(button)->press_time > BUTTON_RESET_TIME) { 57 | event = BTN_PRESS; 58 | DREF(button)->press_time = time; 59 | } else if(!gpio) { 60 | if(event == BTN_LONG_PRESS) { 61 | event = BTN_LONG_RELEASE; 62 | } else { 63 | event = BTN_RELEASE; 64 | } 65 | } 66 | } 67 | 68 | // promote press to a long press 69 | if(gpio && 70 | event == BTN_PRESS && 71 | time - DREF(button)->press_time > LONG_PRESS_TIME) { 72 | event = BTN_LONG_PRESS; 73 | } 74 | 75 | if(event == DREF(button)->event) { 76 | return false; 77 | } else { 78 | DREF(button)->event = event; 79 | return true; 80 | } 81 | } 82 | 83 | // animation types 84 | enum animation { 85 | ANIM_NONE = 0, 86 | ANIM_START, 87 | ANIM_CHANGE, 88 | ANIM_END, 89 | ANIM_READ 90 | }; 91 | 92 | // use button events to adjust the time setting 93 | DTASK(time_setting, struct {int on_time; uint16_t change_enable_ts; bool change_enabled; int animation;}) { 94 | time_setting_t *s = DREF(time_setting); 95 | uint16_t time = *DREF(timer); 96 | int event = DREF(button)->event; 97 | bool change = false; 98 | 99 | // default animation 100 | s->animation = ANIM_NONE; 101 | 102 | // on button event 103 | if(DTASK_AND(BUTTON)) { 104 | if(event == BTN_LONG_PRESS) { 105 | // enable changes to settings with a long press 106 | s->change_enable_ts = time; 107 | s->change_enabled = true; 108 | s->animation = ANIM_START; 109 | change = true; 110 | } else if(event == BTN_RELEASE) { 111 | if(s->change_enabled) { 112 | // cycle through settings after a button press 113 | s->change_enable_ts = time; 114 | s->on_time = ++s->on_time % PUMP_PERIOD; 115 | s->animation = ANIM_CHANGE; 116 | } else { 117 | // changes are not enabled, so just blink back the current setting 118 | s->animation = ANIM_READ; 119 | } 120 | change = true; 121 | } 122 | } 123 | 124 | // disable changes after idle 125 | if(s->change_enabled && time - s->change_enable_ts > ENABLED_TIME) { 126 | s->change_enabled = false; 127 | s->animation = ANIM_END; 128 | change = true; 129 | } 130 | 131 | return change; 132 | } 133 | 134 | // controls a pump 135 | DTASK(pump, bool) { 136 | uint16_t time = *DREF(timer); 137 | time_setting_t *s = DREF(time_setting); 138 | 139 | // enable pump with set duty cycle 140 | bool pump_state = (time >> 9) % PUMP_PERIOD <= s->on_time; 141 | 142 | if(pump_state != *DREF(pump)) { 143 | *DREF(pump) = pump_state; 144 | return true; 145 | } else { 146 | return false; 147 | } 148 | } 149 | 150 | // status led, indicates if the pump is running 151 | DTASK(status_led, bool) { 152 | uint16_t time = *DREF(timer); 153 | bool pump = *DREF(pump); 154 | 155 | // indicate pump status 156 | // - solid = running 157 | // - blink = idle 158 | *DREF(status_led) = pump || (time & 31) == 0; 159 | return true; 160 | } 161 | 162 | // UI led, used to read and change the time setting 163 | DTASK(ui, struct { bool led; uint16_t start; int anim; } ) { 164 | time_setting_t *s = DREF(time_setting); 165 | uint16_t time = *DREF(timer); 166 | 167 | if(DTASK_AND(TIME_SETTING)) { 168 | // start animation indicated from time_setting 169 | DREF(ui)->start = time; 170 | DREF(ui)->anim = s->animation; 171 | } 172 | 173 | uint16_t t = time - DREF(ui)->start; 174 | 175 | // reset animation after maximum length 176 | if(t > MAX_ANIM) DREF(ui)->anim = ANIM_NONE; 177 | 178 | switch(DREF(ui)->anim) { 179 | case ANIM_NONE: 180 | // off 181 | DREF(ui)->led = false; 182 | break; 183 | case ANIM_START: 184 | // single long blink 185 | DREF(ui)->led = t < 16; 186 | break; 187 | case ANIM_CHANGE: 188 | case ANIM_READ: 189 | // blink back time setting slowly 190 | DREF(ui)->led = ((t & 4) != 0) && (t >> 3 <= s->on_time); 191 | break; 192 | case ANIM_END: 193 | // quick flashes 194 | DREF(ui)->led = ((t & 1) != 0) && (t < 8); 195 | break; 196 | default: 197 | break; 198 | } 199 | return true; 200 | } 201 | -------------------------------------------------------------------------------- /examples/test/Makefile: -------------------------------------------------------------------------------- 1 | BUILD_DIR := build 2 | DTASK_ROOT := ../.. 3 | DTASK_SRC := $(DTASK_ROOT)/src 4 | DTASK_TOOLS := $(DTASK_ROOT)/tools 5 | 6 | SRC := $(wildcard *.c) 7 | DTASK_TARGETS := toplevel_tasks factor_tasks simple_tasks 8 | DTASK_GENERATED_HEADERS := $(patsubst %, $(BUILD_DIR)/%.h, $(DTASK_TARGETS)) 9 | HEADERS := $(wildcard *.h) $(DTASK_GENERATED_HEADERS) 10 | OBJS := $(patsubst %.c, $(BUILD_DIR)/%.o, $(SRC)) $(BUILD_DIR)/dtask.o 11 | CFLAGS := -g -O3 -std=c99 -I $(DTASK_SRC) -I $(BUILD_DIR) 12 | 13 | .PHONY: all 14 | all: test 15 | 16 | print-%: 17 | @echo $($*) 18 | 19 | test: $(OBJS) 20 | gcc $^ -o test 21 | 22 | $(DTASK_GENERATED_HEADERS): $(BUILD_DIR)/%.h : $(SRC) 23 | @mkdir -p $(BUILD_DIR) 24 | PYTHONPATH=$(DTASK_ROOT) python2 $(DTASK_TOOLS)/generate_task_header.py -I $(DTASK_SRC) --target $* $(SRC) 25 | mv $*.h $(BUILD_DIR) 26 | 27 | $(BUILD_DIR)/%.o: %.c $(HEADERS) 28 | @mkdir -p $(BUILD_DIR) 29 | gcc -c $< -o $@ $(CFLAGS) $(CFLAGS_EXT) 30 | 31 | $(BUILD_DIR)/dtask.o: $(DTASK_SRC)/dtask.c $(DTASK_SRC)/*.h 32 | @mkdir -p $(BUILD_DIR) 33 | gcc -c $< -o $@ $(CFLAGS) $(CFLAGS_EXT) 34 | 35 | .PHONY: clean 36 | clean: 37 | rm -rf $(BUILD_DIR) 38 | rm -f test 39 | -------------------------------------------------------------------------------- /examples/test/factor_tasks.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #include 16 | #include 17 | #include "types.h" 18 | 19 | #ifndef DTASK_GEN 20 | #include "factor_tasks.h" 21 | #endif 22 | 23 | #define DEBUG 1 24 | 25 | #if DEBUG 26 | #define debugf(args...) printf(args) 27 | #else 28 | #define debugf(...) 29 | #endif 30 | 31 | DTASK_GROUP(factor_tasks) 32 | 33 | DTASK_ENABLE(fizzbuzz) { 34 | printf("%d > fizzbuzz enabled\n", state->config.id); 35 | } 36 | DTASK_DISABLE(fizzbuzz) { 37 | printf("%d > fizzbuzz disabled\n", state->config.id); 38 | } 39 | DTASK(fizzbuzz, int) { 40 | int x = *DREF(count); 41 | (void)DREF(mod_three); 42 | (void)DREF(mod_five); 43 | (void)DREF_WEAK(mod_seven); 44 | printf("%d > ", state->config.id); 45 | if(DTASK_OR(MOD_THREE | MOD_FIVE | MOD_SEVEN)) { 46 | if(state->events & MOD_THREE) { 47 | printf("fizz"); 48 | } 49 | if(state->events & MOD_FIVE) { 50 | printf("buzz"); 51 | } 52 | if(state->events & MOD_SEVEN) { 53 | printf("woof"); 54 | } 55 | printf("\n"); 56 | } else { 57 | printf("%d\n", x); 58 | } 59 | return true; 60 | } 61 | 62 | DTASK_ENABLE(output35) { 63 | printf("%d > output35 enabled\n", state->config.id); 64 | } 65 | DTASK_DISABLE(output35) { 66 | printf("%d > output35 disabled\n", state->config.id); 67 | } 68 | DTASK(output35, int) { 69 | const combine35_t *x = DREF(combine35); 70 | assert(x->m3 * 3 == x->m5 * 5); 71 | debugf("\n"); 72 | printf("%d > ", state->config.id); 73 | printf("%d * 3 == %d * 5 == %d\n", x->m3, x->m5, x->m3 * 3); 74 | debugf("\n"); 75 | return true; 76 | } 77 | 78 | DTASK_ENABLE(output57) { 79 | printf("%d > output57 enabled\n", state->config.id); 80 | } 81 | DTASK_DISABLE(output57) { 82 | printf("%d > output57 disabled\n", state->config.id); 83 | } 84 | DTASK(output57, int) { 85 | const combine57_t *x = DREF(combine57); 86 | assert(x->m5 * 5 == x->m7 * 7); 87 | debugf("\n"); 88 | printf("%d > ", state->config.id); 89 | printf("%d * 5 == %d * 7 == %d\n", x->m5, x->m7, x->m5 * 5); 90 | debugf("\n"); 91 | return true; 92 | } 93 | 94 | DTASK_ENABLE(output357) { 95 | printf("%d > output357 enabled\n", state->config.id); 96 | } 97 | DTASK_DISABLE(output357) { 98 | printf("%d > output357 disabled\n", state->config.id); 99 | } 100 | DTASK(output357, int) { 101 | const combine357_t *x = DREF(combine357); 102 | assert(x->m3 * 3 == x->m5 * 5 && x->m5 * 5 == x->m7 * 7); 103 | debugf("\n"); 104 | printf("%d > ", state->config.id); 105 | printf("%d * 3 == %d * 5 == %d * 7 == %d\n", x->m3, x->m5, x->m7, x->m3 * 3); 106 | 107 | // demonstrate delays 108 | printf("d = [%d", *DELAY_READ(&x->d, int, OUTPUT_DELAY_SIZE, 0)); 109 | for(int i = 1; i < OUTPUT_DELAY_SIZE; i++) 110 | { 111 | int v = *DELAY_READ(&x->d, int, OUTPUT_DELAY_SIZE, i); 112 | printf(", %d", v); 113 | } 114 | printf("]\n"); 115 | debugf("\n"); 116 | return true; 117 | } 118 | 119 | DTASK_ENABLE(combine357) { 120 | printf("%d > combine357 enabled\n", state->config.id); 121 | int zero = 0; 122 | DELAY_FILL(&DREF(combine357)->d, int, OUTPUT_DELAY_SIZE, &zero); 123 | } 124 | DTASK_DISABLE(combine357) { 125 | printf("%d > combine357 disabled\n", state->config.id); 126 | } 127 | DTASK(combine357, struct { int m3, m5, m7; DELAY(int, OUTPUT_DELAY_SIZE) d; }) { 128 | const combine35_t *m35 = DREF(combine35); 129 | int m7 = *DREF(mod_seven); 130 | if(DTASK_AND(COMBINE35 | MOD_SEVEN)) { 131 | DREF(combine357)->m3 = m35->m3; 132 | DREF(combine357)->m5 = m35->m5; 133 | DREF(combine357)->m7 = m7; 134 | 135 | // demonstrate delays 136 | DELAY_WRITE(&DREF(combine357)->d, int, OUTPUT_DELAY_SIZE, DREF_WEAK(count)); 137 | return true; 138 | } else { 139 | return false; 140 | } 141 | } 142 | 143 | DTASK_ENABLE(combine35) { 144 | printf("%d > combine35 enabled\n", state->config.id); 145 | } 146 | DTASK_DISABLE(combine35) { 147 | printf("%d > combine35 disabled\n", state->config.id); 148 | } 149 | DTASK(combine35, struct { int m3, m5; }) { 150 | int m3 = *DREF(mod_three); 151 | int m5 = *DREF(mod_five); 152 | if(DTASK_AND(MOD_THREE | MOD_FIVE)) { 153 | DREF(combine35)->m3 = m3; 154 | DREF(combine35)->m5 = m5; 155 | return true; 156 | } else { 157 | return false; 158 | } 159 | } 160 | 161 | DTASK_ENABLE(combine57) { 162 | printf("%d > combine57 enabled\n", state->config.id); 163 | } 164 | DTASK_DISABLE(combine57) { 165 | printf("%d > combine57 disabled\n", state->config.id); 166 | } 167 | DTASK(combine57, struct { int m5, m7; }) { 168 | int m5 = *DREF(mod_five); 169 | int m7 = *DREF(mod_seven); 170 | if(DTASK_AND(MOD_FIVE | MOD_SEVEN)) { 171 | DREF(combine57)->m5 = m5; 172 | DREF(combine57)->m7 = m7; 173 | return true; 174 | } else { 175 | return false; 176 | } 177 | } 178 | 179 | 180 | DTASK_ENABLE(mod_three) { 181 | printf("%d > mod_three enabled\n", state->config.id); 182 | } 183 | DTASK_DISABLE(mod_three) { 184 | printf("%d > mod_three disabled\n", state->config.id); 185 | } 186 | DTASK(mod_three, int) { 187 | int x = *DREF(count); 188 | if(x % 3 != 0) return false; 189 | *DREF(mod_three) = x / 3; 190 | debugf("[3] "); 191 | return true; 192 | } 193 | 194 | DTASK_ENABLE(mod_five) { 195 | printf("%d > mod_five enabled\n", state->config.id); 196 | } 197 | DTASK_DISABLE(mod_five) { 198 | printf("%d > mod_five disabled\n", state->config.id); 199 | } 200 | DTASK(mod_five, int) { 201 | int x = *DREF(count); 202 | if(x % 5 != 0) return false; 203 | *DREF(mod_five) = x / 5; 204 | debugf("[5] "); 205 | return true; 206 | } 207 | 208 | DTASK_ENABLE(mod_seven) { 209 | printf("%d > mod_seven enabled\n", state->config.id); 210 | } 211 | DTASK_DISABLE(mod_seven) { 212 | printf("%d > mod_seven disabled\n", state->config.id); 213 | } 214 | DTASK(mod_seven, int) { 215 | int x = *DREF(count); 216 | if(x % 7 != 0) return false; 217 | *DREF(mod_seven) = x / 7; 218 | debugf("[7] "); 219 | return true; 220 | } 221 | 222 | DTASK(count, int) { 223 | (*DREF(count))++; 224 | return true; 225 | } 226 | -------------------------------------------------------------------------------- /examples/test/main.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #include 16 | #include "types.h" 17 | 18 | // #define ALL_TASKS_AFTER_TASK_HOOK printf("hook\n") 19 | #ifndef DTASK_GEN 20 | #include "toplevel_tasks.h" 21 | #include "factor_tasks.h" 22 | #include "simple_tasks.h" 23 | #endif 24 | 25 | toplevel_tasks_state_t toplevel_state = DTASK_STATE(toplevel_tasks, NULL, 0); 26 | factor_tasks_state_t factor_state[2] = { 27 | DTASK_STATE(factor_tasks, &toplevel_tasks, 0), 28 | DTASK_STATE(factor_tasks, &toplevel_tasks, 1) 29 | }; 30 | simple_tasks_state_t simple_state = DTASK_STATE(simple_tasks, &toplevel_tasks, 0); 31 | 32 | int main(int argc, char **argv) { 33 | 34 | (void)argc; 35 | (void)argv; 36 | 37 | dtask_state_t *state = (dtask_state_t *)&toplevel_state; 38 | 39 | dtask_enable(state, FACTOR_EVENTS | SIMPLE_TOGGLE); 40 | 41 | factor_state[0].count = -1; 42 | factor_state[1].count = -1; 43 | 44 | printf("\n\n_____ OUTPUT35[0] & OUTPUT57[1] _____\n"); 45 | dtask_switch((dtask_state_t *)&factor_state[0], OUTPUT35); 46 | dtask_switch((dtask_state_t *)&factor_state[1], OUTPUT57); 47 | for(int i = 0; i < 100; i++) { 48 | dtask_run(state, SIMPLE_TOGGLE); 49 | } 50 | 51 | printf("\n\n_____ OUTPUT357[0] _____\n"); 52 | dtask_switch((dtask_state_t *)&factor_state[0], OUTPUT357); 53 | dtask_switch((dtask_state_t *)&factor_state[1], 0); 54 | 55 | for(int i = 0; i < 2000; i++) { 56 | dtask_run(state, SIMPLE_TOGGLE); 57 | } 58 | 59 | printf("\n\n_____ disabling MOD_SEVEN[0] _____\n"); 60 | dtask_switch((dtask_state_t *)&factor_state[0], OUTPUT35 | OUTPUT57); 61 | dtask_select((dtask_state_t *)&factor_state[0]); 62 | dtask_disable((dtask_state_t *)&factor_state[0], MOD_SEVEN); 63 | dtask_select((dtask_state_t *)&factor_state[0]); 64 | for(int i = 0; i < 100; i++) { 65 | dtask_run(state, SIMPLE_TOGGLE); 66 | } 67 | 68 | printf("\n\n_____ clearing MOD_SEVEN[0] _____\n"); 69 | dtask_clear((dtask_state_t *)&factor_state[0], MOD_SEVEN); 70 | for(int i = 0; i < 100; i++) { 71 | dtask_run(state, SIMPLE_TOGGLE); 72 | } 73 | 74 | printf("\n\n_____ disabling OUTPUT35[0] _____\n"); 75 | dtask_switch((dtask_state_t *)&factor_state[0], OUTPUT35 | OUTPUT57); 76 | dtask_disable((dtask_state_t *)&factor_state[0], OUTPUT35); 77 | for(int i = 0; i < 100; i++) { 78 | dtask_run(state, SIMPLE_TOGGLE); 79 | } 80 | 81 | printf("\n\n_____ FIZZBUZZ[0,1], reset count[0] _____\n"); 82 | dtask_switch((dtask_state_t *)&factor_state[0], FIZZBUZZ); 83 | dtask_switch((dtask_state_t *)&factor_state[1], FIZZBUZZ); 84 | factor_state[0].count = 0; 85 | for(int i = 0; i <= 100; i++) { 86 | dtask_run(state, SIMPLE_TOGGLE); 87 | } 88 | 89 | printf("\n\n_____ FIZZBUZZWOOF[1], reset count[1] _____\n"); 90 | dtask_switch((dtask_state_t *)&factor_state[1], FIZZBUZZ | MOD_SEVEN); 91 | factor_state[1].count = 0; 92 | for(int i = 0; i <= 110; i++) { 93 | dtask_run(state, SIMPLE_TOGGLE); 94 | } 95 | 96 | printf("\n\n_____ clearing FIZZBUZZ[1] _____\n"); 97 | dtask_clear((dtask_state_t *)&factor_state[1], FIZZBUZZ); 98 | 99 | printf("\n\n_____ THE END _____\n\n"); 100 | 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /examples/test/simple_tasks.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #include 16 | #include 17 | 18 | #ifndef DTASK_GEN 19 | #include "simple_tasks.h" 20 | #endif 21 | 22 | #define DEBUG 1 23 | 24 | #if DEBUG 25 | #define debugf(args...) printf(args) 26 | #else 27 | #define debugf(...) 28 | #endif 29 | 30 | DTASK_GROUP(simple_tasks) 31 | 32 | DTASK(toggle, bool) { 33 | *DREF(toggle) = !*DREF(toggle); 34 | return true; 35 | } 36 | -------------------------------------------------------------------------------- /examples/test/toplevel_tasks.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #include 16 | #include 17 | #include "types.h" 18 | 19 | #ifndef DTASK_GEN 20 | #include "toplevel_tasks.h" 21 | #include "factor_tasks.h" 22 | #include "simple_tasks.h" 23 | #endif 24 | 25 | #define DEBUG 1 26 | 27 | #if DEBUG 28 | #define debugf(args...) printf(args) 29 | #else 30 | #define debugf(...) 31 | #endif 32 | 33 | DTASK_GROUP(toplevel_tasks) 34 | 35 | extern factor_tasks_state_t factor_state[2]; 36 | extern simple_tasks_state_t simple_state; 37 | 38 | DTASK(factor_events, dtask_set_t) 39 | { 40 | /* connect factor_tasks to simple_tasks */ 41 | dtask_set_t initial = COUNT; 42 | int idx = *DREF(simple_toggle) ? 1 : 0; // being verbose to make indexing clear 43 | 44 | *DREF(factor_events) = dtask_run((dtask_state_t *)&factor_state[idx], initial); 45 | return *DREF(factor_events) != 0; 46 | } 47 | 48 | DTASK_ENABLE(simple_toggle) 49 | { 50 | // deoendency on simple_state.toggle cannot be handled automatically 51 | // because it is within another (sub)graph 52 | dtask_enable((dtask_state_t *)&simple_state, TOGGLE); 53 | } 54 | 55 | DTASK_DISABLE(simple_toggle) 56 | { 57 | // deoendency on simple_state.toggle cannot be handled automatically 58 | // because it is within another (sub)graph 59 | dtask_clear((dtask_state_t *)&simple_state, TOGGLE); 60 | } 61 | 62 | DTASK(simple_toggle, bool) 63 | { 64 | dtask_set_t initial = TOGGLE; 65 | 66 | dtask_set_t simple_events = dtask_run((dtask_state_t *)&simple_state, initial); 67 | 68 | *DREF(simple_toggle) = simple_state.toggle; 69 | 70 | return (simple_events & TOGGLE) != 0; 71 | } 72 | -------------------------------------------------------------------------------- /examples/test/types.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #ifndef __TYPES__ 16 | #define __TYPES__ 17 | 18 | #include "delay.h" 19 | 20 | #define OUTPUT_DELAY_SIZE 5 21 | 22 | DECLARE_DELAY(int, OUTPUT_DELAY_SIZE); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/delay.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #ifndef __DELAY__ 16 | #define __DELAY__ 17 | 18 | #define DECLARE_DELAY(type, len) _DECLARE_DELAY(type, len) 19 | #define _DECLARE_DELAY(type, len) \ 20 | typedef struct { \ 21 | unsigned int t; \ 22 | type z[len]; \ 23 | } delay_##type##_##len##_t; \ 24 | static const type *delay_##type##_##len##_read(const delay_##type##_##len##_t *delay, unsigned int idx) \ 25 | { \ 26 | return &delay->z[(delay->t + (len - idx)) % len]; \ 27 | } \ 28 | static void delay_##type##_##len##_write(delay_##type##_##len##_t *delay, const type *val) \ 29 | { \ 30 | if(++delay->t >= len) delay->t = 0; \ 31 | delay->z[delay->t] = *val; \ 32 | } \ 33 | static void delay_##type##_##len##_fill(delay_##type##_##len##_t *delay, const type *val) \ 34 | { \ 35 | for(unsigned int i = 0; i < len; i++) \ 36 | { \ 37 | delay->z[i] = *val; \ 38 | } \ 39 | delay->t = 0; \ 40 | } 41 | 42 | #define DELAY(type, len) _DELAY(type, len) 43 | #define _DELAY(type, len) delay_##type##_##len##_t 44 | #define DELAY_READ(delay, type, len, idx) _DELAY_READ(delay, type, len, idx) 45 | #define _DELAY_READ(delay, type, len, idx) delay_##type##_##len##_read(delay, idx) 46 | #define DELAY_WRITE(delay, type, len, val) _DELAY_WRITE(delay, type, len, val) 47 | #define _DELAY_WRITE(delay, type, len, val) delay_##type##_##len##_write(delay, val) 48 | #define DELAY_FILL(delay, type, len, val) _DELAY_FILL(delay, type, len, val) 49 | #define _DELAY_FILL(delay, type, len, val) delay_##type##_##len##_fill(delay, val) 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/dtask.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #include 16 | #include 17 | 18 | #include "dtask.h" 19 | 20 | void __dtask_noop(dtask_state_t *state) {} 21 | 22 | // find the first (lowest) id in the set 23 | // requires prev = previous id to optimize for NO_CLZ 24 | // used to iterate through bits using the set_foreach macro 25 | #ifdef NO_CLZ 26 | dtask_id_t dtask_set_find_first(dtask_set_t set, dtask_id_t prev) { 27 | dtask_id_t x = prev; 28 | dtask_set_t s = set << x; 29 | while(s) { 30 | if(dtask_bit(0) & s) { 31 | return x; 32 | } 33 | x++; 34 | s <<= 1; 35 | } 36 | return DTASK_BIT_WIDTH(dtask_set_t); 37 | } 38 | #else 39 | #define dtask_set_find_first(set, prev) (__builtin_clz(set)) 40 | #endif 41 | 42 | // find the last (highest) id in the set 43 | // requires prev = previous id to optimize for NO_CTZ 44 | // used to iterate through bits using the set_foreach_rev macro 45 | #ifdef NO_CTZ 46 | dtask_id_t dtask_set_find_last(dtask_set_t set, dtask_id_t prev) { 47 | dtask_id_t x = DTASK_MAX_ID - prev; 48 | dtask_set_t s = set >> x; 49 | while(s) { 50 | if(1 & set) { 51 | return DTASK_MAX_ID - x; 52 | } 53 | x++; 54 | s >>= 1; 55 | }; 56 | return -1; 57 | } 58 | #else 59 | #define dtask_set_find_last(set, prev) (DTASK_MAX_ID - __builtin_ctz(set)) 60 | #endif 61 | 62 | // call action for each id in set in increasing order 63 | #define set_foreach(set, action) \ 64 | do { \ 65 | dtask_id_t n = 0; \ 66 | dtask_set_t left = (set); \ 67 | while(left) { \ 68 | n = dtask_set_find_first(left, n); \ 69 | action; \ 70 | left &= ~dtask_bit(n); \ 71 | } \ 72 | } while(0) 73 | 74 | // call action for each id in set in decreasing order 75 | #define set_foreach_rev(set, action) \ 76 | do { \ 77 | dtask_id_t n = DTASK_MAX_ID; \ 78 | dtask_set_t left = (set); \ 79 | while(left) { \ 80 | n = dtask_set_find_last(left, n); \ 81 | action; \ 82 | left &= ~dtask_bit(n); \ 83 | } \ 84 | } while(0) 85 | 86 | // determine which dtasks should be selected based on which dtasks are enabled and disabled 87 | static dtask_set_t dtask_requested(dtask_state_t *state) { 88 | dtask_select_t *select = &state->select; 89 | dtask_set_t requested = select->enabled; 90 | const dtask_t *tasks = state->config.tasks; 91 | 92 | // remove disabled dependents 93 | set_foreach(select->disabled, 94 | requested &= ~tasks[n].all_dependents); 95 | 96 | // add dependencies 97 | set_foreach(requested, 98 | requested |= tasks[n].all_dependencies); 99 | 100 | return requested; 101 | } 102 | 103 | // call enable functions for each dtask in the set 104 | static void dtask_call_enable_functions(dtask_state_t *state, dtask_set_t set) { 105 | const dtask_t *tasks = state->config.tasks; 106 | set_foreach(set, 107 | tasks[n].enable_func(state)); 108 | } 109 | 110 | // call disable functions for each dtask in the set 111 | static void dtask_call_disable_functions(dtask_state_t *state, dtask_set_t set) { 112 | const dtask_t *tasks = state->config.tasks; 113 | set_foreach_rev(set, 114 | tasks[n].disable_func(state)); 115 | } 116 | 117 | // request to enable dtasks in set 118 | // does not take effect until dtask_select() 119 | void dtask_enable(dtask_state_t *state, dtask_set_t set) 120 | { 121 | dtask_select_t *select = &state->select; 122 | const dtask_t *tasks = state->config.tasks; 123 | 124 | if(set & ~select->enabled) { 125 | select->dirty = true; 126 | select->enabled |= set; 127 | 128 | // clear conflicting disables 129 | select->disabled &= ~set; 130 | if(select->disabled) { 131 | set_foreach(set, 132 | select->disabled &= ~tasks[n].all_dependencies); 133 | } 134 | } 135 | } 136 | 137 | // request to disable dtasks in set 138 | // does not take effect until dtask_select() 139 | void dtask_disable(dtask_state_t *state, dtask_set_t set) 140 | { 141 | dtask_select_t *select = &state->select; 142 | if(set & ~select->disabled) { 143 | select->dirty = true; 144 | select->disabled |= set; 145 | select->enabled &= ~set; 146 | } 147 | } 148 | 149 | // request to clear dtasks in set 150 | // does not take effect until dtask_select() 151 | void dtask_clear(dtask_state_t *state, dtask_set_t set) { 152 | dtask_select_t *select = &state->select; 153 | 154 | // clear directly conflicting enables and recompute 155 | if((select->enabled | select->disabled) & set) { 156 | select->dirty = true; 157 | select->enabled &= ~set; 158 | select->disabled &= ~set; 159 | } 160 | } 161 | 162 | // request to enable dtasks in set and clear others 163 | // does not take effect until dtask_select() 164 | void dtask_switch(dtask_state_t *state, dtask_set_t set) { 165 | dtask_clear(state, ~(dtask_set_t)0); 166 | dtask_enable(state, set); 167 | } 168 | 169 | // update selection from requests 170 | void dtask_select(dtask_state_t *state) { 171 | dtask_select_t *select = &state->select; 172 | if(select->dirty) { 173 | dtask_set_t requested = dtask_requested(state); 174 | dtask_call_disable_functions(state, ~requested & select->selected); 175 | dtask_call_enable_functions(state, requested & ~select->selected); 176 | select->selected = requested; 177 | select->dirty = false; 178 | } 179 | } 180 | 181 | // run selected dtasks starting with the initial set 182 | #ifndef NO_CLZ 183 | dtask_set_t dtask_run(dtask_state_t *state, dtask_set_t initial) { 184 | dtask_select(state); 185 | 186 | const dtask_set_t selected = state->select.selected; 187 | dtask_set_t 188 | scheduled = initial & selected, 189 | active = 0; 190 | 191 | state->events = 0; 192 | 193 | while(scheduled) { 194 | active = dtask_set_find_first(scheduled, active); 195 | const dtask_set_t id_bit = dtask_bit(active); 196 | const dtask_t *task = &state->config.tasks[active]; 197 | 198 | // run the task if scheduled 199 | // on success, mark events and schedule dependents 200 | if((id_bit & scheduled) && 201 | task->func(state)) { 202 | state->events |= id_bit; 203 | scheduled |= task->dependents & selected; 204 | } 205 | scheduled &= ~id_bit; 206 | } 207 | return state->events; 208 | } 209 | #else 210 | dtask_set_t dtask_run(dtask_state_t *state, dtask_set_t initial) { 211 | dtask_select(state); 212 | return state->config.run(state, initial); 213 | } 214 | #endif 215 | -------------------------------------------------------------------------------- /src/dtask.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. */ 14 | 15 | #ifndef __DTASK__ 16 | #define __DTASK__ 17 | 18 | #include 19 | #include 20 | 21 | // define NO_CLZ for targets that do not efficiently implement __builtin_clz() 22 | #if defined(__TARGET_CPU_CORTEX_M0) || defined(__TARGET_CPU_CORTEX_M0PLUS) 23 | #define NO_CLZ 24 | #define NO_CTZ 25 | #endif 26 | 27 | /*-------------------- utility macros --------------------*/ 28 | 29 | // bits in a type 30 | #define DTASK_BIT_WIDTH(type) (sizeof(type) * 8) 31 | 32 | // number of elements in an array 33 | #define DTASK_LENGTH(a) (sizeof(a) / sizeof((a)[0])) 34 | 35 | /*-------------------- ids and sets --------------------*/ 36 | 37 | // each dtask has a unique id 38 | typedef uint8_t dtask_id_t; 39 | 40 | // sets of dtask ids 41 | typedef unsigned int dtask_set_t; 42 | 43 | // the maximum id that can fit in dtask_set_t 44 | #define DTASK_MAX_ID (DTASK_BIT_WIDTH(dtask_set_t) - 1) 45 | 46 | // all events with ids in x = (ID1 | ID2 ... | IDn) have succeeded 47 | // can only be used inside a dtask 48 | #define DTASK_AND(x) (!(~state->events & (x))) 49 | 50 | // any event with an id in x = (ID1 | ID2 ... | IDn) has succeeded 51 | // can only be used inside a dtask 52 | #define DTASK_OR(x) (state->events & (x)) 53 | 54 | // convert an id to a singleton set (one bit) 55 | static inline 56 | dtask_set_t dtask_bit(dtask_id_t id) { 57 | return ((dtask_set_t)1 << DTASK_MAX_ID) >> id; 58 | } 59 | 60 | /*-------------------- state and config --------------------*/ 61 | 62 | typedef struct dtask_state dtask_state_t; 63 | typedef struct dtask dtask_t; 64 | typedef struct dtask_config dtask_config_t; 65 | typedef struct dtask_select dtask_select_t; 66 | 67 | // describes a dtask and its relations 68 | // a static const dtask table will be generated that can be stored in ROM 69 | struct dtask 70 | { 71 | #ifndef NO_CLZ 72 | // updates state 73 | bool (*func)(dtask_state_t *state); 74 | #endif 75 | // run when enabled 76 | void (*enable_func)(dtask_state_t *state); 77 | // run when disabled 78 | void (*disable_func)(dtask_state_t *state); 79 | #ifndef NO_CLZ 80 | // set of direct dependents 81 | dtask_set_t dependents; 82 | #endif 83 | // (direct or indirect) dependencies and dependents 84 | dtask_set_t all_dependencies, all_dependents; 85 | }; 86 | 87 | // members present in all generated dtask state types 88 | // events: set of successful (i.e. returns true) dtask ids 89 | // config & select: see below 90 | #define DTASK_STATE_HEADER \ 91 | struct \ 92 | { \ 93 | dtask_set_t events; \ 94 | const dtask_config_t config; \ 95 | dtask_select_t select; \ 96 | } 97 | 98 | // stores const static config data 99 | struct dtask_config 100 | { 101 | // dtask table 102 | const dtask_t *tasks; 103 | // parent state 104 | const dtask_state_t *parent; 105 | #ifdef NO_CLZ 106 | // static dtask_run() function 107 | dtask_set_t (*const run)(dtask_state_t *state, dtask_set_t initial); 108 | #endif 109 | // graph state id 110 | const unsigned int id; 111 | }; 112 | 113 | // dtask selection state 114 | struct dtask_select 115 | { 116 | dtask_set_t 117 | enabled, // explicitly enabled 118 | disabled, // explicitly disabled 119 | selected; // dtask will only run if selected 120 | bool dirty; // selection needs to be updated 121 | }; 122 | 123 | // base state type 124 | // the script will generate an extended version of this type for each group 125 | struct dtask_state 126 | { 127 | DTASK_STATE_HEADER; 128 | }; 129 | 130 | // update state, running dtasks in the initial set 131 | dtask_set_t dtask_run(dtask_state_t *state, dtask_set_t initial); 132 | 133 | // request to enable dtasks in the set 134 | void dtask_enable(dtask_state_t *state, dtask_set_t set); 135 | 136 | // request to disable dtasks in the set 137 | void dtask_disable(dtask_state_t *state, dtask_set_t set); 138 | 139 | // clear requests for dtasks in the set, i.e. enable only if needed 140 | void dtask_clear(dtask_state_t *state, dtask_set_t set); 141 | 142 | // explicitly enable dtasks in set, clear others 143 | void dtask_switch(dtask_state_t *state, dtask_set_t set); 144 | 145 | // update selection state 146 | // requests from dtask_enable/disable/clear/switch only take effect after calling this 147 | void dtask_select(dtask_state_t *state); 148 | 149 | 150 | /*-------------------- dtask definition macros --------------------*/ 151 | 152 | // disable macros when preprocessing for script 153 | #ifndef DTASK_GEN 154 | 155 | // define a dtask 156 | #define DTASK(name, ...) \ 157 | bool __dtask_##name(dtask_state_t *state) 158 | 159 | // define a group 160 | #define DTASK_GROUP(group_name) \ 161 | typedef group_name##_state_t dref_t; 162 | 163 | // get a reference (pointer) to a dependency 164 | // can only be used inside a dtask 165 | #define DREF(x) ((x##_t *)&((dref_t *)state)->x) 166 | 167 | // weak version of DREF that doesn't force dependency to be enabled 168 | #define DREF_WEAK(x) DREF(x) 169 | 170 | // passive (doesn't trigger) version of DREF that doesn't force dependency to be enabled 171 | #define DREF_PASS(x) DREF(x) 172 | 173 | // define the enable function for a dtask 174 | #define DTASK_ENABLE(name) \ 175 | void __dtask_enable_##name(dtask_state_t *state) 176 | 177 | // define the disable function for a dtask 178 | #define DTASK_DISABLE(name) \ 179 | void __dtask_disable_##name(dtask_state_t *state) 180 | 181 | #endif 182 | 183 | // convenient macros to create config/state with required const static members 184 | #ifndef NO_CLZ 185 | #define DTASK_CONFIG(name, parent, id) {(name), (dtask_state_t *)(parent), (id)} 186 | #else 187 | #define DTASK_CONFIG(name, parent, id) {(name), (dtask_state_t *)(parent), name##_run, (id)} 188 | #endif 189 | 190 | #define DTASK_STATE(name, parent, id) {{ .config = DTASK_CONFIG(name, parent, id) }} 191 | 192 | /*-------------------- used by generated headers --------------------*/ 193 | 194 | // declare a dtask 195 | #define DECLARE_DTASK(name, type...) \ 196 | typedef type name##_t; \ 197 | bool __dtask_##name(dtask_state_t *state) 198 | 199 | // declare enable function for a dtask 200 | #define DECLARE_DTASK_ENABLE(name) \ 201 | void __dtask_enable_##name(dtask_state_t *state) 202 | 203 | // declare disable function for a dtask 204 | #define DECLARE_DTASK_DISABLE(name) \ 205 | void __dtask_disable_##name(dtask_state_t *state) 206 | 207 | // default function for enable/disable 208 | void __dtask_noop(dtask_state_t *state); 209 | 210 | #endif 211 | -------------------------------------------------------------------------------- /third_party/README.md: -------------------------------------------------------------------------------- 1 | THIRD PARTY SOURCE 2 | ================== 3 | 4 | - [toposort](https://pypi.python.org/pypi/toposort/1.4), Apache License 2.0 -------------------------------------------------------------------------------- /third_party/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/dtask/9e39f3956bb4eb646bbaa1b49f8fa98ad78df058/third_party/__init__.py -------------------------------------------------------------------------------- /third_party/toposort.py: -------------------------------------------------------------------------------- 1 | ####################################################################### 2 | # Implements a topological sort algorithm. 3 | # 4 | # Copyright 2014 True Blade Systems, Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # Notes: 19 | # Based on http://code.activestate.com/recipes/578272-topological-sort 20 | # with these major changes: 21 | # Added unittests. 22 | # Deleted doctests (maybe not the best idea in the world, but it cleans 23 | # up the docstring). 24 | # Moved functools import to the top of the file. 25 | # Changed assert to a ValueError. 26 | # Changed iter[items|keys] to [items|keys], for python 3 27 | # compatibility. I don't think it matters for python 2 these are 28 | # now lists instead of iterables. 29 | # Copy the input so as to leave it unmodified. 30 | # Renamed function from toposort2 to toposort. 31 | # Handle empty input. 32 | # Switch tests to use set literals. 33 | # 34 | ######################################################################## 35 | 36 | from functools import reduce as _reduce 37 | 38 | __all__ = ['toposort', 'toposort_flatten'] 39 | 40 | def toposort(data): 41 | """Dependencies are expressed as a dictionary whose keys are items 42 | and whose values are a set of dependent items. Output is a list of 43 | sets in topological order. The first set consists of items with no 44 | dependences, each subsequent set consists of items that depend upon 45 | items in the preceeding sets. 46 | """ 47 | 48 | # Special case empty input. 49 | if len(data) == 0: 50 | return 51 | 52 | # Copy the input so as to leave it unmodified. 53 | data = data.copy() 54 | 55 | # Ignore self dependencies. 56 | for k, v in data.items(): 57 | v.discard(k) 58 | # Find all items that don't depend on anything. 59 | extra_items_in_deps = _reduce(set.union, data.values()) - set(data.keys()) 60 | # Add empty dependences where needed. 61 | data.update({item:set() for item in extra_items_in_deps}) 62 | while True: 63 | ordered = set(item for item, dep in data.items() if len(dep) == 0) 64 | if not ordered: 65 | break 66 | yield ordered 67 | data = {item: (dep - ordered) 68 | for item, dep in data.items() 69 | if item not in ordered} 70 | if len(data) != 0: 71 | raise ValueError('Cyclic dependencies exist among these items: {}'.format(', '.join(repr(x) for x in data.items()))) 72 | 73 | 74 | def toposort_flatten(data, sort=True): 75 | """Returns a single list of dependencies. For any set returned by 76 | toposort(), those items are sorted and appended to the result (just to 77 | make the results deterministic).""" 78 | 79 | result = [] 80 | for d in toposort(data): 81 | result.extend((sorted if sort else list)(d)) 82 | return result 83 | -------------------------------------------------------------------------------- /tools/generate_task_header.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright 2016 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import re 19 | from third_party.toposort import toposort_flatten 20 | import copy 21 | import subprocess 22 | import argparse 23 | 24 | # options read from argparse 25 | options = None 26 | 27 | # regexs 28 | 29 | # DTASK(name, type) 30 | dtask_re = re.compile(r'DTASK\(\s*(\w+)\s*,\s*(.+)\s*\)') 31 | # DGET(name) 32 | dget_re = re.compile(r'DREF(_WEAK|_PASS)?\(\s*(\w+)\s*\)') 33 | # DTASK_ENABLE(name) | DTASK_DISABLE(name) 34 | dtask_enable_or_disable_re = re.compile(r'DTASK_(EN|DIS)ABLE\(\s*(\w+)\s*\)') 35 | # DTASK_GROUP(group_name) 36 | dtask_group_re = re.compile(r'DTASK_GROUP\(\s*(\w+)\s*\)') 37 | 38 | # initialize task object with name if not present in tasks 39 | def init_task(tasks, name): 40 | if not name in tasks: 41 | tasks[name] = { 42 | 'type': 'int', 43 | 'deps': set(), 44 | 'weak_deps': set(), 45 | 'passive_deps': set(), 46 | 'depnts': set(), 47 | 'all_deps': set(), 48 | 'all_depnts': set(), 49 | 'en': False, 50 | 'dis': False 51 | } 52 | 53 | # look through file line by line, gathering data from regex matches 54 | def find_tasks_in_file(filename): 55 | in_group = False 56 | in_dtask = False 57 | tasks = {} 58 | 59 | last_task = None 60 | includes = ['-I' + i for i in options.include_dirs] 61 | macros = ['-D' + d for d in options.macros] 62 | cpp = subprocess.Popen(['cpp', '-DDTASK_GEN'] + 63 | includes + macros + [filename], 64 | stdout=subprocess.PIPE) 65 | lines = iter(cpp.stdout.readline, '') 66 | for line in lines: 67 | if line[0] != '#': 68 | 69 | #match DTASK_GROUP(...) definitions 70 | match = dtask_group_re.search(line) 71 | if match: 72 | group = match.group(1) 73 | in_group = group == options.target 74 | last_task = None 75 | in_dtask = False 76 | 77 | if in_group: 78 | 79 | #match DTASK(...) definitions 80 | match = dtask_re.search(line) 81 | if match: 82 | name = match.group(1) 83 | type = match.group(2) 84 | init_task(tasks, name) 85 | tasks[name]['type'] = type 86 | last_task = tasks[name] 87 | in_dtask = True 88 | 89 | #match DTASK_(EN|DIS)ABLE(...) definitions 90 | match = dtask_enable_or_disable_re.search(line) 91 | if match: 92 | name = match.group(2) 93 | if match.group(1) == 'EN': 94 | init_task(tasks, name) 95 | tasks[name]['en'] = True 96 | elif match.group(1) == 'DIS': 97 | init_task(tasks, name) 98 | tasks[name]['dis'] = True 99 | in_dtask = False 100 | 101 | #match DREF(...) expressions 102 | if in_dtask: 103 | for match in dget_re.finditer(line): 104 | if match: 105 | if match.group(1) == '_WEAK': 106 | # weak dependency 107 | last_task['weak_deps'].add(match.group(2)) 108 | elif match.group(1) == '_PASS': 109 | # passive dependency 110 | last_task['passive_deps'].add(match.group(2)) 111 | else: 112 | last_task['deps'].add(match.group(2)) 113 | return tasks 114 | 115 | # toposort, then calculate dependencies 116 | def order_tasks(tasks): 117 | sorted = toposort_flatten({k: v['deps'] | v['weak_deps'] | v['passive_deps'] 118 | for (k, v) in tasks.iteritems()}) 119 | 120 | #calculate dependents 121 | for name in sorted: 122 | for d in tasks[name]['deps']: 123 | tasks[d]['depnts'].add(name) 124 | for d in tasks[name]['weak_deps']: 125 | tasks[d]['depnts'].add(name) 126 | 127 | #calculate dependencies transitively 128 | for name in sorted: 129 | d = copy.deepcopy(tasks[name]['deps']) 130 | for dep in tasks[name]['deps']: 131 | d |= tasks[dep]['all_deps'] 132 | tasks[name]['all_deps'] = d 133 | 134 | #calculate dependents transitively 135 | for name in sorted: 136 | for d in tasks[name]['all_deps']: 137 | tasks[d]['all_depnts'].add(name) 138 | 139 | return map(lambda name: (name, tasks[name]), sorted) 140 | 141 | # generate bit set expression in A | B | C format 142 | def show_set(s): 143 | return ' | '.join( 144 | map(lambda x: x.upper(), s)) if s else "0" 145 | 146 | 147 | # id to bit set conversion 148 | def dtask_bit(id): 149 | return (1 << (options.bits - 1)) >> id 150 | 151 | # generate a function name from the task name 152 | def func_name(task_name, type, present): 153 | if present: 154 | return '__dtask_' + type + '_' + task_name 155 | else: 156 | return '__dtask_noop' 157 | 158 | # generate a header based on the script arguments 159 | def generate_header(): 160 | name = options.target 161 | files = options.source 162 | 163 | # touch the header file so that #include "header" doesn't cause cpp to fail 164 | if(options.output_file): 165 | header = options.output_file 166 | else: 167 | header = name + '.h' 168 | 169 | with open(header, 'w') as f: 170 | os.utime(header, None) 171 | 172 | # build information from the files 173 | tasks = {} 174 | for filename in sorted(files): 175 | new_tasks = find_tasks_in_file(filename) 176 | tasks.update(new_tasks) 177 | tasks = order_tasks(tasks) 178 | ids = {} 179 | id = 0 180 | 181 | # preamble 182 | with open(header, 'w') as f: 183 | f.write('''#ifndef __{name}__ 184 | #define __{name}__ 185 | 186 | #include "dtask.h" 187 | 188 | '''.format(name=name.upper())) 189 | 190 | # define id macros 191 | for (task, _) in tasks: 192 | f.write('#define {} 0x{:x}\n'.format(task.upper(), dtask_bit(id))) 193 | f.write('#define {}_ID {:d}\n'.format(task.upper(), id)) 194 | ids[task] = id 195 | id = id + 1 196 | f.write('#define {}_COUNT {:d}\n'.format(name.upper(), id)) 197 | 198 | # define a name string table 199 | f.write('\n#define {}_TASK_NAMES {{ \\\n'.format(name.upper())) 200 | for (task, _) in tasks: 201 | f.write(' "{}", \\\n'.format(task)) 202 | f.write('}\n') 203 | 204 | # initial tasks, can only be run when flagged as initial tasks 205 | initial = set() 206 | for (task, dict) in tasks: 207 | if not dict['deps']: 208 | initial.add(task) 209 | 210 | f.write('\n#define {}_INITIAL ({})\n\n'.format(name.upper(), 211 | show_set(initial))) 212 | 213 | # declare state type 214 | f.write('typedef struct {}_state {{\n'.format(name)) 215 | f.write(' DTASK_STATE_HEADER;\n') 216 | 217 | for (task, dict) in tasks: 218 | f.write(' {} {};\n'.format(dict['type'], task)) 219 | f.write('}} {}_state_t;\n\n'.format(name)) 220 | 221 | #declare task functions 222 | for (task, dict) in tasks: 223 | f.write('DECLARE_DTASK({}, {});\n'.format(task, dict['type'])) 224 | if dict['en']: 225 | f.write('DECLARE_DTASK_ENABLE({});\n'.format(task)) 226 | if dict['dis']: 227 | f.write('DECLARE_DTASK_DISABLE({});\n'.format(task)) 228 | 229 | # dtask table 230 | f.write(''' 231 | static const dtask_t {}[{}] = {{ 232 | '''.format(name, id)) 233 | for (task, dict) in tasks: 234 | f.write(''' {{ 235 | #ifndef NO_CLZ 236 | /* .func = */ __dtask_{task}, 237 | #endif 238 | /* .enable_func = */ {en}, 239 | /* .disable_func = */ {dis}, 240 | #ifndef NO_CLZ 241 | /* .dependents = */ {depnts}, 242 | #endif 243 | /* .all_dependencies = */ {all_deps}, 244 | /* .all_dependents = */ {all_depnts} 245 | }},\n'''.format(task=task, 246 | en=func_name(task, 'enable', dict['en']), 247 | dis=func_name(task, 'disable', dict['dis']), 248 | depnts=show_set(dict['depnts']), 249 | all_deps=show_set(dict['all_deps']), 250 | all_depnts=show_set(dict['all_depnts']))) 251 | f.write(' };\n') 252 | 253 | # define the NO_CLZ run function (compare to dtask_run) 254 | f.write('\n#ifdef NO_CLZ\n') 255 | 256 | # prologue 257 | f.write(''' 258 | #pragma GCC diagnostic ignored "-Wunused-function" 259 | static dtask_set_t {name}_run(dtask_state_t *state, dtask_set_t initial) {{ 260 | const dtask_set_t selected = state->select.selected; 261 | dtask_set_t 262 | scheduled = initial & selected; 263 | state->events = 0; 264 | '''.format(name=name)) 265 | 266 | # dispatch code 267 | for (task, dict) in tasks: 268 | f.write(''' 269 | if(({uptask} & scheduled) && __dtask_{task}(state)) {{ 270 | state->events |= {uptask};'''.format(task=task, 271 | uptask=task.upper())) 272 | if dict['depnts']: 273 | f.write(''' 274 | scheduled |= ({depnts}) & selected;''' 275 | .format(depnts=show_set(dict['depnts']))) 276 | 277 | f.write(''' 278 | } 279 | ''') 280 | 281 | # epilogue 282 | f.write(''' 283 | return state->events; 284 | } 285 | ''') 286 | 287 | f.write(''' 288 | #endif 289 | #endif 290 | ''') 291 | 292 | 293 | def main(): 294 | 295 | # run argparse 296 | global options 297 | parser = argparse.ArgumentParser(description='Generate a DTask header') 298 | parser.add_argument('source', nargs='*', 299 | help='a source file to process') 300 | parser.add_argument('--target', dest='target', help='target name', 301 | required=True) 302 | parser.add_argument('-o', dest='output_file', help='output file') 303 | parser.add_argument('-I', dest='include_dirs', metavar='DIR', 304 | action='append', help='include dir', default=[]) 305 | parser.add_argument('-D', dest='macros', metavar='MACRO', 306 | action='append', help='define macro', default=[]) 307 | parser.add_argument('-b', dest='bits', type=int, default=32, help='bit width of dtask_set_t') 308 | options, _ = parser.parse_known_args() 309 | 310 | # generate the header 311 | if(options.source): 312 | generate_header() 313 | 314 | if __name__ == "__main__": 315 | main() 316 | --------------------------------------------------------------------------------