Problem Statement: FB Interface Explosion with PROFINET WORD Addresses
When a third-party PROFINET device is integrated into a SIMOTION project through its GSDML file, the device's process data is broken into discrete I/O items at the slot and sub-slot level. For devices that expose a wide register map of 16-bit process words, the GSDML creates dozens or hundreds of individual symbolic bindings in the SIMOTION I/O address list. Each symbolic name is typically surfaced as a separate input parameter on a Function Block (FB) that handles the device's communication protocol, producing a wide FB interface that is difficult to maintain, version, and route through HMI tag structures.
Consider a typical scenario. A third-party field device exposes slot 7 at PROFINET I/O address 5600-5601 and slot 8 at address 5602-5603. Each slot contributes a single WORD (16 bits). If the device actually has 32 such slots, the address list contains 32 separate WORD entries, and the FB that processes them has 32 scalar WORD inputs. Adding a single control or status word to the FB brings the interface to 33 inputs, which crosses the practical threshold for maintainable FB design.
The optimization is to consolidate the 32 WORDs into a single ARRAY OF WORD variable, then pass that array to the FB as a single IN parameter. This article details the exact mechanism for doing so on SIMOTION, including why the SIMOTION address list does not accept array syntax directly and how to bridge between the address list's scalar binding model and the array-based FB interface.
The pattern is applicable to any third-party PROFINET device that exposes homogeneous WORD sub-slots: smart sensors, I/O couplers, hydraulic controllers, RFID readers, and similar field equipment. The pattern is also applicable to PROFINET devices with mixed data types, but only on a per-type basis (one array per type).
PROFINET I/O Address Model: Slots, Sub-slots, and Process Words
PROFINET is the open Industrial Ethernet standard maintained by PROFIBUS & PROFINET International (PI). It uses the GSDML (Generic Station Description Markup Language), an XML schema, to describe the I/O structure of every device type. Each PROFINET device is modeled as a physical device (the head module) that contains one or more slots, and each slot may contain one or more sub-slots. Every sub-slot has an associated I/O descriptor that defines the data length, the data type (input, output, or diagnostic), and the slot-relative offset.
For a third-party PROFINET device integrated into a SIMOTION project:
- The PROFINET controller assigns an I/O base address range to the device during PROFINET IO controller configuration.
- Each sub-slot's I/O data is mapped to a contiguous range of process image addresses. Input sub-slots map to PIW (Process Input Word) addresses; output sub-slots map to PQW (Process Output Word) addresses.
- The GSDML file defines the slot index, the data length in bits or bytes, and the data type. A sub-slot with a 16-bit input is exposed as a single PIW.
In a typical 32-WORD example, the device's PROFINET I/O address range might be PIW 5600..5631 (32 words = 64 bytes). The SIMOTION I/O address list sees 32 separate PIW entries, even though the underlying PROFINET cyclic frame is one continuous 64-byte block.
Industrial Ethernet-based PROFINET expands PROFIBUS technology into high-speed data communication. Per the WAGO PROFINET overview, PROFINET supports both real-time (RT) and isochronous real-time (IRT) classes. The I/O addressing model described above applies identically to RT and IRT devices; IRT only adds a deterministic-scheduling layer on top.
SIMOTION Address List: Per-Address Symbolic Mapping
The SIMOTION address list (also called the I/O symbol table or symbol list, depending on the SIMOTION SCOUT version) is the canonical place where absolute PROFINET process image addresses are assigned symbolic names. The address list is structured as a flat table; each row binds a single address (or address range) to a symbolic name and a data type.
In the SIMOTION engineering environment:
- Open the device's PROFINET interface in the project navigator.
- The right-hand pane shows the I/O addresses and the GSDML-derived slot structure.
- For each PIW or PQW entry, assign a symbolic name such as
iDeviceSlot07,iDeviceSlot08, etc. - The address list exports these names as global symbols usable in ST, LAD, FBD, and MCC programs.
The critical constraint: the address list is scalar. Each row corresponds to a single address or a single sub-slot's data, not to an array. The address list does not support syntax such as piw 5600 array 2. Each WORD must be entered individually:
iSlot07 AT %IW5600 : WORD; // slot 7 input word
iSlot08 AT %IW5602 : WORD; // slot 8 input word
This is by design: SIMOTION's I/O address model mirrors the PROFINET sub-slot model one-to-one, which is the simplest mapping for cyclic I/O consistency checking. The cost is that high-channel-count devices produce long address lists.
For a programmer working with a 32-slot device, this means 32 separate entries in the address list. For a 64-slot device, 64 entries. The list is the canonical place to discover and maintain the bindings, but it is not the right place to consume them in a structured FB.
Why the SIMOTION Address List Does Not Accept Array Syntax
A natural optimization is to ask whether the address list can declare an array of WORDs covering the contiguous range. The short answer is no. The address list is a static binding table generated from the GSDML, and its schema permits only scalar declarations. Attempts to use range syntax such as piw 5600 array 2 are rejected by the engineering tool.
The deeper reason is consistency. PROFINET's slot model is not guaranteed to be contiguous. Slots can be re-ordered by GSDML configuration, sub-slots can be inserted between them, and the I/O base address can shift. Even when the GSDML produces a contiguous byte stream today, refactoring the device configuration tomorrow (for example, adding a diagnostic sub-slot) may break the contiguity. The scalar address list guarantees that the PROFINET controller always sees the correct data at the correct symbolic name, even when the user re-arranges the device's slot assignment.
This is also why the engineering response to the original question is correct: the address list must declare each PIW individually. The address list's scalar nature is a feature, not a limitation. The array-based optimization lives one layer above the address list, in the application code.
Declaring Array Variables to Consume Sequential PIW/PQW Ranges
Three patterns are in widespread use for consolidating a PROFINET WORD frame into an array variable on SIMOTION. Each has trade-offs in traceability, performance, and maintainability.
Pattern A: AT-overlay arrays with a base address. If the address list contains N contiguous PIWs starting at a known base, you can declare a global array and use AT bindings to map each element to its underlying address:
VAR_GLOBAL
aiDeviceInputs : ARRAY[1..32] OF WORD; // 32-word input frame
END_VAR
The first element carries the AT binding in the address list (aiDeviceInputs[1] AT %IW5600 : WORD), and the subsequent array elements occupy the following PIW addresses by virtue of the array's storage layout. SIMOTION's symbol resolution permits the array element to carry the AT binding, and the array's storage layout matches the contiguous PROFINET image. This pattern requires that the array elements do not cross an arbitrary gap; if any slot is non-WORD, the array must be split or padded.
Pattern B: Copy block from scalar address-list symbols. If the address list must remain strictly scalar (the most common case in production projects), declare a global ARRAY OF WORD and populate it in a cyclic task:
VAR_GLOBAL
aiDeviceInputs : ARRAY[1..32] OF WORD;
END_VAR
PROGRAM CopyDeviceInputs;
aiDeviceInputs[1] := iSlot01;
aiDeviceInputs[2] := iSlot02;
...
aiDeviceInputs[32] := iSlot32;
END_PROGRAM
This pattern is mechanical, but it scales linearly. For 32 channels it is one screen of code; for 256 channels it is verbose but reliable. The advantage is that the address list stays purely scalar and each binding is individually traceable in the watch table. The disadvantage is the maintenance cost: any slot reordering or GSDML update requires updating both the address list and the copy block.
Pattern C: Direct PIW indexing in the program. If you do not need symbolic names in the address list at all, you can address PIWs directly inside ST using the %IW prefix:
aiDeviceInputs[1] := %IW5600;
aiDeviceInputs[2] := %IW5602;
...
This is the most concise option, but it loses the symbolic-to-address traceability that the address list provides. It also makes slot reorderings silent: a GSDML update that moves slot 7 from offset 0 to offset 4 will not generate a compile error, only a runtime misbehavior. For development and debugging, Pattern C is convenient. For production projects, Pattern A or B is preferred.
Mapping GSDML Slots to PROFINET I/O Addresses
When you import a third-party device's GSDML into SIMOTION SCOUT (or the TIA Portal with SIMOTION support), the engineering tool assigns the device a base I/O address range. The exact base depends on the slot configuration of the PROFINET IO system. The GSDML file contains the slot descriptors that determine which addresses are used for which sub-slots.
To determine the slot-to-address mapping for a given device:
- Open the device's PROFINET interface in the device view.
- Inspect the slot table. Each slot row shows the slot index, the sub-slot (if any), the module name, and the assigned I/O address range.
- Cross-reference the slot index with the GSDML file's
ModuleListandSubmoduleListto identify the data type and length. - Note that the byte offset of a slot is cumulative: slot 7 at offset 0..1 and slot 8 at offset 2..3 means PIW 5600 and PIW 5602 if the device base is 5600.
The example from the field report resolves to the following table:
| Slot | Sub-slot | GSDML data type | Length | Byte offset | I/O address |
|---|---|---|---|---|---|
| 7 | 1 | Input WORD | 2 B | 0..1 | %IW5600 |
| 8 | 1 | Input WORD | 2 B | 2..3 | %IW5602 |
| 9 | 1 | Input WORD | 2 B | 4..5 | %IW5604 |
| 10 | 1 | Input WORD | 2 B | 6..7 | %IW5606 |
| ... | 1 | Input WORD | 2 B | ... | ... |
| 38 | 1 | Input WORD | 2 B | 62..63 | %IW5662 |
This pattern is typical: each module slot contributes one WORD to the input frame. A device with 32 such modules (slot 7 through slot 38) produces a 64-byte input frame spanning %IW5600..%IW5662. The address list contains 32 entries; the array consolidates them into one variable.
The slot-to-offset relationship is also illustrated by the following topology diagram. The PROFINET controller's IO image holds a contiguous block for the device; the GSDML sub-slot descriptors slice the block into named chunks; the address list names each chunk; the application code consumes the chunks through an array.
FB Interface Reduction with ARRAY OF WORD Parameters
The function block that handles communication control can then accept a single IN parameter of type ARRAY[1..N] OF WORD, dramatically reducing the FB interface from N scalar inputs to one array input.
FUNCTION_BLOCK DeviceHandler
VAR_INPUT
aiInputs : ARRAY[1..32] OF WORD; // consolidated input frame
iControl : INT; // control word from outside
END_VAR
VAR_OUTPUT
qStatus : BOOL;
qiErrorCode : DINT;
END_VAR
VAR
iState : INT := 0;
END_VAR
// ... handle the device using aiInputs[1..32] ...
END_FUNCTION_BLOCK
The FB now has 2 inputs instead of 33. Inside the FB, individual channels are accessed by array index, which keeps the body of the FB structured and refactor-friendly. The FB's VAR block can hold derived state, timers, and the slot-specific handling logic without polluting the input signature.
For libraries that need to scale across multiple device sizes, declare the FB with a generic array type or use a pointer-based variant for maximum flexibility. The most common pragmatic choice is to fix the array length in the FB declaration and create one FB per device class. For example:
-
FB_Device16for 16-WORD devices (64 bytes I/O). -
FB_Device32for 32-WORD devices (128 bytes I/O). -
FB_Device64for 64-WORD devices (256 bytes I/O).
If the FB must be size-agnostic, declare a variant or use a generic ANY-style pointer. SIMOTION's ST supports ANY_TYPE and parameter blocks for this purpose, but the syntax is more verbose than a fixed-length array and is rarely needed in practice.
Structured Text Examples for Array-Based PROFINET I/O
Example 1: Reading a 16-bit status word from a third-party device.
PROGRAM ReadDeviceStatus;
VAR
stStatus : STRUCT
bReady : BOOL;
bFault : BOOL;
bWarning : BOOL;
iError : INT;
END_STRUCT;
END_VAR
// aiInputs[1] is the consolidated status word at %IW5600
stStatus.bReady := aiDeviceInputs[1].%X0;
stStatus.bFault := aiDeviceInputs[1].%X1;
stStatus.bWarning := aiDeviceInputs[1].%X2;
stStatus.iError := WORD_TO_INT(aiDeviceInputs[1] AND 16#FFF0) / 16;
END_PROGRAM
Example 2: Writing a 16-bit control word to a third-party device.
PROGRAM WriteDeviceControl;
VAR
wControl : WORD;
END_VAR
wControl.%X0 := TRUE; // enable
wControl.%X1 := FALSE; // reset
wControl.%X2 := bAutoMode;
aoDeviceOutputs[1] := wControl; // writes to %QW5600
END_PROGRAM
Example 3: Cycling through all 32 input channels for diagnostics.
FUNCTION ScanDeviceChannels : INT;
VAR_INPUT
aiFrame : ARRAY[1..32] OF WORD;
END_VAR
VAR
i : INT;
iBad : INT := 0;
END_VAR
FOR i := 1 TO 32 DO
IF aiFrame[i] = 16#FFFF THEN
iBad := iBad + 1;
END_IF;
END_FOR;
ScanDeviceChannels := iBad;
END_FUNCTION
Example 4: Packing two consecutive WORDs into a DWORD (little-endian).
FUNCTION ReadDeviceDword : DWORD;
VAR_INPUT
aiFrame : ARRAY[1..32] OF WORD;
iIndex : INT; // 1..31 (must have a following element)
END_VAR
VAR
dwResult : DWORD;
END_VAR
dwResult.%B0 := aiFrame[iIndex].%B0;
dwResult.%B1 := aiFrame[iIndex].%B1;
dwResult.%B2 := aiFrame[iIndex + 1].%B0;
dwResult.%B3 := aiFrame[iIndex + 1].%B1;
ReadDeviceDword := dwResult;
END_FUNCTION
These examples demonstrate the readability win: the array-based code is one-third the length of the equivalent scalar code and the indices make the data structure self-documenting. The %X, %B, and bit-slice notations are standard SIMOTION ST access patterns for named bits and bytes within a WORD or DWORD.
State Machine Implementation for Device Handling
For devices that require sequenced communication (e.g., a handshake before reading channels), the array-based pattern is particularly valuable when combined with a state machine in the FB. A typical 4-state machine for a third-party device is shown below.
The ST implementation references aiInputs[1..32] directly without touching the address list inside the FB body:
CASE iState OF
0: // Init
aoDeviceOutputs[1].%X0 := TRUE; // request init
iState := 10;
10: // Wait ready
IF aiInputs[1].%X0 THEN // ready bit from device
iState := 20;
END_IF;
20: // Read frame
IF aiInputs[1].%X1 THEN // data valid bit
// copy aiInputs[1..32] into internal scratch
FOR i := 1 TO 32 DO
asInternalFrame[i] := aiInputs[i];
END_FOR;
iState := 30;
END_IF;
30: // Process
qiErrorCode := ScanDeviceChannels(aiInputs);
IF qiErrorCode = 0 THEN
qStatus := TRUE;
iState := 20;
ELSE
iState := 99;
END_IF;
99: // Error
aoDeviceOutputs[1].%X1 := TRUE; // request fault reset
iState := 0;
END_CASE;
The state machine illustrates the consolidation win: the FB has a single array input but can address 32 channels of data and 2 channels of control status with simple index access. The HMI sees one array tag, not 32 individual tags.
Commissioning and Online Verification
After wiring the array consolidation into the project, verify the data path end-to-end:
- Compile and download. Compile the SIMOTION project and download to the controller. Watch the compile output for unresolved symbol or AT-binding errors. A common error is "address already used", which indicates that two address-list entries claim the same PIW.
- Online I/O view. In SCOUT or TIA Portal, open the device's I/O view. Each PIW should show the live process value. Cross-check at least three channels against the device's HMI or web server.
-
Watch the array variable. Open the array variable in the watch table. Confirm that
aiDeviceInputs[1]matches the value at %IW5600,aiDeviceInputs[2]matches %IW5602, and so on. -
Force a single channel. Force
aiDeviceInputs[5]to a known value via the watch table and verify that the HMI sees the forced value. This proves the array-to-address-list binding is correct. - PROFINET diagnostics. Open the device's PROFINET diagnostics. Confirm that no sub-slot is reporting "substitute value" or "fault". A substitute-value indication means the array element is being read correctly but the underlying slot has lost its physical connection.
- Cyclic task timing. If the array is being copied in a MotionTask or BackgroundTask, monitor the task's cycle time. A 32-element copy block adds microseconds, well within any practical cyclic budget.
- PROFINET cycle diagnostics. Open the PROFINET IO controller's cycle diagnostics and verify that the device's cycle time is stable and that no dropped frames appear over a 10-minute observation window.
If any array element disagrees with its underlying PIW, the most common cause is an off-by-one in the array index vs. the address-list symbol. Recheck the order of address-list entries and the array subscript. A second common cause is endianness: a 32-bit REAL arriving as two consecutive PIWs must be reconstructed with the correct byte order. SIMOTION's DWORD from two WORDs follows little-endian by default, but double-check with the device vendor's documentation.
Common Pitfalls and Edge Cases
| Pitfall | Symptom | Resolution |
|---|---|---|
| AT-binding address conflicts | Compile error: address already used | Re-check address list; ensure no two entries claim the same PIW range |
| Mixed scalar and array I/O | Compile error or runtime garbage on mixed slots | Use one array per data type; keep non-homogeneous sub-slots as scalar address-list entries |
| Sub-slot reordering after GSDML update | Data is read but values are scrambled | Re-import GSDML, re-verify slot table, regenerate array index map |
| Diagnostic sub-slots | PIW value never changes; behaves like a status word | Exclude diagnostic sub-slots from the array; declare them as separate scalar entries |
| Endianness mismatch | 32-bit REAL or DWORD reads as swapped bytes | Use explicit byte-slice assembly (%B0, %B1, %B2, %B3) per the device vendor's documentation |
| Array element out of bounds | Runtime fault in cyclic task | Validate array indices at FB entry; never trust external index inputs without a range check |
| Output array written before cycle | Output is one PROFINET cycle late | Ensure the array is written in a task that runs before the PROFINET output cycle; consider the SendClock / ReductionRatio settings |
| IR (isochronous real-time) latency | Jitter on input reads exceeds application requirement | Enable IRT mode in PROFINET configuration; assign the device to a high-priority PROFINET slot |
FB Library Patterns and Reusability
Once the array-based FB pattern is in place for one device, the natural next step is to wrap it in a library. SIMOTION supports both project-local libraries and global libraries, and the array-based FB is library-friendly: the input is a single array parameter, the output is a single status structure, and the FB body is self-contained.
A typical library structure for a third-party PROFINET device family includes:
-
FB_DeviceHandler32: the 32-WORD handling FB. -
FB_DeviceHandler64: the 64-WORD handling FB. -
PRG_CopyDeviceInputs: the array consolidation program (Pattern B). -
DT_DeviceStatus: a derived status data type for the FB output. -
GVL_DeviceShared: a global variable list with the array declarations and address-list bindings.
This structure is portable across SIMOTION projects that use the same device family. When a new device is added, the engineer copies the library, assigns new base I/O addresses in the address list, and re-uses the FB. The FB body does not change.
For projects that need to support multiple third-party devices of the same type, this pattern scales linearly with the number of devices: each device gets its own array and its own FB instance, but the FB type and the array type are shared.
Summary and Recommended Approach
For SIMOTION projects with third-party PROFINET devices that expose many WORD sub-slots, the recommended approach is:
- Keep the address list scalar as required by the GSDML slot model.
- Declare a global
ARRAY OF WORDsized to the device's I/O frame. - Populate the array using Pattern A (AT-overlay) for contiguous frames, Pattern B (copy block) for mixed or maintenance-friendly cases, or Pattern C (direct PIW) for performance-critical applications.
- Pass the array as a single
INparameter to the handling FB. - Verify the binding with a watch table, a force test, and PROFINET diagnostics.
This pattern preserves the address list's PROFINET-model fidelity while reducing the FB interface to a small, well-typed set of array parameters. The result is a more maintainable, version-stable SIMOTION project. It also makes the HMI tag structure cleaner: instead of 32+ scalar tags per device, the HMI binds to one array tag.
Can the SIMOTION address list declare an array such as "piw 5600 array 2"?
No. The SIMOTION address list is a scalar binding table derived directly from the GSDML slot structure. Each PIW or PQW must be entered individually, and array syntax is not supported. The array consolidation is done in a global data block (Pattern A or B) or directly in the ST program (Pattern C), one layer above the address list.
How do I find the slot-to-PIW mapping for my third-party device?
Open the device's PROFINET interface in SIMOTION SCOUT or TIA Portal, select the device in the device view, and inspect the slot table. Each row shows the slot index, the module name, and the assigned I/O address range. Cross-reference the slot index with the GSDML file's ModuleList and SubmoduleList to confirm the data type and length, then calculate the cumulative byte offset from the device base address.
Can I mix scalar I/O variables and array I/O variables in the same SIMOTION project?
Yes. The address list and the global data block are independent. You can keep diagnostic or non-homogeneous sub-slots as scalar address-list entries and only consolidate homogeneous WORD frames into arrays. The handling FB then accepts both styles as separate IN parameters, and the FB body treats them as different data flows.
Will the array pattern work for output words (PQW) as well as input words (PIW)?
Yes. The pattern is symmetric. Declare an ARRAY OF WORD for outputs, populate it in a cyclic task, and each array element writes back to the corresponding %QW address. Be careful with timing: output writes must complete before the PROFINET cycle transfers the data to the device, so the array must be written in a task that runs before the PROFINET output cycle.
How does this pattern differ from the S7-1500 approach?
The S7-1500 in TIA Portal has similar constraints on its PLC tag table, but the optimized block access model in S7-1500 lets you map arrays of PIWs with less indirection. SIMOTION's address list is a separate, more explicit binding layer, which is why the array consolidation happens in a global data block rather than directly in the address list. Both platforms converge on the same end result: a single array parameter to the handling FB.