Siemens SCL: Resolving ARRAY of TON Multi-Instance Errors

David Krause2 min read
S7-300SiemensTechnical Reference
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

Why the SCL Compiler Rejects ARRAY OF TON

In a Siemens S7 function block, declaring a single timer as a static multi-instance works: Test : TON; compiles without error. The declaration Test : ARRAY[1..3] OF TON; does not — the SCL compiler refuses it. The reason is structural: a TON (SFB4) is an FB/SFB type built with IN and OUT parameters, and this type is not permitted as an array element in the static (multi-instance) data area. This is a compiler-level restriction, not a syntax mistake, so no re-declaration trick inside the same static area will make it compile.

This blocks the intuitive pattern of looping over instances, e.g. calling Test[i] or Self_FB[i] inside FOR i := 1 TO 3 DO .... Any loop-based call over indexed multi-instances of an FB type requires one of the workarounds below.

Workarounds for Indexed Multi-Instance Calls

Approach Mechanism Limitation
CASE OF with individual calls Declare three separate multi-instances and select each call with a CASE OF instruction Much code and scrolling; scales poorly
WORD_TO_BLOCK_DB Convert an index/word to a DB number and call with instance DBs Loses the multi-instance concept entirely
Buffer DB with manual copy Create one extra buffer DB and manually copy contents in/out per call Manual copy logic; not clean
Wrapper FB Build an FB that calls one DB with all input/output assignments, then call that FB three times with different DBs Still not a true multi-instance

Indexed Memory Addressing (Use With Caution)

Because each TON occupies 22 bytes of instance data, you can build your own "array": once you know where the first TON starts in memory, index subsequent timers at 22-byte offsets. Example calculation: TON #i starts at base + (i - 1) * 22 bytes. This avoids the CASE OF duplication, but addressing by raw memory offset bypasses the compiler's type checking and is fragile — any change to the static declaration silently shifts the offsets. Treat this as a last resort, not a clean solution.

Recommended Decision Path

  1. If the instance count is small and fixed, use separate multi-instance declarations with a CASE OF selector — verbose but fully type-checked.
  2. If calls must be indexed dynamically, use WORD_TO_BLOCK_DB or a wrapper FB with per-call DBs and accept the loss of true multi-instancing.
  3. Avoid raw 22-byte indexed addressing unless you control the instance layout permanently and document the offsets.

FAQ

TON (SFB4) is an FB/SFB type built with IN and OUT parameters, and this type is not permitted as an array element in the multi-instance static area. A single Test : TON; declaration is fine; the array form is blocked by the compiler.

How do I call multiple TON instances in a FOR loop in S7?

You cannot index multi-instances directly. Use a CASE OF instruction with individually declared instances, or switch to WORD_TO_BLOCK_DB / a wrapper FB called with different instance DBs.

How many bytes does a TON use in S7 instance data?

A TON occupies 22 bytes, so timer #i can theoretically be addressed at base + (i - 1) * 22 bytes. This memory-indexing method bypasses type checking and breaks if the static declaration changes, so it is not considered a clean solution.

Back to blog