TIA Portal Output vs InOut Parameters: FC and FB Behavior

David Krause14 min read
SiemensTechnical ReferenceTIA Portal
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

TIA Portal Output vs InOut Parameters: FC and FB Block Behavior

This technical reference explains how the three interface sections of a function (FC) and function block (FB) — Input, Output, and InOut — behave at call time and at runtime, and why TIA Portal flags an Output tag that is read inside a block with a color change. The article covers reading outputs in FBs vs FCs, set/reset coil restrictions, the InOut bulk-clear pattern, instance-DB storage, ladder diagram layout implications, and a TON-based latch comparison that maps directly to ladder code.

1. Overview of the Three Block Interface Sections

Every FC or FB in the S7-1200 and S7-1500 family exposes a three-zone interface in the declaration window of TIA Portal: Input, Output, and InOut. Each zone produces a different runtime contract when the block is called from OB1, another FB/FC, or a multi-instance.

Interface Section Direction at Call Visibility Inside Block External Writes Reflected Inside Block? Typical Use
Input Caller → Block (read-only inside) Readable as a snapshot at start of call Yes, copied fresh each call Process values, setpoints, mode bits
Output Block → Caller (write-only intent) Read-allowed in FB; restricted in FC No — formal is decoupled from actual Results, calculated states, latched flags
InOut Bidirectional reference Read and write, same memory as actual Yes — actual and formal are the same address Bulk-clear alarms, status shared with HMI, accumulators

The TIA Portal S7-1200 Programmable Controller System Manual and the S7-1500 System Manual define the interface contract, and the STEP 7 (TIA Portal) Programming and Operating Manual details the call-time copy semantics.

2. Call-Time Parameter Mechanics

When a block is called in TIA Portal, the runtime performs a fixed sequence of data transfers across the interface. The exact sequence determines whether reading an Output inside the block is safe, undefined, or illegal.

2.1 Input Parameter Call Sequence

  1. Copy the actual parameter value from the caller's memory into the block's formal parameter storage.
  2. Execute the block body.
  3. Discard the formal parameter at block exit (no write-back to actual).

2.2 Output Parameter Call Sequence

  1. FC: Initialize the formal parameter to the last-written value only if the FC is non-optimized. For optimized FCs the formal starts as 0 / default. The formal is not a copy of the actual.
  2. FB: Initialize the formal parameter from the instance-DB slot reserved for that output.
  3. Execute the block body. Writes to the formal are stored locally (FC: temp/L-stack; FB: instance DB).
  4. At block exit, copy the formal value to the actual parameter in the caller's memory.

2.3 InOut Parameter Call Sequence

  1. Pass a pointer/reference to the actual parameter; no copy is made.
  2. Reads and writes inside the block operate directly on the caller's memory.
  3. No final copy step is required because the formal is the actual.

The crucial consequence: a write from the outside (OB1, another FC/FB) to an Output-bound tag is invisible inside the block for the rest of the current call, but a write to an InOut-bound tag is observed immediately because both sides share one address.

3. Reading an Output Inside an FC

Function blocks (FCs) in S7-1200/1500 do not own a static memory region. Output formal parameters live on the local stack (L-stack) and are uninitialized on entry. The rule is absolute:

