S7-1200 PLCSIM Hangs on SCL Block Fix Infinite Loop Cycle Timeout

David Krause13 min read
S7-1200SiemensTroubleshooting
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 Overview

An S7-1215C CPU is programmed in TIA Portal V16, compiled, and downloaded into S7-PLCSIM V16. The user code includes a custom SCL block that emulates an accelerating pulse train for use as a test/feedback signal. As soon as the block is called from OB1 (main cyclic OB) or from any cyclic interrupt OB, the simulation becomes unresponsive: tag values stop updating, the watch table freezes, online monitoring shows stale values, and TIA Portal reports that the CPU is no longer reachable. The same project compiles without error, the rest of the program runs correctly when the offending block is removed, and the issue survives a full reinstall of PLCSIM.

This pattern is the classic signature of a cycle-time watchdog trip inside the simulation runtime. The CPU is not crashed; it has entered STOP (or remains in RUN with the SF LED on, depending on the configured reaction) because the user block burned through the configured maximum cycle time on its very first scan.

Field note: PLCSIM does not always clearly indicate that the virtual CPU has stopped. The TIA Portal online view simply stops refreshing. The actual CPU state and the diagnostic buffer remain the only reliable source of truth.

2. Affected Environment

The failure mode is not specific to one firmware build. It applies wherever SCL code is executed in a single-pass cyclic OB on a Siemens S7-1200 or S7-1500 with default watchdog settings:

  • CPU: S7-1215C DC/DC/DC (6ES7215-1AG40-0XB0) and AC/DC/RLY (6ES7215-1BG40-0XB0); also observed on S7-1212C, S7-1214C, S7-1211C, and S7-1500.
  • Engineering: TIA Portal V16 Update 7 or later, with S7-PLCSIM V16.
  • PLC firmware: 4.4.x and 4.5.x (S7-1200 G2) and 2.9.x (S7-1500). Default firmware watchdog settings are identical across these releases.
  • Language: SCL (Structured Control Language) compiled to MC7. The same pattern can occur in LAD/FBD if a recursive call or an unbounded loop is written.

Reference the SIMATIC S7-1200 Programmable Controller System Manual for CPU-specific cycle-time behavior and the TIA Portal V16 Release Notes for known compiler and PLCSIM limitations.

3. Root Cause: The Single-Threaded Scan

A PLC program is not a multithreaded application. It is a single execution thread driven by a fixed cycle: read inputs, execute OB1, write outputs, service communications, repeat. Inside that one execution pass, every block — including every SCL block — runs sequentially. Loops inside the block are not preempted by an HMI refresh, by online monitoring, or by a timer tick.

