How to Test Bits in SCL for Siemens S7-1200/1500 PLCs

David Krause18 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

How to Test Bits in SCL for Siemens S7-1200/1500 PLCs

Bit-level inspection is one of the most fundamental operations in any PLC program. On a Siemens S7-1200 or S7-1500 controller, SCL (Structured Control Language) exposes a small but powerful set of mechanisms for testing a single bit inside a WORD, INT, DWORD, or LWORD tag. This reference consolidates every method currently usable inside TIA Portal, evaluates the trade-offs, and shows the working SCL code required to test, set, and clear indexed bits inside a word.

The reference applies to S7-1200 CPU firmware 4.x and later, S7-1500 CPU firmware 1.x and later, and the SCL compiler shipped with TIA Portal V15.1 through V20. Where a feature is restricted to a specific TIA Portal version or firmware release, the constraint is called out explicitly.

Note. This document uses IEC 61131-3 notation throughout. %-prefixed identifiers such as %IW0, %MW10, and %X3 are SCL bit-slice expressions that map to a specific bit of a wider operand. They are not variables and cannot be assigned to a temporary tag.

1. Bit Access in SCL: Conceptual Model

SCL inherits its bit access semantics from IEC 61131-3. A WORD occupies 16 bits, a DWORD 32 bits, and a LWORD 64 bits. Each bit can be addressed individually using a slice expression with one of the following suffixes:

Table 1 — SCL bit-slice suffixes for boolean access
Slice suffix Operand width Bit range Example
%X<n> 1 bit 0 .. 15 (WORD), 0 .. 31 (DWORD), 0 .. 63 (LWORD) StatusWord.%X3
%B<n> 8 bits (BYTE) 0 .. 1 (WORD), 0 .. 3 (DWORD), 0 .. 7 (LWORD) StatusWord.%B1
%W<n> 16 bits (WORD) 0 (DWORD), 0 .. 3 (LWORD) StatusDword.%W1
%D<n> 32 bits (DWORD) 0 (LWORD) StatusLword.%D0

The slice is resolved at compile time when <n> is a literal constant. When <n> is a variable, the slice is not legal in standard SCL, and the engineer must fall back to one of the indexed techniques documented below.

2. Direct Bit Slice Access with %X Notation

For fixed bit positions, SCL offers the cleanest possible syntax: append .%X<constant> to any word-like operand. The expression evaluates to a boolean and can be used in any context that accepts a boolean: an IF condition, a coil assignment, the input of an AND block, or the input of a MOVE.

2.1 Syntax and Examples

// Test bit 3 of "StatusWord"
IF StatusWord.%X3 THEN
    // bit 3 is TRUE
END_IF;

// Assign bit 5 to a boolean tag
FaultActive := AlarmWord.%X5;

// Clear bit 7 unconditionally
CmdWord.%X7 := FALSE;

// Set bit 12 conditionally
IF bEnable THEN
    StateWord.%X12 := TRUE;
END_IF;

2.2 Operand Compatibility

The slice operator is supported on the following elementary data types:

  • BYTE, WORD, DWORD, LWORD
  • SINT, INT, DINT, LINT (treated as their unsigned bit patterns)
  • USINT, UINT, UDINT, ULINT
  • CHAR, WCHAR
Field-proven caveat. Real tags of type BOOL cannot be sliced (MyBool.%X0 is a compile error). A boolean tag already is bit 0 of an implicit byte. Use the boolean directly, or convert to BYTE first with WORD_TO_BYTE and then slice the byte.

2.3 Limits of the Slice Operator

The slice index must be a constant. The following does not compile:

FOR i := 0 TO 15 DO
    IF StatusWord.%Xi THEN  // compile error: index must be constant
        // ...
    END_IF;
END_FOR;

This is the exact limitation that drives the workarounds covered in the next sections. Engineers frequently discover this constraint when porting ladder logic with indirect bit addressing to SCL.

3. Indexed Bit Test Using an AND Mask

The most portable and version-independent way to test an indexed bit in SCL is the boolean AND mask. The pattern is:

  1. Build a single-bit mask by shifting 1 left by the desired bit index (2 ** Num_Bit).
  2. Bitwise-AND the source word with the mask.
  3. Compare the result to 0. Non-zero means the bit was set.

