Resolving Siemens STEP 7 MOVE ENO False: SAVE Bit Root Cause

David Krause12 min read
SiemensTIA PortalTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

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 = FALSE at the start of the instruction, the instruction is skipped and ENO is set to FALSE.
  • If EN = TRUE and the instruction completes without runtime error, ENO = TRUE.
  • If EN = TRUE and 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
Important: According to the Siemens documentation, programming and I/O access errors cannot be reliably evaluated with the EN/ENO mechanism alone. Use the global error bits 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:

  1. When LAD/FBD is translated to STL, several box instructions internally emit a SAVE instruction 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.
  2. The SAVE instruction sets BR to the value of the current RLO, which is typically 1 on successful execution and 0 when the box reports a fault.
  3. When the compiler emits the last SAVE for the block, this final SAVE determines the value of ENO that the parent block observes.
  4. If the last instruction in the block (or in a network under specific optimization) does not emit a SAVE, or if a subsequent operation clears BR (for example, any STL bit-logic instruction that writes RLO before reaching the block end), then ENO can be FALSE even though every MOVE executed correctly.

In the reported case, Network 15 contained:

  • A timer coil (S_ODT) followed by a MOVE that loads 1 (or another non-zero value) into #ACK.
  • A second MOVE writing 0 into #ACK when 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:

  1. Open the FB in the STEP 7 editor (TIA Portal or STEP 7 V5.x).
  2. 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).
  3. Inspect every line that contains the literal instruction SAVE or the mnemonic SAVE compiled from a box.
  4. Identify the last SAVE in the compiled output. Trace the RLO that flows into that SAVE.
  5. Cross-check the STL view of every box preceding the final SAVE for any bit-logic instruction (A, O, X, AN, ON, XN, S, R, assignment =) that may overwrite RLO.
  6. Use the Monitor/Modify function (online → "Monitor") to step through Network 15 with ENO visible. 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
Note: In SCL you can also explicitly control 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

  1. Download the modified FB to the CPU (TIA Portal: "Download to device"; classic STEP 7: "PLC → Download").
  2. Open the FB online and observe ENO on the second MOVE during the timer's transition from running to expired.
  3. Confirm that #ACK is written to 1 on acknowledge, and to 0 two seconds later.
  4. From the WinCC side, verify that the alarm acknowledgment bit transitions correctly without sticking.
  5. Force a fault condition (e.g., assign an invalid operand to a downstream conversion box) and confirm that ENO still goes FALSE for genuine runtime errors - the explicit SAVE must 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 SAVE placement.
  • Avoid relying on ENO from inside a LAD/FBD FB to drive critical safety logic. Use the OK/ENO output only for diagnostic or operator HMI purposes.
  • Place the explicit SAVE at 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 equivalent L/T pair in STL with a deliberate SAVE at the end.
  • Use GET_ERR and GET_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 SAVE reads 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 SAVE because the last box is a no-op NOP.
  • 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.
Safety: Do not use 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.

Back to blog