The BLKMOV (Block Move) instruction copies the contents of one memory area to another in a single operation. When called from inside a Function Block (FB) with re-usable instances, the source and destination addresses typically change per instance. The clean way to handle this in STEP 7 and TIA Portal is to expose the ResultDataPointer / DSTBLK input as an ANY-typed FB parameter, build that ANY pointer at the call site, and let the block copy arbitrary data without recompiling the FB.
This reference covers the S7-300/400 (SFC20 / classic STEP 7), S7-1200/1500 (native BLKMOV in TIA Portal V13.1+), the exact 10-byte (and 8-byte S7-1500) ANY pointer layout, FB declaration in SCL, multi-instance usage, and the most common BLKMOV/ANY errors observed in commissioning.
1. Overview: What BLKMOV Does
BLKMOV moves a contiguous block of bytes, words, or DWords from a source area (SRCBLK / SourceDataPointer) to a destination area (DSTBLK / ResultDataPointer). It is the binary equivalent of a memcpy and is non-destructive at the source.
| Controller | Implementation | Symbolic name | Max length | Notes |
|---|---|---|---|---|
| S7-300 / S7-400 | System Function SFC 20 | BLKMOV (SFC20) | 32 384 bytes | ANY pointer = 10 bytes |
| S7-1200 | Native instruction | MOVE_BLK / BLKMOV | 16 384 bytes | Available in LAD/FBD/SCL |
| S7-1500 | Native instruction | MOVE_BLK / BLKMOV (legacy) | 16 384 bytes (typ.) | ANY pointer = 8 bytes (V2.0+) |
| WinAC / PC-based | SFC 20 | BLKMOV (SFC20) | 32 384 bytes | Same ANY layout as S7-400 |
According to the Siemens manual BLKMOV: Move block (STEP 7 Professional V13.1), the move operation runs in a single OB cycle and is typically used to copy DB contents, I/O areas, or M areas. See the Siemens support entry ID 109011420 for the official description.
2. Prerequisites
- Engineering tool: TIA Portal V13.1 SP1 or later (V16+ recommended for S7-1500). Classic STEP 7 V5.5 SP2+ for SFC20 usage.
-
Controller firmware:
- S7-1200: firmware V4.0 or higher (for optimized block access and the legacy BLKMOV variant).
- S7-1500: firmware V1.5 minimum, V2.0+ recommended (extends ANY pointer handling and supports symbolic slice access).
- Library access: Standard library > Program blocks > Move operations in TIA Portal, or Standard Library > System Function Blocks > SFC20 in classic STEP 7.
- FB knowledge: variable declaration interface (Input, InOut, Static, Temp) and the difference between pass-by-value (Input) and pass-by-reference (InOut) for structured data.
3. The ANY Pointer Structure
An ANY is a 10-byte (S7-300/400) or 8-byte (S7-1500, optimized) descriptor that contains all the information a block needs to access a variable without knowing its absolute address at compile time.
3.1 S7-300 / S7-400 ANY layout (10 bytes)
| Byte | Content | Meaning | Example |
|---|---|---|---|
| 0, 1 | Syntax ID + 0x10 (Type) | Data type code (see Table 3) | 0x10 0x02 = BYTE |
| 2, 3 | Count | Number of elements (not bytes) for elementary types, or DB number for DBW/DBD references | 0x0064 = 100 elements |
| 4, 5 | DB number | Source or destination DB (0 if not a DB) | 0x000A = DB 10 |
| 6 | Memory area | Area code: 0x81=DB, 0x82=DI, 0x83=M, 0x84=P, 0x85=I, 0x86=Q, 0x87=L, 0x00=0x86 area byte padding | 0x81 = DB |
| 7, 8, 9 | Byte offset | 24-bit pointer to start byte (bit-addressing encoded) | 0x00 0x00 0x08 = offset 8 |
3.2 S7-1500 ANY layout (8 bytes, optimized)
On S7-1500, the ANY pointer is reduced to 8 bytes when the parameter is declared as Variant in TIA Portal V14+. The legacy 10-byte form is still accepted by BLKMOV for compatibility, but new code should use the Variant data type for symbolic-any handling. The TIA Portal BLKMOV: Move block (S7-1500) reference describes the legacy BLKMOV that still accepts a 10-byte ANY.
3.3 Data type codes (byte 1 of the ANY)
| Type ID (hex) | Data type | Element size | Count unit |
|---|---|---|---|
| 0x01 | BOOL | 1 bit (packed) | bits |
| 0x02 | BYTE | 1 byte | bytes |
| 0x03 | CHAR | 1 byte | bytes |
| 0x04 | WORD | 2 bytes | words |
| 0x05 | INT | 2 bytes | words |
| 0x06 | DWORD | 4 bytes | double words |
| 0x07 | DINT | 4 bytes | double words |
| 0x08 | REAL | 4 bytes | double words |
| 0x09 | DATE | 2 bytes | words |
| 0x0A | TOD | 4 bytes | double words |
| 0x0B | TIME | 4 bytes | double words |
| 0x0C | S5TIME | 2 bytes | words |
| 0x0E | DATE_AND_TIME | 8 bytes | bytes |
| 0x13 | STRING | 256 bytes max | bytes |
4. Declaring the BLKMOV Inputs in a Function Block
The key decision is which input category to use for the ANY pointer.
| Interface | Pass-by | Any type | Supported? | Use case |
|---|---|---|---|---|
| Input (VAR_INPUT) | Value (copy) | ANY | Yes - copy at call time | Read-only source; FB can read but the caller still owns the data |
| InOut (VAR_IN_OUT) | Reference (pointer) | ANY | Yes - direct reference | Destination written by the FB; caller sees updated data |
| Static (VAR_STAT) | Instance area | ANY | Discouraged | Only for one-off FBs that always move the same area |
Rule of thumb:
-
SRCBLK(source) → declare as VAR_INPUT of type ANY. -
DSTBLK(destination) → declare as VAR_IN_OUT of type ANY. Anything written by the FB must be reflected back to the caller's memory.
4.1 Example FB declaration block (SCL)
FUNCTION_BLOCK FB_MoveBlock
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
SRCBLK : ANY; // Source area (input - pass by value/descriptor)
END_VAR
VAR_IN_OUT
DSTBLK : ANY; // Destination area (in-out - pass by reference)
END_VAR
VAR_INPUT
Execute : BOOL; // Edge-triggered execution
END_VAR
VAR_OUTPUT
Done : BOOL;
Error : BOOL;
Status : WORD; // SFC20/UMOVE_BLK_RET error code
END_VAR
VAR_TEMP
retVal : INT;
END_VAR
BEGIN
IF Execute THEN
retVal := BLKMOV(srcblk := SRCBLK, dstblk := DSTBLK);
IF retVal = 0 THEN
Done := TRUE;
Error := FALSE;
Status := 0;
ELSE
Done := FALSE;
Error := TRUE;
Status := WORD#16#8001 OR INT_TO_WORD(retVal);
END_IF;
END_IF;
END_FUNCTION_BLOCK
This declaration matches the Siemens SCL pattern for BLKMOV described in the TIA Portal V20 BLKMOV (S7-1500) reference.
5. Step-by-Step: Calling the FB from OB1 (SCL)
Step 1. Create two data blocks (or use a DB and a separate M area) to act as the source and destination.
DATA_BLOCK DB_Source
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
STRUCT
Header : ARRAY[0..9] OF BYTE; // 10 bytes
Payload : ARRAY[0..99] OF BYTE; // 100 bytes
Trailer : ARRAY[0..9] OF BYTE; // 10 bytes
END_STRUCT;
END_DATA_BLOCK
Step 2. Declare an instance DB of the FB, e.g. IDB_Move_1.
Step 3. In OB1, call the FB. The ANY pointers are constructed implicitly by TIA Portal when you drag a structured tag to the input pin.
OB1
BEGIN
// Move DB_Source.Payload to a global M area (120 bytes total)
"IDB_Move_1"(
SRCBLK := "DB_Source".Payload,
DSTBLK := "DB_Dest".Target, // VAR_IN_OUT, same structure
Execute := TRUE,
Done => "tag_Move_Done",
Error => "tag_Move_Err",
Status => "tag_Move_Status"
);
END_OB
The compiler automatically builds the ANY descriptor with the right area code (0x81 for DB), DB number, type code (0x02 for BYTE), count = 100, and offset matching the start of Payload. No manual ANY construction is required if the source/dest are symbolic tags.
6. Manual ANY Construction (Classic STEP 7 / SFC20)
In classic STEP 7 V5.5 or when the source/dest is computed at runtime, the ANY must be built in a temporary variable before calling SFC20.
6.1 STL example (S7-300/400)
CALL SFC 20
SRCBLK := P#DB10.DBX8.0 BYTE 100 // 100 bytes from DB10, offset 8
RET_VAL := MW100
DSTBLK := P#M 0.0 BYTE 100 // 100 bytes to MB0
// Status 0 = OK
// Status W#16#80A1 = alignment error, blocks must be byte aligned
// Status W#16#80B2 = area length error
// Status W#16#80B4 = pointer to slot 0 not allowed
6.2 Building an ANY in SCL when the DB number is dynamic
// Returns the size of the moved block, 0 on error
FUNCTION FC_BuildAny : INT
VAR_INPUT
iDB : INT;
iOffset : INT;
iLength : INT;
END_VAR
VAR_IN_OUT
ioAny : ANY; // 10-byte ANY to be filled
END_VAR
VAR_TEMP
pArea : POINTER; // 6-byte pointer helper
END_VAR
BEGIN
// Build pointer portion (6 bytes at offset 4 of ANY)
pArea := ADR(ioAny); // wrong, see below
// Easier: use the temporary as P#DB.DBX
// SCL does not allow direct ANY assembly; use the technique:
ioAny := (DOUBLE_INT_TO_DWORD(iDB) AND 16#0000FFFF)
+ DWORD#16#81000000 // area DB
+ SHL(DWORD#16#0,iOffset*8); // pseudo - placeholder
// In practice, use multi-assignment with TYPE helpers or move from
// a static ANY template. See Siemens entry ID 22783999 for the
// canonical pattern.
FC_BuildAny := 0;
END_FUNCTION
For the canonical approach, refer to the Siemens FAQ ID 22783999: Accessing I/O address area indirectly with SFC20, which shows the exact byte layout and SCL/FB-style ANY templates.
7. S7-300/400 (SFC20) vs. S7-1200/1500 (Native BLKMOV)
| Aspect | SFC20 (S7-300/400) | Native BLKMOV (S7-1200/1500) |
|---|---|---|
| ANY length | 10 bytes | 10 bytes (legacy) / 8 bytes (Variant) |
| RET_VAL semantics | 0 = OK, hex codes for errors | ENO = FALSE on error, status via output parameter |
| Overlapping areas | Not allowed (error 80B1) | Allowed for S7-1500, error on S7-1200 |
| Max length | 32 384 bytes | 16 384 bytes (per call) |
| Optimization | Symbolic not enforced | Optimized blocks preferred (Slice access) |
| Type check | None (raw bytes) | Type-safe when using Variant |
On S7-1500 with optimized blocks, prefer the Variant data type for the source/dest pins and use MOVE_BLK_VARIANT instead of the legacy BLKMOV. The legacy BLKMOV is retained for migration scenarios, as documented in the TIA Portal V20 BLKMOV (S7-1500) reference.
8. Multi-Instance Reuse of the FB
One of the main reasons for parameterizing the ANY pointers is to reuse the same FB in many locations. Declare one FB with ANY inputs and instantiate it multiple times as multi-instance or instance-DB.
// OB1 - three independent moves using the same FB
"IDB_Move_1"(SRCBLK := "DB_Source".Payload, DSTBLK := "DB_Dest".Target, Execute := TRUE);
"IDB_Move_2"(SRCBLK := "DB_Source".Header, DSTBLK := "DB_Log".Header, Execute := TRUE);
"IDB_Move_3"(SRCBLK := "DB_Recipe".Setpoint, DSTBLK := "DB_Drive".Cmd, Execute := TRUE);
Each instance DB contains its own Done, Error, and Status tags, while the FB body is shared. This is exactly the re-usability pattern requested in the original question and is the standard approach in TIA Portal project templates.
9. Error Codes and Status Word
| RET_VAL (hex) | Meaning | Remediation |
|---|---|---|
| 0000 | No error | — |
| 80A1 | Source/dest alignment error (must be byte boundary) | Round offsets to byte, use BYTE arrays only |
| 80B2 | Area length error (count 0 or too large) | Verify count ≤ remaining DB size and ≤ 16 384 / 32 384 |
| 80B1 | Source/dest overlap on S7-300/400 | Use distinct areas or a temporary buffer; OK on S7-1500 |
| 80B4 | Pointer references slot 0 (DI/DB misuse) | Re-check DB number and area code (0x81 vs 0x82) |
| 80B5 | Read-only destination on S7-300 | Switch to writable area; e.g. DB or M, not I |
| 8xxx | General OB priority or memory error | Reduce call frequency, split into multiple BLKMOVs |
DBX addresses, not DBX with bit suffix.10. Verification and Commissioning Steps
-
Static analysis: in TIA Portal, open the FB, place the cursor on the
SRCBLK/DSTBLKinput and read the Effective address in the Inspector. Confirm the area code, DB number, and offset match the intended source/destination. -
Watch table: create a watch table with the source array, the destination array, and the FB instance tags
Done,Error,Status. Force the source pattern (e.g.1..120) and run the FB. - Compare: read both source and destination arrays in online mode and verify they are bit-identical.
-
Edge cases: test with
Execute = FALSE(no move) and with the maximum legal length (e.g. 16 384 bytes on S7-1500). -
Trace: use the PLC trace in TIA Portal to capture the
Execute,Done, andStatusbits over several OB cycles to confirm the move completes in one scan. -
Boundary test: temporarily reduce the FB's
DSTBLKlength to 1 byte less than the source to verify the error path (expect 80B2).
11. Troubleshooting Matrix
| Observed symptom | Likely root cause | Fix |
|---|---|---|
| EN/ENO goes FALSE immediately on call | Source ANY has type code 0x10 (void) - no data type | Reconstruct ANY with non-zero type code (Table 3) |
| Destination unchanged, no error flag | Execute input pulsed too briefly (< 1 OB cycle) | Use a static or set/reset pattern; verify scan |
| Compiler error "Invalid ANY parameter" | VAR_IN_OUT used with elementary BOOL/INT instead of structured tag | Drag the structured array directly onto the pin |
| Online: status = 80B1, move aborts | Source and destination overlap on S7-300/400 | Use distinct areas or switch to S7-1500 with overlap support |
| SF LED on, diagnostics buffer "Area length error during read/write" | ANY count exceeds actual DB size | Reduce count or enlarge destination area |
| Move works in simulation but fails on real CPU | Optimized block access; tag is symbolic, ANY still uses absolute address | Recompile hardware, regenerate ANYs from symbolic tags |
| Move corrupts adjacent tags | Wrong offset, e.g. P#DB10.DBX1200.0 inside a 100-byte DB | Validate against DB length in watch table |
12. Best Practices
-
Prefer symbolic ANYs: drag a structured tag onto the FB input rather than typing
P#.... TIA Portal will build a correct 10-byte ANY for you. - Use VAR_IN_OUT for destinations: avoids an extra copy and ensures the caller sees the result without re-reading the instance DB.
-
Validate before move: in safety-related FBs, call
WRD_LIMor range-check the offset/length before issuing BLKMOV. - Avoid overlapping moves on S7-300/400; for S7-1500 the overlap is supported but the result is implementation-defined - keep copies logically distinct.
- Mark FB optimized: in the FB properties, set Optimized block access to TRUE for S7-1200/1500. The legacy 10-byte ANY is still accepted.
- Document the FB interface: in the FB header comment, state the maximum supported source/dest length, alignment requirement, and endianness (always little-endian on S7).
- Migrate to MOVE_BLK_VARIANT when starting a new S7-1500 project; it provides type checking and eliminates the manual ANY construction step.
13. Frequently Asked Questions
How do I pass an ANY pointer to BLKMOV inside a Siemens function block?
Declare the source as VAR_INPUT SRCBLK : ANY; and the destination as VAR_IN_OUT DSTBLK : ANY;, then call BLKMOV(SRCBLK := SRCBLK, DSTBLK := DSTBLK) in SCL. Wire the actual structured tag to each pin at the call site - TIA Portal will build the 10-byte (S7-300/400) or 8-byte (S7-1500 optimized) ANY descriptor automatically. See Siemens ID 109011420.
What is the difference between SFC20 and the native BLKMOV instruction?
SFC20 is the classic block-move system function for S7-300/400 with a fixed 10-byte ANY and a max length of 32 384 bytes. The native BLKMOV/MOVE_BLK on S7-1200/1500 is integrated into the CPU firmware, supports optimized block access, and integrates with the Variant data type. Status reporting differs: SFC20 uses a RET_VAL word, native BLKMOV uses ENO and an explicit status output.
Why does my BLKMOV return error code 80A1?
Error 80A1 is an alignment error - the source or destination ANY is not byte-aligned, or the type code is invalid. BLKMOV only operates on whole bytes; rebuild the pointer with a byte offset (e.g. P#DB10.DBX8.0 BYTE 100) and use a valid type ID from Table 3. Reference: Siemens ID 22783999.
Can the source and destination memory areas overlap?
On S7-300/400 with SFC20, overlapping areas produce error 80B1. On S7-1500 the firmware handles overlapping blocks, but the result is still implementation-defined, so the recommended practice is to keep source and destination logically separate (e.g. move into a temporary DB and then swap pointers).
How can I reuse the same BLKMOV FB for many different source/destination pairs?
Create one FB with VAR_INPUT SRCBLK : ANY and VAR_IN_OUT DSTBLK : ANY, then create one instance DB per call site (or use multi-instance declaration in a parent FB). Each instance keeps its own Done/Error/Status tags and the same body code moves whatever the caller has wired to the ANY pins.
What is the maximum length BLKMOV can copy in one call?
SFC20 (S7-300/400) supports up to 32 384 bytes. The native BLKMOV on S7-1200/1500 typically supports 16 384 bytes per call, but always check the firmware reference. For larger copies, loop the call in an OB or split into multiple BLKMOVs in a sequence.