Siemens S7 STL +AR1 Indirect Addressing: Area Length Error Fix

David Krause11 min read
S7-300SiemensTroubleshooting
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

1. Problem Statement

On a Siemens SIMATIC S7-300 CPU 319-3 PN/DP (order number 6ES7 318-3EL01-0AB0), a Statement List (STL) routine writes a fixed 400-byte payload per device into a shared Instance DB (DB4). Eleven devices are stored back-to-back, so the 11th block starts at byte offset 4200 and ends at byte 4599. As soon as the routine calculates the pointer for the 11th block, the CPU raises the diagnostic error:

Diagnostic buffer entry: "Area length error when writing" (German: Bereichslängenfehler beim Schreiben, event ID 0x2522 / 0x2942 in the diagnostic buffer of the S7-300/400).

The user observes that the highest byte reachable from the routine is DBB4199, exactly one byte short of the calculated start of the 11th block. Reading returns correct values, but every = DBB[AR1,P#0.0] write inside block 11 fails with the area-length error and the OB1 cycle is interrupted or the call is aborted depending on OB configuration.

2. Symptom and Diagnostic Details

Symptoms presented by the failing STL block:

  • Blocks 1 through 10 (offsets 0.0 to 3999.7) write/read cleanly.
  • Block 11 (offset 4000.0 to 4599.7) returns Area length error when writing on the first byte access at 4000.0; in practice the routine appears to write up to 4199 and then faults.
  • The pointer value displayed in STL monitor mode equals the expected value (for example P#DBX 4200.0 or P#DBX 4000.0 depending on whether the index is 0-based or 1-based).
  • DB4 itself is declared with sufficient length (the area-length error is thrown by the CPU's address-check logic, not by the DB definition).

To confirm, open STEP 7 V5.x → PLC → Diagnose/Setting → Module Information → Diagnostic Buffer. Look for entry class "OB not loaded / area length error". The location (OB number, network, address register) points back to the indirect access.

3. Root Cause: 16-Bit Limitation of +AR1

The original code uses the STL statement:

L <offset>        // ACCU1-LL = 16-bit integer
+AR1                 // Add ACCU1-L to AR1

From the STEP 7 STL Help (entry for +AR1 / +AR2):

"The integer (16 bit) to be added to the contents of AR1 is specified by the value in ACCU 1-L. Values from -32768 to +32767 are permissible."

Two effects follow from this:

  1. Sign extension: ACCU1-L is treated as a 16-bit signed integer, so the actual pointer increment is restricted to -32768 ... +32767 byte units in the offset-of-doubleword sense.
  2. Pointer-format wrap: The internal pointer is a 32-bit double word of the form DB number * 0x10000_0000 + bit offset. When +AR1 is fed a literal greater than 4095.7 (i.e., a 16-bit value whose high three nibbles are 0x1xxx), only the low 12 bits survive, producing a truncated pointer instead of a shifted-up bit address.

For 11 devices × 400 bytes = 4400-byte payload range, the addition required to reach device 11 exceeds the 16-bit-safe envelope of +AR1. The PLC silently corrupts the bit-address field of AR1, the resulting pointer lands outside the legal area, and the next byte/word/DWORD write to DBB[AR1,P#0.0] is rejected by the area-length check.

Key insight: The fault is not in the DB size, the CPU work memory, or the process image. It is a pointer-arithmetic overflow caused by mixing bit-format offsets (P#byte.bit) with a 16-bit addend.

4. Address Register Mechanics (AR1/AR2)

AR1 and AR2 are 32-bit registers used for indirect addressing inside the CPU. In the area-internal / area-cross pointer format (the default in STL), the register layout is:

Bits Content Example for P#DBX 200.0 in DB4
31-24 Area identifier (10 = DB) 0x10
23-16 Byte address (high) 0x00
15-8 Byte address (low) – actually 8 bits, not 4 0xC8
7-3 Reserved / 0 0x00
2-0 Bit number (0-7) 0x0

Because the bit address is encoded in the low 3 bits of the double word, every additional byte of offset must be multiplied by 8 to land in the pointer. The +AR1 instruction internally performs a left-shift-by-3 of the 16-bit ACCU1-L addend before adding. A literal of L 200 therefore contributes 200 × 8 = 1600 = 0x640 to the bit-offset field, which still fits in 12 bits.

For an 11th-block start at byte 4200 the required addend is 4200 × 8 = 33600 = 0x8340. This needs 14 bits and overflows the 12-bit bit-offset field of the pointer. +AR1 cannot express the value, so the carry bit is lost and the resulting pointer points inside the wrong region — usually near the end of DB4 instead of the start of block 11.

5. Solution A – Replace +AR1 with +D and LAR1

The portable fix is to compute the new pointer in a 32-bit double-word using +D (32-bit integer add) and then load it back into the address register with LAR1. Because +D works on ACCU1 / ACCU2 as 32-bit signed integers, the offset envelope becomes ±2 147 483 647 — well past anything a 64 KB DB can address.

5.1 Code Before (faulty)

L     #DeviceIndex         // 0..10, INT
L     400                  // bytes per device
*D                          // product = byte offset
+AR1                        // 16-bit add – OVERFLOWS

5.2 Code After (working)

L     #DeviceIndex         // 0..10, INT (16 bit)
L     400                  // bytes per device, INT
*D                          // 32-bit result in ACCU1
LAR1                        // load directly as POINTER (works)

When the multiplier 400 is already a power-of-two (200, 400, 800) you can also substitute a 32-bit shift:

L     #DeviceIndex
SLD   2                     // 4 × device index (200 bytes case)
LAR1

Both forms keep the pointer in the area-internal / area-cross format and survive any byte offset that fits into 64 KB.

Why not +AR1 with a constant? +AR1 P#0.0 is legal but limited to ±4095.7 (12-bit bit-offset). +AR1 P#4000.0 is rejected at compile time on an S7-300/400 because the assembler cannot encode the constant in 12 bits.

6. Solution B – Resize the Data Block

Although the area-length error is a pointer overflow rather than a DB-size issue, the OP also confirms that the data layout is "The 11th block occupies 400 bytes starting from byte 4000. The total size of the memory needs to be bigger or use only 10 blocks." If your payload does not actually require 11 blocks, you can avoid the failure by reducing the block count.

Required DB4 length for N devices at B bytes per block (bit-offset in bytes):

Required_DB4_bytes = N × B

Maximum DB4 size for the 6ES7 318-3EL01-0AB0 CPU 319-3 PN/DP is 64 KB (65 535 bytes) of net user data per DB, with up to 2 048 simultaneously open DBs. For the cited 11 × 400 B = 4 400 B case the DB definition is not the limit; the limit is the pointer arithmetic on the writer side.

Configuration Devices Bytes / device DB4 size Fits CPU 319-3?
Original target 11 400 4 400 B + 200 B header Yes
Reduced to 10 devices 10 400 4 000 B + 200 B header Yes (avoids 4095+ offset)
Scaled to 100 devices 100 200 20 000 B + 200 B header Yes (still requires +D)
Maximum legal 163 400 65 200 B + 200 B header Yes (still requires +D + LAR1)

7. Alternative Addressing Strategies

Beyond fixing the +AR1 call, three further idioms are worth considering for a heavily-multiplexed DB write like the one in the OP:

  1. DB_ANY with indirect DB load (S7-400 / S7-300 V3+). Open the DB with OPN DB [<DB#>] after computing the DB number in 32 bits, then use LAR1 with a zero area identifier. This decouples payload size from any single DB.
  2. Indexed access via ARRAY in a STEP 7 FB / SCL. Declaring ARRAY[1..11] OF STRUCT (Faults: ARRAY[1..200] OF BYTE; Alarms: ARRAY[1..200] OF BYTE); removes the need for hand-rolled pointer math. The compiler emits the same +AR1 / +D sequence but the bounds check protects against overrun.
  3. PEEK / POKE via standard FCs (FC37 POKE, FC36 POKE_BLK) shipped with STEP 7. These wrap the 32-bit pointer and are read-only on some firmware revisions; check the FC help before deploying them on a CPU 319-3 PN/DP.

8. STL Monitor and Online Check Procedure

To prove the pointer is being correctly formed after the fix, follow this sequence in STEP 7 V5.x:

  1. Open the affected FB/FC, right-click → Monitor.
  2. Set a breakpoint before the LAR1 statement.
  3. Force #DeviceIndex = 10 (the 11th block, 0-based).
  4. Single-cycle. Read ACCU1 (it should display the integer 4000 for the 0-based 11th device at 400 bytes each).
  5. Step over LAR1. Inspect AR1 in the Status column. The expected value is P#DBX 4000.0 (or 4200.0 if the 1-based index is used). Any other value indicates either a wrong multiplier or a stray truncation in the chain.
  6. Step the read/write line. The data view should now show DBB4000..DBB4599 updating without diagnostic-buffer entries.

9. Process Image – What It Is and What It Is Not

The OP asks about "modifying the process image". The process image is the PLC's mirror of physical I/O (PII / PIQ) refreshed once per OB1 scan; it has no relationship to the DB area-length fault. The process image partition table (configured in HW Config → CPU → Cycle/Clock Memory) can be disabled selectively to free CPU time, but it does not extend the byte reach of an indirect write into a DB.

For S7-300, the maximal process image size is 1024 B for inputs and 1024 B for outputs. Beyond that, peripheral accesses (PIB / PQB / PID / PQD) are used. Peripheral accesses do not buffer, do not trigger OB1 read in advance, and are an additional indirect-addressing context covered by Siemens FAQ "Where and when do you need peripheral addressing?"

10. Related Siemens Diagnostic Patterns

Diagnostic text Typical cause Typical fix
Area length error when writing +AR1 / +AR2 overflow, pointer past DB end +D + LAR1; bounds-check loop counter
Area length error when reading Same root cause on read side; or uninitialised AR1 Initialise AR1 = P#0.0 at FB start
DB not loaded / DB not found OPN DB or DB_ANY points to undefined DB Check DB number against HW Config
Area conflict, both inputs and outputs Mixing I and Q with the same bit offset Verify area identifier byte in AR1
Fatal error 0x2522 in OB1 Indirect access violation; OB121 not loaded Insert OB121 to localise; fix pointer
FC100 SWR_START area length error SFC99 / SFC100 called with a parameter pointer outside work memory Re-validate SRCBLK / DSTBLK POINTER

Two official KB articles are essential to keep on hand for this class of problem:

11. Verification Checklist

  1. Pointer build: ACCU1 before LAR1 matches the byte offset × 8 + area-ID hex pattern.
  2. DB end check: Computed offset + payload size ≤ DB4 declared length.
  3. Diagnostic buffer: Empty of OB not loaded / area length error after 100 cycles.
  4. Monitor variables: All 11 device blocks show non-default data; the 11th block changes in lock-step with the input.
  5. Online → Accessible nodes shows DB4 with the expected size; the project recompiles cleanly.
  6. OB121 loaded: Inserted and documented; gives a clean trap-and-recover path if the failure recurs in the field.

12. Safety and Field Commissioning Notes

  • Always load OB121 (error OB) before going to a real plant. Without it, an area-length error in OB1 stops the CPU in STOP with the red SF LED on. With OB121, the fault is logged in the diagnostic buffer and execution continues with the next statement.
  • Wrap the indirect read/write in a conditional that verifies the upper bound of the index, e.g. L #DeviceIndex; L 11; <I; JC err. This catches an FB misuse on the HMI side before it becomes a CPU fault.
  • For a CPU 319-3 PN/DP (6ES7 318-3EL01-0AB0) running firmware ≥ V3.2, SCL-generated code uses +D + LAR1 by default, so porting the same logic to SCL also resolves the symptom.
  • Retain the original STL block in the project (do not delete) so a future regression test can re-apply the faulty version on a test CPU and reproduce the area-length error for training.

13. CPU 319-3 PN/DP – Relevant Data

Parameter Value
Order number (MLFB) 6ES7 318-3EL01-0AB0
Family SIMATIC S7-300
Work memory, code / data 2 048 KB / 2 048 KB
Max DB count 2 048
Max DB size 64 KB
Bit memory (M) 16 KB
Process image 1024 B I / 1024 B Q (default)
AR1 / AR2 width 32-bit pointer (16-bit +AR1 addend)

What does "Area length error when writing" mean on an S7-300?

It is a CPU diagnostic event raised when an indirect DB or peripheral access uses a pointer whose bit-offset falls outside the legal area (DB length, I/O length, or work-memory boundary). It is logged with event ID 0x2522 in the diagnostic buffer. Load OB121 to prevent a CPU STOP and then fix the pointer arithmetic.

Why does +AR1 fail for large offsets but work for small ones?

The +AR1 instruction uses the 16-bit signed value in ACCU1-L as the addend. Internally it shifts the addend left by 3 to form a bit-offset. The pointer's bit-offset field is only 12 bits wide, so anything beyond offset 4095.7 (= 4095 × 8 + 7) silently truncates. Use +D (32-bit add) followed by LAR1 to bypass the limit.

Can I keep using +AR1 and just resize the DB?

Resizing the DB does not help. The fault is on the pointer-arithmetic side, not on the DB-definition side. The DB can be 64 KB and the failure will still occur at offset 4000.0 if +AR1 is left in place. Replace +AR1 with +D + LAR1 to clear the symptom.

What is the difference between +AR1 P#byte.bit and LAR1 with a pointer?

+AR1 P#byte.bit is encoded in 16 bits and is silently limited to ±4095.7 at compile / run time. LAR1 loads an entire 32-bit pointer and can address any byte in a 64 KB area, so it is the correct choice for dynamic indexing across a large array-style DB.

Should I migrate the block to SCL or TIA Portal?

Migration to SCL is the lowest-risk option: the compiler emits the equivalent +D + LAR1 sequence automatically. Migration to TIA Portal with an S7-1500 removes the +AR1 limit entirely because the address registers are 64-bit and SCL is the default. Until then, fix the existing STL with +D + LAR1 and document the change in the FB header.

Back to blog