Problem Overview
A Function Block (FB) compiled in STEP 7 (TIA Portal or classic STEP 7 V5.x) shows the MOVE box returning ENO = FALSE even though the source value, destination tag, and box enable conditions are all valid. The symptom is reproducible only when a second MOVE (for example, a reset to 0 after a timer expires) is placed after the first MOVE in the same network, or when several MOVE operations are chained in subsequent networks of the same FB.
In the reported case the FB was FB53 with the offending logic in Network 15. The two-second off-delay timer (S_ODT / S_OFFDT) drives a MOVE that loads a non-zero constant into a static INT #ACK. After the timer expires, a second MOVE writes 0 back into the same tag to clear it. With both MOVE boxes present, the second MOVE's ENO output is FALSE. Removing the reset MOVE restores ENO = TRUE, but the tag can no longer be cleared automatically.
Adding a --(SAVE) coil that is permanently TRUE at the end of the network restores ENO = TRUE on every MOVE box. The behavior is reproducible in STEP 7 V5.x (FB source in STL or LAD) and in TIA Portal V14..V20 when the block is generated in LAD/FBD and viewed in STL.
EN/ENO Mechanism Fundamentals
The EN (Enable) and ENO (Enable Output) mechanism in STEP 7 is a Boolean handshake applied automatically to a defined set of basic instructions. Per the Siemens TIA Portal documentation "Basics of the EN/ENO mechanism":
- If
EN = FALSEat the start of the instruction, the instruction is skipped andENOis set toFALSE. - If
EN = TRUEand the instruction completes without runtime error,ENO = TRUE. - If
EN = TRUEand a runtime error is detected (overflow, invalid operand, I/O access fault),ENO = FALSE.
The instruction set that uses the EN/ENO handshake per Siemens ID 109742272 (STEP 7 Professional V14.0):
| Instruction Family | Examples | ENO Behavior |
|---|---|---|
| Mathematical functions | ADD, SUB, MUL, DIV, MOD, ABS, NEG, SQR, SQRT, LN, EXP, SIN, COS, TAN | ENO = FALSE on overflow, divide by zero, invalid operand |
| Move operations | MOVE, BLKMOV, UBLKMOV, FILL, NOP | ENO = FALSE on I/O access error only (classic STEP 7) |
| Conversion operations | INT_TO_REAL, REAL_TO_DINT, WORD_TO_BLOCK_DB, etc. | ENO = FALSE on value out of range |
| Word logic operations | AND_WORD, OR_WORD, XOR_WORD | ENO propagated unchanged |
| Shift/rotate | SHL, SHR, ROL, ROR | ENO = FALSE on invalid operand |
| Comparators | EQ, NE, GT, LT, GE, LE (newer style) | ENO propagated unchanged |
OV, OS, CC0, CC1 in STL, or the GET_ERR/GET_ERR_ID instructions in SCL, for detailed error diagnostics.Root Cause: SAVE Instruction in LAD-to-STL Translation
The classic STEP 7 compiler and the TIA Portal translator generate STL code from LAD/FBD. In STL, the SAVE instruction copies the current RLO (Result of Logic Operation, equivalent to the BR / binary result bit) into the BR bit of the status word. The block's ENO output is wired to this BR bit when the block is called from a LAD/FBD parent context.
The mechanism behaves as follows:
- When LAD/FBD is translated to STL, several box instructions internally emit a
SAVEinstruction immediately after their internal logic. Examples in classic STEP 7 include Move, Timer boxes (S_ODT,S_OFFDT,S_PULSE, etc.), Counter boxes, and certain conversion boxes. - The
SAVEinstruction setsBRto the value of the current RLO, which is typically1on successful execution and0when the box reports a fault. - When the compiler emits the last
SAVEfor the block, this finalSAVEdetermines the value ofENOthat the parent block observes. - If the last instruction in the block (or in a network under specific optimization) does not emit a
SAVE, or if a subsequent operation clearsBR(for example, any STL bit-logic instruction that writes RLO before reaching the block end), thenENOcan beFALSEeven though every MOVE executed correctly.
In the reported case, Network 15 contained:
- A timer coil (
S_ODT) followed by a MOVE that loads1(or another non-zero value) into#ACK. - A second MOVE writing
0into#ACKwhen the timer elapsed. - One or more subsequent STL-equivalent bit operations (implicitly generated by the LAD compiler for the timer's output contacts) that did not preserve
BR.
The compiler's final emitted SAVE ended up reflecting the result of the timer's contact evaluation rather than the MOVE's success flag. The visible symptom was ENO = FALSE on the second MOVE. Removing the second MOVE removed the network condition that forced the final SAVE to be re-evaluated, masking the issue.
Diagnostic Procedure
To confirm the SAVE-related cause and isolate the offending network, perform the following steps:
- Open the FB in the STEP 7 editor (TIA Portal or STEP 7 V5.x).
- Switch the programming language view for the affected network from LAD to STL (right-click the network title → "View → STL" in classic STEP 7, or use the language switcher in TIA Portal).
- Inspect every line that contains the literal instruction
SAVEor the mnemonicSAVEcompiled from a box. - Identify the last
SAVEin the compiled output. Trace the RLO that flows into thatSAVE. - Cross-check the STL view of every box preceding the final
SAVEfor any bit-logic instruction (A,O,X,AN,ON,XN,S,R, assignment=) that may overwrite RLO. - Use the Monitor/Modify function (online → "Monitor") to step through Network 15 with
ENOvisible. Confirm the BR bit in the status word toggles between the two MOVE operations.
The expected STL output for a simple two-MOVE sequence (after translation) looks similar to:
// Network 15 - translated from LAD
A "C.acknowledge" // RLO = 1 if acknowledge bit is set
L W#16#1 // Load constant
T #ACK // First MOVE: INT = 1
SAVE // SAVE from internal compiler; sets BR = 1
A #timer_running // STL contact for timer's output
JCN NO_RESET // Skip reset if timer still running
L W#16#0
T #ACK // Second MOVE: INT = 0
NO_RESET: SAVE // Final SAVE - this drives ENO
BEU // Block end unconditional
If the final SAVE sees an RLO of 0 (for example, because the timer's contact was 0 at the moment of evaluation, or because a prior assignment cleared RLO), then BR = 0 and ENO = FALSE for the whole block from the caller's perspective.
Solution Implementation
The standard remedies, ordered from least to most invasive:
Solution 1: Explicit SAVE coil (recommended quick fix)
Add a --(SAVE) coil driven by a permanently-true contact at the end of the network:
// In LAD/FBD
--| |----(SAVE) // contact driven by TRUE constant (e.g. %M0.0 or '1')
Equivalent STL inserted manually:
SET // RLO = 1 unconditionally
SAVE // Force BR = 1
This guarantees that the last SAVE in the network writes 1 to BR, propagating ENO = TRUE regardless of the timer's contact state.
Solution 2: Program the FB natively in STL
If the FB must guarantee deterministic ENO behavior, author it directly in STL. Place a single explicit SAVE immediately before the block-end statement (BE), and avoid intermixing bit-logic with box instructions in the same network.
// Network 15 - hand-written STL
A "C.acknowledge"
L 1
ITD
T #ACK // MOVE 1 to #ACK
A #timer_done
JCN SKIP
L 0
T #ACK // MOVE 0 to #ACK
SKIP: SET
SAVE // Final ENO = TRUE
Solution 3: Re-author the block in SCL
SCL does not use the SAVE-based ENO mechanism the same way as STL. ENO in SCL reflects the OK flag of the last expression in the block and is rarely affected by contact logic. If you must remain in a high-level language, porting the block from LAD/FBD to SCL typically eliminates this class of issue:
IF "C.acknowledge" THEN
#ACK := 1;
END_IF;
IF #timer_done THEN
#ACK := 0;
END_IF;
// ENO automatically = TRUE if the block compiles without error
ENO via the ENO := TRUE; assignment within the FB. For comparison, Mitsubishi's MELSEC iQ-F/iQ-R ST language exposes the same pattern - ENO becomes FALSE when EN is FALSE or when the function sets ENO := FALSE (see Mitsubishi Structured Text Programming Manual, document SH-081483ENG-F).Solution 4: Replace the timer/contact pattern with a clean state machine
For long-term maintainability, separate the reset logic into its own network, and ensure each network ends with a deterministic RLO. Avoid sharing one timer coil across multiple MOVE blocks in the same network.
Verification Steps
- Download the modified FB to the CPU (TIA Portal: "Download to device"; classic STEP 7: "PLC → Download").
- Open the FB online and observe
ENOon the second MOVE during the timer's transition from running to expired. - Confirm that
#ACKis written to1on acknowledge, and to0two seconds later. - From the WinCC side, verify that the alarm acknowledgment bit transitions correctly without sticking.
- Force a fault condition (e.g., assign an invalid operand to a downstream conversion box) and confirm that
ENOstill goesFALSEfor genuine runtime errors - the explicitSAVEmust not mask real faults inside the block.
STL SAVE Reference Table
| Mnemonic | Operation | Effect on BR | Effect on ENO |
|---|---|---|---|
SAVE |
Copy RLO to BR | BR = RLO | ENO follows BR at block boundary |
SET |
Set RLO to 1 | BR unchanged (until next SAVE) | ENO unchanged |
CLR |
Reset RLO to 0 | BR unchanged (until next SAVE) | ENO unchanged |
= (assignment) |
Write RLO to addressed operand | BR unchanged | ENO unchanged |
A / AN / O / ON / X / XN |
Bit-logic AND/OR/XOR | BR unchanged | ENO unchanged |
BE / BEU |
Block end | BR drives ENO of calling context | ENO evaluated |
Box-generated internal SAVE
|
Compiler-emitted | BR = internal fault flag of the box | ENO may flip on box fault |
ENO Behavior by Instruction Family
| Instruction | Sets ENO=FALSE when | Typical trigger |
|---|---|---|
| MOVE (L/T) | I/O access error, BR overwritten by later SAVE | Indirect address out of range, missing SAVE |
| ADD_I / ADD_R | Overflow | Result exceeds INT/DINT/REAL range |
| DIV_I / DIV_R | Divide by zero | Divisor = 0 |
| REAL_TO_INT | Value out of INT range | Source REAL > 32767 or < -32768 |
| SQRT / LN | Negative argument | Source < 0 |
| S_ODT / S_OFFDT / S_PULSE | Internal SAVE reflects RLO of preceding contact | Timer enable coil logic unclear |
| BLKMOV / UBLKMOV | Source/destination overlap, out-of-range | Any pointer miscalculation |
Best Practices
- Author FBs in a single language. Mixing LAD boxes with hand-written STL inside the same network produces unpredictable
SAVEplacement. - Avoid relying on
ENOfrom inside a LAD/FBD FB to drive critical safety logic. Use theOK/ENOoutput only for diagnostic or operator HMI purposes. - Place the explicit
SAVEat the very end of the block, after all other logic, so that no subsequent instruction can overwrite BR. - If you must use MOVE with
ENO, use a MOVE_BLK variant or implement the equivalentL/Tpair in STL with a deliberateSAVEat the end. - Use
GET_ERRandGET_ERR_ID(available in SCL from S7-300/400 firmware ≥ V2.0 and all S7-1200/1500 CPUs) for proper error handling rather than the EN/ENO handshake. - When the block is called from a GRAPH or S7-Graph sequencer, ensure that the interlock logic does not require ENO for branching - GRAPH evaluates ENO as part of the step transition condition.
Related Issues and Field Reports
Several adjacent ENO symptoms trace back to the same SAVE mechanism:
- MOVE after timer contact: identical pattern, ENO flips when timer contact is FALSE at the moment of evaluation.
-
ENO false after arithmetic in the last network: the final ADD/SUB uses the wrong accumulator (ACCU1 vs ACCU2) and the implicit
SAVEreads a 0 RLO. -
ENO false only when block is called from FBD parent: the FBD parent expects BR-driven ENO, but the LAD-compiled child does not emit a final
SAVEbecause the last box is a no-opNOP. - ENO false in OB1 only, not in OB35: cycle-time dependent - the timer's contact state changes between the two OBs, but the FB is only executed in OB1.
ENO as the sole indicator of safe block execution when the FB performs writes to I/O. Combine ENO with a dedicated Done / Error output tag for SIL-rated applications.Troubleshooting Matrix
| Symptom | Likely Cause | First Check | Fix |
|---|---|---|---|
| ENO=FALSE on MOVE only when followed by another MOVE | Final SAVE reads second MOVE's contact RLO | STL view, last SAVE | Add explicit SAVE coil |
| ENO=FALSE on arithmetic | Overflow / division by zero | OV / OS bits | Range-check inputs |
| ENO=FALSE on conversion | Source value out of target range | Min/Max of source | Clamp or branch on EN |
| ENO=FALSE intermittently | Timing-dependent contact state | Cross-reference contact bits | Restructure to deterministic sequence |
| ENO=FALSE only in FBD caller | FBD-specific BR handling | Check BR before BEU | Add SET; SAVE; before BEU |
| ENO=TRUE but value not written | ENO is not an execution guarantee | Monitor destination tag | Add Done output tag |
Why does a MOVE block in a Siemens STEP 7 FB return ENO = FALSE even when the value is moved correctly?
The ENO output is wired to the BR (binary result) bit of the status word, and the BR bit is set by the last SAVE instruction the compiler emits. LAD/FBD-to-STL translation can place that final SAVE after a contact evaluation rather than after the MOVE itself, so a contact in state 0 overwrites BR with 0 and the calling block sees ENO = FALSE. Add an explicit --(SAVE) driven by a constant TRUE contact at the end of the network, or author the FB natively in STL with a deliberate SAVE before BE.
Is ENO a reliable way to detect MOVE errors in STEP 7?
No. Per Siemens documentation (ID 109742272), ENO primarily reflects instruction-level runtime faults such as I/O access errors or overflows. It is not a substitute for explicit error handling. Use SCL's GET_ERR / GET_ERR_ID instructions, or in STL the global flags OV, OS, CC0, CC1, for reliable error diagnostics.
How do I see the STL that STEP 7 generates from my LAD network?
In classic STEP 7 V5.x, right-click the network title and choose "View → STL". In TIA Portal, use the language switcher at the top of the editor to change the active view. Search the resulting STL for the mnemonic SAVE; the last occurrence is what determines the block's ENO output.
Can I just place a --(SAVE) coil at the end of every FB to avoid this issue?
Yes, and it is a common defensive pattern in classic STEP 7 FBs. However, be aware that it also forces ENO = TRUE even when a real runtime fault (overflow, divide by zero) occurred in the block. For safety-critical blocks, combine the explicit SAVE with a dedicated Error / Done output tag so genuine faults are not masked.
Does the same SAVE-based ENO behavior apply to S7-1200 and S7-1500 in TIA Portal?
The mechanism is conceptually identical for FBs compiled from LAD/FBD in TIA Portal V14 through V20. SCL blocks behave differently because their ENO is bound to the OK flag of the last expression rather than to BR. For deterministic ENO behavior on S7-1200/1500, prefer SCL or hand-written STL with explicit SAVE placement.