Variable Length Arrays in TIA Portal S7-1500: Array[*] Setup

David Krause15 min read
SiemensTechnical ReferenceTIA Portal
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

Variable Length Arrays in TIA Portal S7-1500: Array[*] Setup

Siemens S7-1500 controllers running TIA Portal do not support arrays whose length changes at runtime. The PLC must know the lower and upper array limits at compile time so that the symbolic compiler can allocate a fixed memory block in the process image, work memory, or load memory. This restriction often surprises engineers who come from high-level languages (C#, Python, MATLAB/Simulink) where dynamic containers are first-class citizens. Despite the constraint, the TIA Portal environment provides three working patterns that cover virtually every use case for "variable length" data:

  1. Passing arrays of any compile-time-known size into a function block (FB) using the Array[*] type with variable array limits on FB parameters.
  2. Re-sizing arrays at compile time by changing a constant and re-compiling the project — the same FB then operates on a different maximum length.
  3. Implementing a "ring buffer" or "indexed window" pattern inside the FB that uses a fixed-size static array plus a runtime LENGTH variable to behave as if it were dynamic.

This reference documents all three patterns in depth, with the official Siemens syntax, code samples, and a verification procedure. It targets engineers using TIA Portal V15.1 or later on S7-1500 / S7-1200 CPUs.

Hard constraint. No Siemens PLC (S7-300/400/1200/1500, ET 200SP, ET 200pro) supports runtime re-allocation of array memory. Attempting to use a VARIANT-based pseudo-array is possible but loses type safety and is not addressed here. The patterns below are the canonical Siemens-recommended approaches.

1. Variable-Length Array Constraints in S7-1500

The S7-1500 system software (FW ≥ V1.0) requires every ARRAY declaration to carry literal or constant lower/upper bounds. The symbolic compiler refuses any declaration where the bound is a non-constant tag. This applies uniformly to VAR, VAR_TEMP, VAR_IN_OUT, VAR_INPUT, VAR_OUTPUT, VAR_STAT, VAR_GLOBAL, and VAR_IN_OUT sections of FBs, FCs, and DBs.

Why the constraint exists:

  • Determinism. The S7-1500 cycle must be deterministic. Re-allocating memory during OB1 execution would fragment the work memory and break the jitter guarantees for PROFINET IRT, motion, and safety OBs.
  • Code generation. The TIA Portal compiler emits optimised MC7/ML code that addresses array elements via base + offset. The base register width is fixed at compile time.
  • Symbolic loading. The data block layout (and the STRUCT/ARRAY offset map) is generated once and downloaded. Changing it at runtime would require re-loading the entire DB — not supported online.

From the official Siemens TIA Portal V20 documentation — Basic information on ARRAY:

"You can use an ARRAY[*] to program blocks that can process an ARRAY with flexible length. To do this, you declare variable ARRAY limits for the parameters of a block."

The key phrase is variable ARRAY limits for the parameters of a block. This applies only to the FB/FC interface (formal parameters), not to internal static variables.

2. Compile-Time vs Runtime Array Sizing

Two distinct "sizes" must be considered when designing a reusable block:

Size type When fixed How to change Scope
Compile-time maximum When project is compiled Edit a constant, save, recompile, download Global — affects every call site
Runtime logical size At each OB1 cycle Write a UINT tag, use it as the loop boundary Per instance DB or per call

The S7-1500 always allocates memory for the compile-time maximum. Runtime logical size is implemented in user code: the engineer writes a FOR i := 1 TO #iUsedLength DO loop instead of FOR i := 1 TO SIZEOF(arr) DO. The unused tail of the array simply sits in the instance DB and is never touched.

3. Using Array[*] for Flexible FB Parameters

The Array[*] notation (read as "array of any length") is the standard Siemens mechanism for accepting an array whose physical size is decided at the call site, not at the FB's own compile time. It is valid only on FB/FC parameters in the VAR_INPUT (read-only) or VAR_IN_OUT (read-write) sections of the interface.

3.1 Syntax declarations

The following SCL snippet declares an FB with an InOut parameter that accepts any length of Bool array:

FUNCTION_BLOCK "FB_FlexBoolArray"
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : EngRef
FAMILY : ArrayUtil
VERSION : 1.0
VAR_IN_OUT
    // Accept ANY length Bool array passed by reference (no copy).
    // Lower bound defaults to 0; upper bound is the caller's choice.
    i_bArray : Array[*] of Bool;
END_VAR
VAR
    s_iLen  : UDInt;   // runtime length cache
    s_iLow  : DInt;   // lower bound cache
    s_iHigh : DInt;   // upper bound cache
END_VAR
BEGIN
    // Query the actual bounds delivered by the caller.
    #s_iLow  := LOWER_BOUND(i_bArray, 1);
    #s_iHigh := UPPER_BOUND(i_bArray, 1);
    #s_iLen  := #s_iHigh - #s_iLow + 1;

    // Example: count TRUE bits using the runtime length.
    FOR #i := #s_iLow TO #s_iHigh DO
        IF #i_bArray[#i] THEN
            #s_iTrueCount := #s_iTrueCount + 1;
        END_IF;
    END_FOR;
END_FUNCTION_BLOCK

3.2 Built-in functions that operate on Array[*]

Function Return type Purpose with Array[*]
LOWER_BOUND(arr, dim) DINT Returns the actual lower index of the array passed at runtime.
UPPER_BOUND(arr, dim) DINT Returns the actual upper index of the array passed at runtime.
SIZEOF(arr) UDINT Total byte length of the array (not the count of elements).
COUNT_OF_ELEMENTS(arr) UDINT Available on S7-1500 FW ≥ V2.0 / TIA ≥ V15; returns element count.
Edge case. If the calling block passes a BOOL scalar instead of an array, the LOWER_BOUND / UPPER_BOUND calls produce an undefined value. Add a type check with IS_ARRAY (TIA V16+) or use the older TypeOf() = TypeOf(...) pattern.

3.3 Calling the FB

// Caller — instance DB "dbFlex" of FB_FlexBoolArray
// Each call site may pass a different-length array.
"dbFlex"(i_bArray := "dbBig".arrBits_2000);
"dbFlex"(i_bArray := "dbSmall".arrBits_10);

Both calls are accepted by the compiler because arrBits_2000 and arrBits_10 are Array[0..1999] of Bool and Array[0..9] of Bool respectively — their compile-time lengths differ, but both satisfy Array[*] of Bool.

4. Declaring Array Size with Constants

When the size of an array used in VAR, VAR_TEMP, VAR_STAT, or a global DB must change, the cleanest method is to bind the array limits to a constant in a global or local constants block.

4.1 Global constant

Create a global PLC data type or a constants DB:

// In a global constants block (PLC tags > constants)
CONST
    MAX_BITS : UDInt := 2000;   // <-- edit & recompile to resize
END_CONST

Reference it from any FB / DB / FC array declaration:

VAR_GLOBAL CONST
    MAX_BITS : UDInt := 2000;
END_VAR

// Inside a data block
VAR
    arrBits : Array[0.."MAX_BITS"] of Bool;  // expands to Array[0..2000] of Bool
END_VAR

Editing MAX_BITS and recompiling grows or shrinks the entire data block footprint. Online re-allocation of the existing instance is not possible without a download-in-run-mode (RUN) stop, or for S7-1500 with program download to a new active project.

4.2 Local constant inside an FB

Local constants are the FB-scope equivalent and produce the same effect. They are visible in the FB's "Constants" tab and are prefixed with # in SCL.

FUNCTION_BLOCK "FB_BoolArray"
VAR CONST
    MAX_BITS : UDInt := 2000;
END_VAR
VAR
    arrBits : Array[0..#MAX_BITS] of Bool;
END_VAR
BEGIN
    // body
END_FUNCTION_BLOCK

5. FB Interface Design with InOut Parameters

For an FB that operates on arrays of differing length, the standard interface looks as follows:

Caller DB arrBits_10 : Array[0..9] of Bool arrBits_2000 : Array[0..1999] of Bool iUsedLen_10 : UDInt := 10 iUsedLen_2000 : UDInt := 2000 FB_FlexBoolArray (instance DB: idbFlex) VAR_IN_OUT i_bArray : Array[*] of Bool VAR_INPUT i_UsedLen : UDInt // runtime length VAR_OUTPUT q_TrueCount : UDInt VAR CONST MAX_BITS : UDInt := 2000 VAR s_arrScratch : Array[0..#MAX_BITS] of DWord pass by ref UDInt

Three rules of thumb for the interface design:

  1. Always pass arrays by VAR_IN_OUT (reference, not copy). This avoids the 64 KB input copy limit and preserves performance.
  2. Always expose a runtime UsedLen input so the FB can iterate only over meaningful elements without reading garbage at the tail.
  3. Keep the maximum length bound in a VAR CONST on the FB so all call sites scale together when the project requirement changes.

6. Static Arrays Inside an FB: Workarounds

Static (VAR, VAR_STAT) arrays inside an FB cannot use Array[*]. The TIA Portal compiler rejects VAR s_arr : Array[*] of DWord; in the static section. Three workarounds cover the common cases.

6.1 Over-provision with a constant

Declare a static array at the project maximum, then use a runtime UDInt to bound the algorithm.

FUNCTION_BLOCK "FB_BoolOps"
VAR CONST
    MAX_BITS : UDInt := 2000;
END_VAR
VAR
    s_arrDWord  : Array[0..#MAX_BITS] of DWord;   // 2001 elements
    s_iUsed     : UDInt;
END_VAR
BEGIN
    FOR #i := 0 TO #s_iUsed - 1 DO
        // operate on s_arrDWord[#i] safely
    END_FOR;
END_FUNCTION_BLOCK

Memory cost: 2001 × 4 bytes = 8004 bytes of instance DB, regardless of the actual number of bits the caller uses.

6.2 Sizing the static array from the InOut's runtime length

You can read the dynamic length with UPPER_BOUND() on the InOut parameter and use it to size an internal loop (not a declaration). The static array itself remains fixed at the maximum.

#s_iMax  := UPPER_BOUND(i_bArray, 1);
#s_iMin  := LOWER_BOUND(i_bArray, 1);
#s_iLen  := #s_iMax - #s_iMin + 1;

// Reuse the same scratch for any caller length up to MAX_BITS
FOR #i := 0 TO #s_iLen - 1 DO
    #s_arrDWord[#i] := BOOL_TO_DWORD(i_bArray[#s_iMin + #i]);
END_FOR;

6.3 Multiple-instance FBs scaled per caller

Instead of one FB with a giant scratch buffer, instantiate the FB once per actual data-set length. Each instance DB holds a static array sized to the constant of that instance. The constants are still compile-time, but you can use a different constant per instance by using a derived FB type with overridden constants — supported in TIA Portal V16+ via the "Multi-instance" pattern and the "Derived data type" mechanism.

Workaround Memory profile Code complexity Best for
Over-provision with constant Worst case always allocated Low Single FB, small footprint
Loop bound to UPPER_BOUND() Same as above, but algorithm scales Low One FB, many array sizes
Multiple-instance FBs Right-sized per instance Medium Few distinct sizes (e.g. 10 / 100 / 2000)
Use ARRAY DBs of fixed length Right-sized per DB Medium Large batch data with homogeneous length

7. Step-by-Step Implementation Guide

The following procedure builds a working "FB_FlexBoolArray" that counts the TRUE bits in a caller-supplied array of any length up to 2000.

7.1 Prerequisites

  • TIA Portal V15.1 or later (V16+ recommended for the COUNT_OF_ELEMENTS helper).
  • S7-1500 CPU with firmware ≥ V2.0 (any current S7-1500 standard ships with this).
  • A program block container for new FBs and instance DBs.

7.2 Procedure

  1. Create the FB. In the project tree, expand Program blocks > Add new block > Function Block. Name it FB_FlexBoolArray, language SCL, set number to a free slot.
  2. Open the FB interface and add the following elements in order:
    VAR_IN_OUT
        i_bArray : Array[*] of Bool;   // caller-supplied
    END_VAR
    VAR_INPUT
        i_UsedLen : UDInt;             // runtime length cap
    END_VAR
    VAR_OUTPUT
        q_TrueCount : UDInt;
        q_Error     : Bool;
        q_Status    : Word;            // 16#0000 OK, 16#8001 i_UsedLen > MAX
    END_VAR
    VAR CONST
        MAX_BITS : UDInt := 2000;
    END_VAR
    VAR
        s_iMin : DInt;
        s_iMax : DInt;
        s_iLen : UDInt;
    END_VAR
  3. Write the body:
    BEGIN
        #q_Error     := FALSE;
        #q_Status    := 16#0000;
        #q_TrueCount := 0;
    
        #s_iMin := LOWER_BOUND(i_bArray, 1);
        #s_iMax := UPPER_BOUND(i_bArray, 1);
        #s_iLen := UDINT_TO_DINT(#s_iMax) - #s_iMin + 1;
    
        IF (#i_UsedLen = 0) OR (#i_UsedLen > #MAX_BITS) THEN
            #q_Error  := TRUE;
            #q_Status := 16#8001;
            RETURN;
        END_IF;
    
        FOR #i := 0 TO #i_UsedLen - 1 DO
            IF i_bArray[#s_iMin + #i] THEN
                #q_TrueCount := #q_TrueCount + 1;
            END_IF;
        END_FOR;
    END_FUNCTION_BLOCK
  4. Compile the FB. Resolve any syntax errors flagged in the info window.
  5. Create the instance DB by dragging the FB into Program blocks (TIA creates a single-instance DB automatically when you call the FB for the first time in OB1 or another block).
  6. Call the FB from OB1:
    // Calls with two different lengths
    "idbFlex10"(i_bArray := "dbSmall".arrBits,
                i_UsedLen := 10,
                q_TrueCount => "stat".tc10,
                q_Error     => "stat".err10,
                q_Status    => "stat".st10);
    
    "idbFlex2000"(i_bArray := "dbBig".arrBits,
                  i_UsedLen := 2000,
                  q_TrueCount => "stat".tc2000,
                  q_Error     => "stat".err2000,
                  q_Status    => "stat".st2000);
  7. Compile & download the entire program to the CPU.

8. Memory and Performance Considerations

The S7-1500 stores Bool arrays efficiently in optimised DBs: one bit per element, packed to whole bytes. For an array of 2000 Booleans the effective instance-DB size is ceil(2000 / 8) = 250 bytes. Array of DWord of the same logical size costs 2000 × 4 = 8000 bytes.

Performance rules:

  • Use Array[*] of Bool as the InOut type — it minimises copy cost on FB entry and supports the lowest memory overhead.
  • Avoid accessing arr[#i] in tight loops when the index expression is non-trivial. Capture the result of UPPER_BOUND() into a DINT local before the loop.
  • For arrays > 32 KB, prefer VAR_IN_OUT with { S7_Optimized_Access := 'TRUE' }; non-optimised (absolute) access is slower on the 1500.
  • The SIZEOF() intrinsic on an Array[*] parameter returns the size in bytes of the actual instance; use this for memory diagnostics but not in hot loops.
Watch the work-memory limit. Each instance DB lives in the work memory. An S7-1511 has 150 KB of work memory for data; a single 2000-element DWord array already consumes 5 % of that. For larger data sets consider DB_GENERATED arrays in load memory plus READ_DBL / WRITE_DBL for streaming.

9. TIA Portal Version Compatibility

Feature Minimum TIA Portal Minimum CPU FW Notes
Array[*] with variable limits on FB parameters V14 SP1 S7-1500 V1.8 / S7-1200 V4.2 Initial release of the flexible-length interface
COUNT_OF_ELEMENTS() intrinsic V15 S7-1500 V2.0 Returns element count of an array
Multi-instance FBs with overridden constants V16 S7-1500 V2.6 Derived FB types
IS_ARRAY type check V16 S7-1500 V2.6 Use for runtime type validation
Array slices / Array[lo..hi] range V17 S7-1500 V2.9 Slice operations on Array[*]
Multi-dimensional Array[*] V18 S7-1500 V3.0 Up to 6 dimensions with mixed length

The TIA V15.1 environment cited in the field report supports Array[*] on FB parameters and constants for array sizing, but not the V16+ type-check or array-slice features.

10. Troubleshooting Matrix

Symptom Likely cause Diagnostic step Fix
Compiler error: "Array limits must be constants" Static array uses a runtime tag as a bound Open the FB and inspect VAR / VAR_STAT array declarations Replace the bound with a VAR CONST literal or constant
Compiler error: "Array[*] is not allowed in VAR_STAT" Attempted to use flexible limits on a static array Confirm the array is in VAR_IN_OUT not VAR Move the array to the InOut interface; keep the static array fixed-size and bound by MAX_BITS
Online error: "Area length error when reading" FOR loop exceeds the actual array length Cross-check the loop bound with the caller's UsedLen input Use i_UsedLen as the loop boundary, not SIZEOF()
Wrong element count after resize Old instance DB still on the CPU with the previous footprint Compare online vs offline DB offsets Delete the instance DB on the CPU, recompile and download
Performance: OB1 cycle time grew by 30 ms Array passed by value (copy) due to VAR_INPUT Inspect the FB interface Change the array parameter from VAR_INPUT to VAR_IN_OUT
Unexpected element values in unused tail Caller wrote random data beyond UsedLen Use a watch table to inspect the array tail Always initialise the entire array in the caller DB or initialise it inside the FB with memset-equivalent fill
Compile succeeds, online red light on the FB Firmware too old for the syntax used Compare CPU FW with the compatibility table Upgrade the CPU firmware or downgrade the TIA syntax

11. Verification and Commissioning

After implementing the FB, perform the following sequence of checks before signing off the code:

  1. Compile with strict checks — TIA > Project > Compile > Software (rebuild all). Resolve every warning in the info pane; warnings about implicit type conversions often mask length bugs.
  2. Static analysis — In the FB, hover the cursor over the Array[*] parameter; TIA should show "compatible with all array types". A yellow exclamation mark means the parameter is incorrectly scoped.
  3. PLCSIM simulation — Load the project into S7-PLCSIM or PLCSIM Advanced. Create a watch table that monitors q_TrueCount for both 10-element and 2000-element test arrays.
  4. Edge-case unit tests — Write a quick SCL test FB that exercises:
    • Lower bound = 0, upper bound = 9 (default array)
    • Lower bound = 1, upper bound = 10 (indexed array)
    • Lower bound = -5, upper bound = 5 (negative index range, S7-1500 supports this)
    • UsedLen = 0 (must return error)
    • UsedLen = 2001 (must return error)
  5. Online monitoring — Open the FB in the online view; TIA shows the actual array length under "Array limits". Verify it matches the caller's DB.
  6. Cycle-time check — In the CPU diagnostics > Cycle time, capture the worst-case OB1 time across one full buffer test. Acceptable: cycle time increase < 5 ms for a 2000-element scan.

If all six steps pass, the FB is ready for production. Re-run the same checks after every TIA Portal upgrade because syntax interpretation can subtly change between major versions.

12. Frequently Asked Questions

Can a Siemens S7-1500 change an array's size while the CPU is running?

No. The S7-1500 requires every array to have literal or constant bounds fixed at compile time. The TIA Portal compiler rejects any declaration whose bound is a runtime tag. Re-size requires editing the constant, recompiling, and downloading — the array memory is not reallocated online.

What is the difference between Array[*] and a normal Array of Bool on an FB parameter?

Array[*] of Bool on a VAR_IN_OUT parameter accepts any length at the call site — the FB operates on whatever array the caller hands it. A normal Array[0..200] of Bool parameter would only accept arrays of exactly that length. The runtime functions LOWER_BOUND() and UPPER_BOUND() return the actual indices of the supplied array.

Why does my FB reject Array[*] in the VAR_STAT section?

Variable-length arrays are only valid on FB/FC parameters in the VAR_INPUT or VAR_IN_OUT interface. The static section requires a fixed compile-time length. Use a VAR CONST for the bound and iterate the static array with a runtime length variable instead.

Does the Array[*] notation copy the array when the FB is called?

Only if the parameter is declared as VAR_INPUT. Declaring the parameter as VAR_IN_OUT passes it by reference (pointer to the caller's memory). For arrays of more than a few hundred elements always use VAR_IN_OUT to avoid the input-image copy and the 64 KB input copy limit.

Which TIA Portal version introduced Array[*]?

Array[*] with variable limits on FB/FC parameters was introduced in TIA Portal V14 SP1 and requires S7-1500 firmware V1.8 or S7-1200 firmware V4.2. Helper functions such as COUNT_OF_ELEMENTS arrived in TIA V15, and the IS_ARRAY runtime type check arrived in TIA V16. See the version table above for the full compatibility matrix.

How can I right-size a static array for callers that use 10 vs 2000 elements?

Declare the static array at the project maximum (e.g. 2000) and add a UDInt runtime length input. Use UPPER_BOUND() on the InOut to detect the caller's length, then iterate the static array with the smaller of the two values. The unused tail of the static array simply sits in the instance DB and is never read or written.

Back to blog