Indexing S7-1500 Static Areas with PEEK/POKE in TIA Portal V19

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

Indexing S7-1500 Static Areas with PEEK/POKE in TIA Portal V19

The TIA Portal V19 STL editor refuses to compile an indexed ANY pointer that targets a Static area of a Function Block on S7-1500 CPUs. The compiler accepts the same construct against an external Global DB, accepts a literal ANY on a Static area, and accepts an indexed DB number on a Global DB. The single combination that fails is P#DB[#dbnr] DBX0.0 WORD n referenced against a Static member. This article documents the exact compiler rule, explains the underlying CPU architecture, and provides PEEK/POKE and BLKMOV workarounds that compile and execute reliably on a CPU 1515-2 PN (and equivalent) with TIA Portal V19.

Editor version scope. All examples in this article apply to STEP 7 V19 in the TIA Portal engineering suite, validated against CPU 1500 firmware V2.9.x. Behaviour on V17 and V18 is identical for the constructs shown; V20 adds PEEK_BOOL/POKE_BOOL overloads that are not yet relevant to indexed ANY access.

Problem Statement

An engineer working in Statement List (STL) on a CPU 1500 attempts to write:

CALL  MOVE
      SRCBLK := P#DB[#dbnr] DBX0.0 WORD 2
      RET_VAL := #retVal
      DSTBLK := P##staticBuffer

The TIA Portal STL parser returns "The ANY pointer can only point to a data block area in the local instance." The same engineer confirms that #dbnr is correctly populated from the input area (for example, loaded with L DINO after an OPN instruction). Replacing WORD 2 with BYTE 4, DWORD 1, or REAL 2 produces the same diagnostic. The construct compiles cleanly when P##staticBuffer is replaced by a Global DB symbol, confirming the restriction is scoped to the Static area of an Instance DB.

The same engineer compares the behaviour with STEP 7 V5.x on an S7-300 CPU. There the construct compiles and executes. The S7-1500 path is therefore a deliberate change in the TIA Portal V19 STL grammar, not a regression.

Background: ANY Pointer Grammar in TIA Portal V19

The ANY pointer in the S7-1500 world is a 10-byte structure with the layout shown below. The TIA editor enforces this structure at compile time, the CPU interprets it at runtime, and the two are not symmetric in the S7-1500 architecture.

Byte Content Notes
0..1 Syntax ID (10h for ANY) Always 10h
2..3 Internal length / padding Reserved by compiler
4..5 DB number (0 for non-DB areas) 0..65535
6 Area code (0x84 = DB, 0x86 = DI, etc.) Set by TIA
7 Reserved Always 0
8..9 Byte offset (DINT, only low 24 bits used) Bit offset << 3
10..11 Type code (BYTE, WORD, DWORD, INT, REAL...) Type-safe
12..13 Length in elements (WORD) Triggers the compiler rule

The STL literal P#DB100 DBX10.0 BYTE 20 is a parser convenience that pre-fills the structure at edit time. When the DB number is replaced by an indexed variable (#dbnr), the TIA parser cannot resolve the structure at edit time and refuses to emit the literal. The CPU would happily execute the resulting 10-byte block at runtime, but the editor will not assemble it.

According to Siemens Industry Online Support, the rule is enforced because the S7-1500 CPU recognises the configured Transmit areas of its connected partners at compile time. Allowing the editor to assemble an indexed ANY on a Static area would create a runtime situation where the CPU accepts the move against a buffer whose actual length is unknown to the FW. Siemens opted to reject the construct at edit time instead of at runtime.

Root Cause Analysis

Three independent causes converge to block the syntax:

  1. Literal restriction on Static members. The TIA Portal V19 STL grammar disallows an indexed DB number in a P# literal when the destination is a Static area of an FB. The grammar allows the indexed DB number for a Global DB operand.
  2. Block optimisation default. The S7-1500 series uses optimised blocks by default. An optimised Instance DB hides its absolute address layout from the user, so the editor cannot generate a valid offset/length tuple for the ANY structure. The user must explicitly disable the "Optimized block access" attribute on the FB.
  3. Length-safety check. The length field in the ANY structure (bytes 12..13) must match a declared length in the Static area. When the indexed DB number is taken from an HMI tag or arithmetic expression, the editor cannot verify the length and refuses the literal outright.

None of these three checks are CPU limitations. The CPU firmware accepts an ANY pointer whose DB number is computed at runtime and whose length field is arbitrary. The editor is the gatekeeper.

Compiler Behaviour Matrix: S7-300 vs S7-1500

