S7-1200 Function Block Array Instances: TIA Portal SCL Guide

David Krause14 min read
S7-1200SiemensTutorial / How-to
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

S7-1200 Function Block Array Instances: TIA Portal SCL Implementation Guide

When a Siemens S7-1200 application must evaluate hundreds of identical alarm conditions, copying a Function Block (FB) call into a ladder network or SCL source one tag at a time becomes unmaintainable. The scalable solution is to instantiate the alarm FB as a typed array inside a parent block and iterate the array with an index loop. This guide walks through the full pattern: child FB design, parent FB with array of multi-instances, named constant indexing, SCL source generation, memory sizing, and on-line verification. The same pattern applies to S7-1500 and to SCL inside STEP 7 V5.x for S7-300/400, with minor syntactic differences called out where relevant.

1. Problem Definition and Architecture Choice

The classic anti-pattern looks like this: an engineer creates one Instance Data Block (Instance DB) per alarm point, then wires the FB call in a ladder segment. With 200 alarms the project balloons into 200 DBs, 200 call sites, and a maintenance burden that scales linearly with the I/O count.

Three architectural options are available on the S7-1200 platform:

Pattern Memory Location Number of DBs Best For
Single-Instance DB per call Separate global DB N (one per FB call) Legacy code, single-call use
Multi-instance inside parent FB Instance DB of parent FB 1 (parent only) Repeating functions, libraries
Array of FBs in global DB (UDT) Global DB typed as ARRAY OF FB 1 (global) Cross-block access, HMI visibility

For alarm processing the multi-instance inside a parent FB is the cleanest solution because all instances share one Instance DB, the symbolic name "ParentFBInstance".Alarm[i] is used throughout the program, and the HMI can still be configured to point at individual array elements.

Multi-instance support on S7-1200: Multi-instances were first introduced for S7-1200 in STEP 7 (TIA Portal) V11+ with firmware V2.0 on the CPU. S7-1200 firmware V4.0 and later, used with TIA Portal V13 SP1 and newer, adds optimized block access and improved array handling. Verify your CPU firmware with the online > Accessible nodes diagnostic before relying on complex nested multi-instances.

2. Prerequisites

  • STEP 7 (TIA Portal) V15.1 or newer (V17 / V18 recommended for current S7-1200 firmware V4.5+ support). See the Siemens TIA Portal release notes for version compatibility.
  • S7-1200 CPU with firmware >= V4.2 (for full SCL V6 features and multi-instance arrays without length restrictions in the compiler).
  • Defined alarm tag list. A typical input is an export from the HMI tag database or a CSV with columns: TagName, BitAddress, AlarmClass, Priority.
  • Basic understanding of FB static variables, Instance DBs, and the difference between optimized and standard block access. See How do you program an FB with optimized block access in STEP 7 (TIA Portal)?

3. Step 1 - Design the Child Alarm FB

The child FB encapsulates one alarm point. Keep the interface tight; large VAR_INPUT sections are duplicated for every instance and inflate the parent FB's instance footprint.

Recommended interface for an alarm FB:

Section Name Type Purpose
VAR_INPUT RawBit Bool Physical or tag-level alarm bit
VAR_INPUT Enable Bool Global alarm enable
VAR_INPUT DebounceTime Time Configurable debounce per point
VAR_OUTPUT Active Bool Debounced active state
VAR_OUTPUT Latched Bool Set-Reset latched alarm
VAR_OUTPUT Timestamp DTL Last transition time
VAR LastState Bool Edge detection memory
VAR TonInst TON_TIME / IEC_TIMER Debounce timer (multi-instanced)
IEC_TIMER vs TON_TIME: On S7-1200 firmware V4.0+, prefer the system data type IEC_TIMER (or its 32-bit companion IEC_LTIMER) declared as a multi-instance. The legacy TON_TIME still works but generates a warning in the SCL compiler when using optimized block access.

Simplified SCL body for the child FB (FB1, "AlarmPoint"):