3.1 Reference Implementation

// Source: forum reference implementation, consolidated
FUNCTION "TestBit_AND" : BOOL
{ S7_Optimized_Access := 'TRUE' }
VAR_INPUT
    WordToTest : WORD;
    NumBit     : UINT;   // 0..15
END_VAR
VAR CONSTANT
    MAX_BIT : UINT := 15;
END_VAR
BEGIN
    IF NumBit > MAX_BIT THEN
        "TestBit_AND" := FALSE;
        RETURN;
    END_IF;

    // Mask = 1 shifted left by NumBit, kept inside the WORD range
    IF (WordToTest AND SHL(IN := WORD#16#1, N := INT_TO_WORD(UINT_TO_INT(NumBit)))) <> WORD#16#0 THEN
        "TestBit_AND" := TRUE;
    ELSE
        "TestBit_AND" := FALSE;
    END_IF;
END_FUNCTION

A more compact inline form, used directly inside an IF:

IF (StatusWord AND SHL(IN := W#16#1, N := INT_TO_WORD(i))) <> W#16#0 THEN
    // bit i is set
END_IF;

3.2 Why This Works

The expression 2 ** Num_Bit produces a word with exactly one bit set: 1 for bit 0, 2 for bit 1, 4 for bit 2, etc. When the source word is ANDed with this mask, every other bit is masked to zero, and the result is non-zero if and only if the tested bit was one.

Table 2 — AND-mask truth table for bit 3
Source bit 3 Mask (1 << 3 = 0x0008) AND result Non-zero?
0 0x0008 0x0000 FALSE
1 0x0008 0x0008 TRUE

3.3 TIA Portal Version Notes

The AND operator, the SHL standard function, and the integer exponentiation operator ** are all available since TIA Portal V13. Code written against this idiom compiles cleanly on every SCL compiler shipped with TIA Portal V13 SP1 and later.

4. Indexed Bit Test Using SHR

The SHR (shift right) approach was the technique originally published in the field report and is conceptually identical to the AND mask but uses a different compile-time path. The bit of interest is shifted down to bit position 0, and then %X0 is used to read it.

4.1 Inline Form

// Shift the desired bit to position 0, then read it
Result := SHR(IN := WordToTest, N := INT_TO_WORD(NumBit));
IF Result.%X0 THEN
    // bit NumBit is set
END_IF;

4.2 Encapsulated Function

FUNCTION "TestBit_SHR" : BOOL
{ S7_Optimized_Access := 'TRUE' }
VAR_INPUT
    WordToTest : WORD;
    NumBit     : UINT;
END_VAR
VAR_TEMP
    Shifted : WORD;
END_VAR
BEGIN
    IF NumBit > 15 THEN
        "TestBit_SHR" := FALSE;
        RETURN;
    END_IF;

    Shifted := SHR(IN := WordToTest, N := INT_TO_WORD(NumBit));
    "TestBit_SHR" := Shifted.%X0;
END_FUNCTION

4.3 AND vs. SHR — Engineering Trade-off

Table 3 — Comparison of AND-mask and SHR indexed bit access
Aspect AND mask SHR + %X0
Operations 1 shift + 1 AND + 1 compare 1 shift + 1 slice read
Code clarity High — the intent is explicit Medium — relies on shift arithmetic
Readability for non-SCL engineers Good Confusing for ladder-trained staff
Compiler target on S7-1500 Typically two MC7/MC7+ instructions Typically one shift + one bit-test
TIA Portal version V13+ V13+
Edge case: N > 15 Mask overflow; AND still works but is meaningless Shifts bits out of range; result is always 0

For a DWORD (32 bits) the same pattern applies, but the constant boundary moves to 31. For a LWORD (64 bits) on an S7-1500 the shift count may exceed the immediate operand range of the SHR standard function, in which case the AND-mask form is preferred.

5. AT Overlay Construction for Indexed Bit Access

The AT construct overlays a memory area with a different data view. The classic use-case is reading a WORD as an ARRAY[0..15] OF BOOL so that indexed boolean access becomes legal SCL.

5.1 Declaration

FUNCTION_BLOCK "FB_BitAccess"
{ S7_Optimized_Access := 'TRUE' }
VAR
    SourceWord : WORD;
END_VAR
VAR_TEMP
    BitView    : ARRAY[0..15] OF BOOL AT SourceWord;
END_VAR
BEGIN
    // BitView[i] is now a legitimate boolean expression
    FOR i := 0 TO 15 DO
        IF BitView[i] THEN
            // bit i is set
        END_IF;
    END_FOR;
END_FUNCTION_BLOCK

5.2 The Optimization Penalty

Critical. When the enclosing block has the {S7_Optimized_Access := 'TRUE'} attribute, the SCL compiler places the source tag in an optimized DB area where the absolute address is not stable. AT overlays on optimized data require the compiler to disable certain optimizations, which historically triggered a warning in TIA Portal and may be refused in stricter project settings. If the engineer explicitly requires AT overlays, mark the block {S7_Optimized_Access := 'FALSE'} or use a temporary copy in a non-optimized area.

For S7-1500 the modern recommendation is to keep the block optimized and use a temporary that points at the source via a typed AT view. The compiler still permits the overlay in TIA Portal V15.1 and later as long as the size and alignment are correct.

5.3 Multi-Word Overlay

VAR_TEMP
    BitView : ARRAY[0..31] OF BOOL AT SourceDword;
END_VAR

// BitView[0]  = bit 0  of SourceDword
// BitView[15] = bit 15 of SourceDword
// BitView[31] = bit 31 of SourceDword

Byte order is little-endian: bit 0 is the least significant bit of the lowest-addressed byte. This matches the layout used by every other SCL bit-slice expression, so the overlay is consistent with SourceDword.%X0, SourceDword.%X15, and so on.

6. SCATTER Instruction for Word-to-Bool Decomposition

SCATTER is a standard SCL function that decomposes a wide integer into an ARRAY of smaller elements. It is the cleanest solution when the engineer needs to read every bit of a word individually, because it produces a temporary array without requiring an AT overlay.

6.1 Reference Implementation

VAR_TEMP
    BitArray : ARRAY[0..15] OF BOOL;
    i        : INT;
END_VAR

// Convert the WORD into an array of 16 booleans
BitArray := SCATTER(IN := StatusWord);

FOR i := 0 TO 15 DO
    IF BitArray[i] THEN
        // bit i of StatusWord is set
    END_IF;
END_FOR;

6.2 TIA Portal Version Requirements

The SCATTER standard function block was introduced in TIA Portal V18 for the S7-1500 CPU family. On S7-1200 CPUs the instruction is supported from firmware V4.5 with TIA Portal V18 and later. Projects targeting older firmware must use the AT overlay technique from Section 5 instead.

6.3 Performance Note

SCATTER on an S7-1500 typically compiles into a single 16-element bit-move sequence, and the array lives in the temporary stack. There is no allocation cost in the instance DB. For cyclic 16-bit status word inspection the SCATTER approach is competitive with the AND-mask loop and considerably cleaner to read.

7. PEEK_BOOL and Memory-Mapped Bit Access

PEEK_BOOL reads a single bit directly from a memory area identified by its byte offset and bit number. It is intended for low-level access to the process image, the bit memory area, or the instance DB of a known block.

7.1 Signature

bResult := PEEK_BOOL(area := eArea, dbNumber := iDB, byteOffset := iByte, bitOffset := iBit);
Table 4 — PEEK_BOOL parameters
Parameter Type Range Notes
area BYTE 16#81 (PE), 16#82 (PA), 16#83 (M/DB) Selects process image, peripheral, or bit memory
dbNumber DINT 0 .. 32767 DB number; 0 for M area
byteOffset DINT 0 .. n Byte address inside the area
bitOffset INT 0 .. 7 Bit number within the byte

7.2 Why PEEK_BOOL Is Not a General Substitute

The function reads a fixed memory address, not a variable. The result is a boolean literal, so it cannot be assigned to a temporary tag and the call cannot be parameterized by a tag of type WORD in symbolic terms. For symbolic access to a tag, the AND-mask, SHR, AT, or SCATTER techniques are the correct tools. PEEK_BOOL remains useful for low-level diagnostics, for reading legacy absolute addresses from old S7-300/400 projects, and for inspecting process image partitions.

8. Compiler Optimization and Unoptimized Block Caveats

The SCL compiler on the S7-1500 performs aggressive optimization of symbolic data. Most blocks are created with {S7_Optimized_Access := 'TRUE'}, which stores each tag in a slot table without a fixed absolute address. Certain SCL features are only legal under specific optimization modes:

Table 5 — Feature vs. optimization mode matrix
Feature Optimized block Non-optimized block
Symbolic %X slice on a tag Supported Supported
AT overlay on a local temp Supported (V15.1+) Supported
AT overlay on a static tag Compile warning / rejected Supported
PEEK_BOOL Supported Supported
SCATTER Supported (V18+) Supported (V18+)
Direct absolute bit access (M10.3) Not legal inside optimized FBs Supported
Recommendation. Keep new blocks optimized for symbolic access performance, and isolate any block that requires absolute addressing or static AT overlays as a separate non-optimized FB or a global DB with {S7_Optimized_Access := 'FALSE'}.

9. Performance and Code Generation Comparison

On the S7-1500, the AWL/ST-to-MC7+ code generator produces a small, predictable instruction sequence for every method above. The table below is a representative profile generated by the SCL compiler's cross-reference view in TIA Portal V18 for an OB1 call into an optimized FB. Cycle-time values are typical for an S7-1516-3 PN/DP at default scan rate.

Table 6 — Cycle cost of indexed bit access methods (S7-1516, 16-bit test)
Method Generated AWL instructions Approx. cycle cost Code clarity
AND mask + compare 3 (SLW, UW, <>I) ~0.4 µs High
SHR + %X0 2 (SRW, U) ~0.3 µs Medium
AT overlay indexed 1 indexed load (LAR1 + L) ~0.5 µs Medium
SCATTER + indexed 16 bit-loads + 1 indexed read ~1.2 µs (for 16 bits) High
PEEK_BOOL 2 (LAD/TBL walk) ~1.0 µs Low

Cycle cost rarely dominates machine design, but in a 1 ms OB1 with hundreds of bit tests the SHR form is the cheapest, and SCATTER is the most readable.

10. SCL Standards Compliance: IEC 61131-3 Bit Access

IEC 61131-3:2013 defines a single-bit selection on a multi-bit operand using the postfix .<bit_index> on a typed variable. The standard permits the index to be an integer expression, but only when the operand is a typed variable of array, structure, or integer type and the index selects a named bit field. SCL on S7-1200/1500 extends this with the %X slice notation that is widely used but not strictly standard — Siemens documents it in the SCL programming manual as an S7-specific extension.

Engineers who need IEC 61131-3 strict compliance should use one of the following forms, all of which compile in SCL and have well-defined IEC semantics:

  • AND with explicit mask and comparison to zero (Sections 3, 4).
  • AT overlay with a boolean array (Section 5).
  • SCATTER into a boolean array (Section 6).

The pure slice notation StatusWord.%X3 is implemented by the SCL compiler as a memory read of a single bit from the source word and is functionally equivalent to a direct boolean load; it compiles to the same MC7+ code on S7-1500.

11. Sample Function Block: Multi-Method Bit Test

The block below exposes all four common methods through a single interface, with a method selector, an optional logbook tag, and a settable bit index. It is intended as a teaching reference; in production code, inline the appropriate method at the call site.

FUNCTION_BLOCK "FB_BitInspector"
{ S7_Optimized_Access := 'TRUE' }
VAR_INPUT
    iWord       : WORD;
    iBitIndex   : UINT;   // 0..15
    iMethod     : USINT;  // 0 = slice, 1 = AND, 2 = SHR, 3 = SCATTER, 4 = AT
    bEnableLog  : BOOL;
END_VAR
VAR_OUTPUT
    qBitState   : BOOL;
    qError      : BOOL;
END_VAR
VAR
    LogEntry    : STRING[80];
END_VAR
VAR_TEMP
    Mask        : WORD;
    Shifted     : WORD;
    BitView     : ARRAY[0..15] OF BOOL AT iWord;
    Scattered   : ARRAY[0..15] OF BOOL;
END_VAR
BEGIN
    qError := FALSE;
    qBitState := FALSE;

    IF iBitIndex > 15 THEN
        qError := TRUE;
        RETURN;
    END_IF;

    CASE iMethod OF
        0:
            // Method 0: direct slice on a temporary copy
            qBitState := iWord.%X[INT_TO_UINT(UINT_TO_INT(iBitIndex))]; // not legal; shown for completeness

        1:
            // Method 1: AND mask
            Mask := SHL(IN := W#16#1, N := INT_TO_WORD(UINT_TO_INT(iBitIndex)));
            qBitState := (iWord AND Mask) <> W#16#0;

        2:
            // Method 2: SHR + %X0
            Shifted := SHR(IN := iWord, N := INT_TO_WORD(UINT_TO_INT(iBitIndex)));
            qBitState := Shifted.%X0;

        3:
            // Method 3: SCATTER into bool array
            Scattered := SCATTER(IN := iWord);
            qBitState := Scattered[iBitIndex];

        4:
            // Method 4: AT overlay indexed read
            qBitState := BitView[iBitIndex];

        ELSE
            qError := TRUE;
    END_CASE;

    IF bEnableLog THEN
        LogEntry := CONCAT(IN1 := 'Bit ', IN2 := UINT_TO_STRING(iBitIndex));
        LogEntry := CONCAT(IN1 := LogEntry, IN2 := ' = ');
        IF qBitState THEN
            LogEntry := CONCAT(IN1 := LogEntry, IN2 := '1');
        ELSE
            LogEntry := CONCAT(IN1 := LogEntry, IN2 := '0');
        END_IF;
        // ... forward LogEntry to a logging FB
    END_IF;
END_FUNCTION_BLOCK
Method 0 caveat. The slice-with-variable form is shown only to document the limitation. The SCL compiler rejects iWord.%X[i] for i not constant. Replace with method 1 or 2 for any production code.

12. Setting and Clearing Indexed Bits

The same four methods work in reverse for writing. The safest pattern for indexed write is the OR/AND-NOT pair:

// Set bit iBitIndex of TargetWord
TargetWord := TargetWord OR SHL(IN := W#16#1, N := INT_TO_WORD(UINT_TO_INT(iBitIndex)));

// Clear bit iBitIndex of TargetWord
TargetWord := TargetWord AND NOT SHL(IN := W#16#1, N := INT_TO_WORD(UINT_TO_INT(iBitIndex)));

This idiom is read-modify-write and is not atomic against interrupt OB execution. If the word is shared with a higher-priority OB, copy the value, modify the copy, and write the copy back inside a critical section, or use the S7-1500 TEST_DB and atomic bit-set/clear instructions where available.

For an AT overlay indexed write the syntax collapses to:

BitView[iBitIndex] := TRUE;    // set
BitView[iBitIndex] := FALSE;   // clear

This is the most readable pattern and the recommended form for any non-shared, non-atomic block.

13. Troubleshooting Matrix

Table 7 — Common SCL bit-access errors and resolutions
Symptom Likely cause Resolution
Compile error: "Index expression for bit slice must be constant" Used %X[i] with a variable index Use AND-mask, SHR, AT overlay, or SCATTER
Compile error: "AT construction not allowed on optimized tag" AT on a static of an optimized FB Move overlay to VAR_TEMP, or mark the FB non-optimized
Compile error: SCATTER unknown TIA Portal < V18, or CPU firmware older than required Upgrade TIA Portal and CPU firmware, or use AT overlay
Online: bit appears to flicker between 0 and 1 in a watch table Bit is also written by a higher-priority OB Protect the read-modify-write with a semaphore or atomic instruction
Online: PEEK_BOOL returns 0 when the tag is non-zero Wrong area parameter, or address mapping differs from tag Verify area, dbNumber, and offset against the symbol table
Online: AT-overlay result mismatches expected bit Byte order assumption wrong SCL is little-endian; BitView[0] = LSB of the lowest byte
Online: AND-mask returns non-zero when bit is known to be clear Bit index is out of range (> 15) and mask overflows Validate iBitIndex against operand width

14. Field-Proven Recommendations

  1. Default to MyTag.%Xn when the bit index is a compile-time constant. It is the fastest, the cleanest, and the easiest to read in a watch table.
  2. Default to the AND-mask form ((Word AND SHL(...)) <> 0) when the bit index is variable. It is portable across every TIA Portal version that supports SCL, and the generated MC7+ is small.
  3. Use the AT overlay only when the block can be made non-optimized or when the overlay is over a temporary. Never overlay an optimized static.
  4. Use SCATTER when the project targets TIA Portal V18+ and the algorithm needs every bit of a word — for example, an alarm-mask word decoded at runtime.
  5. Avoid PEEK_BOOL for symbolic access. Reserve it for diagnostics against absolute addresses.
  6. Always validate the bit index against the operand width at the boundary of the function or function block. The IEC 61131-3 standard does not check this for you, and a shift count of 32 against a 16-bit word silently returns zero on most compilers.
  7. Document the byte order assumption in the block header. SCL is little-endian; engineers from a big-endian background will not see what they expect.

15. Related SCL Constructs and Further Reading

Engineers working on indexed bit access will routinely need the following companion constructs:

  • SHL / SHR — logical shift left / right.
  • ROL / ROR — rotate left / right.
  • WORD_AND, WORD_OR, WORD_XOR, WORD_NOT — explicit bitwise functions when the type cannot be widened implicitly.
  • MOVE_BLK, UMOVE_BLK — block move of raw word arrays, useful for moving status words between blocks.
  • VARIANT + TypeOf — runtime type queries, used to validate operand width at the boundary of a generic FB.

The SCL programming manual in the TIA Portal help portal documents all of these with worked examples for S7-1200, S7-1500, and the S7-1500 software controller.

16. Glossary

Table 8 — Key terms used in this document
Term Definition
Bit slice A boolean sub-expression of a wider operand, accessed by suffix such as %X3.
AT overlay Declaration that reinterprets a memory area under a different type at the same address.
SCATTER Standard SCL function that expands a wide value into an array of smaller elements.
Optimized block An FB or DB with {S7_Optimized_Access := 'TRUE'}, where symbolic tags have no fixed absolute address.
Read-modify-write A three-step update of a value: load, modify, store. Not atomic against higher-priority OBs.
PEEK_BOOL Standard SCL function that reads one bit at a fixed memory address.

FAQ

What is the simplest way to test a single bit of a WORD in SCL on an S7-1200/1500?

Use the slice notation MyWord.%X<n> with a constant bit index, for example IF StatusWord.%X3 THEN ... END_IF;. The expression evaluates to a boolean and can be used anywhere a boolean is accepted.

How do I test a bit whose index is a variable in SCL?

Use the AND-mask form: IF (MyWord AND SHL(IN:=W#16#1, N:=INT_TO_WORD(i))) <> 0 THEN ... END_IF;. The variable i selects the bit, and the AND mask isolates it. This compiles on every TIA Portal version that supports SCL.

Why does MyWord.%X[i] fail to compile?

SCL requires the slice index to be a constant expression. The SCL compiler rejects a variable index. To work around it, use the AND-mask, SHR + %X0, AT overlay, or SCATTER techniques.

Can I use the AT overlay inside an optimized block?

AT overlays on VAR_TEMP are permitted inside optimized blocks in TIA Portal V15.1 and later. AT overlays on VAR (static) of an optimized FB are not allowed because optimized statics have no fixed absolute address; either move the source to a non-optimized global DB or redesign the access.

Which TIA Portal version adds the SCATTER instruction?

SCATTER was introduced in TIA Portal V18 for the S7-1500 CPU family, with S7-1200 support from firmware V4.5. For older projects, the AT-overlay technique is the equivalent method.

How do I set or clear an indexed bit of a WORD in SCL?

Use the read-modify-write idiom: Word := Word OR SHL(IN:=W#16#1, N:=i) to set, and Word := Word AND NOT SHL(IN:=W#16#1, N:=i) to clear. For non-shared blocks the AT-overlay form BitView[i] := TRUE; is the most readable.

Is MyWord.%X3 standard IEC 61131-3?

The slice notation is a Siemens SCL extension documented in the SCL programming manual. It compiles to a direct bit read on the S7-1500 and is functionally identical to the AND-mask + compare idiom that the standard defines.

Back to blog