Resolving Multi-Instance FB Output Override in TIA Portal

David Krause14 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

1. Problem Description

A common but confusing symptom appears in Siemens TIA Portal projects that use multi-instance function blocks (FBs) inside an S7-1200 or S7-1500 CPU: an FB whose enable input is hard-wired FALSE still appears to overwrite the value of an output variable at the call site. The FB body is effectively empty, yet the output it drives toggles to a value the engineer did not write.

The classic trigger is a call pattern similar to the following, typically generated inside a cyclic OB (OB1) or a higher-level FB:

  • Twenty multi-instance calls of the same FB, declared as FB_Situation[1..20] in the static area of the calling block.
  • Calls 1..10 logically belong to Situation A.
  • Calls 11..20 logically belong to Situation B.
  • A boolean bSituationA drives the enable input of all twenty calls.
  • Only the matching ten calls should be active at any one time.

While the situation A flag is TRUE, the situation B calls receive enable := FALSE and "should not execute." Despite this, the output parameter of the situation A call is observed to switch to a value the user did not assign. The engineer typically concludes that the disabled call is "still writing the output," which is misleading and is the starting point of this troubleshooting guide.

2. Root Cause Analysis

Two separate mechanisms collide in this pattern, and either one can produce the symptom. Diagnosing the actual cause requires checking both.

2.1 Output parameters persist in the multi-instance DB

When a function block is declared as a multi-instance, its instance data — including all VAR_OUTPUT interface entries — is stored inside the instance DB of the calling FB. According to the Siemens TIA Portal multi-instances documentation, the called FB does not get its own DB; the calling FB owns the static memory and the multi-instance offsets are sub-structures of that DB.

Consequence: the VAR_OUTPUT value of FB_Situation[12] is a memory cell that lives across scan cycles. If the FB body is not executed — because enable = FALSE short-circuits the logic — the cell still contains whatever was written by the last fully executed call. An FB never implicitly resets its own output parameters between calls.

2.2 The call that does not happen

Each time the cyclic OB runs, only ten of the twenty multi-instances are actually exercised. The other ten keep their stale value. The next time the situation flag flips, the ten "fresh" calls overwrite the stale ones — or fail to overwrite them, which is the bug.

2.3 The real bug in the field

In the most frequently reported form of this defect, the engineer has one fewer call than expected in the active situation. For example, the situation A branch contains 9 multi-instance calls while the situation B branch contains 10. The last situation A instance (FB_Situation[10]) is therefore never written when situation A is active. Its output keeps the value that situation B wrote on the previous cycle. The engineer observes this as "the situation B call is overwriting the situation A output," but in reality the situation A call is the one that is missing.

This is the single most common miscount when the call list is built by hand from a list of I/O points, and it is the cause to verify first.

3. How Multi-Instance FBs Store Output State

Before applying a fix it is worth understanding the storage layout. A multi-instance FB in TIA Portal V17–V20 with an S7-1500 CPU uses the following memory model:

Variable section Storage location Persists across calls? Reset on FB entry?
VAR_INPUT Caller stack / instance DB copy No (read snapshot) N/A
VAR_OUTPUT Instance DB (multi-instance offset) Yes No
VAR_IN_OUT Pointer into caller / instance DB Yes No
VAR_STAT Instance DB Yes No
VAR_TEMP Local stack (L stack) No (re-initialized each call on S7-1500) Yes (L stack clear on entry)

The critical row is VAR_OUTPUT. On S7-1500 CPUs (firmware V2.0 and later) and on S7-1200 CPUs (firmware V4.0 and later), the FB's output interface is mapped into the instance DB. Once the FB returns, the cell remains. On the next call, the FB body must explicitly assign a value to that output, or it will read back the previous value to the caller.

Engineering rule: If the FB body is bypassed by an early RETURN or an IF enable THEN ... END_IF guard, the output retains its prior value. The caller does not see 0 or any "neutral" value — it sees whatever the FB last wrote.

4. The Enable Input vs. Conditional Execution

The enable boolean at the FB input is a regular input. Wiring enable := FALSE does not prevent the FB from being called, nor does it clear the output cell. It only gives the programmer a flag to branch on inside the FB body. The two implementation patterns below illustrate this distinction.