FUNCTION_BLOCK "AlarmPoint" { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 VAR_INPUT Enable : Bool; RawBit : Bool; DebounceTime : Time := T#500ms; END_VAR VAR_OUTPUT Active : Bool; Latched : Bool; Timestamp : DTL; END_VAR VAR TonInst : IEC_TIMER; LastState : Bool; TonElapsed : Bool; END_VAR VAR_TEMP tNow : DTL; END_VAR BEGIN // Debounce "TonInst".TON(IN := Enable AND RawBit, PT := DebounceTime, Q => TonElapsed); Active := TonElapsed; // Edge-triggered latching on rising edge of Active IF Active AND NOT LastState THEN Latched := TRUE; Timestamp := RD_SYS_T(); END_IF; LastState := Active; // Acknowledgement is exposed via Latched reset from outside END_FUNCTION_BLOCK

The block above is multi-instance capable because all timers and static variables are declared in the VAR section, not in a global DB. When placed inside a parent FB, TIA Portal automatically creates a separate copy of the static memory for each child instance.

4. Step 2 - Create the Parent FB With an Array of Child Instances

The parent FB is the wrapper that contains the array. Its single Instance DB will hold all the alarm points. The parent FB's VAR section declares the array of multi-instances.

Interface of the parent FB (FB2, "AlarmManager"):

FUNCTION_BLOCK "AlarmManager" { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 VAR_INPUT EnableAll : Bool := TRUE; AckAll : Bool; END_VAR VAR_OUTPUT AnyActive : Bool; ActiveCount : Int; END_VAR VAR Points : ARRAY[1..256] OF "AlarmPoint"; // 256 child instances END_VAR VAR_TEMP i : Int; END_VAR BEGIN AnyActive := FALSE; ActiveCount := 0; FOR i := 1 TO 256 DO // Drive the child instance from a globally-mapped bit source. // GpaAlarms is a global DB populated elsewhere (see Step 3). "Points"[i](Enable := EnableAll, RawBit := "GpaAlarms".Bits[i]); IF "Points"[i].Active THEN AnyActive := TRUE; ActiveCount := ActiveCount + 1; END_IF; IF AckAll THEN "Points"[i].Latched := FALSE; END_IF; END_FOR; END_FUNCTION_BLOCK
Memory warning: Each instance of AlarmPoint carries IEC_TIMER (16 bytes), DTL (8 bytes), three Bools, and padding. On an S7-1200 with optimized access, the per-instance footprint is roughly 32-40 bytes. 256 instances therefore consume approximately 8-10 KB of work memory for the parent Instance DB alone, in addition to the SCL program code. Always check the CPU's available work memory in the PLC properties > 'Resources' tab before scaling past ~400 instances. CPU 1214C ships with 50 KB work memory, CPU 1217C with 150 KB.

5. Step 3 - Populate the Source Bit Array

Two clean sources for the input bits feeding "Points"[i].RawBit are supported on S7-1200:

  1. Global DB with an ARRAY[1..N] OF Bool - the bit field is filled by other parts of the program (sensor scans, comms buffers, computed flags).
  2. Direct PLC tag slice - on the S7-1200 you can use the syntax %I0.0 through %I127.7 for the local digital inputs (CPU 1214C provides 14 / 10 digital inputs depending on model). For indirect addressing, copy the input image into a Bool array first.

Example global DB (DB3, "GpaAlarms"):

DATA_BLOCK "GpaAlarms" { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 STRUCT Bits : ARRAY[1..256] OF Bool; END_STRUCT; END_DATA_BLOCK

On the S7-1200 with optimized block access, the entire bit array is retentive by default; if you want volatile behavior, declare the DB as NON_RETAIN in its properties.

6. Step 4 - Use Named Constants for Human-Readable Indexing

Scattered throughout the program, calls like "Points"[217].Latched := FALSE; are not self-documenting. Replace magic numbers with constants in a dedicated constants block or in a global DB of constants.

Example constant declarations (DB4, "AlarmIdx"):

DATA_BLOCK "AlarmIdx" { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 STRUCT // Pump area PUMP1_HIGH_TEMP : Int := 1; PUMP1_LOW_FLOW : Int := 2; PUMP1_VIBRATION : Int := 3; PUMP2_HIGH_TEMP : Int := 4; // Tank area TANK_A_HIGH_LEVEL : Int := 5; TANK_A_LOW_LEVEL : Int := 6; TANK_B_HIGH_LEVEL : Int := 7; // ... up to 256 END_STRUCT; END_DATA_BLOCK

Now elsewhere in the program:

"MainInstance".AlarmMngr."Points"["AlarmIdx".PUMP1_HIGH_TEMP].Latched := FALSE;

This pattern is recommended in the Siemens S7-1200 Program Design guideline (entry ID 81318674) because it allows a single source of truth for tag-to-instance mapping and survives re-ordering of the array.

7. Step 5 - Generate the Bit Mapping in Bulk (Excel-Driven SCL)

For the original use case ("hundreds of bits"), the most productive workflow is to build the SCL source of the global mapping DB in Excel and paste it into the TIA Portal SCL editor. The technique:

  1. Export the I/O list to Excel with columns: TagName, SourceAddress (e.g. %I0.3 or DB20.DBX4.2), Index.
  2. Use a CONCATENATE formula in Excel to produce a row per index:
    ="Bits["&C2&"] := "&D2&;"
  3. Paste the 200+ generated lines into a startup SCL block or an initialization function called once on cold restart.

Example generated initialization block (FC1, "InitAlarmMap"):

FUNCTION "InitAlarmMap" : Void { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 BEGIN "GpaAlarms".Bits[1] := "IO_In".Pump1HighTemp; "GpaAlarms".Bits[2] := "IO_In".Pump1LowFlow; "GpaAlarms".Bits[3] := "IO_In".Pump1Vibration; "GpaAlarms".Bits[4] := "IO_In".Pump2HighTemp; -- ... 200 more lines generated by Excel ... "GpaAlarms".Bits[256] := "IO_In".FieldSpare256; END_FUNCTION

Call InitAlarmMap from OB100 (warm restart) or from the first scan flag in OB1. This approach scales to thousands of points without writing them by hand, and the symbolic HMI tags remain readable.

8. Step 6 - HMI and Diagnostics Visibility

With the array in place, the HMI can be configured with a multi-element tag list. In TIA Portal HMI configuration:

  1. Select the parent FB's instance DB and click the HMI tag icon for Points.
  2. Set Access mode to "Symbolic access to a complete array".
  3. On the HMI side, use a "multiplex tag" or array polling - WinCC Professional and Comfort Panel both support cyclic polling of Array[i].

For diagnostic detail, expose the index that just triggered. A simple approach is to add an FirstActiveIndex : Int static to the parent FB and update it inside the FOR loop:

IF "Points"[i].Active AND NOT AnyActive THEN FirstActiveIndex := i; END_IF;

The HMI alarm view can then display both the symbolic name (looked up in AlarmIdx) and the raw index.

9. Multi-Instance Memory Math and Sizing

Use the following formula to estimate the per-instance footprint:

Footprint (bytes) = Σ (size of each VAR declared in child FB) + alignment padding

For the AlarmPoint design above on an S7-1200 with optimized access:

Variable Type Bytes
TonInst IEC_TIMER (struct) 16
Timestamp DTL 8
LastState Bool (1 byte slot) 1
Active, Latched Bool 2
Subtotal 27
Alignment padding (4-byte boundary) ~5
Per-instance total ~32

For N = 256 instances: 32 × 256 = 8192 bytes ≈ 8 KB. The parent FB itself (FB2 with the array header) adds ~256 bytes for the array descriptor, plus the static variables AnyActive, ActiveCount, FirstActiveIndex. Round to 9 KB for the Instance DB and 8-12 KB for the compiled SCL code including the FOR loop unrolled. Confirm by downloading the project to the CPU and reading the 'Resources' tab in TIA Portal > Online > Diagnostics.

Limits on S7-1200: The maximum ARRAY index range in TIA Portal is 32767 for static arrays. The S7-1200 supports arrays declared up to 64 KB in the Instance DB. For the practical limit, the bottleneck is always the CPU's work memory and the cycle time, not the language syntax.

10. Cycle Time Impact

A 256-iteration FOR loop on a S7-1200 1214C at 100 MHz bit-execution time (firmware V4.4) takes approximately 0.6-1.2 ms. Worst case at 1.5 ms is well within the typical 10-50 ms OB1 cycle budget for a machine controller. If the FOR loop must run faster, break it into smaller batches triggered by a time slice counter, or distribute the work across OB35 cyclic interrupts at 100 ms intervals.

11. Comparison With Alternatives

Method Scalability HMI Access Library Use CPU Memory Cycle Time (256 pts)
Multi-instance array (recommended) High Per element via array Yes - FB is library-capable ~9 KB ~1 ms
Single-instance DBs (legacy) Low (one DB per point) Direct, one tag per DB Yes ~16 KB (DB overhead) ~2 ms (call overhead)
POU calls in STL source from Excel Medium Indirect Awkward ~8 KB ~1.5 ms
UDT-based array in global DB High Per element, clean Yes, via UDT ~9 KB ~1 ms
PLCopen-style function block library Medium (for standardized I/O) External tool Best (vendor-neutral) Same Same

For a vendor-neutral approach that scales across PLC brands, the PLCopen function block standard defines a reusable interface. Reference material on the standard is available at the PLCopen function blocks overview. On third-party platforms, similar array patterns are documented for Beckhoff TwinCAT 3 (see Beckhoff Information System on extending function blocks) and Eaton easyE4 (see the Eaton function blocks white paper), confirming the pattern is industry-wide rather than Siemens-specific.

12. Verification and Commissioning

After downloading, perform the following checks in order:

  1. Compile clean. In TIA Portal, 'Compile > Software (rebuild all)' should report zero errors. SCL warnings about unreachable code or unused variables can be ignored.
  2. Online > Download to device. Confirm the Instance DB is created with the expected size. Watch the 'Download to device' dialog for any memory warnings.
  3. Monitor the array. Open the parent FB's Instance DB in 'Monitor/Modify' mode. With a forced RawBit on index 1, verify that Points[1].Active rises after the debounce time elapses.
  4. Check cycle time. In 'Online > Diagnostics > Cycle time', confirm the OB1 cycle time has not increased beyond budget. The 'Maximum cycle time since last reset' should remain well below any configured OB80 time-error threshold.
  5. HMI test. On a real panel or in the RT simulation, display the active count and a few indexed points. Toggle the corresponding bit in the input image and confirm the HMI updates within one PLC scan.
  6. Edge cases. Force AckAll = TRUE and confirm all Latched outputs clear. Force EnableAll = FALSE and confirm no new alarms latch. Force the array index out of range (e.g. write to index 257) and confirm the SCL does not enter a fault state - the S7-1200 with optimized access raises a range-violation diagnostic for Indexed access, which surfaces in the diagnostic buffer.
Diagnostic buffer entry example: If a programming error forces an out-of-range index, the S7-1200 writes diagnostic event ID 0x2522 ("Range length violation when reading") or 0x2521 ("Range length violation when writing"). Cross-reference in the STEP 7 (TIA Portal) documentation for the full list of S7-1200 diagnostic events.

13. Library Distribution and Versioning

Once the pattern is stable, distribute the child FB and parent FB as a master-copy library. TIA Portal supports type-versioning for FBs; increment the version on every interface change. When the child FB interface is changed, TIA Portal automatically updates every instance of the parent FB and its Instance DB, but only if the change is type-compatible (added optional VAR_INPUT, added new VAR). Breaking changes (renaming, reordering, removing variables) require a manual update at the call site.

For cross-project use, the Siemens S7-1200 Program Design guideline (entry ID 81318674, accessible from the Siemens Industry Online Support portal) is the canonical reference for naming conventions, version handling, and instance-DB layout.

14. Frequently Asked Questions

How many FB instances can I put in a multi-instance array on an S7-1200?

The TIA Portal SCL compiler accepts static ARRAY sizes up to 32 767 elements, but the practical limit on the S7-1200 is set by work memory. Each instance of the example AlarmPoint FB consumes roughly 32 bytes; on a CPU 1214C (50 KB work memory) you can fit approximately 1 000 instances before the work memory is exhausted, and well below the number the SCL compiler would accept. The S7-1500 with its larger memory does not encounter this limit until you reach several thousand instances.

Can I use the same array index pattern with optimized block access on the S7-1200?

Yes. Optimized block access is fully supported for ARRAYs of multi-instance FBs on S7-1200 firmware V4.0 and newer, paired with TIA Portal V13 SP1 or later. Optimized access gives you retentive configuration at the DB level and symbolic-only access; the trade-off is that the absolute address is no longer visible to HMI, so you must reference the symbol. If your HMI tool only supports absolute addressing, fall back to standard block access and accept the manual address offsets.

What is the difference between a multi-instance FB and a UDT-based array of FBs?

A multi-instance FB is declared in the VAR section of another FB, and its memory lives inside the parent FB's Instance DB. A UDT-based array is declared in a global DB typed as ARRAY OF UDT, and the UDT itself contains the FB instance. The two are functionally equivalent in many use cases, but the multi-instance approach is more idiomatic for IEC 61131-3 and keeps everything inside a single Instance DB. The UDT-array approach is preferred when you need cross-block access to the same data without going through a parent FB interface.

How do I generate hundreds of FB calls from an Excel list?

Build the SCL source of an initialization FC (or a piece of the global mapping DB) using Excel CONCATENATE formulas. Each row becomes one assignment of the form "GpaAlarms".Bits[i] := "IO_In".<symbolicTag>;. Paste the generated lines into the SCL editor. This is faster and less error-prone than typing each call by hand, and the symbolic names in the HMI tag list remain unchanged. Keep the generated block in version control so re-exports are reproducible.

Why does the S7-1200 raise diagnostic event 0x2521 / 0x2522 in some configurations?

Events 0x2521 (range length violation when writing) and 0x2522 (range length violation when reading) are raised when an indexed access goes outside the declared array bounds. With optimized access, the runtime check is automatic. To clear the fault, correct the index expression, restart the CPU, or in OB100 initialize the index to a known valid value. The error is recoverable once the source of the bad index is fixed.

Back to blog