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.TONmethod. - When the instance is retyped to
TON_TIMEand 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 |
.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:
-
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 bindmyIECtimeras a method call target on a variable of PLC data typeIEC_TIMER. BecauseIEC_TIMERis a system-defined UDT (user-defined type) whoseTONoverload expects to live in an instance DB that the runtime owns, the parser emits an error. -
Runtime ownership of timer instance DBs. Even when the SCL code bypasses the method call and writes
myIECtimer.IN,myIECtimer.PT, and readsmyIECtimer.Qdirectly, 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, soET(elapsed time) never advances andQstays FALSE. - 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
- In the project tree, right-click Program blocks → Add new block → Function Block. Name it, for example,
FB_TimerWrapper. - Open
FB_TimerWrapperand declare the timer as a Static tag of typeIEC_TIMER(for TP, TON, TOF useIEC_TIMER; for long timers above 2 h 46 m 30 s useIEC_LTIMER/TIME-based variants on S7-1500):
VAR myTimer : IEC_TIMER; END_VAR - 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 - Write the SCL body of
FB_TimerWrapper:
BEGIN myTimer.TON(IN := start, PT := presetTime); running := myTimer.Q; elapsed := myTimer.ET; END_FUNCTION_BLOCK - In the parent FC/FB, declare a multi-instance tag:
VAR phase1Timer : FB_TimerWrapper; END_VAR - 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.
- Declare the timer in a global DB (or as a static of the calling FB) as type
TON_TIME(notIEC_TIMER). - Declare the FC INOUT parameter:
VAR_INOUT localTimer : TON_TIME; END_VAR - Call the timer inside the FC:
#localTimer.TON(IN := #bIn, PT := T#2s); - From the calling block (OB1 / parent FB), pass the global-DB tag as the INOUT argument:
myFC(localTimer := "TIMEDB".timer01);
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):
- Open the global DB in the project tree.
- Select the
IEC_TIMERvariable. - In the Retain column of the declaration table, check the box.
- Compile the program. The system writes the corresponding retain setting into the DB's initial values.
Verification Procedure
-
Compile. In the project tree, right-click the SCL block and choose Compile → Software (rebuild all). The build must complete without error code
0174:0or any reference toIEC_TIMERmethod binding. - Download. Transfer the program to the S7-1200/1500 or to S7-PLCSIM.
-
Monitor in online mode. Open the parent FB instance DB, watch the
myTimer.ETtag. With the inputINheld TRUE,ETmust increment in 10 ms steps until it reachesPT, at which pointQbecomes TRUE andETstops incrementing. -
Reset behaviour. Drop the
INinput.Qmust clear immediately andETmust reset toT#0ms. -
Retentivity. Stop the CPU, run again, and confirm the timer's last
ETandQvalues 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_TIMERstatic 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
Retainattribute 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.