Construct STEP 7 V5.x / S7-300 TIA V19 / S7-1500 Notes
P#DB100 DBX0.0 BYTE 20 on Global DB Compiles Compiles External DB, any length
P#DB[#dbnr] DBX0.0 BYTE 20 on Global DB Compiles Compiles Indexed DB number, literal offset
P#DB100 DBX0.0 BYTE 20 on Static Compiles Compiles Literal ANY, non-optimised block
P#DB[#dbnr] DBX0.0 BYTE 20 on Static Compiles Rejected Compiler enforces static type
PEEK on Static (byte granularity) Limited Compiles (STL/SCL) Recommended workaround
BLKMOV with constructed ANY Compiles Compiles Use for length-aware block moves

The Instance DB itself must be non-optimised for the workarounds below to function. The setting is found in the FB properties under Attributes > Optimized block access. Un-checking the box forces TIA to retain the absolute offset map. Siemens Industry Online Support documents this attribute in the S7-1500 system manual as the switch that exposes the address layout to pointer instructions.

Solution 1: PEEK and POKE for Byte-Granular Static Access

The S7-1500 base instruction set provides PEEK and POKE variants that bypass the P# literal grammar entirely. They accept a runtime-evaluated DB number and a byte offset, and they read/write single bytes (or BOOL with the _BOOL overloads) against the address.

STL Example: Indexed Write to Static Array