4.1 Pattern A — enable guard inside the FB (the trap)

FUNCTION_BLOCK FB_Motor
VAR_INPUT
  enable : BOOL;
  cmd    : BOOL;
END_VAR
VAR_OUTPUT
  running : BOOL;
END_VAR
BEGIN
  IF enable THEN
    running := cmd;
  END_IF;
  // No ELSE branch: running is NOT cleared.
END_FUNCTION_BLOCK

When enable = FALSE, the body skips the assignment. running keeps its previous value. The caller, which reads running via the multi-instance offset, sees the stale state.

4.2 Pattern B — early RETURN inside the FB (also a trap)

BEGIN
  IF NOT enable THEN
    RETURN;          // Body skipped, outputs untouched.
  END_IF;
  running := cmd;
END_FUNCTION_BLOCK

This is functionally identical to Pattern A with respect to output persistence. The RETURN is correct in the sense that it avoids wasteful work, but it does not clear the output. The output cell is unchanged.

4.3 Pattern C — explicit output clear (safe)

BEGIN
  running := FALSE;          // Default first.
  IF NOT enable THEN
    RETURN;
  END_IF;
  running := cmd;
END_FUNCTION_BLOCK

Now an inactive call writes a known value, and the caller can rely on the default. This is the safest pattern for an FB whose outputs are read by another block that does not know the FB is disabled.

5. Diagnostic Procedure

