SIMOTION D435 TON Timer Array: Resolving Multi-Instance Mismatch

David Krause11 min read
HMI ProgrammingSiemensTroubleshooting
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 Statement: Shared MYTON[1] Array Element Across Six Function Blocks

A SIMOTION D435 motion controller programmed in SIMOTION SCOUT exhibits inconsistent timer behavior when a globally-declared TON (timer on-delay) array element — MYTON[1] — is referenced from six different function blocks. Each FB passes a different enable condition into the same timer instance, producing non-deterministic state transitions, missed output pulses, and what operators describe as "crazy behavior" during machine cycling.

This pattern is a classic IEC 61131-3 instance-scope violation. The single MYTON[1] instance has only one internal state machine, but six FBs attempt to drive its IN and read its Q/ET simultaneously. The final write wins; the first caller reads stale values. Motion paths, interlocks, and safety-relevant timeouts driven from this shared instance become unreliable.

Safety implication: If any of the six FBs participates in a safety chain (e.g., a stop category 1 timer, an axis enable watchdog, or a gripper release delay), the shared-instance problem is a latent functional-safety defect. Re-validate the affected path under SIMOTION Safety Integrated commissioning rules after the fix is applied.

SIMOTION D435 Hardware and Programming Environment Context

The SIMOTION D435 is a V4.5-class (and D4x5-2 V5.x) SIMOTION module that integrates a SIMOTION kernel CPU with a SINAMICS S120 drive line-up on a single rack. Key facts that affect timer handling:

Property Value
Module family SIMOTION D4x5 / D4x5-2
Programming environment SIMOTION SCOUT (TIA Portal integration from V5.2 / SCOUT TIA)
IEC 61131-3 languages ST, LAD/FBD, MCC (motion control chart), CFC optional
Timer types TON, TOF, TP — IEC 61131-3 instances, not S5-style word timers
Execution context Cyclic IPO/task system (IPO, IPO_2, Servo, BackgroundTask)
Scan budget per FB Determined by the assigned task; not a free-running 100 ms

Because timers are IEC instances rather than memory-mapped S5 timers, their visibility and lifetime are governed by the variable declaration (VAR, VAR_INPUT, VAR_IN_OUT, VAR_GLOBAL, VAR_TEMP). Sharing one instance across multiple program organization units is permitted by the language grammar but is almost never what the application author intended.

Root Cause: Single TON Instance with Multiple Writers and Readers

The root cause of the fault is the scope of the MYTON declaration. If MYTON is declared in a global data block (DB) or as a VAR_GLOBAL in the SCOUT program container, every FB that references MYTON[1] references the same internal structure. The TON function block holds at least four pieces of state:

  • Last value of IN (rising/falling-edge detection)
  • Cumulative elapsed time ET
  • Preset PT
  • Internal time-base accumulator for the current call

When FB-A sets MYTON[1].IN := TRUE with PT = 2000 ms and FB-B writes MYTON[1].IN := FALSE one scan later with PT = 500 ms, the instance is rewritten before the elapsed time has accumulated. Whichever FB is scheduled last in the current task cycle "wins" that cycle. The result:

  • ET resets unpredictably
  • Q flips on edges that no individual FB caused
  • Diagnostics (trace, watch table) appear incoherent
  • Subsequent FBs read a Q that corresponds to a different condition than the one they wrote

This is the "mismatch" symptom reported on the machine. It is not a SCOUT bug, a D435 firmware bug, or a TON implementation bug — it is a correct implementation of a poorly-scoped variable.

Diagnostic clue: Open the program in SCOUT, attach an online watch on MYTON[1], and trigger the six FBs in sequence. If the ET value resets between scans without any single FB's IN falling, the instance is being written by another caller.

Solution Path 1: Declare TON as a Local (VAR) in Each FB

The IEC-correct fix is to move the TON instance out of the global scope and into the VAR section of each calling FB. The instance then exists once per FB invocation; its state is private to that FB and cannot be corrupted by sibling FBs.

Before (faulty):

// In the program container, VAR_GLOBAL
VAR_GLOBAL
    MYTON : ARRAY[1..16] OF TON;   // shared instance – do NOT use this way
END_VAR
// In FB_A, FB_B, … FB_F (six blocks)\code>MYTON[1](IN := startCondA, PT := T#2s);

After (correct):

// In each FB's VAR section (private instance)
FUNCTION_BLOCK FB_A
VAR
    myTon : TON;          // local, single-instance
END_VAR

