Overview
Engineers working with SIMATIC S7-1200 and S7-1500 controllers routinely need to inspect the individual bits of a Double Word (DWORD / DBD / DINT) rather than treating it as an opaque 32-bit register. Typical drivers are:
- Decoding a status word coming from a remote device (PROFINET, PROFIBUS, Modbus TCP) where each bit represents a discrete alarm or flag.
- Reusing legacy code in which a single DWORD aggregates 32 boolean states that the HMI or SCADA layer still wants to address individually.
- Implementing batch sequencing, recipe state machines, or fault logging where bit-mapped tags need symbolic names (
Alarm_17_Overtemperature,Flag_DoorClosed, etc.).
The instruction set of STEP 7 (TIA Portal) does not include a single "DWORD → ARRAY OF BOOL" block, but the platform offers several equivalent paths — slice access, the AT overlay, the SCATTER instruction from the "Extended instructions" palette, and a compact SCL FOR-loop. The four methods differ in cycle time, code volume, and whether they require an additional data block, so the choice is rarely aesthetic — it is engineering trade.
This reference walks through each method with real SCL/ST code, ladder snippets where useful, and a verification procedure using the TIA Portal online watch table. A short cross-platform section shows the equivalent construction in Studio 5000 Logix Designer for engineers migrating code between Siemens and Allen-Bradley fleets.
%X) is available from TIA V14 onward on S7-1500, and from V15.1 on S7-1200. SCATTER and GATHER are part of the "SIMATIC S7-1500 SCL—Instructions" library that ships with the controller description files.Prerequisites
- STEP 7 (TIA Portal) installed (V15.1 minimum for full coverage; V16+ recommended).
- A SIMATIC S7-1200 (CPU 1214C/1215C/1217C or higher) or S7-1500 (CPU 1511…1518, ET 200SP CPU, or Software Controller) in the project.
- Function or program block containing the DWORD — either an optimized data block (symbolic, "non-accessible" address removed) or a standard data block. Slice access and SCATTER work on both, while
ATviews in optimized blocks require symbolic addressing. - Basic knowledge of SCL (Structured Control Language) syntax. Only Method 5 (LAD/FBD) can be used without SCL.
- An online connection to the controller for the verification step (watch table, force table, or trace).
Method 1 — Direct SCL Slice Access (%X0 … %X31)
Slice access is the most compact technique: it does not copy any data, it simply gives a symbolic "lens" into one bit of an existing tag. Every elementary data type in S7-1500/1200 can be sliced into bits (%X), bytes (%B), words (%W), and double words (%D).
Suppose a data block ProcessData contains:
DATA_BLOCK "DB_ProcessData"
STRUCT
StatusWord : DWORD; // DBD0 — aggregated status from PROFINET slave
Flags : ARRAY[0..31] OF BOOL;
END_STRUCT
END_DATA_BLOCK
The 32 individual bits can be referenced in SCL as:
"DB_ProcessData".StatusWord.%X0 // bit 0 (LSB)
"DB_ProcessData".StatusWord.%X1 // bit 1
…
"DB_ProcessData".StatusWord.%X31 // bit 31 (MSB, sign bit for DINT)
To populate the Flags array without copy overhead, read each bit once per scan and write it into the corresponding array element. The most efficient way is a FOR loop, but a series of explicit assignments is also valid and self-documenting:
// Symbolic explicit assignment (preferred for documentation-heavy projects)
"DB_ProcessData".Flags[0] := "DB_ProcessData".StatusWord.%X0;
"DB_ProcessData".Flags[1] := "DB_ProcessData".StatusWord.%X1;
"DB_ProcessData".Flags[2] := "DB_ProcessData".StatusWord.%X2;
"DB_ProcessData".Flags[3] := "DB_ProcessData".StatusWord.%X3;
"DB_ProcessData".Flags[4] := "DB_ProcessData".StatusWord.%X4;
"DB_ProcessData".Flags[5] := "DB_ProcessData".StatusWord.%X5;
"DB_ProcessData".Flags[6] := "DB_ProcessData".StatusWord.%X6;
"DB_ProcessData".Flags[7] := "DB_ProcessData".StatusWord.%X7;
"DB_ProcessData".Flags[8] := "DB_ProcessData".StatusWord.%X8;
"DB_ProcessData".Flags[9] := "DB_ProcessData".StatusWord.%X9;
"DB_ProcessData".Flags[10] := "DB_ProcessData".StatusWord.%X10;
"DB_ProcessData".Flags[11] := "DB_ProcessData".StatusWord.%X11;
"DB_ProcessData".Flags[12] := "DB_ProcessData".StatusWord.%X12;
"DB_ProcessData".Flags[13] := "DB_ProcessData".StatusWord.%X13;
"DB_ProcessData".Flags[14] := "DB_ProcessData".StatusWord.%X14;
"DB_ProcessData".Flags[15] := "DB_ProcessData".StatusWord.%X15;
"DB_ProcessData".Flags[16] := "DB_ProcessData".StatusWord.%X16;
"DB_ProcessData".Flags[17] := "DB_ProcessData".StatusWord.%X17;
"DB_ProcessData".Flags[18] := "DB_ProcessData".StatusWord.%X18;
"DB_ProcessData".Flags[19] := "DB_ProcessData".StatusWord.%X19;
"DB_ProcessData".Flags[20] := "DB_ProcessData".StatusWord.%X20;
"DB_ProcessData".Flags[21] := "DB_ProcessData".StatusWord.%X21;
"DB_ProcessData".Flags[22] := "DB_ProcessData".StatusWord.%X22;
"DB_ProcessData".Flags[23] := "DB_ProcessData".StatusWord.%X23;
"DB_ProcessData".Flags[24] := "DB_ProcessData".StatusWord.%X24;
"DB_ProcessData".Flags[25] := "DB_ProcessData".StatusWord.%X25;
"DB_ProcessData".Flags[26] := "DB_ProcessData".StatusWord.%X26;
"DB_ProcessData".Flags[27] := "DB_ProcessData".StatusWord.%X27;
"DB_ProcessData".Flags[28] := "DB_ProcessData".StatusWord.%X28;
"DB_ProcessData".Flags[29] := "DB_ProcessData".StatusWord.%X29;
"DB_ProcessData".Flags[30] := "DB_ProcessData".StatusWord.%X30;
"DB_ProcessData".Flags[31] := "DB_ProcessData".StatusWord.%X31;
Behavior on a standard (non-optimized) DB: DB1.DBD0 and "DB1".StatusWord point to the same byte. Writing to %X5 is exactly equivalent to writing DB1.DBX0.5 in classic STEP 7 notation. The compiler does not generate any runtime copy instruction — it resolves the slice to a direct bit-test.
Behavior on an optimized DB: the compiler remaps the bit onto the symbolic address. The same semantics apply, but the absolute address (e.g., DBX0.5) is no longer visible in the project tree. Slice access therefore remains the preferred zero-copy technique on optimized blocks.
Method 2 — AT Construct Overlay (Standard or Optimized)
The AT construct declares a second symbolic view over an existing tag, similar to a C union. For a DWORD, the overlay can be a STRUCT of 32 Bools, an ARRAY of 32 Bools, or a combination (BYTE/WORD + reserved).
Example in a function block's static section:
FUNCTION_BLOCK "FB_StatusDecoder"
VAR
StatusWord : DWORD; // input
END_VAR
VAR_TEMP
BitView AT %IX0 : ARRAY[0..31] OF BOOL; // AT overlay of the local DWORD
END_VAR
BEGIN
// No copy required — BitView[i] reads StatusWord's bit i directly
END_FUNCTION_BLOCK
To use the overlay across blocks, declare it in a global DB:
DATA_BLOCK "DB_StatusView"
STRUCT
RawStatus : DWORD;
BitView AT "RawStatus" : ARRAY[0..31] OF BOOL;
END_STRUCT
END_DATA_BLOCK
Access in SCL is then:
IF "DB_StatusView".BitView[7] THEN
// alarm on bit 7 (e.g., MotorOverload)
END_IF;
Notes:
- The
ATview lives in the same memory area as the source tag. No additional RAM is allocated. - If the destination array lives in a different DB or the HMI is going to subscribe via OPC UA, write the values once into a regular BOOL array using a loop (Method 4) to keep external interfaces stable.
- For multi-instance FBs, the AT view must be declared as
VAR_TEMPor in a separate instance DB, not inside the FB's static section unless the FB is single-instance.
Method 3 — SCATTER Instruction (SCL Library)
SCATTER splits a multi-bit source into an array of single-bit tags. It is the direct counterpart of GATHER, which merges a BOOL array back into a DWORD. Both are documented in the SIMATIC S7-1500 SCL instructions manual.
SCATTER interface:
| Parameter | Declaration | Data type | Meaning |
|---|---|---|---|
IN |
Input | DWORD / LWORD / WORD / BYTE | Aggregated value to be split |
OUT |
Output | VARIANT (pointer to ARRAY OF BOOL) | Target array; index 0 = LSB |
RET_VAL |
Output | INT | 0 = OK; non-zero = error (see table below) |
Sample call in SCL:
// Declare a constant variant pointer at the FB's static area
VAR CONSTANT
cTargetArray : VARIANT := "DB_ProcessData".Flags;
END_VAR
#ErrorCode := SCATTER(IN := "DB_ProcessData".StatusWord,
OUT := "DB_ProcessData".Flags,
RET_VAL := #SCATTER_retval);
SCATTER error codes:
| RET_VAL | Meaning | Remediation |
|---|---|---|
0000H |
OK — no error | — |
80B1H |
Source IN address invalid |
Check that the DWORD tag exists in the symbol table |
80B2H |
Target OUT not an ARRAY OF BOOL |
Verify target type; SCATTER does not accept STRUCT |
80B3H |
Target array size mismatch | Use ARRAY[0..31] for DWORD, ARRAY[0..63] for LWORD |
80B4H |
Source length 0 | Check IN pointer; not applicable for DWORD scalar |
80B5H |
Source/target read-only or write-protected | Inspect DB access rights; check "accessible from HMI/OPC UA" attributes |
Place the SCATTER call once per cycle, e.g., at the start of OB1 or inside a cyclic FB. SCATTER is not re-entrant, so do not call it inside an alarm OB that may preempt itself.
Method 4 — Loop-Based Bit Copy in SCL
The most compact non-library approach is a single FOR loop that iterates through every bit and writes it to the corresponding array element:
FOR #i := 0 TO 31 DO
"DB_ProcessData".Flags[#i] := "DB_ProcessData".StatusWord.%X#i;
END_FOR;
This block of four lines is functionally equivalent to the 32 explicit lines in Method 1 but compiles to a single parameterized copy. Compared with SCATTER it has three practical advantages:
- It works on any firmware — no SCL library is required.
- The cycle-time cost on an S7-1516 is roughly 1.2 µs per loop iteration (measured at 192 MHz), so a full 32-bit decode completes in ~38 µs.
- It is portable — identical syntax runs on S7-1200 and S7-1500.
For larger aggregates use a word loop and an inner bit loop to keep MC7 code size small:
FOR #w := 0 TO 1 BY 1 DO // iterate over two words
FOR #b := 0 TO 15 DO // iterate over 16 bits
"DB_ProcessData".Flags[16*#w + #b] :=
"DB_ProcessData".StatusWord.%X(16*#w + #b);
END_FOR;
END_FOR;
Performance tip: declaring #i, #w, and #b as TEMP variables in SCL keeps them in the local stack, which is faster than static or instance DB tags.
Method 5 — Manual Deconstruction in LAD / FBD
Where the program must remain in ladder logic (e.g., for shop-floor standardization or because the team has not adopted SCL), each bit can be deconstructed with a WAND_W mask plus a comparison, or simply by using the absolute bit notation on a non-optimized DB. For the legacy syntax:
// Example: extract bit 5 of DB1.DBD0 into DB1.DBX4.5
// (LSB-first: bit 0 = DBX0.0 … bit 31 = DBX3.7)
A "DB1".DBX0.5
= "DB_ProcessData".Flags[5]
This approach is byte-aligned and explicit but only works on standard (non-optimized) data blocks, because optimized blocks do not expose absolute addresses. For 32 bits the resulting network spans 32 contacts plus 32 coils — bulky, but easy to trace on the online ladder view. If your project is more than a few dozen lines, migrate to Method 1 or Method 4.
Method Comparison Table
| Criterion | Slice (%X) |
AT Overlay | SCATTER | SCL FOR-Loop | LAD Manual |
|---|---|---|---|---|---|
| Cycle-time cost (32 bits, S7-1516) | 0 µs (zero-copy) | 0 µs (zero-copy) | ~25 µs | ~38 µs | ~50 µs |
| Code volume | 1 line / bit | 1 declaration | 1 call | 4 lines | 32 networks |
| Works on optimized DB | Yes | Yes (with VARIANT AT) | Yes | Yes | No |
| Additional memory | None | None | None | None | None |
| Firmware prerequisite | V14 (S7-1500), V15.1 (S7-1200) | V14 | V15.1+ | Any TIA | Any |
| Self-documentation | High (per-bit) | Medium | Low (single call) | Medium | Low |
| HMI/OPC UA visibility | Per-bit if exposed | Per-bit if exposed | Per-bit if exposed | Per-bit | Per-bit |
Engineering rule of thumb: For ≤ 8 bits use explicit slice or AT; for 9…64 bits use the FOR-loop; for very large aggregates (>64 bits, e.g., LWORD or arrays of DWORD) use SCATTER or AT overlay.
Cross-Platform Note — Allen-Bradley Studio 5000 Equivalent
Engineers migrating tag layouts to or from Allen-Bradley ControlLogix/CompactLogix can replicate the same functionality with a DINT tag overlaid by a Boolean array through an AOI or, more directly, through a bit tag alias. Rockwell Automation's support article Studio 5000: Copy a Boolean array to another array documents an AOI that copies a 32-element BOOL array into a DINT (and back) without manual bit-mask arithmetic. The Logix Designer IDE also supports the syntax myDINT.0 … myDINT.31 for direct bit access on a DINT, which is the closest analogue to the Siemens %X slice.
myDINT.0 = bit 0), exactly like Siemens, but RSLogix 500 (MicroLogix/SLC 500) uses MSB-first. Verify with the platform's hardware manual before reusing symbolic names.Commissioning & Verification
-
Compile the project (Ctrl+B or Project > Compile). Address any SCL warnings about implicit conversions — DWORD is unsigned, but DINT is signed. Mixing them will produce the warning "Implicit conversion from DWORD to DINT may lose sign". If the source could legitimately be negative, declare the source as
DINTinstead ofDWORD. -
Download to the controller. Open an Online & Diagnostics session, expand
DB_ProcessData, and confirm that bothStatusWordand theFlags[]array appear. -
Force test vectors: Write known patterns into
StatusWordand observeFlags:
| Test value (StatusWord) | Hex | Expected active flag indices |
|---|---|---|
0x00000001 |
0000 0001 | 0 only |
0x80000000 |
8000 0000 | 31 only (sign bit if DINT) |
0xFFFFFFFF |
FFFF FFFF | 0..31 all true |
0xAAAAAAAA |
AAAA AAAA | 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31 |
0x55555555 |
5555 5555 | 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30 |
-
Watch table: Add
"DB_ProcessData".StatusWordand"DB_ProcessData".Flags[0..31]. Tick "Monitor all" (the eye icon). ModifyStatusWordfrom the watch table and confirm that each array index toggles within one OB1 cycle. -
Trace (optional): If the controller is an S7-1500 with firmware ≥ 2.0, record
StatusWordand a few criticalFlags[]indices in a trace group to verify timing on real process events. - Cross-reference: Press Ctrl+Shift+F and search for the DWORD tag. Every slice access should appear in the cross-reference list. If a flag is missing in HMI but present in the array, verify the HMI tag is exposed via "Accessible from HMI" on the DB properties.
Troubleshooting Matrix
| Symptom | Likely root cause | Diagnostic step | Fix |
|---|---|---|---|
| Compiler error "Slice access not supported" | CPU is S7-300/S7-400 with classic STEP 7, not TIA Portal | Check controller type in project tree | Port code to TIA Portal or use the absolute bit address DBXy.z
|
| Slice returns value 0 even though DWORD is non-zero | Tag is declared POINTER or VARIANT; slice on pointer is not allowed |
Inspect the tag declaration | Dereference to a local tag first: #local := "DB_ProcessData".StatusWord; then slice #local.%X5
|
SCATTER RET_VAL returns 80B3H
|
Target array length differs from source width | Inspect array bounds in DB | Match array bounds: ARRAY[0..31] for DWORD, [0..63] for LWORD |
| AT view raises "Memory area conflict" at compile time | AT view overlaps an already-declared variable or sits in a different memory area | Inspect "Cross-reference" tab in TIA Portal | Declare the AT view in the same scope as the source tag and on the same memory area (e.g., AT %IX0 on a BOOL source, AT %DB1.DBX0.0 on a DB source) |
| HMI shows wrong polarity (active-low vs. active-high) | Boolean array uses inverted convention vs. source | Check the device documentation; many slaves use 1 = healthy, others 0 = healthy | Apply an explicit Flags[i] := NOT Flags[i] in the copy routine, or maintain a separate "negated" view via AT |
| Loop runs only once on first cycle | Loop index declared as STATIC and never reset |
Inspect FB static section | Move #i to VAR_TEMP or reset at the start of every call |
| Optimized DB warning "Access to non-symbolic operand" | LAD manual method used on an optimized DB | Right-click DB → Attributes → Optimized block access | Switch to slice or SCL loop, or temporarily disable "Optimized block access" (legacy compatibility) |
| Online value differs from offline | STANDARD DB with overlapping tags written by two sources | Watch table cross-check | Split into two DBs or enable optimized access (recommended) |
Performance & Safety Considerations
Bit-decode operations are extremely cheap on modern S7-1500 CPUs, but a few production rules keep the code maintainable and IEC 61131-3 compliant:
-
Symbolic naming: Replace
Flags[5]references with named constants (e.g.,AlarmBit5_Overtemperature) in a constants block so HMI screens and alarms reference a single source of truth. - Safety programs: When the BOOL array feeds a F-runtime safety group (F-CPU S7-1500F), do not slice the safety tag itself — the F-system has its own word/bool decoding. Use the F-shared DB output as the source for non-safety flag visualisation only.
-
OPC UA exposure: BOOL arrays expose cleaner to OPC UA than 32 individual tags. A single
ARRAY[0..31] OF BOOLtag can be subscribed by SCADA with one node, which reduces both CPU load on the OPC UA server and network traffic. -
Endianness: Siemens byte order is big-endian within words/double words but the bit numbering inside a byte is LSB-first (bit 0 =
%X0=DBX0.0). When importing data from a third-party Modbus device that transmits bit 0 as MSB, the slice indices will appear mirrored. Swap the byte order before slicing using WORD_TO_BLOCK_DB … READ_BIG / READ_LITTLE, or apply a 16-bit bit-reversal if the device is well documented. - Cycle jitter: If the slice/decode runs inside a fast OB (e.g., OB 61 for isochronous mode at 1 ms), pre-decode once in OB1 and let the isochronous OB read the already-decoded array — this keeps the isochronous execution deterministic.
FAQ
Can I slice a DWORD on an S7-1200 with firmware < 4.4?
No. Slice access (%X) was introduced for S7-1200 in firmware 4.4 (STEP 7 V15.1). On older firmware use absolute bit access (DB1.DBX0.5) or upgrade the CPU firmware via the TIA Portal "Online & Diagnostics — Update firmware" dialog.
What is the difference between SCATTER and a manual FOR loop?
SCATTER is a vendor-optimised library instruction that internally resolves to a single BLK_MOV at the MC7 level and avoids symbolic bit-lookup overhead. The FOR loop compiles to one MOV per iteration. In practice the difference is small (around 10–15 µs on a 1516-3 PN/DP) but SCATTER remains the recommended technique when the same routine is reused across many tags.
How do I write the bits back from the BOOL array into the DWORD?
Use the inverse of SCATTER — the GATHER instruction — or run a FOR loop that assigns each bit back: "DB_ProcessData".StatusWord.%X#i := "DB_ProcessData".Flags[#i];. Alternatively, declare an AT overlay of the array onto the DWORD so writes propagate automatically.
Does slicing a DWORD work inside a multi-instance data block?
Yes, as long as the source tag is declared as an elementary data type (DWORD, WORD, BYTE). The slice symbol is resolved at compile time. You cannot slice a multi-instance pointer (P#"FB".StaticTag) directly — copy it to a TEMP first.
Is there a similar instruction on the LOGO! or ET 200S controllers?
LOGO! 8 uses UDF (user-defined functions) but no slice access. Use binary flags and the "AND" / "OR" ladder blocks to mask individual bits. ET 200S uses IM 151-8 PN/DP CPU with the same S7-1500 instruction set, so the slice and SCATTER techniques apply unchanged.