Converting S7-300 SCL Timers to S7-1200: IEC_TIMER Migration Guide
Engineers porting legacy S7-300 SCL (Structured Control Language) blocks to an S7-1200 CPU under TIA Portal frequently encounter a hard compile error when they try to reuse the original S_ODT, S_PULSE, or S_OFFDT calls. The legacy TIMER data type that S7-300/400 accepted as a VAR_INPUT or VAR_INOUT parameter does not exist in the S7-1200/1500 system data-type catalogue, and the legacy timer-function-block calls are replaced by IEC 61131-3 standard timer instances (TP, TON, TOF, TONR) wrapped in an IEC_TIMER / IEC_LTIMER structure. This reference walks through the conversion, explains the architectural reasoning, provides drop-in SCL syntax, and lists the verification steps that confirm the migrated timer behaves identically.
Architectural Differences: S7-300 Timers vs S7-1200 IEC Timers
The S7-300/400 family exposes timers as a fixed resource pool. Each CPU has a maximum number of timer words (for example, CPU 315-2 PN/DP has 256), and a timer was referenced by a 16-bit integer index plus the legacy TIMER type. The original SCL snippet in the source material demonstrates that pattern:
// S7-300 SCL legacy form (STEP 7 V5.x)
#TimeTS5 := S_ODT(
T_NO := #IntTimer, // word-type index into the CPU timer pool
S := NOT(#resetT), // start input
TV := #TimeIntT, // preset time as S5TIME
R := #resetT, // reset input
BI := #TimeT, // current time (BCD output)
Q := #B_I_Q); // timer status
The S7-1200 family removes the timer pool entirely. Every timer is now a software instance of an IEC 61131-3 system function block (TP, TON, TOF, TONR) stored either inside an FB's static area (multi-instance) or inside a dedicated instance DB (single instance). The CPU firmware enforces the limit by RAM usage, not by a fixed word count. The IEC_TIMER and IEC_LTIMER data types are thin wrappers around the timer instance:
| Field | Type | Description |
|---|---|---|
IN |
BOOL | Start / trigger input |
PT |
TIME | Preset duration (in ms resolution) |
Q |
BOOL | Timer output (status) |
ET |
TIME | Elapsed time since trigger |
STATE / ST
|
WORD / BYTE | Internal state word (visible in IEC_LTIMER and the watch table) |
The S_ODT call in the source material maps cleanly onto an IEC TON (on-delay timer). The reset behaviour of S_ODT requires the TOF or the rising-edge reset idiom; engineers should not pick the first IEC timer that "looks similar" without mapping the functional behaviour.
IEC 61131-3 Timer Type Mapping
The S7-300/400 legacy timer family has five members. The S7-1200/1500 IEC family has four. The mapping below preserves the input/output behaviour of the original.
| S7-300 Legacy FB | Behaviour | S7-1200/1500 IEC FB | Notes |
|---|---|---|---|
S_PULSE |
Pulse, output follows input while timing | TP |
Direct replacement |
S_PEXT |
Extended pulse, retriggerable | TP |
Same block, edge-triggered input differs |
S_ODT |
On-delay, output rises after PT | TON |
Direct replacement |
S_ODTS |
Latching on-delay, reset by R | TONR |
Retentive; manual reset |
S_OFFDT |
Off-delay, output falls after PT | TOF |
Direct replacement |
S_ODT resets immediately when R is high. TON resets only on a falling edge of IN. If the legacy program relied on R being a level-sensitive clear (e.g., a safety reset that holds the output at zero), implement the equivalent with a parallel reset on Q via an additional R_TRIG / F_TRIG construct or by gating IN with the reset signal.
Prerequisites
- TIA Portal V16 or later (V17 / V18 / V19 / V20 supported per the Siemens TIA Portal Help).
- STEP 7 S7-SCL option package, version 5.3 SP5 or later, installed on the source machine for migration import.
- Target S7-1200 CPU with firmware 4.2 or later (firmware 4.4+ recommended for full
IEC_LTIMERsupport on all variants). - Source project archive (\*.s7p, \*.zap) exported from STEP 7 V5.x or STEP 7 Professional V11-V15.
S7-300 SCL Source Syntax (Original)
The original snippet declares an IntTimer input as the legacy TIMER data type and calls S_ODT with named parameters:
VAR_INPUT
IntTimer : TIMER; // legacy word-index into CPU timer pool
TimeIntT : S5TIME; // preset value in S5 BCD format
resetT : BOOL;
END_VAR
VAR_TEMP
TimeTS5 : S5TIME;
TimeT : WORD;
B_I_Q : BOOL;
END_VAR
The TIMER type is a pointer-sized word; it identifies a slot in the timer area of the CPU's system memory. This pattern is illegal in S7-1200 SCL and will be flagged at compile time with error 0xE0D8 (Unknown data type) or 0xE002 (Parameter type mismatch), depending on the SCL parser version.
S7-1200 SCL Target Syntax (Converted)
Replace the VAR_INPUT timer declaration with an IEC_TIMER (or IEC_LTIMER) declared inside the FB's VAR_STAT section as a multi-instance, or declare a globally visible DB of type IEC_TIMER for a single-instance approach. The call site changes from the legacy FB form to the IEC FB form.
Multi-Instance Form (Recommended for FBs)
FUNCTION_BLOCK "FB_OnDelay"
{ S7_Optimized_Access := 'TRUE' }
VAR
resetT : BOOL;
Start : BOOL;
PT : TIME; // preset, e.g. T#5s
END_VAR
VAR_STAT
InstTON : IEC_TIMER; // multi-instance: lives inside FB instance DB
ET_Out : TIME;
Q_Out : BOOL;
END_VAR
BEGIN
// edge-triggered start input, equivalent to S_ODT behaviour
InstTON(IN := NOT(#resetT) AND #Start,
PT := #PT);
// optional level-sensitive reset (preserves S_ODT R input behaviour)
IF #resetT THEN
InstTON.IN := FALSE;
END_IF;
#Q_Out := InstTON.Q;
#ET_Out := InstTON.ET;
END_FUNCTION_BLOCK
Single-Instance DB Form
VAR
sODT_DB : "DB_OnDelay_Inst"; // instance DB of type IEC_TIMER
END_VAR
BEGIN
sODT_DB.IN := NOT(#resetT);
sODT_DB.PT := T#5s;
sODT_DB(); // explicit FB call
END_VAR
Step-by-Step Conversion Procedure
- Open the legacy project in TIA Portal. Use Project > Migrate project. Confirm the S7-SCL option 5.3 SP5+ is installed; otherwise the SCL sources are imported as plain text and lose their block attributes.
-
Locate the FB containing
S_ODT. Open the SCL editor. The migration tool rewrites the legacy FB call toS_PULSE / S_ODT / S_OFFDTas a comment and inserts a stubIEC_TIMERinstance. -
Replace the
TIMERdeclaration inVAR_INPUT. Delete the line and add the timer as a multi-instance inVAR_STAT, or as a separate instance DB if it must be addressable from outside the FB. -
Convert the
TVargument. LegacyS5TIME(BCD format, e.g.,S5T#5s) becomesTIME(e.g.,T#5s). For variables, change the type toTIMEat the calling site. -
Map the FB call. Replace
S_ODTwithTON,S_PULSEwithTP,S_ODTSwithTONR, andS_OFFDTwithTOF. WireIN :=,PT :=, readQandETas outputs. -
Re-implement the
Rinput. The legacyRis level-sensitive. Use a guardedIF #resetT THEN InstTON.IN := FALSE; END_IF;block, or wrap the start signal with anF_TRIGonresetTto produce a single-cycle reset. -
Set the optimisation attribute. Mark the FB as Optimised block access (default in new TIA Portal projects). The
IEC_TIMERfields are then symbolic; access by offset is no longer required. -
Compile and resolve errors. Expected residual errors:
0xE0D8(unknown type - leftoverTIMER),0xE002(call parameter mismatch),0x8010(parameter assignment missing). -
Download to the PLC. Use the fully qualified symbolic path in the watch table to confirm
InstTON.ETincrements andInstTON.Qlatches afterPT.
Why VAR_INPUT ... : TIMER Fails on S7-1200
The TIMER type exists only as a legacy alias for a 16-bit word in the timer area of the S7-300/400 CPU. S7-1200/1500 firmware has no timer area, so the parser rejects the type. The diagnostic message returned by the SCL compiler in TIA Portal V17/V18/V19 is:
Error 0xE0D8: Unknown data type 'TIMER'.
In FB 'FB_OnDelay', VAR_INPUT section, line 4.
The supported equivalent at an FB boundary is the fully-qualified IEC system data type IEC_TIMER (32-bit time resolution, ms) or IEC_LTIMER (64-bit long time resolution, ns). For a single-instance DB created from the System > Standard Library > IEC Timer panel, the visible fields are the same as those listed in the architecture table above.
Multi-Instance vs Single-Instance DB Configuration
Choose the storage mode by call site, not by preference.
| Criterion | Multi-Instance (VAR_STAT) | Single-Instance DB |
|---|---|---|
| Memory overhead | Low; one DB per FB instance holds all timers | High; one DB per timer |
| External HMI access | Indirect via InstFB.InstTON.Q
|
Direct via DB_T1.Q
|
| Number of timers supported | Limited only by FB instance count | Limited by CPU data-block count |
| Best use case | FBs called many times (reactor banks, conveyor lines) | Standalone timers addressed from many blocks |
Common Migration Errors and Resolution
| Compiler Error | Cause | Resolution |
|---|---|---|
0xE0D8 Unknown data type |
Leftover VAR_INPUT ... : TIMER line |
Remove the line; declare IEC_TIMER in VAR_STAT
|
0xE002 Parameter assignment |
Calling S_ODT(T_NO := ...) with IEC_TIMER |
Use InstTON(IN := ..., PT := ...)
|
0x8010 Missing parameter |
PT or IN not wired |
Explicitly wire both, even to default values |
0x8012 Type mismatch |
PT declared as S5TIME
|
Change to TIME
|
0x8038 Unknown symbol |
#TimeTS5 removed but still referenced |
Delete the reference or declare new ET_Out tag |
0xC030 Multiple assignment |
Legacy BI output wired to TIME tag |
Wire BI equivalent to ET instead |
Verification Procedure
- Static syntax check. Right-click the FB → Compile > Software (only). Resolve every error and warning before download.
-
Symbolic watch table. Create a watch table with
"FB_OnDelay_DB".InstTON.IN,.PT,.ET,.Q. ForceINhigh with a rising-edge trigger and observeETincrementing at OB1 cycle rate. -
Trace recording. Open Traces > New trace, add the four
IEC_TIMERsignals, and confirmQrises exactly atET >= PTminus one scan. - Online-Diff against legacy. If the legacy program is still available, run it on a PLCSIM instance of the S7-300 in parallel and compare trace records sample-for-sample. Drift greater than one OB1 cycle indicates a wiring mistake.
-
Reset behaviour test. Hold
resetThigh for 5 seconds, release, confirmQis false atET = 0, then re-trigger and confirmQrises afterPTas in the original.
Cross-Platform Notes: S7-1500 Compatibility
The exact same conversion applies to S7-1500, with one additional option: IEC_LTIMER supports nanosecond resolution and exposes a STATE byte in the watch table for diagnostics. If the migrated FB will be reused on both S7-1200 (firmware 4.2+) and S7-1500, declare IEC_TIMER only and verify each target's firmware supports the data type. The S7-1200 firmware 4.2 baseline supports IEC_TIMER; firmware 4.4 adds full IEC_LTIMER support on selected CPUs. Refer to the Siemens Industry Online Support entry for your specific CPU article number before relying on IEC_LTIMER.
Edge Cases and Field-Proven Caveats
-
S5TIME range: Legacy
S5TIMEtops out at 9990 seconds (2h 46m 30s).TIMEon S7-1200 covers 24 days 20h 31m 23s 647ms. If the original program relied on the S5 range as a safety clamp, replicate the clamp with anIF PT > T#9990s THEN PT := T#9990s; END_IF;guard. -
BCD
BIoutput: The legacyBIreturned the residual time as a BCD word for display on operator panels. Replace withInstTON.ETrendered as a TIME/HMI string. HMI tags must change type fromWORDtoTIMEat the WinCC panel side. -
Optimised block access: With Optimised block access on, the
IEC_TIMERfields can no longer be addressed by absolute offset (e.g.,DB1.DBX0.0). Any third-party HMI driver relying on absolute offsets must be updated to symbolic addressing. -
Retentivity:
TONandTOFare non-retentive by default;TONRretains the elapsed time across CPU restarts. If the legacyS_ODTwas expected to remember its state across a STOP/RUN transition, declare the timer instance with the Set in IDB retentive attribute, or switch toTONR. - Multi-instance naming clash: Two FBs cannot declare a static of the same name with different timer types. TIA Portal V17+ enforces unique-instance names across the project; pre-V15 an old compiler accepted the collision and produced silent overlap.
FAQ
How do I declare a timer in S7-1200 SCL inside a VAR_INPUT section?
You cannot use the legacy TIMER type in VAR_INPUT on an S7-1200. Declare the timer as IEC_TIMER or IEC_LTIMER inside the FB's VAR_STAT section (multi-instance) or create a global instance DB of type IEC_TIMER and pass it as an IN_OUT reference. Then call Inst(IN := ..., PT := ...) with the IEC function block (TP, TON, TOF, or TONR).
What is the direct replacement for S_ODT in S7-1200 SCL?
Use the IEC on-delay timer TON. The legacy TV argument becomes PT and must be of type TIME (e.g., T#5s) instead of S5TIME. The R input has to be re-implemented with a level-sensitive clear of the IN signal because TON resets on a falling edge of IN rather than on a held R.
Which IEC timer maps to S_PEXT (extended pulse)?
S_PEXT is a retriggerable pulse; the closest IEC equivalent is TP (pulse timer). The TP block ignores further triggers while the pulse is running, so if the legacy code relied on retriggering, add the new trigger via an R_TRIG on the rising edge of IN and feed the result back into IN through a latch.
Why does my SCL compile fail with error 0xE0D8 after migration?
Error 0xE0D8 means an unknown data type. The most common cause is a leftover VAR_INPUT ... : TIMER; line that the importer did not strip. Open the FB interface, delete the line, and replace it with an IEC_TIMER instance in the VAR_STAT section. Recompile.
Can I keep using S5TIME as the preset value type?
No. S5TIME is not part of the S7-1200/1500 system data-type catalogue. Change the input tag to TIME and update any HMI tag that read it as a WORD. If you need the S5 range safety clamp, validate against T#9990s in code.
Does the converted timer keep the same accuracy as the S7-300 version?
The IEC IEC_TIMER updates every OB1 cycle with ms resolution on a standard S7-1200 and ns resolution on CPUs that support IEC_LTIMER. The legacy S_ODT also updated once per OB1 cycle, so accuracy is unchanged or better. The ET field is now a true millisecond count instead of BCD, eliminating the rounding error that affected the legacy BI output.