// Body
myTon(IN := startCondA, PT := T#2s);
IF myTon.Q THEN
    // …
END_IF;

The same pattern is used in FB_B through FB_F, each with its own enable condition and preset. There is no need to keep MYTON[1] as an array at all in most cases; an array of timers is only justified when the same logic is iterated over many indexed elements (e.g., conveyor segments, indexing axes).

Solution Path 2: Multi-Instance (Multi-Ton per FB)

SIMOTION SCOUT supports the IEC 61131-3 multi-instance model: a single FB can declare TON as an additional instance alongside its own static data, and the FB is then instantiated as a multi-instance in another FB or in a program. This is the preferred pattern when the same FB body is reused, and each call must own its own TON.

FUNCTION_BLOCK FB_GripperSequence
VAR
    tonClamp   : TON;   // private multi-instance
    tonRelease : TON;
END_VAR

When FB_GripperSequence is called from another FB as a multi-instance (gripperA : FB_GripperSequence; gripperB : FB_GripperSequence;), each call site gets its own private tonClamp and tonRelease. This is the cleanest expression of the requirement and is the recommended SCOUT idiom.

Solution Path 3: Array of Timers — Only When Iteration Is the Intent

If the original design intent really was "16 indexed timers" (e.g., one TON per axis of a multi-axis cluster), then an ARRAY[1..16] OF TON is still acceptable — but it must be local to the FB that drives the iteration, not global.

FUNCTION_BLOCK FB_MultiAxisWatchdog
VAR
    axisTon : ARRAY[1..16] OF TON;
END_VAR
VAR_INPUT
    enable : ARRAY[1..16] OF BOOL;
    presetMs : ARRAY[1..16] OF TIME;
END_VAR
VAR_OUTPUT
    expired : ARRAY[1..16] OF BOOL;
END_VAR

FOR i := 1 TO 16 DO
    axisTon[i](IN := enable[i], PT := presetMs[i]);
    expired[i] := axisTon[i].Q;
END_FOR;

The array lives in the FB's VAR block; the FB is instantiated once; the array of 16 TONs is owned by that one instantiation. No second FB can write into it.

TON Timer Semantics: Reference for TIA Portal SCL

Although SIMOTION SCOUT and TIA Portal are separate IDEs, the TON instruction is defined by IEC 61131-3 and the S7-1200/S7-1500 documentation gives the canonical behavior. The TON "Generate on-delay" instruction delays setting of Q by the programmed duration PT after a rising edge on IN; the accumulated time is reported on ET as a TIME datatype. See the official TON: Generate on-delay (S7-1200, S7-1500) — TIA Portal help for parameter semantics.

Key behavior that the SIMOTION D435 TON implements identically:

Parameter Direction Type Semantics
IN Input BOOL Enable; rising edge starts the timer
PT Input TIME Preset duration (e.g., T#2s500ms)
Q Output BOOL TRUE when ET ≥ PT, latched until IN falls
ET Output TIME Elapsed time since the last rising edge on IN

Because ET is an accumulator, it must be owned by a single caller. Two callers cannot both accumulate into the same ET and have it be meaningful.

Implementation Steps in SIMOTION SCOUT

  1. Open the SCOUT project and locate each of the six FBs that reference MYTON[1]. Use Edit > Find and Replace with the pattern MYTON[1] to enumerate every call site.
  2. Open the program container where MYTON is declared globally. Right-click the global DB and inspect the VAR_GLOBAL section. Record the array bounds and data type for traceability.
  3. In FB_A, add a private TON instance in the VAR block: myTonA : TON;. Replace MYTON[1](IN := …, PT := …); with myTonA(IN := …, PT := …); in the body.
  4. Repeat for FB_B through FB_F, choosing unique instance names per FB (myTonB, myTonC, …). Where a preset is reused, declare presetA : TIME := T#2s; as a VAR CONSTANT in the FB for clarity.
  5. Compile the unit (F7 or Project > Compile). SCOUT will report any remaining MYTON[1] reference that the find/replace missed.
  6. Decide whether the global MYTON array should be removed entirely. If no other program organization unit references it, delete it from the global DB. This prevents a future programmer from re-introducing the same anti-pattern.
  7. Download the project to the D435 in STOP mode and restart the assigned cyclic task. The transition to RUN must show the six FBs reporting independent ET values in the online watch.

Verification Procedure

  1. Static check: Search the entire project for the literal MYTON. The only legitimate matches should now be inside one of the six FBs, each with its own scoped name.
  2. Online watch: Open each FB online, force a known input condition, and confirm that ET increments monotonically from 0 to PT, that Q rises exactly once, and that the value is unaffected by activity in the other five FBs.
  3. Trace recording: Use SCOUT's trace tool to record myTonA.IN, myTonA.Q, myTonF.IN, and myTonF.Q for a full machine cycle. The waveforms must be independent.
  4. Stopwatch test: For each FB, drive a known-duration input and measure the time from IN rising to Q rising with a 1 ms resolution oscilloscope on a hardware output. Compare to PT. A deviation larger than one IPO cycle indicates a task-load problem, not a scope problem.
  5. Regression test: Run the full machine sequence for at least 200 cycles. Record any anomaly. Acceptance: zero timer-related faults.

Diagnostic Matrix: Symptom to Cause to Fix

Observed Symptom Likely Cause Corrective Action
ET resets mid-count without IN falling Another FB is writing IN Move TON to FB-local VAR
Q rises at non-deterministic times Multiple IN drivers with different PTs Use a TON per FB, with its own PT
Timer works on bench, fails on machine Scan time / task assignment changes IPO context Verify the FB is in the intended task (e.g., IPO_2 vs Servo)
Compiler warns "Instance is used multiple times" Cross-coupling between FBs Refactor to multi-instance or local instance
Watch table shows MYTON[1].ET = 0 always FB never reaches the line that calls MYTON[1] Confirm FB is being called; check EN/ENO behavior

Common Pitfalls When Migrating from S7 to SIMOTION

Engineers moving from a S7-300/400 to a SIMOTION D435 often carry the habit of declaring timers in a global DB, because in STEP 7 the TIMER word area is genuinely shared and indexed. SIMOTION's IEC instances do not work that way:

  • Pitfall 1: Using ARRAY[1..n] OF TON as a global lookup table "for convenience." The convenience disappears the moment two routines write into the same element.
  • Pitfall 2: Calling the same FB instance from two different program organization units. Even without an explicit array, this is the same multi-writer problem.
  • Pitfall 3: Initializing the TON's PT from a global HMI tag every cycle. If the HMI tag changes, the ET accumulator is reset because the TON sees a new PT as a fresh start condition in some SIMOTION versions.
  • Pitfall 4: Forgetting to call the TON on every execution. Because the instance is not a free-running hardware timer, missing one scan effectively pauses accumulation and gives a fictitious Q behavior.

Best Practices for Timer Management in SIMOTION D435 Programs

  1. One TON per logical timing concern. If a timing concern is owned by an FB, the TON lives in that FB.
  2. Arrays of timers only when iterating. Reserve ARRAY[..] OF TON for cases where a FOR loop walks the array. A static reference like MYTON[1] is almost always a code smell.
  3. Use VAR CONSTANT for fixed presets. A preset : TIME := T#500ms; declared as a constant makes the timing policy auditable.
  4. Tag the timer semantically. gripperCloseTimer is better than myTon[1]. The array indexing hides intent.
  5. Document the task assignment. The accuracy of ET is bounded by the task that schedules the FB. Place time-critical FBs in IPO or IPO_2; do not bury them in BackgroundTask.
  6. Validate with trace, not just watch. A watch table gives a snapshot. A trace gives a waveform. State-mismatch bugs are far easier to diagnose from a waveform.

FAQ

Why does my SIMOTION D435 timer behave differently every cycle when I share MYTON[1] across six FBs?

A globally declared TON instance has one internal state (last IN, elapsed ET, preset PT, edge detector). When six FBs write the same instance, the last writer in the current task cycle wins, and the other five FBs read stale or reset values. The behavior is non-deterministic because task scheduling determines the order of writes per cycle.

Should the TON instance be global or local in a SIMOTION SCOUT FB?

Local, in the FB's VAR block. The IEC 61131-3 instance model scopes the TON to the FB that owns it. Use a separate VAR entry per FB so each FB owns its own timer state and cannot be corrupted by sibling FBs.

Is an array of timers ever the right design pattern?

Yes, when a single FB iterates over the array with a FOR loop and each element represents an independent, parallel timing concern (e.g., 16 axis watchdogs). The array must live in the FB's local VAR block, not in a global DB, and the FB must be the only writer.

What is the difference between a multi-instance FB and a local instance TON in SIMOTION?

A multi-instance FB is itself instantiated as a static variable in another FB, giving it its own data area and its own private TON instances. A local instance TON is a TON declared in VAR of the FB that uses it. Both isolate state from other call sites; multi-instance adds the further benefit of bundling the timer logic with the FB that consumes it.

How do I verify the fix for a shared-toner mismatch on a running D435?

Open the SCOUT trace, record each FB's IN, Q, and ET for a full machine cycle, and confirm independent monotonic accumulation and exactly one rising edge on Q per IN rising edge. A watch table alone is insufficient; trace waveforms are the authoritative proof.

Back to blog