node {
TimeMs nextTrig;
void evaluate(Context ctx) {
TimeMs tNow = transactionTime();
auto ival = getValue<input_IVAL>(ctx);
if (ival < 0) ival = 0;
TimeMs dt = ival * 1000;
TimeMs tNext = tNow + dt;
auto isEnabled = getValue<input_EN>(ctx);
auto isRstDirty = isInputDirty<input_RST>(ctx);
if (isTimedOut(ctx) && isEnabled && !isRstDirty) {
emitValue<output_TICK>(ctx, 1);
nextTrig = tNext;
setTimeout(ctx, dt);
}
if (isRstDirty || isInputDirty<input_EN>(ctx)) {
// Handle enable/disable/reset
if (!isEnabled) {
// Disable timeout loop on explicit false on EN
nextTrig = 0;
clearTimeout(ctx);
} else if (nextTrig < tNow || nextTrig > tNext) {
// Start timeout from scratch
nextTrig = tNext;
setTimeout(ctx, dt);
}
}
}
}
__time(ms) | IVAL | EN | RST | TICK |
0 | 0.1 | true | no-pulse | no-pulse |
100 | 0.1 | true | pulse | no-pulse |
120 | 0.1 | true | no-pulse | no-pulse |
201 | 0.1 | true | no-pulse | pulse |
250 | 0.1 | true | no-pulse | no-pulse |
302 | 0.1 | true | no-pulse | pulse |
350 | 0.1 | false | no-pulse | no-pulse |
403 | 0.1 | false | no-pulse | no-pulse |
0 | 1 | true | pulse | no-pulse |
500 | 1 | true | pulse | no-pulse |
1001 | 1 | true | no-pulse | no-pulse |
1501 | 1 | true | no-pulse | pulse |
// edge case: when IVAL is 0, `clock` should constantly emit pulses | ||||
0 | 0 | true | no-pulse | pulse |
1 | 0 | true | no-pulse | pulse |
2 | 1 | true | no-pulse | pulse |
1003 | 1 | true | no-pulse | pulse |
// edge case: when IVAL is negative, behave like it's 0 | ||||
0 | -1 | true | no-pulse | pulse |
1 | -1 | true | no-pulse | pulse |
2 | 1 | true | no-pulse | pulse |
1003 | 1 | true | no-pulse | pulse |