S7 Indirect Addressing in STL: Pointers, AR1/AR2 and DB Index

David Krause23 min read
S7-300SiemensTechnical Reference
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

Overview

Indirect addressing in Siemens S7 STL (Statement List) lets the program compute the operand address at runtime instead of hardcoding it. The technique is required whenever the operand location is decided by the process itself — a recipe slot chosen by an operator, a stream number pushed by a WinCC faceplate, a configurable UDT element in a tooling change-over, or an entry in a circular history buffer. The S7-300 and S7-400 CPUs (and the S7-1500 in STL compatibility mode) implement indirect addressing through two pointer formats and a small set of address-register instructions: LAR1, LAR2, +AR1, +AR2, TAR1, TAR2, and the bracketed memory reference [AR1, P#0.0] / [AR2, P#0.0].

Two pointer formats coexist and the choice between them is the first design decision. The intra-area pointer P#byte.bit resolves inside the currently open DB (or inside one of the bit-addressable areas M, I, Q, T, C) and is a 32-bit value where bits 0…2 hold the bit address and bits 3…31 hold the byte address. The cross-area pointer — the form written as P#DB1.DBX0.0 — adds a 6-bit area identifier in the high byte and resolves anywhere in the CPU memory map. Most field programs only need the intra-area form. The intra-area form is also what causes the recurring "system failure" symptom, because the indexing arithmetic must match the pointer layout exactly. The general concept of how an instruction locates its operand through a register, constant, or computed offset is described at length in the Wikipedia addressing-mode reference; the rest of this article maps that general concept onto Siemens STL.

The official Siemens reference for the modern TIA Portal variant of these techniques is the STEP 7 V20 Indirect Addressing manual. This article uses that reference as the canonical description and adds the field-diagnostic patterns that surface when the patterns are applied in production FCs.

Pointer Format and the SLD 3 Operation

The intra-area pointer is the source of every recurring "SLD 3 mystery". The encoding is:

Byte address (29 bits, bits 3..31) 000 bit 0..2 P#DBX 10.3 = (10 SHL 3) OR 3 = 0x53 P#DBX 0.5 = (0 SHL 3) OR 5 = 0x05 P#DBX 256.7 = (256 SHL 3) OR 7 = 0x0807 For byte-aligned access: pointer = (byte_offset SHL 3) | 0 For bit-aligned access: pointer = (byte_offset SHL 3) | bit_offset

The arithmetic identity that every STL recipe exploits is:

pointer_word = (byte_offset << 3) | bit_offset

For byte-aligned access the bit offset is zero, so the calculation reduces to SLD 3 on a 32-bit integer. For bit-indexed access the bit offset is non-zero and the integer index is added directly to the byte-aligned pointer without further shifting. This is why one recipe for DBB[#idx] uses ITD + SLD 3 while a recipe for DBX[#idx] uses ITD alone. The distinction is the most common source of "Area length error" stops in the field.

Concrete encoding examples

Source Integer Operations Pointer word (hex) Effect
Recipe slot number (byte index) 1 ITD + SLD 3 0x00000008 DBB 1 (byte 1)
Bit position in a byte 5 ITD (no shift) 0x00000005 DBX 0.5 (bit 5 of byte 0)
Bit index across 3 bytes 19 ITD (no shift) 0x00000013 DBX 2.3 (byte 2 bit 3)
Two-byte word offset 2 ITD + SLD 3 + 4 0x00000024 DBW 4 — count bytes, then SHL 3

The "word offset" line is the trap: word-indexed access is not a separate pointer encoding. A DBW is two bytes, but the pointer always addresses bytes. A word offset of 2 needs a byte offset of 4, which after SLD 3 gives 0x20 and resolves to DBW 4. Writing SLD 4 instead of SLD 3 shifts the byte address one extra position and the CPU lands on the wrong word boundary; the symptom is identical to the "SLD 3 omitted" case.

Cross-area pointer format

The cross-area pointer is what the constant P#DB1.DBX0.0 produces. Its 32 bits are laid out as:

  • Bits 0…2: bit address (0…7).
  • Bits 3…31: byte address.
  • Bits 32…63 of the input representation hold the area identifier (000101bin = DB, 100000bin = M, 100001bin = I, 100010bin = Q).

When you load P#DB1.DBX0.0 into AR1 the high bits of the 32-bit register hold the area identifier and the [AR1, P#0.0] reference then resolves to DB1 even if a different DB is currently open. This is the only way to read or write a DB that is not the open DB without first issuing OPN DB.

STL Instruction Set for Indirect Addressing

Instruction Operands Function FC/FB usage note
LAR1 P#byte.bit / MD / DBD / DINT Load address register 1 with a 32-bit pointer Save before in FC
LAR2 P#byte.bit / MD / DBD / DINT Load address register 2 with a 32-bit pointer Save before in FC
TAR1 MD / DBD / DINT Transfer AR1 to a word or double word Use for diagnostics and save
TAR2 MD / DBD / DINT Transfer AR2 to a word or double word Use for diagnostics and save
+AR1 ACCU1 (legacy 16-bit form) Add ACCU1 to AR1 Legacy — prefer +AR1 P#x.y
+AR1 P#byte.bit Pointer constant Add a 16-bit signed offset to AR1 Safe form
-AR1 / -AR2 P#byte.bit Subtract pointer constant from AR1/AR2 For ring-buffer wraparound
A [AR1, P#0.0] AND-scan the bit pointed to by AR1 Resolves against open DB or M
= [AR2, P#0.0] Assign RLO to the bit pointed to by AR2 Resolves against open DB or M
L DBW [AR1, P#0.0] Load the word at AR1+offset Resolves against open DB
T DBD [AR2, P#0.0] Store ACCU1 to the DBD at AR2+offset Resolves against open DB
L P##variable Static symbol Load the cross-area pointer to a STAT variable Used for symbolic access

Two ARs are available because the CPU can read from one operand and write to another in the same statement list — for example copying DB20.DBBk to DB22.DBBk. Splitting source and destination between AR1 and AR2 also avoids re-loading AR1 between the two accesses. The price is that both AR1 and AR2 are CPU-global registers — see the FC reusability section below.

Address register persistence

AR1 and AR2 are not local to the FC — they are CPU-global registers shared by every code block in the OB1 cycle. If your FC loads AR1 with LAR1 P#DBX0.0, performs the indirect access, then returns, the next FC loaded by OB1 sees the same AR1 value. Three rules prevent this:

  1. Saving AR1 / AR2 with TAR1 / TAR2 to TEMP at FC entry, restoring with LAR1 / LAR2 at FC exit. The save/restore is the compiler’s responsibility when the FC attribute "Save address registers" is set; in hand-written STL or older projects the save/restore is the programmer’s responsibility.
  2. Using the __VARINFO block attributes in SCL to mark the block as "saves address registers" so the compiler inserts the save/restore automatically.
  3. Avoiding AR1 / AR2 altogether for purely local pointer work by using the bracket syntax with a TEMP word: L DBB[#i].

Indexed Byte Selection from a Recipe Data Block

The most common application of indirect addressing is the recipe slot pattern: the operator writes 1, 2 or 3 into DBB 0 of DB 1, and the program copies DBB 1, DBB 2 or DBB 3 into DBB 4 depending on the index. STL implementation:

OPN DB 1                       // open the recipe DB
L   DBB 0                      // load the index (INT, 16-bit)
ITD                             // convert INT (16-bit) to DINT (32-bit)
SLD 3                           // shift byte offset into pointer position
T   #select_stream              // store the pointer in a TEMP DINT
L   DBB [#select_stream]        // indirect read: DBB 1, 2 or 3
T   DBB 4                       // write to the result slot

The three preprocessing instructions (ITD, SLD 3, T #select_stream) guarantee that the bracketed DBB [#select_stream] operand is a well-formed pointer. Omitting any one of them produces one of three predictable faults:

Omitted step Value of #select_stream for index 1, 2, 3 Effect
None (correct) 0x08, 0x10, 0x18 DBB 1, DBB 2, DBB 3 loaded as expected
Skip ITD 0x0001, 0x0002, 0x0003 (16-bit zero-extended) Bit 0, 1 or 2 of DBB 0 read as a byte
Skip SLD 3 0x0001, 0x0002, 0x0003 Address register decoded as bit offset; CPU reads/writes non-existent DB bit
Skip both 0x00000001..0x00030000 depending on sign Wild address; almost always area access error
Replace SLD 3 with SLD 4 0x10, 0x20, 0x30 Pointer resolves to DBB 2, DBB 4, DBB 6 — one byte too high

1-based vs 0-based indexing

The recipe slot pattern assumes a 1-based recipe index. A 0-based index is equally valid; simply skip SLD 3 when the first byte of the value range is DBB 0. The shift is only required when the index counts bytes starting from an offset other than zero. For HMI inputs that allow 1-based selection, the cleanest pattern is to L 0; -I; ITD; SLD 3 so a HMI value of 1 becomes a byte offset of 0. Document this subtraction in the block header; future maintainers will otherwise read the source as a bug.

Bit-Level Indexed Copy Between Two Data Blocks

When the granularity is a single bit, the index represents the bit position, not the byte offset. The pointer still has to be pre-multiplied by 8 for the byte field of the pointer, and the bit offset must be added directly (not shifted). The simplest pattern uses AR1 as source pointer and AR2 as destination pointer:

OPN DB 1                            // open the source DB
L   P#DB1.DBX1.0                    // base address of DBB 1
LAR1                                // AR1 points at the start bit of DBB 1
L   P#DB1.DBX4.0                    // base address of DBB 4
LAR2                                // AR2 points at the start bit of DBB 4
L   DB1.DBB0                        // bit index (already a bit offset)
NOP 0
+AR1                                // add bit index to AR1
+AR2                                // add bit index to AR2
A   [AR1, P#0.0]                    // read source bit
=   [AR2, P#0.0]                    // write destination bit

The pattern generalises to copying from one DB to another. L P#DB1.DBX1.0 encodes the base as a fully qualified cross-area pointer constant; the +AR1 then adds the bit offset from the index. If the source and destination are in different DBs, OPN DB source must precede the source read and OPN DB destination must precede the destination write — only one DB is open at a time and the [ARn, P#0.0] reference resolves against the currently open DB.

Multi-bit range copy

For multi-bit ranges (e.g., copy 16 bits from one DB to another), use a loop counter and a small constant offset on the ARn references:

OPN DB 20
L   P#DB20.DBX0.0
LAR1
L   P#DB22.DBX0.0
LAR2
L   #count                          // number of bits to copy
NEXT: T   #i
A   [AR1, P#0.0]
=   [AR2, P#0.0]
+AR1 P#0.1                          // advance to next bit
+AR2 P#0.1
L   #i
LOOP NEXT

Note the use of +AR1 P#0.1 — a pointer constant offset of one bit. This is the STL idiom for bit-level stepping. Word-level stepping uses +AR1 P#2.0 (two bytes per word).

The FC Reusability Pitfall: M Bits, AR1/AR2, Multiple Calls

The most common field symptom is "my FC works once, fails when called more than three times". Root cause is almost always a hardcoded global variable inside the FC — an M bit, an M word, an M byte, or a fixed timer. Every call of the FC in the same OB cycle competes for that global, and the last call wins. Three symptoms map to this fault:

  1. Reading works in every call, writing only in the last call — the read happens before the global is corrupted by a later call.
  2. Different calls produce different results on the same input — each call mutates shared M memory.
  3. Performance is fine until the third call — two calls fit within the residual budget of certain CPU flags (BR, OV, OS), the third call trips a system bit that exposes the latent bug.

The correct fix is to remove every hardcoded address from the FC and use one of the four legal storage classes:

Storage Scope Lifetime Use in FC/FB
TEMP Local to the call One OB1 cycle pass Scratch pointers, intermediate results, the index word
IN / OUT / IN_OUT Per call (passed by OB) Duration of the call Parameters from the calling block
STAT Local to the instance DB Until next cold restart Persistent state of an FB instance
Global M / DB Whole CPU Until overwritten Only when the value must survive every call (avoid)
Never share M bits between FC calls. An FC written with A M0.1 as the "enable to DB22" flag and A M0.2 as the "enable to DB23" flag will desynchronise the moment a second FC call writes different values to M0.1 / M0.2 in the same cycle. Replace each M reference with a TEMP local whose value is derived from the call’s own IN parameters. The exception is a flag that is intentionally global across the program — and even then, document the access in the block’s header.

Save and restore AR1/AR2 in FCs

AR1 and AR2 need the same treatment as M bits. The FC must save both registers at entry with TAR1 #save_AR1 / TAR2 #save_AR2 and restore them at exit with LAR1 #save_AR1 / LAR2 #save_AR2. In STEP 7 classic the FC property "Saves address registers" is set in the block attributes and the compiler inserts the save/restore automatically; in hand-written STL or older projects the save/restore is the programmer’s responsibility. A symptom of an unsaved AR1 in FC12 is that the second instance of FC12 in OB1 still points at the first instance’s DB and overwrites the wrong byte.

FC vs FB: choosing the right block

The choice between a Function (FC) and a Function Block (FB) is not just a matter of style. FCs have no instance data, so every variable declared inside the FC is a TEMP that lives for one call. FBs have instance data (STAT) that lives for the lifetime of the instance DB. Indirect-addressing routines with persistent state belong in an FB; stateless indexed reads and writes belong in an FC. If the indexed routine must run with different indices in different parts of the program, an FB with multiple instance DBs is the correct choice. A single FC called multiple times will share TEMP storage correctly across calls (OB1 reuses the local stack per call), but it will not allow each call to retain its own pointer offset across cycles. For the "one FC called N times" pattern the FB is mandatory.

Cross-DB Access Errors and System Failures

The error "Area length error when reading", "Area length error when writing", or "DB not loaded" appears when the bracketed operand points outside the open DB. Three root causes dominate:

  1. Wrong open DB. L P#DB1.DBX1.0 loads a cross-area pointer constant that encodes "DB1" in the high byte, but if the current OPN is for DB20 the CPU may still try to resolve the access through DB20 and fail. Always re-issue OPN DB n immediately before a [ARn, P#0.0] read or write.
  2. Index out of range. A HMI tag or WinCC input gives a negative or oversized index. Validate LIM the index against the DB length before using it as a pointer: L DB1.DBLG gives the DB length in bytes, compare with the byte offset.
  3. Bit offset interpreted as byte offset. If SLD 3 was omitted the index is treated as a bit offset and the CPU reads/writes bits outside the byte boundary.

System failure diagnostics on S7-300/400 are stored in the diagnostic buffer (SFC 6 RD_SINFO or the PG online view). The first event is the symptom (e.g. "OB not loaded"); the second is usually the actual cause (e.g. "Area length error when writing"). Read both events in order — the symptom event is never the root cause.

Reading the diagnostic buffer

A practical diagnostic block that prints the failing pointer and DB at runtime:

// On STOP, OB121 reads the diagnostic buffer
CALL SFC  6                       // RD_SINFO
RET_VAL  := #ret_val
TOP_INFO := #top_info
// #top_info.MN_FAULT_1 contains the failing block
// #top_info.MN_FAULT_2 contains the failing operand

For S7-1500 the equivalent is the GET_ERROR instruction inside the OB that catches the error. The OB121-fault pattern only runs when an error OB is loaded; without OB121 the CPU remains in STOP without writing the failing operand to the buffer.

Pointer Validation, PEEK/POKE, and Range Checks

Validate the pointer before the indirect access. The validation uses the DB length attribute DBLG:

OPN DB 1
L   DBB 0                      // index from HMI
L   1
>=I                            // must be at least 1
JC  OK1
L   0
T   DBB 0                      // clamp to safe value
OK1: L   DBB 0
L   DBLG                       // DB length in bytes
<=I                            // index must fit in DB
JC  OK2
L   0
T   DBB 0                      // clamp to safe value
OK2: NOP 0
// now the index is safe to use as a pointer

For bit indices, divide the index by 8 and compare with the DB length in bytes. Always check both the lower bound (zero) and the upper bound (length − 1). A negative or oversized index fed from a corrupted HMI tag is the most common cause of unexplained area-length errors in production.

PEEK and POKE for S7-300/400

For programmers who prefer not to use AR1 / AR2 at all, the IEC standard SFCs provide PEEK and POKE that read and write a byte, word or double-word at a runtime address:

SFC Function Inputs
SFC 20 BLKMOV Copy a block of bytes source ANY, destination ANY
PEEK Read byte / word / DWORD area, DB number, byte offset
POKE Write byte / word / DWORD area, DB number, byte offset, value

For S7-300/400 the equivalent is to build an ANY pointer with SFC 20 BLKMOV and copy from a source ANY to a destination ANY. The ANY pointer is a 10-byte structure that encodes DB number, byte offset, and length. ANY pointers are necessary when the length of the copy is itself a runtime parameter.

Modern Alternative: Indirect Addressing in S7-1200 and S7-1500

STL is still supported on the S7-1500 in compatibility mode, but most new programs use SCL or the structured variants available in TIA Portal. The mechanism is the same – a DINT pointer computed at runtime – but the notation is cleaner. The TIA Portal V20 indirect addressing manual defines four variants:

Variant Syntax Use case
Area-internal, runtime offset on PEEK / POKE PEEK_WORD(area := 16#81, dbNumber := 1, byteOffset := #idx) Read a word from DB1 at runtime offset #idx
Area-internal, pointer variable pMyWord : POINTER TO WORD; pMyWord := ADR(DB1.wordArray[#idx]); Typed pointer to a DBW
Array element value := "DB_recipe".values[#idx]; Index directly into an ARRAY inside the DB
Variant / slice access value := DB_variant.#idx :=; Index a Variant DB at runtime

On the S7-1500 the compiler verifies the index range at compile time when #idx is declared as INT with an array index – this removes most of the "wild pointer" failure modes of the S7-300/400 STL approach. Use the array-of-words pattern unless you must interface to legacy STL code.

SCL with ARRAY

The SCL array pattern replaces the manual SLD 3 + L DBB[#select_stream] with a compiler-checked array index:

FUNCTION FC12 : VOID
VAR_INPUT
  idx : INT;
END_VAR
VAR_TEMP
  tmp : INT;
END_VAR
tmp := "DB_recipe".values[idx];
"DB_result".result := tmp;
END_FUNCTION

The SCL compiler enforces that idx is within [0..LEN(values)-1] at compile time when the array has a fixed bound. If the array bound is dynamic (e.g., based on the DB length), the compiler still warns when the bound cannot be proven at compile time. This is the lowest-risk pattern for new code.

Edge Cases, Performance, and Consistency with HMI

Large DBs above 8 KB

S7-300/400 DBs are limited to 8 KB on most CPUs and 16 KB on the larger S7-400 CPUs. The pointer arithmetic is the same for both cases — the byte offset simply becomes a larger number. The cross-area pointer constant P#DB20.DBX8000.0 is a 32-bit value that encodes the offset above 8000. If the recipe slot must address byte 8190 (one past the end), LIM rejects it before the indirect access. The S7-1500 raises the limit to 64 KB for optimised DBs, so the same recipe pattern works without change.

Cycle time impact

Each indirect access costs roughly 1…3 microseconds on an S7-300 CPU and a fraction of a microsecond on an S7-1500. The total cycle-time impact of an indexed recipe handler is negligible compared to the OB1 cycle of 10…100 ms. The cost rises sharply only if the indexed routine is in a cyclic interrupt OB (OB35) running at 1 ms — in that case prefer PEEK / POKE to AR1 / AR2 because PEEK / POKE has a fixed predictable execution time.

HMI tag alignment

Half of the "indirect addressing does not work" tickets in production are actually HMI configuration errors. The PLC reads and writes the correct value; the WinCC faceplate is bound to the wrong DBW. Three checks for consistency:

  1. Cross-reference the PLC tag name in the WinCC tag list; verify that the WinCC tag points to the same DB and same byte offset as the PLC program.
  2. Force the DBW in the VAT online and watch the HMI display. If the HMI shows a different value than the VAT, the WinCC tag is wrong; if both show the same wrong value, the PLC program is wrong.
  3. Check the WinCC connection refresh rate. A 1-second refresh on a 100-ms process will appear to "miss" updates and give the impression that the indirect addressing is broken.

Safety-related applications

Indirect addressing is generally not permitted inside a F-runtime group on F-CPUs (S7-300F, S7-400F, S7-1500F). The F-CPU requires that every safety-related operand be addressed directly so that the safety signature check can verify the integrity of the program. If an F-FB or F-FC requires runtime indexing, the F-system provides certified function blocks (e.g., F_DIAG variants) that wrap the indexing logic; the wrapper itself is then certified. Do not write direct indirect addressing inside an F-runtime group without explicit F-certification of the routine.

Troubleshooting Matrix and Verification

Symptom Likely cause Diagnostic step Fix
System failure, "Area length error" Index out of DB range Online > Monitor > Diagnostic buffer; check the byte offset Validate index with LIM; reject negative or oversized values
CPU goes STOP on FC12 second call Hardcoded M bit mutated between calls Cross-reference; watch M bits in VAT online Replace M with TEMP; pass values via IN/OUT
Bit copy reads correct value, writes wrong bit Bit offset not added to destination AR Online > Monitor AR1 / AR2 Add +AR2 for the destination pointer
Reading OK, writing always to DB20 instead of DB22/DB23 Missing OPN DB between read and write Online > DB open dialog Insert OPN DB22 before the DB22 write, OPN DB23 before the DB23 write
Result is shifted by one byte SLD 3 omitted Online > Monitor the pointer word Insert SLD 3 after ITD
Result is shifted by one bit Bit index loaded where byte index was expected (or vice versa) Online > Monitor the pointer word Shift by 3 only when the source integer is a byte offset
Works in OB1, fails in OB35 (cyclic interrupt) AR1 / AR2 not saved Check FC attributes "Save address registers" Save AR1/AR2 to TEMP at entry, restore at exit
Operator sees correct value, WinCC shows wrong value WinCC tag connected to wrong DBW WinCC tag list cross-reference Repoint WinCC tag to the correct DBW; refresh the HMI project
Works at index 0, fails at index > 31 Integer signedness — SLD 3 on a negative INT produces a negative DINT Online > Monitor the pointer word at the moment of failure Use ABS before SLD 3, or declare the index as WORD instead of INT
All calls work in isolation, fails when chained in OB1 Static variable in FB shares storage across instances Watch the instance DBs in online mode Use multi-instance DBs or move the shared state to a TEMP

Verification procedure

  1. Open the DB online, place the program in single-step mode (CRTL+F8 in STEP 7 classic, or "Monitor single step" in TIA Portal).
  2. Force the index word (e.g., DB1.DBB0) to 0, 1, 2 and observe the result slot (DB1.DBB4). It must equal DBB 0, DBB 1, DBB 2 respectively (if the recipe uses 1-based indexing, shift the comparison).
  3. Open VAT_1 with the indirect pointer word and AR1; verify that the pointer equals (index SHL 3) + base at the moment of access.
  4. For the multi-DB pattern, set a VAT with three columns — source DBW (AR1), destination DBW (AR2), and the index — and confirm that the destination DB number changes after each OPN DB.
  5. Test FC12 with 1, 2, 3 and 5 calls in OB1. The result must be identical for each call instance; if it diverges, the FC is still using a global variable.
  6. Force the index to -1 and 999 in turn. The CPU must either reject the value via LIM / range check or remain in RUN with the result unchanged. If it goes STOP, add range validation before the indirect access.
  7. Cycle the CPU power. Confirm that the pointer behaviour is identical after a cold restart — this catches any STAT variable in an FB that is not re-initialised.

FAQ

Why must I shift the index left by 3 with SLD 3 before using it as a pointer?

The intra-area pointer P#byte.bit stores the byte address in bits 3…31 and the bit position in bits 0…2. Shifting the byte offset left by 3 lines it up with the byte field; the bit position is then added directly (no shift). For byte-aligned access the bit position is zero and SLD 3 on a 16-bit integer is sufficient. For bit-indexed access you must skip the shift and add the integer directly as a bit offset.

My FC works the first time it is called in OB1 but fails on the second and third calls. What is wrong?

Almost certainly a hardcoded global memory address — an M bit, M byte or timer — inside the FC. Every call competes for that global and the last call wins. Replace M bits with TEMP variables or IN/OUT parameters so each call gets its own copy. Also confirm that AR1 and AR2 are saved and restored, otherwise the second call inherits the first call’s pointer.

How do I read from one DB and write to another with a single index?

Use AR1 for the source pointer and AR2 for the destination pointer. Load both base pointers with LAR1 P#DB20.DBX0.0 and LAR2 P#DB22.DBX0.0, add the same index to both with +AR1 and +AR2, then issue OPN DB 20 before the source read and OPN DB 22 before the destination write. Only one DB is open at a time, so the OPN must be repeated for each side.

Does indirect addressing work the same way on S7-1500?

STL still works on the S7-1500 in compatibility mode and the same SLD 3 / LAR1 patterns apply. For new code prefer SCL with an ARRAY inside the DB — the compiler checks the index range at compile time and removes the most common fault modes. The TIA Portal V20 indirect addressing manual documents the modern variants.

Why does the CPU go to STOP with "Area length error" when my HMI sends a valid-looking index?

The index is being applied without shift or with the wrong shift, so the effective pointer points outside the DB. Check whether the program does ITD followed by SLD 3 for byte indexing, and whether the same index is reused as a bit offset somewhere else. Add range validation (LIM against DB1.DBLG) before the indirect access.

Back to blog