Resolving TON Timer Global DB Issues in SCL with TIA Portal

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

Problem Overview

Calling a TON (on-delay timer) instruction whose instance is declared in a global data block from an SCL (Structured Control Language) block fails to compile or fails to execute correctly on S7-1200/1500 controllers programmed with TIA Portal V13 SP1. The same construct compiles and runs without issue in LAD (ladder) or FBD (function block diagram). This is a known compiler-level restriction: the TIA Portal V13 SP1 Update 8 SCL compiler does not permit "myDB".myTimer.TON(...) on an instance variable typed as IEC_TIMER from inside an FC (function).

The reported failure modes are:

  • Compiler rejects the qualified instance syntax "myDB".IEC_TIMER_variable.TON(...) with an error pointing at the .TON method.
  • When the instance is retyped to TON_TIME and accessed via ".TON(...)", the compiler still rejects the call.
  • When only the struct fields are written via .IN, .PT, .Q, the block compiles but the timer never advances in S7-PLCSIM, because the system timer background OB is not bound to the global-DB instance.
  • Any timer instance dragged into an FC is automatically backed by a system-generated instance DB under Program resources → System blocks → Program blocks, which is why the "single instance" wizard always materialises a new DB even when the user typed a global-DB path.

Affected Versions and Platforms

Component Versions with the restriction Behavior
TIA Portal V13 SP1 (Update 8 verified), V14, V15 SCL compiler rejects globalDB.timer.TON() from FCs; LAD/FBD allow the same syntax
CPU family S7-1200 (all firmware), S7-1500 (all firmware) Restriction applies to SCL source generation
Block types FC (function) FB (function block) accepts multi-instance IEC_TIMER without restriction
Simulator S7-PLCSIM V13 SP1 / V14 / V15 Reveals the runtime failure because no OB is bound to the global-DB timer instance
Timer data types IEC_TIMER, IEC_LTIMER, TON_TIME, TOF_TIME, TP_TIME Compiler accepts TON_TIME as INOUT in FC but rejects .TON() method on global-DB-qualified instances
Siemens official confirmation (Ticket reference): Calling a timer instance declared in a global DB via the .TON method is permitted in KOP (ladder) and FUP (function block diagram) only. SCL is not supported for this construct in the affected TIA Portal versions.

Root Cause Analysis

Three interlocking restrictions produce the symptom:

  1. Compiler grammar for SCL. The SCL front-end in TIA Portal V13 SP1 only resolves TIME-typed timer instructions against (a) a locally declared instance, (b) a multi-instance of the enclosing FB, or (c) a single-instance DB the compiler is allowed to materialise itself. The grammar check is intentionally strict: when the compiler sees "myDB".myIECtimer.TON(...) it tries to bind myIECtimer as a method call target on a variable of PLC data type IEC_TIMER. Because IEC_TIMER is a system-defined UDT (user-defined type) whose TON overload expects to live in an instance DB that the runtime owns, the parser emits an error.
  2. Runtime ownership of timer instance DBs. Even when the SCL code bypasses the method call and writes myIECtimer.IN, myIECtimer.PT, and reads myIECtimer.Q directly, the runtime needs to know the instance DB so that OB1 / cyclic OB can update the timer's internal elapsed-time register every scan. With a custom global DB, this runtime hookup does not occur automatically, so ET (elapsed time) never advances and Q stays FALSE.
  3. FC scoping rules. An FC has no instance memory of its own, so any timer used inside an FC must be backed by either a single-instance DB (auto-created by the compiler) or a multi-instance inside an enclosing FB. A timer declared as a static tag of a global DB cannot be bound to the FC at runtime through SCL syntax in V13 SP1.

Why It Works in LAD/FBD

The LAD and FBD editors in TIA Portal V13 SP1 generate an implicit call wrapper for timer instructions that includes the necessary instance-DB binding code. When you place a TON coil on a rung with the instance set to a global-DB tag of type IEC_TIMER, the editor performs the same call the SCL compiler refuses to compile. This is a language-front-end difference, not a CPU firmware limitation: the underlying STL instructions generated are identical.

Recommended Workaround: FB Multi-Instance

The cleanest and most portable solution is to wrap each timer inside a small FB and call the FB as a multi-instance from the parent code. This pattern is officially supported by Siemens for S7-1200/1500 and survives every TIA Portal version from V13 onward.

