Problem: Local BOOL Latch Fails Inside a Single LAD Network
A recurring fault on SIMATIC S7-1200 CPU 1214C (firmware V4.5) programmed with TIA Portal V15 is a BOOL local variable (declared in the block Temp interface, e.g. #Preconditions_2) that appears OFF in the network where it is written but ON in a later network of the same block. The same symptom is reported with global merker (M) bytes and with process-image outputs (Q) when the same tag is read in two different functions (FCs) or function blocks (FBs).
Typical report:
- Network 1:
--[contact]--(#Preconditions_2)--with a parallel seal-in branch on the same tag. The seal-in contact visually appears as if it should be TRUE because the coil was set, but it reads FALSE during the same cycle. - Network 2 (or another FC/FB):
--[ #Preconditions_2 ]--reads the tag as TRUE. - Result: latching never engages, downstream logic sees a one-cycle pulse, or output
Q1.3oscillates between ON and OFF across block calls.
The PLC is not defective, the wiring is correct, and no diagnostic buffer entry is generated. The cause is a combination of LAD execution order and temporary variable lifetime.
Root Cause: LAD Network Scan Order and Temp Variable Lifetime
Two distinct mechanisms are responsible. Both must be understood before the symptom is debugged.
1. Ladder logic is sequential, not parallel
A LAD network is not evaluated as a combinational logic diagram. The S7-1200 instruction list (refer to the S7-1200 Programmable Controller System Manual) executes the rung strictly from left contact to right contact, top branch to bottom branch, in source order. The compiled STL/MC7 code is sequential. If a coil sits to the right of a contact that reads the same tag, the contact sees the tag's value before the coil writes it.
Concretely, for the rung:
| A B #Preconditions_2
|--| |-------+--()( )--|
| |
| #Preconditions_2
|--| |
+-----------|
The MC7 sequence is A, B, #Preconditions_2_old, #Preconditions_2 := (A OR #Preconditions_2_old) AND B. The seal-in contact in the parallel branch reads the previous value of the tag, not the value that the coil is about to write. On the very first scan the tag is the default initialization value (FALSE for BOOL Temp), so the seal-in can never engage in cycle 0 and only engages in cycle 1 if the rung result is TRUE on cycle 0. If the rung is FALSE on cycle 0, the seal-in never engages and the local variable looks dead.
2. Local (Temp) variables are re-initialized on every block call
Per the S7-1200 Programming and Operating Manual and the STEP 7 (TIA Portal) help portal, temporary variables declared in the Temp section of an FB or FC interface:
- Are stored in the local data stack (L stack) of the calling OB.
- Are overwritten with the initial value of the data type at every block call (FALSE for BOOL, 0 for INT, '' for STRING, etc.).
- Are undefined after the block exits (the L stack frame is released).
This is the second mechanism: even if the coil inside the network did manage to set #Preconditions_2 = TRUE on cycle N, when the OB restarts the block call on cycle N+1 the variable is reset to FALSE at the very first line of the block body, before any user code runs. A naive IF #Preconditions_2 THEN #Preconditions_2 := TRUE; END_IF; self-reference therefore reduces to the identity function f(x)=x and cannot latch state on its own.
Why Local BOOL Variables Behave Differently from M and Q
| Memory area | Scope | Lifetime | Retained across cycles | Process image |
|---|---|---|---|---|
L / Temp (local) |
Block-local | One block call (L stack) | No, reset to default at entry | No |
M (merker / flag) |
Global, all blocks | CPU run-time | Yes (unless non-retain) | No (bit memory) |
Q (output PI) |
Global | Updated at end of OB1 | Yes (process image) | Yes, written back to terminals |
I (input PI) |
Global | Updated at start of OB1 | Yes (process image) | Yes, read from terminals |
Stat (FB instance) |
FB instance DB | Until instance DB is reset / reinitialized | Yes (retain configurable) | No |
DB (global DB) |
Global | Until DB is reloaded or reset | Yes (retain configurable) | No |
Local Temp variables cannot latch state across OB cycles by definition. A user observation of "the same M bit reads ON in one FC and OFF in another" almost always traces to one of the following:
- The bit is written by HMI / OPC UA / another OB at a different priority class.
- The bit is reset by a
RESETcoil, by_IO_EMG_STOPhandling, or by a startup OB (OB100) before the FC is called. - Symbolic access resolves to a different absolute address in the second FC because the tag's absolute checkbox is checked in a way that the symbolic name does not follow the rename.
- Two FCs are called in different priority classes; the lower-priority one samples a stale image.
For outputs Q, a value visible in the online watch table is the value latched in the process image output (PIQ), which is written to the terminals only at the end of OB1. A read inside the same cycle sees the PIQ; a read after the cycle sees what the terminal actually returned, which can differ for fast outputs (PWM, PTO) on the S7-1200 SB modules.
Diagnostic Workflow: Identifying the Latch Pattern
- Open the block in LAD/FBD in TIA Portal V15. Switch the editor to FBD (FUP) by right-clicking the network and selecting Change to FBD for a graphical view of the scan order.
- Locate every reference to the tag: right-click the variable in the interface and select Cross-references (Ctrl+Alt+F7). Filter for the current block. Note every read, write, Set, Reset, and the network number of each.
- Check the order of operations within the suspect network. The compiled STL is shown by selecting Start > LAD/FBD > STL view if the project permits it. Confirm that the read contact occurs before the write coil in source order.
- Open the Interface tab and verify the tag's Section:
-
Temp→ re-initialized to data-type default at every block call. Cannot retain. -
Stat(FB only) → stored in the instance DB, persists across calls. Retain attribute configurable. -
In/Out/InOut→ parameter; persistence depends on the caller's argument.
- Go online and force a monitor on the tag in the block. Use Monitor & force > Monitor all with a 500 ms trigger so that consecutive OB cycles are visible. Watch the value at network 1 (write) and at network 2 (read) of the next cycle.
- Check priority class and OB call order in Program blocks > System blocks. The S7-1200 supports OB1 (main), OB10x (startup), OB2x (time-of-day), OB8x (hardware interrupt), OB121/OB122 (error). A hardware interrupt OB can pre-empt OB1 and overwrite an M or Stat tag mid-cycle.
-
Verify the data type. A
BOOLtag occasionally shows asFALSEbecause the editor bound it to a 32-bit word and the high bits are masking the bit. Re-declare asBOOLat the symbol level.
Solution Patterns for Reliable Latching
Three patterns are used to make a BOOL latch behave deterministically on the S7-1200.
Pattern A: Set / Reset instead of standard coil
Replace the assignment coil with a Set coil (S) and provide a separate Reset coil (R) elsewhere. The S coil forces the bit to TRUE on the next MC7 instruction, and once set, the bit stays TRUE until an explicit R is executed.
Network 1 (set condition):
|--[ condition ]--(S)--#Preconditions_2 --|
Network 2 (reset condition):
|--[ reset_cond ]--(R)--#Preconditions_2 --|
This works for both M and Stat (FB) tags. It is the only pattern that is safe to use with Temp variables, because the Temp's re-initialization on block entry means the latch must engage within a single scan of the block, and Set/Reset semantics do not depend on a re-read of the same bit within the network.
Pattern B: Use FB Static or Global DB for cross-cycle memory
If the latch must survive across OB cycles, move the tag from Temp to either the FB Stat section (stored in the instance DB) or a global DB. Both areas retain their value at the end of the OB cycle.
- FB Static: open the FB Interface tab, change the section of
#Preconditions_2from Temp to Stat, and add a default value ofFALSE. Compile and download. The instance DB is generated automatically. - Global DB: create a new Data block in Program blocks, declare a
Booltag Preconditions_2, and reference it as"MyDB".Preconditions_2.
Both approaches also allow the Retain attribute to be set so that the bit survives a CPU stop-start and warm restart.
Pattern C: Rearrange the contact order in the network
If the design requires the standard seal-in pattern, the contact that reads the tag must be placed before the coil that writes it in the source code. LAD processes contacts left to right, so the seal-in branch must branch out to the left of the coil, not the right:
#Preconditions_2
| A +--+ |
|--| |-------| |----(#Preconditions_2)--|
| +--|
| B
| | |
+--------------| |
This is the canonical PLC seal-in layout. The MC7 compiled order becomes A, #Preconditions_2, B, write and the latch can engage on cycle 1 because the previous value of #Preconditions_2 is read before it is written.
Step-by-Step Fix in TIA Portal V15 on CPU 1214C V4.5
- Open the project in TIA Portal V15 and expand Program blocks.
- Open the affected block (FB or FC). Switch to LAD if it is currently in FBD or STL.
- Open the Interface tab. For each BOOL tag that must latch, change the Section from Temp to Stat if the block is an FB. For an FC, convert the FC to an FB (right-click > Change block type) or move the tag to a global DB.
- Insert Set / Reset: select the assignment coil in the network, open the instruction catalog, and drag Set from Bit logic operations. Repeat for Reset on a separate network.
- Compile the project (F7) and resolve any type-conflict warnings. The instance DB is regenerated automatically.
- Download to the CPU 1214C. Use Online & diagnostics > Download to device. Select Stop and restart if the instance DB structure changed.
- Go online, open the block, and toggle the Monitor all icon. Cycle the input condition and verify that the Set coil turns the bit TRUE and it remains TRUE until the Reset coil is energized.
Verification and Online Monitoring Procedure
-
Watch table: create a watch table in Watch and force tables. Add the tag, the Set input, the Reset input, and the affected output
Q1.3. Set the trigger to Periodic, 500 ms and click Monitor all. - Single-step the cycle: from the watch table, right-click the input that should set the latch and select Modify to 1. Verify that the tag reads TRUE on the next scan.
- Cross-reference audit: re-run Cross-references (Ctrl+Alt+F7) on the tag. The expected count is one Set, one Reset, zero standard assignment coils, and N reads. Any unaccounted write is a candidate for further bug fixing.
- OB scan-time check: in Online & diagnostics > Diagnostics > Cycle time, confirm that OB1 scan time is below 80 % of the configured OB1 minimum cycle time (default 100 ms on the 1214C). A scan that runs longer than the minimum cycle time triggers the cycle time watchdog and can leave a Tag in an inconsistent state.
- Force test: only if commissioning requires it, force the tag with Force to 1 from the watch table. Remove the force before leaving the site, because a forced bit persists across a download and a CPU restart.
Memory Layout and Local Data Stack Limits
The S7-1200 1214C firmware V4.x supports a per-priority-class local data stack sized in the CPU properties (System & clock memory > Local data). The default is 32 KB per priority class. Exceeding the configured size raises OB121 (programming error) with error code 80A4 (local data length error).
| Data type | Bytes consumed per declaration (Temp) | Notes |
|---|---|---|
BOOL |
1 (padded) | MC7 packs BOOL into bytes; declared as 1 byte each in the L stack. |
INT |
2 | Aligned on word boundary by default. |
DINT / REAL
|
4 | Aligned on dword boundary. |
STRING[n] |
n + 2 | Length header + n characters of payload. |
ARRAY[0..n] |
n * element size | Add alignment overhead for nested structures. |
For a deeply nested call stack, the L stack frames accumulate. If the block is called recursively or from many priority classes, the L stack can be exhausted even though the global work memory is still free. Monitor Online & diagnostics > Memory to see current L stack usage.
Cross-Platform Notes: S7-300/S7-400 and TIA Portal Differences
The same rule (LAD is sequential) applies on S7-300 and S7-400, but the implications differ:
-
S7-300 / S7-400 with Step 7 V5.x: temp variables are also re-initialized at block call. The Set/Reset instruction is the same
S/Roperator. The instance DB for an FB-SFB pair follows the same Stat section rule. - S7-1500: Temp initialization behavior is identical, but the optimized block access feature moves tags into S7-1500 system memory areas and the cross-reference engine is stricter. Always declare FB multi-instances for nested calls to avoid L stack pressure.
-
S7-200 / S7-200 SMART: the local V memory area is a persistent global data area, not a stack. A BOOL stored in
VBis global and retains across cycles. The seal-in pattern works as expected without Stat / DB conversion.
The same TIA Portal V15 project can target any of the S7-1200, S7-1500, and S7-300/400 families; the LAD editor is the same code generator with the same scan order semantics.
Common Pitfalls and Best Practices
- Do not use Temp BOOL for cross-cycle state. Always move such tags to FB Stat or to a global DB. The default value is what the tag will read on the next call.
-
Prefer Set/Reset over assignment coils for any latch that must be cleared by a fault or a permissive loss. The
SandRcoils are priority-aware: a Reset written to the same tag after a Set in the same cycle still wins, which is the desired behavior for safety circuits. - Use FBD as a sanity check. FBD in TIA Portal lays out the network as a data-flow graph. A seal-in contact that the LAD view shows as "obviously TRUE" often appears visually wrong in FBD, exposing the scan-order bug immediately.
- Use symbolic addressing only. Disable the Access to absolute address optimization for BOOL latches so that a tag rename in the project tree propagates to every reference.
-
Reserve a process image partition for fast I/O if a hardware interrupt OB updates an M bit faster than OB1 reads it. On the 1214C, the default process image is updated in OB1; a fast OB can use the PIQ directly with
Psuffix (e.g.Q1.3:P). - Document the intended scan order. Add a comment above the network stating the order of contact evaluation. Six months later, the maintenance engineer will thank you.
6ES7214-1AG40-0XB0, firmware V4.5.6, programmed with TIA Portal V15.1 Update 4. Behavior is identical on AC/DC/RLY variants and on the 1212C / 1215C / 1217C firmware V4.x lines.FAQ
Why does my BOOL Temp variable read FALSE in the same network where a coil sets it?
LAD executes strictly left to right, top to bottom. The contact that reads the tag is compiled into MC7 before the coil that writes the tag, so the contact sees the previous value (FALSE on the first call, because Temp variables are initialized to the data-type default at block entry). Move the read contact to the left of the write coil, or use Set/Reset, or move the tag to FB Stat.
Can a Temp variable retain its value across OB cycles on the S7-1200?
No. Temp variables are stored in the local data stack and are re-initialized with the data-type default (FALSE for BOOL, 0 for INT, '' for STRING) at every block call. Use the FB Stat section or a global DB for any value that must persist across OB cycles.
My M bit reads ON in one FC and OFF in another. What is wrong?
Cross-reference the M bit (Ctrl+Alt+F7) to find every read and write. The most common causes are (1) a Reset coil in a higher-priority OB such as a hardware interrupt, (2) the bit is also written from the HMI / OPC UA tag set, or (3) the FC was called from a different priority class. Add the bit to a watch table with a 200 ms trigger to catch the asynchronous write.
Is the Set/Reset coil priority fixed or last-wins?
On the S7-1200, multiple Set/Reset operations on the same bit in a single cycle follow source order (compiled MC7 order). A Reset that follows a Set in the source will clear the bit, even if the Set was true. For safety-critical latches, write the Reset in a higher network (compiled earlier) so the bit is cleared if the safety condition appears at any time during the scan.
Do I need to convert my FC to an FB to add a Stat tag?
Yes. FCs have no Static section; they only have Temp, Input, Output, InOut, Constant, and Return. To retain a value across calls, either convert the FC to an FB (right-click > Change block type in TIA Portal), declare an instance DB, or add the tag to a global DB and access it symbolically.