Drive an instance input with rules instead of a wire¶
You have a verified building block — a latch, a limiter, a counter — and you want to say when it should be set, not compute a value to feed it. Writing the wire means inventing an expression; writing the rule means stating the condition you actually have in mind.
Both are available. This page shows when the second one is the honest choice, and what the compiler demands in return.
The wire and the rule side by side¶
A latch instance normally gets its inputs wired:
FUNCTION_BLOCK WiredLatch
VAR_INPUT Start : BOOL; Stop : BOOL; END_VAR
VAR_OUTPUT Running : BOOL; END_VAR
VAR latch : RS; END_VAR
IMPLEMENTATION
latch(S := Start, R1 := Stop);
Running := latch.Q1;
END_IMPLEMENTATION
END_FUNCTION_BLOCK
The same block, with the set input governed by rules:
FUNCTION_BLOCK Governed
VAR_INPUT Start : BOOL; Stop : BOOL; END_VAR
VAR_OUTPUT Running : BOOL; END_VAR
VAR latch : RS; END_VAR
IMPLEMENTATION
latch(R1 := Stop);
Running := latch.Q1;
RULE Start IMPLIES latch.S;
RULE latch.S ONLY_WHEN Start;
END_IMPLEMENTATION
END_FUNCTION_BLOCK
Both behave identically — set on Start, hold, reset on Stop. The second
says it in two sentences a plant engineer would recognise: Start sets the
latch, and nothing else may.
Why two rules and not one¶
This is the part worth understanding, because leaving out either one is a compile error rather than a shortcut.
RULE Start IMPLIES latch.S is a trigger: when Start holds, the latch
must be set. On its own it says nothing about the other case — with Start
false, latch.S could still be anything.
RULE latch.S ONLY_WHEN Start is a permission: the latch may only be set
while Start holds. On its own it also leaves a gap — it never requires the
latch to be set at all.
Together they pin the value exactly: set precisely when Start, and never
otherwise. That is the requirement the compiler enforces — a governed input
must be determined, one value for every input assignment, not merely
constrained. State only the permission and you get:
verification failed:
[rmc1-sink-unconstrained] the governed input latch.S is missing a
sufficient clause (… IMPLIES latch.S) — without both directions its
value is not determined (RMC-1.1)
Not a complaint about style: an underdetermined input has no single meaning, and the compiler will not pick one for you.
A sink is wired or governed, never both¶
Adding S := Start to the instance call and keeping the rules is rejected:
verification failed:
[rmc1-sink-conflict] the input latch.S is both wired and governed by
clauses (line 8); a sink is exactly one of the two (RMC-1.2)
Two mechanisms describing one input is one description too many. If they ever disagreed, nothing in the language would say which wins — so the language does not allow the question to arise.
When to reach for it¶
Use rules when the condition is what you know and the value is not:
- Interlocks. "The pump may only run while the valve is open" is a
permission. Written as a wire, it becomes an
ANDwhose reason has to be reconstructed by the next reader. - Several conditions from different places. Permissions accumulate; the compiler folds them into one wire, and each rule stays readable on its own.
- Members of verified library blocks. The block keeps its proof, and the glue around it stays combinational — which is why a stateful system can be expressed without reaching for temporal constructs.
Use a wire when you genuinely have a value: an arithmetic result, a passed-on input, anything where the expression is the intent.
What the compiler reports¶
| Code | Meaning |
|---|---|
rmc1-sink-conflict |
the input is both wired and governed — choose one |
rmc1-sink-unconstrained |
one direction is missing; a trigger and a permission are both needed |
rmc1-unknown-instance |
the rule names an instance that is not declared |
An input with neither wire nor rules is reported as unwired, exactly as before — governance is an alternative to wiring, not an exemption from it.
Related¶
docs/reference/language.md— the clause forms (IMPLIES,ONLY_WHEN,IFF,EXCLUDES) and what each one statesdocs/adr-relational-member-composition.md— the design decision and its uniqueness requirement
Both messages above are the compiler's actual output, checked with
lola --target check while writing this page.