Step-by-Step

  1. In the project tree, right-click Program blocks → Add new block → Function Block. Name it, for example, FB_TimerWrapper.
  2. Open FB_TimerWrapper and declare the timer as a Static tag of type IEC_TIMER (for TP, TON, TOF use IEC_TIMER; for long timers above 2 h 46 m 30 s use IEC_LTIMER / TIME-based variants on S7-1500):
    
    VAR
        myTimer : IEC_TIMER;
    END_VAR
    
  3. Add the input/output interface:
    
    VAR_INPUT
        start : BOOL;
        presetTime : TIME;
    END_VAR
    VAR_OUTPUT
        running : BOOL;
        elapsed : TIME;
    END_VAR
    VAR
        myTimer : IEC_TIMER;  // static, multi-instance
    END_VAR
    
  4. Write the SCL body of FB_TimerWrapper:
    
    BEGIN
        myTimer.TON(IN := start, PT := presetTime);
        running := myTimer.Q;
        elapsed := myTimer.ET;
    END_FUNCTION_BLOCK
    
  5. In the parent FC/FB, declare a multi-instance tag:
    
    VAR
        phase1Timer : FB_TimerWrapper;
    END_VAR
    
  6. Call it from SCL:
    
    phase1Timer(start := #bStartPhase1, presetTime := T#5s);
    IF phase1Timer.running THEN ... END_IF;
    

The multi-instance mechanism stores myTimer inside the parent's instance DB, so the runtime binding is correct and the timer advances every scan regardless of which global DB you reference from the editor.

Alternative Workaround: INOUT with TON_TIME in an FC

If you must stay inside an FC and cannot refactor to an FB, you can pass the timer as an INOUT parameter typed as TON_TIME. This compiles in TIA Portal V13 SP1 because the INOUT is bound to a caller-owned storage location and the compiler does not need to materialise a DB.

  1. Declare the timer in a global DB (or as a static of the calling FB) as type TON_TIME (not IEC_TIMER).
  2. Declare the FC INOUT parameter:
    
    VAR_INOUT
        localTimer : TON_TIME;
    END_VAR
    
  3. Call the timer inside the FC:
    
    #localTimer.TON(IN := #bIn, PT := T#2s);
    
  4. From the calling block (OB1 / parent FB), pass the global-DB tag as the INOUT argument:
    
    myFC(localTimer := "TIMEDB".timer01);
    
Limitation: The TON_TIME data type does not include the TON method as a callable function in all TIA Portal versions; verify the call compiles before relying on it. If the SCL editor rejects the method call, fall back to direct field access localTimer.IN := ...; localTimer.PT := ...; localTimer(); — but only for blocks where the timer's storage is bound to an FB instance DB, not a global DB.

Alternative Workaround: Peek/Poke for Universal Indirect Use

For programs that must run on S7-300/400 (classic STEP 7) and S7-1200/1500 (TIA Portal), the recommended Siemens pattern is an FB named ind that encapsulates PEEK/POKE for the S7-1200/1500 target and equivalent area-pointer logic for S7-300/400.

Signature:


FUNCTION_BLOCK ind
VAR_INPUT
    tag : VARIANT;        // symbolic tag to read
    tagtype : BYTE;        // 1=M, 2=DB, 3=I, 4=Q
    offset : DINT;         // byte offset
    length : INT;          // number of bytes
    plcType : INT;         // 1200, 1500, 300, 400
END_VAR
VAR_OUTPUT
    value : DWORD;         // data returned
END_VAR

Internally, the FB switches on plcType and uses PEEK/POKE (S7-1500/1200) or P# area-pointer arithmetic (S7-300/400). Call it everywhere you would otherwise have used global-DB-resident timers, and centralise platform differences in one place.

Retentive Timer Configuration

When the timer instance lives in a global DB and you need the elapsed-time or output to survive a CPU restart, mark the timer as retentive. Per the official SIMATIC S7-1200 Manual Collection — Basic Instructions — Timer Operations (IEC Timers):

  1. Open the global DB in the project tree.
  2. Select the IEC_TIMER variable.
  3. In the Retain column of the declaration table, check the box.
  4. Compile the program. The system writes the corresponding retain setting into the DB's initial values.
A timer can only be retentive if its enclosing DB is also configured retentive. Right-click the global DB, choose Properties → Attributes, and confirm Retain / Non-retain matches the desired scope.

Verification Procedure

  1. Compile. In the project tree, right-click the SCL block and choose Compile → Software (rebuild all). The build must complete without error code 0174:0 or any reference to IEC_TIMER method binding.
  2. Download. Transfer the program to the S7-1200/1500 or to S7-PLCSIM.
  3. Monitor in online mode. Open the parent FB instance DB, watch the myTimer.ET tag. With the input IN held TRUE, ET must increment in 10 ms steps until it reaches PT, at which point Q becomes TRUE and ET stops incrementing.
  4. Reset behaviour. Drop the IN input. Q must clear immediately and ET must reset to T#0ms.
  5. Retentivity. Stop the CPU, run again, and confirm the timer's last ET and Q values survive the restart (only relevant when the global DB / FB instance has retain enabled).

Troubleshooting Matrix

Symptom Likely cause Fix
Compiler error: "IEC_TIMER cannot be used as instance for TON in SCL" Calling .TON() on a global-DB tag of type IEC_TIMER from an FC in SCL Refactor to FB multi-instance or use INOUT TON_TIME pattern
Code compiles, ET stays at 0, Q never sets Direct field writes (.IN, .PT, .Q) bypass runtime hookup to the global DB Move the timer into an FB instance DB; use .TON() method call inside an FB
TIA inserts a new instance DB under System blocks Compiler auto-creates single-instance DB because SCL grammar demands one Accept the auto-generated DB or switch to multi-instance FB pattern
Timer works in LAD but not in SCL LAD/FBD editors generate wrapper calls the SCL compiler refuses Use the FB multi-instance pattern in SCL; keep LAD/FBD only for top-level orchestration
Timer loses state across CPU restart Global DB or instance DB not configured retentive Enable the Retain checkbox on the timer tag and on the enclosing DB
Long timers (above 2h 46m 30s) roll over IEC_TIMER uses 16-bit TIME base Use IEC_LTIMER on S7-1500; on S7-1200 use multiple cascaded IEC_TIMER instances or a user-defined TIME-based counter
S7-PLCSIM shows timer running, real CPU does not OB1 / cyclic OB not downloaded or cyclic interrupt OB priority changed Verify OB1 is present in the program and that the timer's enclosing FB is called from OB1 or a higher-priority OB

Design Recommendation

For new projects, adopt the following conventions to avoid hitting the SCL/global-DB-timer restriction entirely:

  • Declare every timer as a multi-instance IEC_TIMER static of the FB that owns the timing logic.
  • Wrap small timing primitives (TON, TOF, TP) inside their own FBs so they can be reused as multi-instances.
  • Reserve global DBs for configuration data only (preset times, mode flags) — not for live runtime instances.
  • Use the Retain attribute on the enclosing FB instance DB only when the application genuinely needs to survive power-cycle.
  • Validate all timing code under S7-PLCSIM before downloading to a physical CPU; S7-PLCSIM exposes binding bugs that hardware may mask initially.

Why does calling TON with a global-DB instance work in LAD but not in SCL under TIA Portal V13 SP1?

The LAD and FBD editors automatically wrap the timer call so the runtime binds to the global DB instance. The SCL compiler in TIA Portal V13 SP1 (verified up to Update 8) only accepts timer method calls on locally declared variables, multi-instance FBs, or auto-generated single-instance DBs; it rejects "myDB".myIECtimer.TON(...) from inside an FC. This was confirmed by Siemens support.

Can I keep my timer instance in a global DB and still use SCL?

Indirectly yes: declare the timer as an INOUT parameter of type TON_TIME in an FC, or move the timer into an FB as a multi-instance of type IEC_TIMER. Both patterns compile in SCL and the runtime updates the timer correctly. Direct access to a global-DB IEC_TIMER tag from SCL is not supported in V13 SP1.

How do I make an IEC_TIMER retentive in a global DB?

Open the global DB, select the IEC_TIMER tag, and check the Retain column. Make sure the DB itself has the retain attribute set under Properties → Attributes. See the official SIMATIC S7-1200 timer documentation for details.

What is the maximum preset time for IEC_TIMER on S7-1200?

IEC_TIMER on S7-1200 stores ET in a 16-bit BCD-style register with 10 ms resolution, giving a maximum preset of 2 h 46 m 30 s (9 999 * 10 ms). For longer timers on S7-1500 use IEC_LTIMER (32-bit, nanosecond resolution); on S7-1200 cascade multiple IEC_TIMER instances or build a user-defined counter.

Does writing myTimer.IN, myTimer.PT, myTimer.Q directly work as a workaround?

It compiles but the timer does not run, because the runtime needs the timer's instance DB to be hooked into the cyclic OB. Direct field writes from SCL on a global-DB-resident IEC_TIMER never advance ET. Use the FB multi-instance pattern instead so the instance lives inside an FB instance DB that the runtime owns.

Back to blog