If a block contains a tight loop that polls a static input (for example, a WHILE #Start loop, a REPEAT ... UNTIL #Start loop, or a FOR loop that exits only when a global flag is true), and the input never changes state within that scan, the loop never terminates. The CPU continues iterating, the cycle time accumulates, and as soon as it exceeds the configured maximum the watchdog fires.

On the S7-1200 the default maximum cycle time is 150 ms. The default reaction is to stop the CPU and write a diagnostic-buffer entry with error code SF and the text "Cycle time exceeded" (German: "Zykluszeitüberschreitung"). The exact diagnostic event ID is 0x0001_0102 (OB1 cycle-time error) or 0x0E0F_8082 depending on firmware generation; both map to the same root cause.

4. Why the Compiler Did Not Catch the Loop

Unlike a language such as C# or Java, the SCL compiler performs only structural checks. It verifies types, variable declarations, and FB/FC interface consistency. It does not perform data-flow analysis, and it does not model the runtime value of a tag. The compiler cannot tell that #Start will remain FALSE for the entire scan because that determination requires evaluating the program, not just its syntax.

This is by design: PLC programs must be deterministic. The compiler is intentionally conservative about control-flow analysis because the same SCL source can be compiled for different targets (S7-300, S7-400, S7-1200, S7-1500, PLCSIM, PLCSIM Advanced), each with a different cycle model. The runtime watchdog is the safety net.

Some loop constructs that are guaranteed to terminate — for example, a FOR i := 1 TO 10 DO with constant bounds — are accepted by the compiler, but any loop that depends on a runtime condition is allowed to compile and will only fail at runtime. See the S7-PLCSIM V16 Function Manual for the simulation runtime boundary conditions.

5. Understanding the Cycle-Time Watchdog on S7-1200

The S7-1200 maintains two relevant timing values for OB1:

Parameter Default Min Max Where configured
Scan cycle monitoring time (max cycle time) 150 ms 1 ms 60000 ms CPU Properties > Cycle
Communication load (OB1 share for comms) 50 % 0 % 50 % CPU Properties > Cycle
OB1 minimum cycle time 0 ms 0 ms 60000 ms CPU Properties > Cycle
Cyclic interrupt OB period (OB30–OB38) 50 ms (OB30) 1 ms 60000 ms OB Properties > Cycle

If the OB1 execution time exceeds the configured scan cycle monitoring time, the CPU writes a diagnostic event and either (a) enters STOP or (b) stays in RUN with the SF LED on and a system fault in the diagnostic buffer, depending on the configured reaction. The same watchdog is applied to each cyclic interrupt OB. Because cyclic interrupts can interrupt OB1, a runaway block in OB30 can also trip the OB1 watchdog if OB1 is delayed too long by the interrupt service routine.

Useful online tags for diagnostics (visible in the system clock and runtime meters of the S7-1200):

  • OB1_LAST_CYCLE_TIME – execution time of the previous OB1 pass (ms)
  • OB1_MIN_CYCLE_TIME – minimum OB1 execution time since last start (ms)
  • OB1_MAX_CYCLE_TIME – maximum OB1 execution time since last start (ms)
  • OB1_CYCLE_TIME – current configured maximum (ms)

6. Decoding the PLCSIM Symptom

When the block trips the watchdog in PLCSIM, the visible symptoms are:

  1. The TIA Portal online view shows the CPU as "running" for a brief moment, then TIA Portal stops refreshing tag values.
  2. Watch tables appear frozen; force tables remain visible but writes do not commit.
  3. The PLCSIM status indicator (system tray icon) does not always change color, which is why many students believe PLCSIM itself has crashed.
  4. Online > Online & Diagnostics shows the CPU in STOP with diagnostic event "Cycle time exceeded" or, less commonly, in RUN with the SF LED on.

Always open Online & Diagnostics > Diagnostic buffer first. The first entry is the most recent. The watchdog event is typically followed within milliseconds by a STOP entry. This single check resolves 90 % of "PLCSIM hung" complaints on S7-1200 projects.

7. Solution 1: Add a Boolean Enable Input

The most direct fix is to add an enable input to the FB/FC and gate the entire block on it. The enable is a normal boolean tag driven by the HMI, by a start pushbutton, or by a one-shot edge detector.

LAD / FBD call site:

|     bRunSim    <FC100>     SIM_FB_RUN
|----|>|------( EN )--|  fOut |-->-->--
|                ENO         bBusy  |-->--

SCL implementation pattern (gated block body):

FUNCTION_BLOCK "SIM_PulseRamp"
>STAT
    s_rState : INT;          // ramp state machine
    s_fFreq  : REAL;         // current pulse frequency Hz
    s_fAccel : REAL;         // acceleration Hz per second
    s_tLast  : TIME;         // timestamp of last edge
END_STRUCT
END_VAR

BEGIN
    // Gate: do nothing if enable is FALSE
    IF NOT #bEnable THEN
        #s_rState := 0;
        #fFrequency := 0.0;
        #bPulse := FALSE;
        RETURN;
    END_IF;

    // ... ramp logic here, only runs when bEnable is TRUE ...
END_FUNCTION_BLOCK

With the enable in place, the block executes one scan, then returns. The cyclic OB is free to refresh, the watchdog is satisfied, and PLCSIM continues normally.

8. Solution 2: Edge-Triggered Start Bit

For pulse-train emulators that should run only on command, replace the level-sensitive start with a rising-edge detector. This guarantees that the "run" condition is true for exactly one scan, eliminating any chance of an infinite loop on a stuck input.

FUNCTION_BLOCK "SIM_PulseRamp"
>VAR
    iStartEdge : BOOL;       // edge flag
    rtStart    : R_TRIG;     // rising-edge detector instance
END_VAR
BEGIN
    rtStart(CLK := bStartCmd);
    iStartEdge := rtStart.Q;

    IF iStartEdge THEN
        // initialize ramp
        s_fFreq  := s_fFreqStart;
        s_tLast  := TIA_TIME_CURRENT();
    END_IF;

    IF s_fFreq > 0.0 THEN
        // compute next pulse timestamp using IEC timer, not a tight loop
    END_IF;
END_FUNCTION_BLOCK

Edge triggering is the preferred pattern for any block that uses a pulse train, counter, or state machine triggered by an HMI button.

9. Solution 3: Execute in a Cyclic Interrupt OB

Sometimes the ramp calculation is intentionally heavy and cannot be made fast enough to fit in OB1. In that case, move the block to a cyclic interrupt OB (OB30–OB38) so the OB1 watchdog is not affected:

  1. Project tree > Program blocks > Add new block > Organization block > Cyclic interrupt (OB30).
  2. Open OB30, set the period to the desired sample time (e.g., 10 ms for a 100 Hz pulse train).
  3. Set the phase offset if you have multiple cyclic OBs to avoid jitter from concurrent interrupts.
  4. Call the SCL FB from OB30 instead of OB1.
  5. Verify that OB30's own watchdog is configured higher than the worst-case block execution time.

OB30 itself has a separate maximum cycle time parameter. Default is the same 150 ms inherited from the CPU; configure it to the worst-case execution time of the ramp block plus a safety margin (typically +20 %).

10. Solution 4: Splitting the Ramp with a State Machine

The most robust pattern is to redesign the block so that it never contains a loop at all. Instead, store the ramp state in instance DB data and recompute the next pulse timestamp on every scan:

FUNCTION_BLOCK "SIM_PulseRamp_Stepped"
>VAR
    s_fFreq       : REAL;    // current frequency
    s_fFreqStart  : REAL;    // start frequency
    s_fFreqEnd    : REAL;    // end frequency
    s_rampTime    : TIME;    // total ramp duration
    s_tStart      : TIME;    // ramp start time
    s_tNextPulse  : TIME;    // next pulse emission time
    s_tStep       : TIME;    // inter-pulse interval at current freq
END_VAR
BEGIN
    IF NOT #bEnable THEN
        s_fFreq := 0.0;
        s_tNextPulse := T#0s;
        bPulse := FALSE;
        RETURN;
    END_IF;

    IF bStartEdge THEN
        s_fFreq := s_fFreqStart;
        s_tStart := TIA_TIME_CURRENT();
        s_tNextPulse := s_tStart + PulseInterval(s_fFreq);
    END_IF;

    IF TIA_TIME_CURRENT() >= s_tNextPulse THEN
        bPulse := TRUE;
        s_fFreq := ComputeNextFreq(TIA_TIME_CURRENT(), s_tStart, s_fFreqStart, s_fFreqEnd, s_rampTime);
        s_tNextPulse := TIA_TIME_CURRENT() + PulseInterval(s_fFreq);
    ELSE
        bPulse := FALSE;
    END_IF;
END_FUNCTION_BLOCK

This pattern is the gold standard for PLC pulse generation: one scan = constant work, no loops, deterministic timing, and immune to watchdog trips.

11. Verification Procedure

After applying any of the fixes, verify the cycle-time behavior in this order:

  1. Download the project to PLCSIM, click "Start" (green arrow).
  2. Go online, open a watch table, force the enable bit TRUE.
  3. Open Online & Diagnostics > Cycle time. Confirm that OB1_LAST_CYCLE_TIME is below the configured maximum.
  4. Trigger the block by toggling the start edge; confirm pulse output visible on a digital output tag.
  5. Check Diagnostic buffer for any "Cycle time exceeded" event.
  6. Let the simulation run for at least 60 s; confirm OB1_MAX_CYCLE_TIME remains bounded.
  7. Stop the simulation, re-enable the original (unguarded) version, and confirm the same code now trips the watchdog — this proves the fix, not the environment, is the cause.

12. Best Practices for SCL Pulse Generators on S7-1200

  • Always declare an explicit bEnable input. Default it FALSE in the FB instance DB so the block is inert until deliberately armed.
  • Use edge detection (R_TRIG, F_TRIG) for any "start" command that is expected to latch into a long-running process.
  • Compute the next event timestamp from the current IEC time, never from a counted loop. Loops in PLCs are for batch processing, not for emulating time.
  • Set the OB maximum cycle time to a value that reflects the actual worst case. Do not set it to 60 000 ms to silence the watchdog — that hides real faults.
  • For pulse rates above 100 Hz, prefer a hardware PTO (Pulse Train Output) on the S7-1200 signal board rather than a software emulation. The S7-1200 PTO supports up to 100 kHz on DC outputs and is unaffected by OB1 cycle time.
  • Reserve cyclic interrupt OBs for time-critical, deterministic work. Do not run heavy ramp math in OB1 if the same math can be offloaded to OB30 at 10 ms.
  • Add a sanity check inside the block: if a computed frequency, interval, or counter exceeds a defined limit, force the output to a safe state and set a status bit. This is the PLC equivalent of a watchpoint.

13. Why PLCSIM Behaves Differently from a Real CPU

PLCSIM runs the same MC7 / STL bytecode as the physical CPU, but the watchdog is implemented in the simulation host (the Windows process), not in firmware. As a result:

  • A watchdog trip in PLCSIM is not always reflected by an SF LED on the simulated CPU; sometimes the simulation process simply stops responding to TIA Portal polling.
  • Communication load is not simulated exactly; the OB1 time you see online is real, but the communications portion of the cycle is partially absorbed by the host.
  • Forces and watch table writes from TIA Portal to PLCSIM are synchronous; on a real CPU they are queued through the online interface and may be deferred if the CPU is in STOP.

The diagnostic buffer remains the authoritative source in both environments. Always check it before assuming PLCSIM is broken.

14. Diagnostic-Buffer Quick Reference

Event ID Text Meaning Action
0x0001_0102 Cycle time exceeded OB1 ran longer than the configured maximum Add enable input; move work to cyclic OB
0x0E0F_8082 OB cycle time exceeded Cyclic interrupt OB exceeded its maximum Raise OB max cycle time or shorten block
0x0001_0121 STOP due to STOP command Online STOP or mode switch None — expected during commissioning
0x0001_0107 Communication error Online interface lost Check TIA Portal connection

For the full event ID list, see the SIMATIC S7-1200 System Manual, section on diagnostic events.

Why didn't the TIA Portal compiler catch my infinite loop in SCL?

The SCL compiler in TIA Portal V16 performs only static checks: type compatibility, variable declarations, and interface consistency. It does not evaluate runtime values, so a WHILE #Start loop with a FALSE input compiles without error. The cycle-time watchdog in the CPU or in PLCSIM is the actual enforcement mechanism; it trips when the loop exceeds the configured OB maximum (default 150 ms on S7-1200).

How do I confirm the simulation has tripped the watchdog and not actually crashed?

Open Online & Diagnostics > Diagnostic buffer on the S7-1215C. The first entry will be a "Cycle time exceeded" event (ID 0x0001_0102 for OB1, 0x0E0F_8082 for cyclic interrupt OBs). The CPU state shown in the same dialog will be STOP or RUN-with-SF depending on the configured reaction. This is faster and more reliable than watching the SF LED on a real CPU or the PLCSIM status icon.

What is the default maximum cycle time on an S7-1215C, and can I change it?

Default is 150 ms. Open CPU Properties > Cycle in the TIA Portal project, then set "Scan cycle monitoring time" between 1 ms and 60000 ms. Increase it only if you have a legitimate, bounded reason; setting it to 60000 ms to hide the problem is a code smell, not a fix.

Can I still call my pulse-train block from a cyclic interrupt OB?

Yes. Cyclic interrupt OBs (OB30–OB38) are the recommended location for periodic, deterministic work such as a software pulse-train generator. Each cyclic OB has its own maximum cycle time parameter; configure it to a value above the worst-case execution time of the block. Note that cyclic OBs can interrupt OB1, so a runaway block in OB30 can also trip the OB1 watchdog if OB1 is starved of execution time.

Should I rewrite my SCL block in LAD to avoid the issue?

No. The issue is not the language; it is the runtime pattern. LAD/FBD can exhibit the same infinite-loop behavior, for example via a recursive FB call or an MC7 instruction that is itself a loop. The correct fix is structural: add an enable input, use edge detection for start, and prefer timestamp-based scheduling over counted loops. The SCL state-machine pattern in section 10 is the recommended rewrite.

Back to blog