Rule: In an FC, an Output parameter may not be read before it has been written in the same call. Reading it first yields the L-stack default (0, '', 0.0, T#0s) for an optimized FC, or the residual value of the L-stack slot for a non-optimized FC — both are unpredictable in practice.

3.1 The Forbidden Pattern

// FC body — INCORRECT
// Q_OUTPUT declared as Output, BOOL
// Read BEFORE write:
A   Q_OUTPUT          // contact uses the uninitialized value
=   Q_OUTPUT          // assignment writes it

If the ladder network executes the contact before the coil, the contact reads the L-stack value, not the value the caller passed. The compiler does not forbid this; TIA Portal simply highlights the network in a warning color (typically yellow or orange) — the source of the color-change observation reported by the original developer.

3.2 Set/Reset Coils on FC Outputs

The set (---(S)) and reset (---(R)) coils interpret the operand's existing value implicitly:

// FC body — INCORRECT for outputs
S   Q_OUTPUT          // sets the bit; reads the existing value first
R   Q_OUTPUT          // same problem

Because the existing value of an uninitialized FC output is undefined, the S coil may or may not produce a TRUE state depending on what was left on the L-stack slot from the previous scan. Replace with an explicit assignment:

// FC body — CORRECT
A   SomeCondition
=   Q_OUTPUT          // explicit assignment, no read-before-write

4. Reading an Output Inside an FB

A function block owns an instance data block (IDB), and every Output formal parameter is a slot in that IDB. The IDB is initialized at FB entry with the value from the previous scan (or with the start value if it is the first call after restart). This memory is the entire reason reading an output in an FB is legal:

Behavior FC (no instance memory) FB (with instance DB)
Read Output before write Undefined / L-stack residual Returns last cycle's written value from IDB
Use ---(S) / ---(R) on Output Illegal — reads undefined bit Legal — bit is read from IDB
Latching / self-hold of Output Impossible without external memory Idiomatic — works across cycles
External write to actual visible inside? No (decoupled) No (decoupled; only IDB initial copy on entry)

5. The TIA Portal Color-Change Warning Explained

When TIA Portal detects a network inside a block that uses an Output-typed tag in a read-only operand position (a contact, a comparator, a MOVE source, etc.) before that tag has been written, the editor changes the color of the operand from the default blue to a warning hue. The exact color depends on the installed TIA Portal version, but the meaning is identical across V15.1, V16, V17, V18, and V19:

  • Blue — normal operand, value source confirmed.
  • Yellow / orange / red — compiler cannot guarantee the value at runtime; the operand may be undefined.

The warning is informational, not blocking. The program still compiles and downloads. In an FB the warning is benign because the IDB supplies a defined prior value; in an FC the warning is genuine and should be treated as a bug. The recommended remediation is to either:

  1. Move the data to the InOut section if bidirectional access is required.
  2. Assign the calculated value to a local Temp variable first, then assign the temp to the output at the end of the block.
  3. Move the logic into an FB so the output owns persistent memory.

6. Latching an Output — The TON Pulse Pattern

The original developer's "latching of an output" use case is a classic 0.5 s pulse generator. The two reference implementations below expose the Output-vs-InOut contract precisely.

6.1 FB1 with Output Parameter Q_TEST

Inside FB1 a timer toggles Q_TEST every 0.5 s. The caller in OB1 also modifies the actual parameter M1.0 with a TON_1 reset after 0.5 s. Because Q_TEST is an Output, the FB's formal parameter is decoupled from the caller's actual parameter: the FB keeps toggling regardless of what OB1 does to M1.0. The caller observes only the final formal value copied back at FB exit.

// FB1 body (SCL-equivalent)
IF Q_TEST THEN
    Q_TEST := FALSE;
ELSE
    Q_TEST := TRUE;
END_IF;

6.2 FB2 with InOut Parameter IQ_TEST

Inside FB2 the same toggle logic operates on IQ_TEST declared as InOut. Because InOut is a reference, OB1's TON_2 reset of M2.0 is observed by FB2 on its next read of the formal. The result is that the pulse pattern is corrupted — the FB sees the externally-forced 0 and stops toggling on the next cycle.

6.3 OB1 Call Site

// OB1 ladder
M1.0          TON_1         M1.0
---| |---------|TON|--------(R)
T#0.5s ---|PT   |

M2.0          TON_2         M2.0
---| |---------|TON|--------(R)
T#0.5s ---|PT   |

CALL FB1, "iDB_FB1"
   Q_TEST := M1.0          // Output - decoupled

CALL FB2, "iDB_FB2"
   IQ_TEST := M2.0         // InOut - shared memory
Result matrix:
  • M1.0: FB toggles at its own rhythm; OB1's TON_1 reset is invisible to FB1's internal logic and is overwritten by the next FB1 copy-back.
  • M2.0: FB2's toggle logic reads the value FB2 itself wrote last cycle AND the value OB1 may have overwritten; pulses are skewed or halted.

7. Bulk Alarm Reset Pattern with InOut

The single most useful reason to choose InOut over Output is collective clearing. Suppose 32 alarm bits are produced by a single FC. Declaring them as Output forces the caller to issue 32 individual resets if the operator presses the master reset button on the HMI. Declaring them as InOut allows the master reset to clear all 32 with one MOVE / fill operation that is observed by every subsequent block execution.

// OB1 master reset (SCL)
IF "HMI.Master_Reset" THEN
    "DB_Alarms".AlarmWord_0 := 0;
    "DB_Alarms".AlarmWord_1 := 0;
    "DB_Alarms".AlarmWord_2 := 0;
    "DB_Alarms".AlarmWord_3 := 0;
END_IF;

// FC_AlarmHandler with 32 InOut parameters (or one InOut WORD/DWORD)
// Setting an alarm:
IF "IO_Alarm_0" THEN
    "DB_Alarms".AlarmWord_0.%X0 := TRUE;
END_IF;

Because InOut is a reference, the FC's writes land directly in DB_Alarms, and the master-reset MOVE clears them in one CPU cycle. With Output, the FC's written values would be copied back at block exit and would clobber the cleared DB the very next cycle.

8. Memory Layout and Instance DB Implications

Every InOut parameter and every Input parameter of an FB occupies space in the instance DB even though the data lives in the caller's memory at runtime. The compiler reserves a pointer-sized slot (8 bytes on S7-1500, 6 bytes on S7-1200) per InOut, and a value-sized slot per Input. Output parameters occupy a value-sized slot only.

Section Slot in IDB Pointer to Caller Memory? IDB Size Impact
Input Value copy No Size of declared type
Output Value copy No Size of declared type
InOut Pointer Yes 6 or 8 bytes per parameter
Static Value copy No Size of declared type
Temp None (L-stack) No 0

This is the source of the original developer's observation that the IDB became "very large" after switching many parameters to InOut — the pointer slots stack up quickly when an FB has 30+ tags. For a routine that produces many status flags without needing bulk reset, the Output zone is more compact.

9. Ladder Diagram Layout: Why InOut Crowds the Block

In TIA Portal ladder (LAD) and function block diagram (FBD) editors, the block's graphical representation reserves the left edge for inputs and InOuts (the operand column) and the right edge for outputs (the coil column). When an FB has 40 InOut parameters, the block graphic expands leftward and the right side remains visually empty, producing an unbalanced box that is hard to navigate in a large FC that calls dozens of FBs in a row.

The trade-off matrix is:

Goal Recommended Section Ladder Layout Cost
Calculated status, written by FB only Output Right edge (clean)
Tag modified by both FB and HMI / other logic InOut Left edge (crowded)
Read-only setpoint or mode Input Left edge (crowded)
Internal scratch, not exposed to caller Static (FB) or Temp (FC) Hidden

10. Complete Working Examples

10.1 FC — Output Assigned Once, Never Read Before Write

// FC_LimitCalc, optimized block access
// Outputs: Q_LimitHi (REAL), Q_LimitLo (REAL), Q_Valid (BOOL)

#Q_LimitHi := "SP_High" * 1.10;
#Q_LimitLo := "SP_Low"  * 0.90;

IF "RawValue" > #Q_LimitHi OR "RawValue" < #Q_LimitLo THEN
    #Q_Valid := FALSE;   // explicit assignment; no read of #Q_Valid before write
END_IF;

10.2 FB — Output Latched With Set Coil

// FB_MotorCtrl, instance DB holds Q_Run and Q_Fault
// Output: Q_Run (BOOL), Q_Fault (BOOL)

IF "Start_PB" AND NOT "Stop_PB" AND NOT #Q_Fault THEN
    S   #Q_Run;        // legal: IDB supplies prior value
END_IF;

IF "Stop_PB" OR "Q_Fault_Condition" THEN
    R   #Q_Run;
    S   #Q_Fault;
END_IF;

10.3 FB with InOut for Bulk-Cleared Alarms

// FB_AlarmHandler
// InOut: IO_AlarmGroup (DWORD) - shared with HMI and master reset logic

IF "Sensor_OverTemp" THEN
    #IO_AlarmGroup.%X0 := TRUE;   // writes directly to caller's DWORD
END_IF;

IF "Sensor_OverPressure" THEN
    #IO_AlarmGroup.%X1 := TRUE;
END_IF;

The call site can issue #IO_AlarmGroup := 0; from any HMI-triggered logic, and the FB's next read observes the cleared bits.

11. Verification and Commissioning Checks

After choosing the section layout, verify the contract with these steps in TIA Portal (V16 / V17 / V18 / V19):

  1. Compile check — Open the block and look for yellow/orange operands. Resolve every instance by either (a) writing the output first, (b) switching to InOut, or (c) moving logic to an FB with IDB support.
  2. Watch table test — Force the actual parameter, call the FC/FB once, and observe whether the forced value is overwritten. If it is overwritten, the parameter is an Output; if it persists across the call, the parameter is an InOut.
  3. Cross-reference — Right-click the tag in the declaration table, choose "Cross-references", and confirm that the tag is written from the expected block. An Output should be written by exactly one block; an InOut may be written by several.
  4. Instance DB inspection — Open the FB's IDB in online mode, change the start value of an output, and confirm the change is retained across a CPU restart (warm restart) but reset on a cold restart. This validates that the output is IDB-backed and not L-stack-backed.
  5. Trace recording — Use the S7-1500 Trace function to record both the formal and the actual parameter across two consecutive cycles and verify the copy-back behavior described in section 2.

12. Decision Flowchart

Use this flow when declaring each new tag in an FC or FB:

New tag to declare? Is data written by the caller too? Does the caller need bulk clear / share? YES → InOut NO → Output YES NO

13. Common Pitfalls and Field-Proven Caveats

  • Optimized block access hides the warning. With optimized access enabled (default for S7-1500), the L-stack slot is zeroed deterministically. The "color" warning may not appear, but the logic is still illegal — read-before-write is meaningless in both cases.
  • Multi-instance FBs share IDB space. When an FB is called as a multi-instance inside a parent FB, the outputs of the child land in the parent's IDB. Switching a child output to InOut adds 8 bytes of pointer storage to the parent IDB per call site.
  • ARRAY InOut and Variant InOut behave identically to scalar InOut — pointer semantics apply — but require dereferencing inside the block, which costs a small amount of code.
  • HMI tags bound to FC outputs observe the value at the END of the call cycle, not continuously. If the HMI needs the current value mid-cycle, route the data through a DB and mark the DB tag as the InOut actual.
  • Retain / non-retain in the Output section of an FB: an output marked non-retain is initialized to its start value on every cold restart. Latched outputs that must survive power-cycle MUST be retained, or stored in a separate retain DB and read back at startup.
  • S7-1200 vs S7-1500 pointer width: S7-1500 uses 64-bit pointers (AnyPointer) for InOut; S7-1200 uses a 48-bit DB-pointer format. Both yield identical semantics but the IDB size differs by 2 bytes per InOut.

14. Standards and Documentation Cross-Reference

The interface contract described in this article is governed by the IEC 61131-3 standard, which defines the semantics of VAR_INPUT, VAR_OUTPUT, and VAR_IN_OUT in section 6.4. Siemens implements the standard with the extensions described in the S7-1200/1500 system manuals linked above. For site-specific acceptance, cross-check against the customer's PLC coding standard (typically an internal document referencing IEC 61131-3 §6.4.2 for output-by-reference rules).

15. Summary Recommendations

  1. Default to Output for any value that the block alone produces. Keep IDB size small and ladder diagram right-edge clean.
  2. Default to InOut only for tags that the caller (HMI, master reset, supervisory FB) must modify between calls.
  3. Never read an Output in an FC before writing it. Use a Temp variable or move the logic into an FB.
  4. Read-before-write of an Output in an FB is legal and idiomatic for latching and self-hold patterns.
  5. Treat the TIA Portal color change as a design hint, not a hard error. Resolve it on principle.

Why does TIA Portal change the color of an Output operand in a ladder network?

The compiler flags an Output that is read before it is written in the same block. In an FC the value is undefined (L-stack residual), in an FB the value is the prior cycle's IDB copy. Yellow/orange/red is informational, not a compile error.

Can I use a Set/Reset coil on an Output of an FC?

No. The ---(S) and ---(R) coils implicitly read the operand's existing value, which is uninitialized in an FC. Use an explicit assignment coil (---( )) instead, or move the logic into an FB where the IDB supplies a defined prior value.

When should I choose InOut instead of Output?

Choose InOut when the caller must read or write the same tag between block calls — for example, an alarm bit that the HMI clears with a master-reset, or a counter that an external FB increments. The InOut is a pointer to caller's memory, so external writes are immediately visible inside the block.

Why did my instance DB grow large after switching many tags to InOut?

Each InOut reserves a 6-byte (S7-1200) or 8-byte (S7-1500) pointer slot in the instance DB, regardless of the data type. Thirty InOut parameters add 180-240 bytes of overhead. Switch tags back to Output if the caller does not need bidirectional access to reduce IDB size.

Does an Output parameter survive a CPU restart?

Only if it is declared inside an FB and the IDB is configured as retentive. Outputs of FCs are not retained because FCs have no instance memory. For restart-survivable latches, either use an FB with retentive IDB or store the state in a global retentive DB and bind it as InOut.

Back to blog