Resolving S7-1500 BLKMOV Variable DB Pointer Syntax Errors

David Krause13 min read
S7-1200SiemensTroubleshooting
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 Overview

A program ported from SIMATIC S7-300/400 to SIMATIC S7-1500 stops compiling in TIA Portal when the BLKMOV (Move block) instruction is parameterized with a pointer whose Data Block number is supposed to be evaluated at runtime. The legacy ANY pointer literal P#DBX0.0 BYTE 10 is rejected by the S7-1500 compiler with the F1 help text:

The address is missing the specification of the data block.

The original STL block looks like this:

OPN   DB [ "Row Number" ]                // open the DB selected by the row index
CALL  BLKMOV
      SRCBLK := #"Carrier lift 1"        // ANY pointer, source
      RET_VAL := #"retval blkmov"
      DSTBLK := P#DBX0.0 BYTE 10          // compiler error in S7-1500

The OPN DB [#RowNumber] opens the correct DB for direct access, but BLKMOV does not consume the open DB. BLKMOV uses the pointer passed at SRCBLK and DSTBLK as an absolute, fully qualified address, and P#DBX0.0 BYTE 10 lacks the DB number (and the correct separator for S7-1500). The result is a compile-time error, not a runtime error.

2. Why the S7-300 Syntax Fails on S7-1500

Siemens tightened the type system of the S7-1500 CPU family. Compared with the S7-300/400 ANY pointer, three differences matter for BLKMOV:

Property S7-300/400 S7-1500
Pointer length 10 bytes (ANY) 16 bytes (ANY / POINTER / VARIANT)
Literal syntax P#DBX0.0 BYTE 10 (no DB number required in some contexts) P#DB10.DBX0.0 BYTE 10 (DB number mandatory, dot separator)
Type checking Permissive, deferred to runtime Strict, compile-time ANY must be fully resolvable
DB selection OPN DB has side effect on following pointer instructions OPN DB has no effect on ANY/POINTER parameters

The S7-1500 BLKMOV implementation is documented in the Siemens function manual BLKMOV - Move block (S7-1500). The instruction copies the contents of a memory area (source area) to a destination area, and both areas must be defined by a complete ANY pointer. The S7-1500 compiler refuses any ANY pointer whose DB number is not a constant or whose area is unresolved.

3. Root Cause Analysis

Three distinct problems hide behind the single F1 message:

  1. Missing DB number. The literal P#DBX0.0 BYTE 10 never contains a DB number. In S7-300 STL the open DB context was used as a fallback, but S7-1500 BLKMOV must see a fully qualified pointer.
  2. Wrong separator. Even with a DB number, S7-1500 expects P#DB<n>.DBX<b>.<bit>. The legacy form P#DB<n>DBX<b>.<bit> is rejected.
  3. Dynamic DB selection. The user wants the DB to be variable, selected by "Row Number" at runtime. A literal ANY pointer cannot be dynamic; a static ANY requires a fixed DB number.

Reading the TIA Portal F1 help on the Pointer, ANY, and VARIANT data types confirms that an ANY parameter of BLKMOV must either be a constant literal with a fixed DB number, a previously constructed ANY, or a VARIANT that is bound to a variable at call time.

4. Solution 1 - Use a Constant DB Number in the ANY Literal

If the destination DB is always the same instance, replace the broken literal with a fully qualified one. This is the smallest change to the original code:

CALL  BLKMOV
      SRCBLK := P#DB100.DBX0.0 BYTE 10
      RET_VAL := #"retval blkmov"
      DSTBLK := P#DB200.DBX0.0 BYTE 10

Notes:

  • Both SRCBLK and DSTBLK must be fully qualified ANY pointers; the S7-1500 will not assume a DB from OPN DB.
  • The block size is fixed at compile time. Any variation in length must be done by recomputing the pointer or by switching to Solution 2/3/4.
  • This approach mirrors the S7-300 code semantically only when the row index is not actually needed; otherwise it is a workaround, not a fix.

5. Solution 2 - Use the VARIANT Data Type

The cleanest S7-1500-native solution is to switch blk_type from ANY to VARIANT. The BLKMOV block for S7-1500 is overloaded: the same symbol can be called with ANY, POINTER, or VARIANT parameters. When blk_type := VARIANT, both SRCBLK and DSTBLK accept a tag of an FB, DB, multi-instance, or PLC data type, and the actual DB number is bound at call time.

// FB interface
VAR_IN_OUT
    srcVariant : VARIANT;        // bound to "Carrier lift 1" at call site
    dstVariant : VARIANT;        // bound to the target row in the call FB
END_VAR
VAR
    retval_blkmov : Int;
END_VAR

// Body
#retval_blkmov := BLKMOV(srcblk := #srcVariant,
                         dstblk := #dstVariant);

In the calling FB/OB, pass any "Row[i]".field or a full DB tag:

"carrier".move(#srcVariant := "Carrier lift 1",
               #dstVariant := "rowDB"."row"[#rowIdx]);

The VARIANT form is the recommended pattern in TIA Portal V15 and later for S7-1500 and ET 200SP CPUs. It removes the need to construct ANY pointers manually and gives full type safety.

6. Solution 3 - PEEK and POKE for Single-Element Access

If the application is element-oriented (reading/writing a single variable, not a block), replace BLKMOV with the S7-1500 native PEEK and POKE instructions. Both accept a fully qualified pointer to the DB and the byte/bit offset. PEEK/POKE work with symbolic references, constants, or pointer parameters and avoid ANY construction entirely.

// Read INT from DB[rowIdx].data[0] at offset 0
#value := PEEK_WORD(area := 16#84,        // 0x84 = DB area
                    dbNumber := #rowIdx,
                    byteOffset := 0);

// Write INT to the same location
POKE_WORD(area := 16#84,
          dbNumber := #rowIdx,
          byteOffset := 0,
          value := #value);

PEEK/POKE variants exist for BOOL, BYTE, WORD, DWORD, CHAR, INT, DINT, REAL, and LREAL. They are the natural substitute for any S7-300 code that read/wrote through a self-built ANY pointer in a loop.

7. Solution 4 - Construct an ANY Pointer at Runtime

For applications that still need a true ANY because the call site must be a generic BLKMOV (for example, in a library FC that has to remain source-compatible with S7-300), the only correct approach is to build the 16-byte ANY in a temporary variable and pass that variable to the block.

The S7-1500 ANY layout (big-endian byte offsets shown):

Byte Field Meaning
0..1 ID 10 = ANY (must be 0x10 0x00)
2..3 Data type 02 = BYTE, 04 = WORD, 05 = INT, 06 = DWORD, 07 = DINT, 08 = REAL, ...
4..5 Count Number of elements (data type units)
6..7 DB number 0 if area is not DB
8 Area 16#84 = DB, 16#81 = Inputs (I), 16#82 = Outputs (Q), 16#83 = Merkers (M)
9..11 Byte/bit byte offset (24 bit), bit in low bits of byte 9
12..15 Reserved 0

SCL example that builds the ANY and calls BLKMOV:

VAR_TEMP
    srcAny : ANY;        // 16 bytes
    dstAny : ANY;
    rVal   : Int;
END_VAR

// Initialise source ANY pointing to DB[srcDB].DBX[srcOffset] for 10 bytes
#srcAny :=  ANY#10#02#10#0#0#%DB(srcDB)#%DBX(srcOffset, 0)#0#0#0#0#0#0;
#dstAny :=  ANY#10#02#10#0#0#%DB(dstDB)#%DBX(dstOffset, 0)#0#0#0#0#0#0;

#rVal := BLKMOV(srcblk := #srcAny,
                dstblk := #dstAny);

STL equivalent using the block BLKMOV and a previously constructed temp ANY:

// Build srcAny in 16 bytes of TEMP using absolute assignment
      L     B#16#10               // ANY ID
      T     LB    0
      L     B#16#0
      T     LB    1
      L     B#16#2                // data type BYTE
      T     LB    2
      ...
      L     #srcDB                // DB number from runtime
      T     LW    6
      L     B#16#84               // area = DB
      T     LB    8
      ...
      CALL  BLKMOV
            SRCBLK := #srcAny
            RET_VAL := #retval
            DSTBLK := #dstAny
Field tip: always pre-initialise the 16 bytes with zeros before writing the fields. TIA Portal V16+ accepts the SCL literal form ANY#... shown above; for older firmware (V13-V15) the literal is not available and you must use the FC MOVE_BLK_VARIANT (see Solution 5) or build the ANY byte-by-byte in STL.

8. Solution 5 - MOVE_BLK_VARIANT for Source-Code-Compatible Libraries

The Siemens library block MOVE_BLK_VARIANT (FC, distributed with the global Siemens library) accepts a VARIANT and a fully symbolic destination. Unlike BLKMOV, it does not require ANY construction in the caller, supports optimized block access, and works with arrays of any data type.

"MOVE_BLK_VARIANT"(src := #srcVariant,
                   dst := #dstVariant,
                   count := 10);

When the legacy BLKMOV FC cannot be rebuilt, this is the lowest-risk replacement.

9. BLKMOV Parameter Reference (S7-1500)

Parameter Type Direction Description
SRCBLK ANY / VARIANT Input Pointer to source area; must be fully qualified.
RET_VAL INT Output Error code (see below).
DSTBLK ANY / VARIANT Output Pointer to destination area; must be fully qualified.

Common RET_VAL codes returned by BLKMOV on S7-1500:

RET_VAL (hex) Meaning
0000 No error
80A1 Source and destination areas overlap and cannot be copied
80B1 Source area is invalid (DB does not exist or wrong length)
80B2 Destination area is invalid (DB does not exist or wrong length)
80B4 Area length of 0 or negative value
80B5 Source area not a multiple of the data type width
80B6 Destination area not a multiple of the data type width
80B7 Source and destination overlap in a way BLKMOV cannot resolve
80B8 Access to a DB that is currently locked (e.g. by another block)
80B9 Access to a deleted DB

These codes match the legacy S7-300 behaviour and are documented in the TIA Portal online help for the BLKMOV instruction.

10. SCL Implementation Pattern

The cleanest S7-1500 implementation for a "move a row from a source DB to a row in a destination DB, where both DBs and the row number are variable" looks like this:

FUNCTION_BLOCK "carrier_move"
VAR
    srcRow : REF TO "row_type";     // symbolic reference to a row
    dstRow : REF TO "row_type";
END_VAR

VAR_TEMP
    srcVar : VARIANT;
    dstVar : VARIANT;
    retVal : Int;
END_VAR

BEGIN
    #srcVar := #srcRow;            // implicit dereferencing into VARIANT
    #dstVar := #dstRow;
    #retVal := BLKMOV(srcblk := #srcVar,
                      dstblk := #dstVar);
    IF #retVal <> 0 THEN
        ; // raise diagnostic, set status word, log to HMI
    END_IF;
END_FUNCTION_BLOCK

REF TO is a S7-1500 feature that gives compile-time-checked indirection without ANY manipulation, and can be assigned to VARIANT for BLKMOV. This pattern replaces the manual OPN DB [#row] + BLKMOV idiom used on S7-300.

11. STL Implementation Pattern

When the project must remain in STL (for example, in a project that also supports S7-300 IEC targets), build the 16-byte ANY once and call BLKMOV. Use the F1 help cross-reference to confirm syntax for the firmware version you are running (TIA Portal V18 / V19 / V20).

FUNCTION FC 100 : Void
VAR_TEMP
    srcAny : ANY;     // 16 bytes, all zeroed by default
    dstAny : ANY;
    retval : Int;
END_VAR
BEGIN
NETWORK
TITLE  = Build ANY for source
      L     10                          // ANY ID
      T     LB  0
      L     0
      T     LB  1
      L     2                           // data type BYTE
      T     LB  2
      L     0
      T     LB  3
      L     10                          // count = 10 bytes
      T     LW  4
      L     #srcDB                      // dynamic DB number
      T     LW  6
      L     B#16#84                     // area = DB
      T     LB  8
      L     0                           // byte offset
      T     LW  9
      L     0
      T     LW 11
      L     0
      T     LW 12
      T     LW 14

NETWORK
TITLE  = Same for destination
      ...                               // mirror for dstAny

NETWORK
TITLE  = Call BLKMOV
      CALL BLKMOV (
           SRCBLK  := #srcAny,
           RET_VAL := #retval,
           DSTBLK  := #dstAny);
END_FUNCTION
Field tip: when copying the source pattern, build dstAny only after srcAny - the compiler can warn or fold the temp variables if you do both in one network. Always zero the entire 16-byte temp before assigning fields; the S7-1500 firmware reads the reserved bytes 12-15 and non-zero garbage can produce 80B1 at runtime.

12. Verification Procedure

  1. Compile. TIA Portal must compile the FB/FC without warnings. A re-occurrence of "address is missing the specification of the data block" means the ANY is still incomplete.
  2. Download to PLCSIM or to a physical CPU (S7-1511, S7-1515, S7-1518, ET 200SP CPU 1510SP-1 PN, etc.) and place a watchpoint on the RET_VAL output.
  3. Force a normal move with valid DB numbers and confirm RET_VAL = 0 in online view.
  4. Force the boundary cases that S7-300 code often skipped:
    • Length = 0 (expect 80B4).
    • Length not a multiple of the data type (expect 80B5 / 80B6).
    • Overlapping source and destination (expect 80A1 or 80B7).
    • DB number of a DB that has not been loaded (expect 80B1).
  5. Read/write the moved bytes in the online watch table and confirm they match the source. For symbolic access, use "Monitor/Modify" with the actual DB instance.
  6. Test the row-dispatch path by varying the row index input and verifying the destination area is updated for each row.

13. Migration Checklist from S7-300 to S7-1500

Item S7-300/400 idiom S7-1500 replacement
Open DB before any access OPN DB [#row] Symbolic reference or build ANY
Dynamic BLKMOV BLKMOV(SRCBLK := P#DBX0.0 BYTE 10) VARIANT, ANY construction, PEEK/POKE, MOVE_BLK_VARIANT
Pointer length 10 bytes 16 bytes
Type check Runtime Compile time
Optimised block access Not used Use symbolic / REF TO / VARIANT for best performance

When porting a STEP 7 V5.5 program to TIA Portal, run the migration tool first. The compiler will flag every dynamic ANY that needs to be re-built. Plan a re-test pass for each call site - runtime errors that were silent on S7-300 (because of permissive ANY handling) often surface as 80B1/80B2 on S7-1500.

14. Troubleshooting Matrix

Symptom Likely cause Action
Compile error: "address is missing the specification of the data block" ANY literal P#DBX0.0 BYTE 10 lacks DB number Add DB number, use P#DB<n>.DBX0.0 BYTE 10
Compile error in SCL: "Incompatible types" Passes WORD to an ANY input Use VARIANT or build a temp ANY
Runtime 80B1 DB number variable holds a number for which no DB is loaded Check "Active DB" in DB-info; verify the DB number is within the project
Runtime 80A1 Source and destination overlap, BLKMOV cannot resolve Split the copy into two non-overlapping moves, or use the MOVE_BLK_VARIANT copy routine
Runtime 80B4 Length = 0 (variable was zeroed at runtime) Guard the call with a length > 0 check
Destination data is shifted by one byte Reserved bytes 12-15 of the 16-byte ANY were left as garbage from a previous call Zero the temp ANY in a startup OB or before each BLKMOV call
Compile warning: "Implicit conversion ANY → VARIANT" BLKMOV is the legacy FC; new code should use the SCL instruction BLKMOV(src, dst) Migrate to the SCL inline form to use VARIANT

15. Performance and Memory Notes

S7-1500 BLKMOV operates on a per-byte basis at runtime. The SCL inline BLKMOV() instruction can be flagged as "move in CPU" in the project properties; for non-overlapping areas, the compiler may lower it to a single MOV block operation, which is significantly faster. The legacy FC BLKMOV is a real FB that always loops; replace it with the inline instruction whenever you do not need the FC's blk_type selector.

For arrays of structured data types, prefer MOVE_BLK_VARIANT (which can move by element count, not by byte count) or the optimised symbolic REF TO pattern; both avoid the byte-by-byte BLKMOV loop and avoid the manual ANY construction.

16. Frequently Asked Questions

Why does S7-1500 BLKMOV reject P#DBX0.0 BYTE 10 even though it worked on S7-300?

Because the S7-1500 compiler requires a fully qualified ANY pointer, including the DB number and the dot separator. P#DBX0.0 BYTE 10 has neither. Replace it with P#DB<n>.DBX0.0 BYTE 10 or move to a VARIANT-based call.

Can BLKMOV on S7-1500 use a DB number that is computed at runtime?

Not directly through a literal. Build a 16-byte ANY in a temporary variable (Solution 4) and pass that to BLKMOV, or switch the input to VARIANT and bind the row at the call site (Solution 2). Both approaches are supported from TIA Portal V14 onwards.

Is there a quick replacement for BLKMOV that does not need ANY construction?

Yes. Use PEEK and POKE for single-element reads/writes, or MOVE_BLK_VARIANT from the global Siemens library for block copies. Both work with symbolic references and a fully dynamic DB number.

What does the S7-1500 BLKMOV return code 80B1 mean?

80B1 means the source area is invalid - typically because the DB number passed in the ANY does not exist in the loaded project, or the byte offset plus length exceeds the DB length. Verify the DB number, the byte offset, and the count of bytes in the call.

Does OPN DB still work for dynamic DB access on S7-1500?

OPN DB still works for direct DBx access, but it has no side effect on BLKMOV, PEEK, POKE, or any other instruction that takes a pointer or VARIANT parameter. Use ANY construction or VARIANT for those instructions.

Where is the official BLKMOV (S7-1500) manual?

See the Siemens documentation portal at BLKMOV - Move block (S7-1500) for the S7-1500 STL/SCL reference, and the Siemens Industry Online Support at support.industry.siemens.com for KB articles, firmware notes, and library blocks such as MOVE_BLK_VARIANT.

Back to blog