Apply the following sequence on a live PLC or in the PLCSIM instance connected to the project. The goal is to determine whether the symptom is (a) a stale multi-instance output, (b) a missing call in the active branch, or (c) a third party modifying the same tag from elsewhere.

  1. Open the instance DB of the calling FB in Project tree > Program blocks > System blocks > Program resources > [Calling FB's instance DB] and switch to Online & diagnostics > Monitor / force.
  2. Expand the multi-instance structure (for example, the SitA, SitB arrays). Watch the running output byte for each instance across multiple scan cycles.
  3. Force bSituationA := FALSE and observe. The situation B instances should now produce fresh writes on every cycle. If their running output changes, the FB is being called with enable = TRUE and the body is executing — the logic is wrong, not the wiring.
  4. Force bSituationA := TRUE and count the writes. Use a cross-reference (right-click the tag > Cross-references) to count how many of the twenty multi-instance call sites have the bSituationA input connected. Compare the count to the number of physical I/O points the situation A branch is supposed to drive.
  5. Toggle the enable on the suspected stale instance by writing TRUE to its enable input from the watch table. If the output updates correctly, the FB is healthy and the call site is the issue.
  6. Check for indirect writes: search the project for any other block (HMI tags, another FB, an OB) that writes the same output tag. Use Go to usage on the output symbol.

6. Solution Patterns

Four robust solutions are available. The choice depends on whether the FB has a single fixed owner or whether the situation A / situation B selection is a runtime mode switch.

6.1 Initialize every output at FB entry

The simplest and most local fix. Put default-value assignments at the very top of the FB body, before any IF or CASE that may RETURN. This guarantees the caller reads a defined value on every cycle, even when the FB is disabled.

BEGIN
  running    := FALSE;
  fault      := FALSE;
  actualSpeed := 0;
  
  IF NOT enable THEN
    RETURN;
  END_IF;
  
  // Normal logic follows.
  running := cmd;
END_FUNCTION_BLOCK

6.2 Conditional call from the caller

Wrap each call site in a CASE or IF so that exactly one branch of instances is invoked per cycle. The non-active branch is not called at all, and its outputs are not touched. This is appropriate when the situation A / situation B flag is mutually exclusive and slow-changing.

CASE iActiveSituation OF
  1:  // Situation A
      FB_Situation[1](enable := TRUE,  cmd := CmdA1);
      FB_Situation[2](enable := TRUE,  cmd := CmdA2);
      // ... 10 calls total
      OutA1 := FB_Situation[1].running;
      OutA2 := FB_Situation[2].running;

  2:  // Situation B
      FB_Situation[11](enable := TRUE, cmd := CmdB1);
      // ... 10 calls total
      OutB1 := FB_Situation[11].running;

  ELSE
      // No situation active: do not call any instance.
END_CASE;

6.3 Single array-driven loop

When the number of instances is large and uniform, use a FOR loop driven by a runtime parameter. This eliminates the miscount risk that caused the original bug.

FOR i := 1 TO 20 DO
  FB_Situation[i].enable := (i >= iFirstActive) AND (i <= iLastActive);
  FB_Situation[i].cmd    := aCmdBank[i];
  FB_Situation[i]();
END_FOR;

6.4 Move the selection into the data, not the call

For large machines, refactor the situation A / situation B dichotomy into a UDT (user-defined type) array. Each row of the array carries its own enable flag, command, and feedback. The OB walks the array once per cycle and the call site is identical for every instance, which makes it impossible to forget one.

TYPE UDT_Channel :
  STRUCT
    bEnable   : BOOL;
    bCmd      : BOOL;
    bRunning  : BOOL;
    bFault    : BOOL;
  END_STRUCT
END_TYPE

7. Code Example — Complete SCL Implementation

The following SCL snippet combines patterns 6.1 and 6.2 in a single calling FB, FB_Controller, with twenty multi-instance calls. The point of the example is to show that the active branch always writes the output and that the inactive branch is not invoked.

FUNCTION_BLOCK FB_Controller
VAR
  FB_Situation : ARRAY[1..20] OF FB_Motor;   // 20 multi-instances
  aCmd         : ARRAY[1..20] OF BOOL;
END_VAR
VAR_TEMP
  i : INT;
END_VAR
BEGIN
  // Default: clear all outputs every cycle.
  FOR i := 1 TO 20 DO
    aRunning[i] := FALSE;
  END_FOR;

  IF bSituationA THEN
    FOR i := 1 TO 10 DO
      FB_Situation[i](enable := TRUE, cmd := aCmd[i]);
      aRunning[i] := FB_Situation[i].running;
    END_FOR;
  ELSE
    FOR i := 11 TO 20 DO
      FB_Situation[i](enable := TRUE, cmd := aCmd[i]);
      aRunning[i] := FB_Situation[i].running;
    END_FOR;
  END_IF;
END_FUNCTION_BLOCK

Two safeguards are present: the initial FOR loop clears the output array, and the active branch is selected with an exclusive IF/ELSE. The inactive multi-instances are never called, so their outputs cannot be touched. The CPU is an S7-1516-3 PN/DP with firmware V2.9; the project is built in TIA Portal V18.

8. Verification Steps

After applying any of the solutions above, perform the following checks before signing off the change.

  1. Cross-reference the enable input. In TIA Portal right-click the FB symbol and choose Cross-references. The count of references for the enable input on each multi-instance should match the number of call sites in the active branch.
  2. Watch the instance DB online. Trigger the situation A / situation B transition and confirm that the relevant running bits transition cleanly. The non-active branch bits should not change.
  3. Force a transition in the watch table. Force bSituationA := FALSE and then := TRUE. The ten active instances should react within one OB1 cycle. If a single instance lags, it is the one whose call site is wrong or missing.
  4. Check the loadable function block (LFB) consistency. Recompile the project. In TIA Portal V18 / V19 / V20, choose Project > Compile > Software (rebuild all blocks). A clean compile without warnings is the easiest proof that every multi-instance call is syntactically complete.
  5. Run a trace. Configure a Trace on the calling FB's instance DB and capture the running bits of all twenty multi-instances at 10 ms intervals for at least one mode transition. The trace should show clean, exclusive activation.
  6. CPU diagnostics buffer. Open Online & diagnostics > Diagnostics buffer and confirm there are no OB priority-class errors or "area length error" events related to the calling FB.

9. Edge Cases and Field-Proven Caveats

Edge case Symptom Likely cause Countermeasure
S7-1200 firmware older than V4.2 Multi-instance not accepted at compile time Multi-instances require S7-1200 CPU firmware V4.0 or later, with full support from V4.2 onward Upgrade CPU firmware, or use a separate instance DB per call
Optimized block access Output symbol not visible in the standard tag table Optimized blocks hide the interface structure by design Read the output via the multi-instance name in the calling FB, not via a global tag
OB1 with long cycle time Stale values appear only under load The inactive call ran in a prior scan; the active call was missed due to cycle overrun Reduce OB1 cycle time, or split the call into a lower-priority OB (e.g., OB35)
HMI forcing the output tag Output changes even though the FB is healthy HMI is writing the same tag via a direct connection or an OPC UA tag Remove the HMI write authorization, or rename the output to break the alias
Multi-instance in a library FB Behavior changes after a library update Master copy changed the FB interface Re-link the types and recompile; check the library version compatibility list
Indirect call via ARRAY OF FB Compile error "Instance cannot be used as multi-instance" Some older TIA Portal versions restrict dynamic multi-instance indexing Use a static multi-instance and a runtime index, or upgrade TIA Portal to V18 or later

10. Best Practices for Multi-Instance FB Programming

  • Always default-assign every output at the top of the FB body. Treat VAR_OUTPUT as if it were an uninitialized local variable: assume garbage, write a known value first.
  • Prefer a single, loop-driven call site over manually repeated calls. Repetition invites miscounting — the very class of bug described in this article.
  • Use the enable input as a runtime mask, not as a call guard. Pair it with an IF enable THEN ... END_IF body guard, or wire the call site through a CASE.
  • Avoid mixing VAR_OUTPUT writes with RETURN early-out. If early-out is required for performance, write the outputs first.
  • Keep multi-instance arrays and their command arrays at the same length and with the same indexing. Index misalignment is the silent killer of multi-instance code.
  • Use the Siemens Industry Online Support portal to check the latest S7-1200 / S7-1500 system manual for multi-instance restrictions on your specific CPU firmware.
  • Document the situation A / situation B selection mechanism in the block header, including which multi-instance indices belong to which situation. A short comment table prevents the next engineer from miscounting.

11. Quick Diagnostic Matrix

Observed behavior Most likely cause First check
Output toggles exactly one cycle late Active branch is missing one call Count call sites vs. count of I/O
Output retains value from a previous mode Inactive branch was called with enable = FALSE and did not write Add default assignment at FB top
Output toggles randomly every few cycles Multiple blocks write the same tag Cross-reference the tag across the project
Output is TRUE on first scan Instance DB start values are TRUE Set start values explicitly in the FB interface
Output never updates Multi-instance is never called (entire branch is dead code) Verify the CASE selector and the IF condition

12. FAQ

Does a FALSE enable input reset the FB outputs in TIA Portal?

No. A FALSE enable input only gates the FB body via the engineer's IF enable branch. Output parameters (VAR_OUTPUT) are stored in the multi-instance DB and keep their last assigned value until the FB body explicitly writes a new one. Always default-assign outputs at the top of the FB body.

Why does the situation A output appear to be overwritten by situation B even though situation B is disabled?

Two possibilities. Either the situation B call is in fact being executed and is writing the output (check the enable wiring), or — more often — the situation A branch is missing one call and the stale situation B value is simply not being overwritten. Count the call sites against the number of physical signals the branch must drive.

What is the difference between a multi-instance and a single-instance FB in S7-1500?

A single-instance FB is given its own dedicated instance DB. A multi-instance FB has its instance data stored as a sub-structure inside the calling FB's instance DB, which saves DB numbers and is the recommended pattern for FBs that are called many times from the same parent. The output persistence rule is identical for both.

Does an early RETURN inside the FB clear its outputs?

No. RETURN in SCL exits the body without executing subsequent code, but the output cells in the instance DB are not reset. To make a disabled call safe, assign default values to all outputs before the RETURN.

Which TIA Portal version and S7 CPU firmware fully support multi-instance FBs in arrays?

Multi-instances are supported on S7-300 / S7-400, on S7-1200 from firmware V4.0 (full support V4.2), and on all S7-1500 CPUs. TIA Portal V16 or later is recommended for indexed multi-instance access in optimized blocks; projects on V18 / V19 / V20 are the most reliable target for new development.

Back to blog