// The sketch is auto-generated with XOD (https://xod.io). // // You can compile and upload it to an Arduino-compatible board with // Arduino IDE. // // Rough code overview: // // - Configuration section // - STL shim // - Immutable list classes and functions // - XOD runtime environment // - Native node implementation // - Program graph definition // // Search for comments fenced with '====' and '----' to navigate through // the major code blocks. #include #include #include /*============================================================================= * * * Configuration * * =============================================================================*/ // Uncomment to turn on debug of the program //#define XOD_DEBUG // Uncomment to trace the program runtime in the Serial Monitor //#define XOD_DEBUG_ENABLE_TRACE /*============================================================================= * * * STL shim. Provides implementation for vital std::* constructs * * =============================================================================*/ namespace std { template< class T > struct remove_reference {typedef T type;}; template< class T > struct remove_reference {typedef T type;}; template< class T > struct remove_reference {typedef T type;}; template typename remove_reference::type&& move(T&& a) { return static_cast::type&&>(a); } } // namespace std /*============================================================================= * * * XOD-specific list/array implementations * * =============================================================================*/ #ifndef XOD_LIST_H #define XOD_LIST_H namespace xod { namespace detail { /* * Cursors are used internaly by iterators and list views. They are not exposed * directly to a list consumer. * * The base `Cursor` is an interface which provides the bare minimum of methods * to facilitate a single iteration pass. */ template class Cursor { public: virtual ~Cursor() { } virtual bool isValid() const = 0; virtual bool value(T* out) const = 0; virtual void next() = 0; }; template class NilCursor : public Cursor { public: virtual bool isValid() const { return false; } virtual bool value(T*) const { return false; } virtual void next() { } }; } // namespace detail /* * Iterator is an object used to iterate a list once. * * Users create new iterators by calling `someList.iterate()`. * Iterators are created on stack and are supposed to have a * short live, e.g. for a duration of `for` loop or node’s * `evaluate` function. Iterators can’t be copied. * * Implemented as a pimpl pattern wrapper over the cursor. * Once created for a cursor, an iterator owns that cursor * and will delete the cursor object once destroyed itself. */ template class Iterator { public: static Iterator nil() { return Iterator(new detail::NilCursor()); } Iterator(detail::Cursor* cursor) : _cursor(cursor) { } ~Iterator() { if (_cursor) delete _cursor; } Iterator(const Iterator& that) = delete; Iterator& operator=(const Iterator& that) = delete; Iterator(Iterator&& it) : _cursor(it._cursor) { it._cursor = nullptr; } Iterator& operator=(Iterator&& it) { auto tmp = it._cursor; it._cursor = _cursor; _cursor = tmp; return *this; } operator bool() const { return _cursor->isValid(); } bool value(T* out) const { return _cursor->value(out); } T operator*() const { T out; _cursor->value(&out); return out; } Iterator& operator++() { _cursor->next(); return *this; } private: detail::Cursor* _cursor; }; /* * An interface for a list view. A particular list view provides a new * kind of iteration over existing data. This way we can use list slices, * list concatenations, list rotations, etc without introducing new data * buffers. We just change the way already existing data is iterated. * * ListView is not exposed to a list user directly, it is used internally * by the List class. However, deriving a new ListView is necessary if you * make a new list/string processing node. */ template class ListView { public: virtual Iterator iterate() const = 0; }; /* * The list as it seen by data consumers. Have a single method `iterate` * to create a new iterator. * * Implemented as pimpl pattern wrapper over a list view. Takes pointer * to a list view in constructor and expects the view will be alive for * the whole life time of the list. */ template class List { public: constexpr List() : _view(nullptr) { } List(const ListView* view) : _view(view) { } Iterator iterate() const { return _view ? _view->iterate() : Iterator::nil(); } // pre 0.15.0 backward compatibility List* operator->() __attribute__ ((deprecated)) { return this; } const List* operator->() const __attribute__ ((deprecated)) { return this; } private: const ListView* _view; }; /* * A list view over an old good plain C array. * * Expects the array will be alive for the whole life time of the * view. */ template class PlainListView : public ListView { public: class Cursor : public detail::Cursor { public: Cursor(const PlainListView* owner) : _owner(owner) , _idx(0) { } bool isValid() const override { return _idx < _owner->_len; } bool value(T* out) const override { if (!isValid()) return false; *out = _owner->_data[_idx]; return true; } void next() override { ++_idx; } private: const PlainListView* _owner; size_t _idx; }; public: PlainListView(const T* data, size_t len) : _data(data) , _len(len) { } virtual Iterator iterate() const override { return Iterator(new Cursor(this)); } private: friend class Cursor; const T* _data; size_t _len; }; /* * A list view over a null-terminated C-String. * * Expects the char buffer will be alive for the whole life time of the view. * You can use string literals as a buffer, since they are persistent for * the program execution time. */ class CStringView : public ListView { public: class Cursor : public detail::Cursor { public: Cursor(const char* str) : _ptr(str) { } bool isValid() const override { return (bool)*_ptr; } bool value(char* out) const override { *out = *_ptr; return (bool)*_ptr; } void next() override { ++_ptr; } private: const char* _ptr; }; public: CStringView(const char* str = nullptr) : _str(str) { } CStringView& operator=(const CStringView& rhs) { _str = rhs._str; return *this; } virtual Iterator iterate() const override { return _str ? Iterator(new Cursor(_str)) : Iterator::nil(); } private: friend class Cursor; const char* _str; }; /* * A list view over two other lists (Left and Right) which first iterates the * left one, and when exhausted, iterates the right one. * * Expects both Left and Right to be alive for the whole view life time. */ template class ConcatListView : public ListView { public: class Cursor : public detail::Cursor { public: Cursor(Iterator&& left, Iterator&& right) : _left(std::move(left)) , _right(std::move(right)) { } bool isValid() const override { return _left || _right; } bool value(T* out) const override { return _left.value(out) || _right.value(out); } void next() override { _left ? ++_left : ++_right; } private: Iterator _left; Iterator _right; }; public: ConcatListView() { } ConcatListView(List left, List right) : _left(left) , _right(right) { } ConcatListView& operator=(const ConcatListView& rhs) { _left = rhs._left; _right = rhs._right; return *this; } virtual Iterator iterate() const override { return Iterator(new Cursor(_left.iterate(), _right.iterate())); } private: friend class Cursor; const List _left; const List _right; }; //---------------------------------------------------------------------------- // Text string helpers //---------------------------------------------------------------------------- using XString = List; /* * List and list view in a single pack. An utility used to define constant * string literals in XOD. */ class XStringCString : public XString { public: XStringCString(const char* str) : XString(&_view) , _view(str) { } private: CStringView _view; }; } // namespace xod #endif /*============================================================================= * * * Basic algorithms for XOD lists * * =============================================================================*/ #ifndef XOD_LIST_FUNCS_H #define XOD_LIST_FUNCS_H namespace xod { /* * Folds a list from left. Also known as "reduce". */ template TR foldl(List xs, TR (*func)(TR, T), TR acc) { for (auto it = xs.iterate(); it; ++it) acc = func(acc, *it); return acc; } template size_t lengthReducer(size_t len, T) { return len + 1; } /* * Computes length of a list. */ template size_t length(List xs) { return foldl(xs, lengthReducer, (size_t)0); } template T* dumpReducer(T* buff, T x) { *buff = x; return buff + 1; } /* * Copies a list content into a memory buffer. * * It is expected that `outBuff` has enough size to fit all the data. */ template size_t dump(List xs, T* outBuff) { T* buffEnd = foldl(xs, dumpReducer, outBuff); return buffEnd - outBuff; } } // namespace xod #endif /*============================================================================= * * * Runtime * * =============================================================================*/ //---------------------------------------------------------------------------- // Debug routines //---------------------------------------------------------------------------- #ifndef DEBUG_SERIAL # define DEBUG_SERIAL Serial #endif #if defined(XOD_DEBUG) && defined(XOD_DEBUG_ENABLE_TRACE) # define XOD_TRACE(x) { DEBUG_SERIAL.print(x); DEBUG_SERIAL.flush(); } # define XOD_TRACE_LN(x) { DEBUG_SERIAL.println(x); DEBUG_SERIAL.flush(); } # define XOD_TRACE_F(x) XOD_TRACE(F(x)) # define XOD_TRACE_FLN(x) XOD_TRACE_LN(F(x)) #else # define XOD_TRACE(x) # define XOD_TRACE_LN(x) # define XOD_TRACE_F(x) # define XOD_TRACE_FLN(x) #endif //---------------------------------------------------------------------------- // PGM space utilities //---------------------------------------------------------------------------- #define pgm_read_nodeid(address) (pgm_read_word(address)) /* * Workaround for bugs: * https://github.com/arduino/ArduinoCore-sam/pull/43 * https://github.com/arduino/ArduinoCore-samd/pull/253 * Remove after the PRs merge */ #if !defined(ARDUINO_ARCH_AVR) && defined(pgm_read_ptr) # undef pgm_read_ptr # define pgm_read_ptr(addr) (*(const void **)(addr)) #endif //---------------------------------------------------------------------------- // Compatibilities //---------------------------------------------------------------------------- #if !defined(ARDUINO_ARCH_AVR) /* * Provide dtostrf function for non-AVR platforms. Although many platforms * provide a stub many others do not. And the stub is based on `sprintf` * which doesn’t work with floating point formatters on some platforms * (e.g. Arduino M0). * * This is an implementation based on `fcvt` standard function. Taken here: * https://forum.arduino.cc/index.php?topic=368720.msg2542614#msg2542614 */ char *dtostrf(double val, int width, unsigned int prec, char *sout) { int decpt, sign, reqd, pad; const char *s, *e; char *p; s = fcvt(val, prec, &decpt, &sign); if (prec == 0 && decpt == 0) { s = (*s < '5') ? "0" : "1"; reqd = 1; } else { reqd = strlen(s); if (reqd > decpt) reqd++; if (decpt == 0) reqd++; } if (sign) reqd++; p = sout; e = p + reqd; pad = width - reqd; if (pad > 0) { e += pad; while (pad-- > 0) *p++ = ' '; } if (sign) *p++ = '-'; if (decpt <= 0 && prec > 0) { *p++ = '0'; *p++ = '.'; e++; while ( decpt < 0 ) { decpt++; *p++ = '0'; } } while (p < e) { *p++ = *s++; if (p == e) break; if (--decpt == 0) *p++ = '.'; } if (width < 0) { pad = (reqd + width) * -1; while (pad-- > 0) *p++ = ' '; } *p = 0; return sout; } #endif namespace xod { //---------------------------------------------------------------------------- // Type definitions //---------------------------------------------------------------------------- typedef double Number; typedef bool Logic; typedef unsigned long TimeMs; typedef uint8_t DirtyFlags; //---------------------------------------------------------------------------- // Global variables //---------------------------------------------------------------------------- TimeMs g_transactionTime; //---------------------------------------------------------------------------- // Metaprogramming utilities //---------------------------------------------------------------------------- template struct always_false { enum { value = 0 }; }; //---------------------------------------------------------------------------- // Forward declarations //---------------------------------------------------------------------------- TimeMs transactionTime(); void runTransaction(bool firstRun); //---------------------------------------------------------------------------- // Engine (private API) //---------------------------------------------------------------------------- namespace detail { template bool isTimedOut(const NodeT* node) { TimeMs t = node->timeoutAt; // TODO: deal with uint32 overflow return t && t < transactionTime(); } // Marks timed out node dirty. Do not reset timeoutAt here to give // a chance for a node to get a reasonable result from `isTimedOut` // later during its `evaluate` template void checkTriggerTimeout(NodeT* node) { node->isNodeDirty |= isTimedOut(node); } template void clearTimeout(NodeT* node) { node->timeoutAt = 0; } template void clearStaleTimeout(NodeT* node) { if (isTimedOut(node)) clearTimeout(node); } } // namespace detail //---------------------------------------------------------------------------- // Public API (can be used by native nodes’ `evaluate` functions) //---------------------------------------------------------------------------- TimeMs transactionTime() { return g_transactionTime; } template void setTimeout(ContextT* ctx, TimeMs timeout) { ctx->_node->timeoutAt = transactionTime() + timeout; } template void clearTimeout(ContextT* ctx) { detail::clearTimeout(ctx->_node); } template bool isTimedOut(const ContextT* ctx) { return detail::isTimedOut(ctx->_node); } } // namespace xod //---------------------------------------------------------------------------- // Entry point //---------------------------------------------------------------------------- void setup() { // FIXME: looks like there is a rounding bug. Waiting for 100ms fights it delay(100); #ifdef XOD_DEBUG DEBUG_SERIAL.begin(115200); #endif XOD_TRACE_FLN("\n\nProgram started"); xod::runTransaction(true); } void loop() { xod::runTransaction(false); } /*============================================================================= * * * Native node implementations * * =============================================================================*/ namespace xod { //----------------------------------------------------------------------------- // xod/core/gate implementation //----------------------------------------------------------------------------- namespace xod__core__gate { struct State { }; struct Node { State state; Logic output_T; Logic output_F; union { struct { bool isOutputDirty_T : 1; bool isOutputDirty_F : 1; bool isNodeDirty : 1; }; DirtyFlags dirtyFlags; }; }; struct input_GATE { }; struct input_TRIG { }; struct output_T { }; struct output_F { }; template struct ValueType { using T = void; }; template<> struct ValueType { using T = Logic; }; template<> struct ValueType { using T = Logic; }; template<> struct ValueType { using T = Logic; }; template<> struct ValueType { using T = Logic; }; struct ContextObject { Node* _node; Logic _input_GATE; Logic _input_TRIG; bool _isInputDirty_TRIG; }; using Context = ContextObject*; template typename ValueType::T getValue(Context ctx) { static_assert(always_false::value, "Invalid pin descriptor. Expected one of:" \ " input_GATE input_TRIG" \ " output_T output_F"); } template<> Logic getValue(Context ctx) { return ctx->_input_GATE; } template<> Logic getValue(Context ctx) { return ctx->_input_TRIG; } template<> Logic getValue(Context ctx) { return ctx->_node->output_T; } template<> Logic getValue(Context ctx) { return ctx->_node->output_F; } template bool isInputDirty(Context ctx) { static_assert(always_false::value, "Invalid input descriptor. Expected one of:" \ " input_TRIG"); return false; } template<> bool isInputDirty(Context ctx) { return ctx->_isInputDirty_TRIG; } template void emitValue(Context ctx, typename ValueType::T val) { static_assert(always_false::value, "Invalid output descriptor. Expected one of:" \ " output_T output_F"); } template<> void emitValue(Context ctx, Logic val) { ctx->_node->output_T = val; ctx->_node->isOutputDirty_T = true; } template<> void emitValue(Context ctx, Logic val) { ctx->_node->output_F = val; ctx->_node->isOutputDirty_F = true; } State* getState(Context ctx) { return &ctx->_node->state; } void evaluate(Context ctx) { if (!isInputDirty(ctx)) return; if (getValue(ctx)) { emitValue(ctx, 1); } else { emitValue(ctx, 1); } } } // namespace xod__core__gate //----------------------------------------------------------------------------- // xod/core/digital_input implementation //----------------------------------------------------------------------------- namespace xod__core__digital_input { struct State { int configuredPort = -1; }; struct Node { State state; Logic output_SIG; union { struct { bool isOutputDirty_SIG : 1; bool isNodeDirty : 1; }; DirtyFlags dirtyFlags; }; }; struct input_PORT { }; struct input_UPD { }; struct output_SIG { }; template struct ValueType { using T = void; }; template<> struct ValueType { using T = Number; }; template<> struct ValueType { using T = Logic; }; template<> struct ValueType { using T = Logic; }; struct ContextObject { Node* _node; Number _input_PORT; Logic _input_UPD; bool _isInputDirty_UPD; }; using Context = ContextObject*; template typename ValueType::T getValue(Context ctx) { static_assert(always_false::value, "Invalid pin descriptor. Expected one of:" \ " input_PORT input_UPD" \ " output_SIG"); } template<> Number getValue(Context ctx) { return ctx->_input_PORT; } template<> Logic getValue(Context ctx) { return ctx->_input_UPD; } template<> Logic getValue(Context ctx) { return ctx->_node->output_SIG; } template bool isInputDirty(Context ctx) { static_assert(always_false::value, "Invalid input descriptor. Expected one of:" \ " input_UPD"); return false; } template<> bool isInputDirty(Context ctx) { return ctx->_isInputDirty_UPD; } template void emitValue(Context ctx, typename ValueType::T val) { static_assert(always_false::value, "Invalid output descriptor. Expected one of:" \ " output_SIG"); } template<> void emitValue(Context ctx, Logic val) { ctx->_node->output_SIG = val; ctx->_node->isOutputDirty_SIG = true; } State* getState(Context ctx) { return &ctx->_node->state; } void evaluate(Context ctx) { if (!isInputDirty(ctx)) return; State* state = getState(ctx); const int port = (int)getValue(ctx); if (port != state->configuredPort) { ::pinMode(port, INPUT); // Store configured port so to avoid repeating `pinMode` on // subsequent requests state->configuredPort = port; } emitValue(ctx, ::digitalRead(port)); } } // namespace xod__core__digital_input //----------------------------------------------------------------------------- // xod/core/flip_flop implementation //----------------------------------------------------------------------------- namespace xod__core__flip_flop { struct State { }; struct Node { State state; Logic output_MEM; union { struct { bool isOutputDirty_MEM : 1; bool isNodeDirty : 1; }; DirtyFlags dirtyFlags; }; }; struct input_SET { }; struct input_TGL { }; struct input_RST { }; struct output_MEM { }; template struct ValueType { using T = void; }; template<> struct ValueType { using T = Logic; }; template<> struct ValueType { using T = Logic; }; template<> struct ValueType { using T = Logic; }; template<> struct ValueType { using T = Logic; }; struct ContextObject { Node* _node; Logic _input_SET; Logic _input_TGL; Logic _input_RST; bool _isInputDirty_SET; bool _isInputDirty_TGL; bool _isInputDirty_RST; }; using Context = ContextObject*; template typename ValueType::T getValue(Context ctx) { static_assert(always_false::value, "Invalid pin descriptor. Expected one of:" \ " input_SET input_TGL input_RST" \ " output_MEM"); } template<> Logic getValue(Context ctx) { return ctx->_input_SET; } template<> Logic getValue(Context ctx) { return ctx->_input_TGL; } template<> Logic getValue(Context ctx) { return ctx->_input_RST; } template<> Logic getValue(Context ctx) { return ctx->_node->output_MEM; } template bool isInputDirty(Context ctx) { static_assert(always_false::value, "Invalid input descriptor. Expected one of:" \ " input_SET input_TGL input_RST"); return false; } template<> bool isInputDirty(Context ctx) { return ctx->_isInputDirty_SET; } template<> bool isInputDirty(Context ctx) { return ctx->_isInputDirty_TGL; } template<> bool isInputDirty(Context ctx) { return ctx->_isInputDirty_RST; } template void emitValue(Context ctx, typename ValueType::T val) { static_assert(always_false::value, "Invalid output descriptor. Expected one of:" \ " output_MEM"); } template<> void emitValue(Context ctx, Logic val) { ctx->_node->output_MEM = val; ctx->_node->isOutputDirty_MEM = true; } State* getState(Context ctx) { return &ctx->_node->state; } void evaluate(Context ctx) { bool oldState = getValue(ctx); bool newState = oldState; if (isInputDirty(ctx)) { newState = !oldState; } else if (isInputDirty(ctx)) { newState = true; } else { newState = false; } if (newState == oldState) return; emitValue(ctx, newState); } } // namespace xod__core__flip_flop //----------------------------------------------------------------------------- // xod/core/digital_output implementation //----------------------------------------------------------------------------- namespace xod__core__digital_output { struct State { int configuredPort = -1; }; struct Node { State state; union { struct { bool isNodeDirty : 1; }; DirtyFlags dirtyFlags; }; }; struct input_PORT { }; struct input_SIG { }; template struct ValueType { using T = void; }; template<> struct ValueType { using T = Number; }; template<> struct ValueType { using T = Logic; }; struct ContextObject { Node* _node; Number _input_PORT; Logic _input_SIG; }; using Context = ContextObject*; template typename ValueType::T getValue(Context ctx) { static_assert(always_false::value, "Invalid pin descriptor. Expected one of:" \ " input_PORT input_SIG" \ ""); } template<> Number getValue(Context ctx) { return ctx->_input_PORT; } template<> Logic getValue(Context ctx) { return ctx->_input_SIG; } template bool isInputDirty(Context ctx) { static_assert(always_false::value, "Invalid input descriptor. Expected one of:" \ ""); return false; } template void emitValue(Context ctx, typename ValueType::T val) { static_assert(always_false::value, "Invalid output descriptor. Expected one of:" \ ""); } State* getState(Context ctx) { return &ctx->_node->state; } void evaluate(Context ctx) { State* state = getState(ctx); const int port = (int)getValue(ctx); if (port != state->configuredPort) { ::pinMode(port, OUTPUT); // Store configured port so to avoid repeating `pinMode` call if just // SIG is updated state->configuredPort = port; } const bool val = getValue(ctx); ::digitalWrite(port, val); } } // namespace xod__core__digital_output //----------------------------------------------------------------------------- // xod/core/continuously implementation //----------------------------------------------------------------------------- namespace xod__core__continuously { struct State { }; struct Node { State state; TimeMs timeoutAt; Logic output_TICK; union { struct { bool isOutputDirty_TICK : 1; bool isNodeDirty : 1; }; DirtyFlags dirtyFlags; }; }; struct output_TICK { }; template struct ValueType { using T = void; }; template<> struct ValueType { using T = Logic; }; struct ContextObject { Node* _node; }; using Context = ContextObject*; template typename ValueType::T getValue(Context ctx) { static_assert(always_false::value, "Invalid pin descriptor. Expected one of:" \ "" \ " output_TICK"); } template<> Logic getValue(Context ctx) { return ctx->_node->output_TICK; } template bool isInputDirty(Context ctx) { static_assert(always_false::value, "Invalid input descriptor. Expected one of:" \ ""); return false; } template void emitValue(Context ctx, typename ValueType::T val) { static_assert(always_false::value, "Invalid output descriptor. Expected one of:" \ " output_TICK"); } template<> void emitValue(Context ctx, Logic val) { ctx->_node->output_TICK = val; ctx->_node->isOutputDirty_TICK = true; } State* getState(Context ctx) { return &ctx->_node->state; } void evaluate(Context ctx) { emitValue(ctx, 1); setTimeout(ctx, 0); } } // namespace xod__core__continuously } // namespace xod /*============================================================================= * * * Main loop components * * =============================================================================*/ namespace xod { // Define/allocate persistent storages (state, timeout, output data) for all nodes constexpr Number node_0_output_VAL = 12; constexpr Number node_1_output_VAL = 11; constexpr Number node_2_output_VAL = 13; constexpr Logic node_3_output_TICK = false; xod__core__continuously::Node node_3 = { xod__core__continuously::State(), // state default 0, // timeoutAt node_3_output_TICK, // output TICK default false, // TICK dirty true // node itself dirty }; constexpr Logic node_4_output_SIG = false; xod__core__digital_input::Node node_4 = { xod__core__digital_input::State(), // state default node_4_output_SIG, // output SIG default true, // SIG dirty true // node itself dirty }; constexpr Logic node_5_output_SIG = false; xod__core__digital_input::Node node_5 = { xod__core__digital_input::State(), // state default node_5_output_SIG, // output SIG default true, // SIG dirty true // node itself dirty }; constexpr Logic node_6_output_T = false; constexpr Logic node_6_output_F = false; xod__core__gate::Node node_6 = { xod__core__gate::State(), // state default node_6_output_T, // output T default node_6_output_F, // output F default false, // T dirty false, // F dirty true // node itself dirty }; constexpr Logic node_7_output_T = false; constexpr Logic node_7_output_F = false; xod__core__gate::Node node_7 = { xod__core__gate::State(), // state default node_7_output_T, // output T default node_7_output_F, // output F default false, // T dirty false, // F dirty true // node itself dirty }; constexpr Logic node_8_output_MEM = false; xod__core__flip_flop::Node node_8 = { xod__core__flip_flop::State(), // state default node_8_output_MEM, // output MEM default true, // MEM dirty true // node itself dirty }; xod__core__digital_output::Node node_9 = { xod__core__digital_output::State(), // state default true // node itself dirty }; void runTransaction(bool firstRun) { g_transactionTime = millis(); XOD_TRACE_F("Transaction started, t="); XOD_TRACE_LN(g_transactionTime); // Check for timeouts detail::checkTriggerTimeout(&node_3); // defer-* nodes are always at the very bottom of the graph, so no one will // recieve values emitted by them. We must evaluate them before everybody // else to give them a chance to emit values. // // If trigerred, keep only output dirty, not the node itself, so it will // evaluate on the regular pass only if it pushed a new value again. // Evaluate all dirty nodes { // xod__core__continuously #3 if (node_3.isNodeDirty) { XOD_TRACE_F("Eval node #"); XOD_TRACE_LN(3); xod__core__continuously::ContextObject ctxObj; ctxObj._node = &node_3; // copy data from upstream nodes into context xod__core__continuously::evaluate(&ctxObj); // mark downstream nodes dirty node_6.isNodeDirty |= node_3.isOutputDirty_TICK; node_4.isNodeDirty |= node_3.isOutputDirty_TICK; node_7.isNodeDirty |= node_3.isOutputDirty_TICK; node_5.isNodeDirty |= node_3.isOutputDirty_TICK; } } { // xod__core__digital_input #4 if (node_4.isNodeDirty) { XOD_TRACE_F("Eval node #"); XOD_TRACE_LN(4); xod__core__digital_input::ContextObject ctxObj; ctxObj._node = &node_4; // copy data from upstream nodes into context ctxObj._input_PORT = node_0_output_VAL; ctxObj._input_UPD = node_3.output_TICK; ctxObj._isInputDirty_UPD = node_3.isOutputDirty_TICK; xod__core__digital_input::evaluate(&ctxObj); // mark downstream nodes dirty node_6.isNodeDirty |= node_4.isOutputDirty_SIG; } } { // xod__core__digital_input #5 if (node_5.isNodeDirty) { XOD_TRACE_F("Eval node #"); XOD_TRACE_LN(5); xod__core__digital_input::ContextObject ctxObj; ctxObj._node = &node_5; // copy data from upstream nodes into context ctxObj._input_PORT = node_1_output_VAL; ctxObj._input_UPD = node_3.output_TICK; ctxObj._isInputDirty_UPD = node_3.isOutputDirty_TICK; xod__core__digital_input::evaluate(&ctxObj); // mark downstream nodes dirty node_7.isNodeDirty |= node_5.isOutputDirty_SIG; } } { // xod__core__gate #6 if (node_6.isNodeDirty) { XOD_TRACE_F("Eval node #"); XOD_TRACE_LN(6); xod__core__gate::ContextObject ctxObj; ctxObj._node = &node_6; // copy data from upstream nodes into context ctxObj._input_GATE = node_4.output_SIG; ctxObj._input_TRIG = node_3.output_TICK; ctxObj._isInputDirty_TRIG = node_3.isOutputDirty_TICK; xod__core__gate::evaluate(&ctxObj); // mark downstream nodes dirty node_8.isNodeDirty |= node_6.isOutputDirty_F; } } { // xod__core__gate #7 if (node_7.isNodeDirty) { XOD_TRACE_F("Eval node #"); XOD_TRACE_LN(7); xod__core__gate::ContextObject ctxObj; ctxObj._node = &node_7; // copy data from upstream nodes into context ctxObj._input_GATE = node_5.output_SIG; ctxObj._input_TRIG = node_3.output_TICK; ctxObj._isInputDirty_TRIG = node_3.isOutputDirty_TICK; xod__core__gate::evaluate(&ctxObj); // mark downstream nodes dirty node_8.isNodeDirty |= node_7.isOutputDirty_F; } } { // xod__core__flip_flop #8 if (node_8.isNodeDirty) { XOD_TRACE_F("Eval node #"); XOD_TRACE_LN(8); xod__core__flip_flop::ContextObject ctxObj; ctxObj._node = &node_8; // copy data from upstream nodes into context ctxObj._input_SET = node_7.output_F; ctxObj._input_RST = node_6.output_F; ctxObj._isInputDirty_TGL = false; ctxObj._isInputDirty_SET = node_7.isOutputDirty_F; ctxObj._isInputDirty_RST = node_6.isOutputDirty_F; xod__core__flip_flop::evaluate(&ctxObj); // mark downstream nodes dirty node_9.isNodeDirty |= node_8.isOutputDirty_MEM; } } { // xod__core__digital_output #9 if (node_9.isNodeDirty) { XOD_TRACE_F("Eval node #"); XOD_TRACE_LN(9); xod__core__digital_output::ContextObject ctxObj; ctxObj._node = &node_9; // copy data from upstream nodes into context ctxObj._input_PORT = node_2_output_VAL; ctxObj._input_SIG = node_8.output_MEM; xod__core__digital_output::evaluate(&ctxObj); // mark downstream nodes dirty } } // Clear dirtieness and timeouts for all nodes and pins node_3.dirtyFlags = 0; node_4.dirtyFlags = 0; node_5.dirtyFlags = 0; node_6.dirtyFlags = 0; node_7.dirtyFlags = 0; node_8.dirtyFlags = 0; node_9.dirtyFlags = 0; detail::clearStaleTimeout(&node_3); XOD_TRACE_F("Transaction completed, t="); XOD_TRACE_LN(millis()); } } // namespace xod