// FB Inputs:
//   #dbnr   : INT    - target Global DB number
//   #offset : DINT   - byte offset into the target DB
//   #value  : BYTE   - value to write
// FB Static:
//   ARRAY[0..31] of BYTE
//   ARRAY[0..31] of BYTE indexed by #dbnr (HMI/PLC tag)

      L     #value
      POKE  DB [#dbnr], #offset, #value    // 16-bit extended STL POKE

      L     #value
      POKE  DB [#dbnr], #offset            // Byte-granular write

Note the syntax DB [#dbnr] - the brackets denote a runtime DB number, identical to the legacy AUF DB [#dbnr] grammar. This is the only context in TIA V19 STL where a runtime DB number may appear inside a memory instruction.

STL Example: Byte-Wise Bulk Read into Static Buffer

// Loop over 32 bytes; copy from DB[#dbnr] at #offset to #staticBuffer[i]
      L     0
next: T     #i
      L     #i
      ITD
      +     #offset
      SLW   1
      LAR1
      PEEK  DB [#dbnr], AR1, #tmpByte
      L     #i
      ITD
      LAR1
      POKE  DI [#i], AR1, #tmpByte         // DI is the instance DB

      L     #i
      +     1
      T     #i
      L     #i
      L     32
      <I
      JC    next

Limitations of PEEK/POKE

  • Byte-granular only. Word or DWORD access requires multiple PEEK calls plus manual byte swapping on little-endian S7-1500.
  • PEEK/POKE do not honour the optimised-block protection. A read against an optimised block returns 0, not a protection violation.
  • The DB number passed to PEEK DB [#dbnr] must be in the valid DB range (0..65535). Out-of-range values are silently treated as DB 0.

Solution 2: BLKMOV with Runtime-Constructed ANY

For length-aware bulk transfer, build the source ANY pointer in a TEMP structure, pass it to BLKMOV together with a compile-time generated destination ANY for the Static area. The TIA Portal compiler accepts P##staticBuffer as a destination ANY; the source ANY must be constructed at runtime.

STL Example: Copy N Bytes from Indexed DB into Static Buffer

// FB TEMP layout (10-byte ANY structure):
//   #tempSrc.syntaxID    : BYTE   = 0x10
//   #tempSrc.pad         : BYTE   = 0
//   #tempSrc.length      : INT    = runtime
//   #tempSrc.dbNumber    : INT    = #dbnr
//   #tempSrc.areaCode    : BYTE   = 0x84  // DB
//   #tempSrc.reserved    : BYTE   = 0
//   #tempSrc.byteOffset  : DINT   = 0
//   #tempSrc.typeCode    : INT    = 0x11  // BYTE

      L     B#16#10
      T     #tempSrc.syntaxID
      L     0
      T     #tempSrc.pad
      L     #byteCount
      T     #tempSrc.length
      L     #dbnr
      T     #tempSrc.dbNumber
      L     B#16#84
      T     #tempSrc.areaCode
      L     0
      T     #tempSrc.reserved
      L     L#0
      T     #tempSrc.byteOffset
      L     W#16#11
      T     #tempSrc.typeCode

      CALL  BLKMOV
        SRCBLK   := #tempSrc
        RET_VAL  := #retVal
        DSTBLK   := P##staticBuffer

The asymmetry is intentional. The source ANY lives in TEMP because the DB number is runtime-evaluated; the destination ANY is generated by TIA at compile time as P##staticBuffer because the Static address is fixed for the FB instance. The CPU copies length bytes from the source DB starting at byte 0 into the Static buffer. retVal is 0 on success; non-zero indicates a length mismatch or invalid DB number.

Common Error Codes Returned by BLKMOV

retVal Meaning Resolution
0 Success No action
0x80B1 Source length < destination length Reduce byteCount or pad source
0x80B2 Destination length < source length Verify Static buffer size
0x80B5 Source ANY area code invalid Set areaCode to 0x84 for DB
0x80C3 DB does not exist (PLCSIM only) Create target DB in PLCSIM
0x80C4 DB exists but wrong length Resize or re-map target DB

Solution 3: DPRD_DAT and DPWR_DAT for PROFINET Consistency

For PROFINET slots with a configured transmit area, the S7-1500 provides consistent-data read/write instructions that bypass the ANY grammar entirely.

  • DPRD_DAT - read consistent data from a partner slot
  • DPWR_DAT - write consistent data to a partner slot

The Instance DB Static area must be exactly the same size as the configured transmit area, otherwise TIA Portal raises "Area length mismatch" at compile time. Siemens Support confirms: "A 1500 CPU knows about the configured Transmit areas of its connected parts." This is why size mismatches are blocked at edit time instead of at runtime.

STL Example: 32-Byte PROFINET Slot Read

// FB Static:
//   ARRAY[0..31] of BYTE  - exactly 32 bytes
// Input:
//   #hwId : INT  - hardware identifier of the slot

      CALL  DPRD_DAT
        LADDR   := #hwId
        RET_VAL := #status
        RECORD  := P##inputSlot32

The instruction returns 0 on success. Common error returns are 0x8090 (LADDR invalid) and 0x80A0 (negative acknowledgment from IO controller). The HW identifier is found in the device properties under System constants > HW Identifier.

Instance DB Configuration Checklist

Setting Required Value Where to Find
Optimized block access Disabled FB Properties > Attributes
Data block write-protected in PLCSIM Off (PLCSIM only) PLCSIM Options
Accessible from HMI/OPC UA Enabled if HMI reads FB Properties > Attributes
Knows about connected Transmit areas Yes (default for S7-1500) Compile output, not configurable
Compiler warnings for hidden address Treat as errors during migration Project Settings > Compile

Troubleshooting Matrix

Symptom Likely Cause Resolution
Compiler error: "The ANY pointer can only point to a data block area in the local instance" Literal P# with indexed DB on Static Use POKE, BLKMOV with constructed ANY, or DPRD_DAT
PEEK returns 0 against an optimised block Block is configured as optimised Disable Optimized block access on the FB
BLKMOV returns 0x80B1 Source ANY length exceeds destination Static size Reduce #byteCount or extend the Static array
BLKMOV returns 0x80B2 Destination Static size exceeds source Verify length field matches Static declaration
DPRD_DAT returns 0x8090 LADDR or HW ID invalid Re-check device properties > System constants
DPRD_DAT returns 0x80A0 IO controller NACK Verify partner is online and not faulted
PEEK/POKE reads stale data AR1 not re-loaded between calls Re-load AR1 with TAR1 #destAddr before each PEEK
AREA LENGTH MISMATCH at compile time Static area shorter than PROFINET transmit area Resize Static array to match the configured slot
Code compiles on S7-300 but not S7-1500 Compiler grammar differs across CPU families Wrap the construct in an IF family = 1500 END_IF guard, or rewrite using the workarounds above

Best Practices

  1. Choose the right primitive. PEEK/POKE for one-off byte writes, BLKMOV with constructed ANY for bulk transfers, and DPRD_DAT/DPWR_DAT for PROFINET consistency. Mixing them in the same FB obscures intent.
  2. Keep the Instance DB non-optimised. When an FB will use indexed ANY pointers, un-check Optimized block access on the FB. Document this requirement in the FB header comment so the next engineer does not re-enable it.
  3. Validate the runtime DB number. The S7-1500 does not raise a protection violation for an in-range but wrong DB number; it silently reads/writes. Add a whitelist check (#dbnr between 1 and 10, for example) before issuing the move.
  4. Document the ANY-pointer construction. Pointer-string assembly in TEMP variables is a frequent source of cut-and-paste errors. Add a comment block above the assembly explaining each field's purpose and value range.
  5. Test on PLCSIM first, then on real hardware. PLCSIM returns different error codes (0x80C3, 0x80C4) when the target DB is missing or the wrong size. A clean PLCSIM run is a strong indicator that the real CPU will accept the move.
  6. Avoid dual-target code. The same STL source will not always compile identically on S7-300 and S7-1500. Maintain two FB variants if the project requires both, or use the compiler-conditional {S7_1500} pragma to gate language extensions.

Verification Procedure

  1. Compile the FB in TIA Portal V19. No "pointer string invalid" or "ANY pointer can only point to a data block area in the local instance" errors should remain.
  2. Download to a CPU 1515-2 PN (or equivalent S7-1500 model) running firmware V2.9.x. PLCSIM V19 may be used for offline validation.
  3. Online > Monitor/Modify > trigger the indexed move with #dbnr = 1, #byteCount = 32, #offset = 0.
  4. Inspect the destination Static buffer (e.g., in a watch table) and compare against a known pattern in the source DB. A byte-by-byte diff should return zero.
  5. Sweep #dbnr from 1 to 10 and confirm all 10 transfers complete with retVal = 0.
  6. Force a deliberate mismatch (e.g., set #byteCount = 64 against a 32-byte Static buffer) and confirm BLKMOV returns 0x80B1 rather than overwriting adjacent memory.
  7. Reset the Instance DB retention and re-run the sweep to confirm cold-start behaviour matches warm-start.

Field-Proven Caveats

Three additional findings from the original engineering thread and the Siemens support response are worth documenting before closing:

  • DB_ANY conversion does not help. The user attempted to convert the DB number via DB_ANY and pass it as a parameter; the resulting POINTER/ANY still fails the same Static-area compiler check. DB_ANY is a transport type, not a binding for ANY construction.
  • L DINO + OPN is the canonical pre-step. Loading the DB number with L DINO after OPN DB [#dbnr] is the standard pattern; the failure is not in the load but in the subsequent ANY literal. The OPN instruction does not need to remain in the FB - it suffices that #dbnr holds a valid value when BLKMOV reads it.
  • DPRD_DAT/DPRW_DAT and Instance DBs are tightly coupled. The "connected parts" comment from Siemens Support refers to PROFINET IO devices whose transmit-area lengths are configured in the device description. If the Static area is one byte longer or shorter than the configured transmit area, the FB will refuse to compile regardless of how BLKMOV is wired.

FAQ

Why does TIA Portal V19 reject P#DB[#dbnr] DBX0.0 WORD 2 on a Static area when the same construct compiles on a Global DB?

The TIA Portal V19 STL grammar allows indexed DB numbers in a P# literal only when the destination is a Global DB. For a Static area, the editor cannot validate the length field at edit time and refuses the literal. Use BLKMOV with a runtime-constructed ANY or PEEK/POKE for indexed Static access.

Does disabling Optimized block access on the FB fix the compile error?

No. Disabling Optimized block access is a prerequisite for PEEK/POKE to read the correct offset, but it does not change the compiler rule that bans indexed P# literals on Static areas. Both changes are required: disable optimisation, and replace the P# literal with one of the workarounds in this article.

What is the difference between PEEK and POKE variants for S7-1500?

PEEK reads and POKE writes a single byte from/to a memory area. The CPU 1500 instruction set adds PEEK_BOOL/POKE_BOOL for bit access. PEEK/POKE accept a runtime DB number in the form PEEK DB [#dbnr], #offset, which is the only memory instruction that allows an indexed DB number directly without constructing an ANY.

Can BLKMOV copy data between two Static areas of different FBs?

Yes, provided both Instance DBs are non-optimised. Construct the source ANY in TEMP from a parameter or input, and use P##staticBufferSrc / P##staticBufferDst as the ANY literals. The CPU copies the smaller of the two declared lengths; an explicit length check before the call is recommended.

Why does DPRD_DAT fail to compile when the Static buffer does not match the PROFINET transmit area size?

The S7-1500 CPU recognises the configured Transmit areas of its connected PROFINET partners at compile time. TIA Portal therefore enforces a length match between the RECORD operand and the partner's transmit area; a mismatch raises "Area length mismatch" at edit time. Resize the Static array to the exact byte count configured in the device properties.

What retVal does BLKMOV return on a successful move, and how should the FB handle non-zero values?

BLKMOV returns 0 on success, 0x80B1 if the source is shorter than the destination, 0x80B2 if the destination is shorter, 0x80B5 for an invalid area code, 0x80C3/0x80C4 in PLCSIM when the target DB is missing or wrong-sized. The FB should branch on retVal and surface a diagnostic bit; silent ignoring masks data-integrity issues during commissioning.

Back to blog