S7-200 to S7-300 Indirect Addressing Conversion Using SFC20 BLKMOV
Siemens S7-200 indirect addressing syntax such as *LD4 (where the destination address is read from local double-word LD4) does not have a 1:1 equivalent in the S7-300 / S7-400 instruction set. The replacement pattern is to construct an ANY pointer in a temporary variable and pass it to SFC20 BLKMOV. This article covers the ANY data structure, the SFC20 interface, the conversion procedure, complete STL and SCL code, verification steps, and the RET_VAL error matrix.
1. Overview of the Conversion Problem
In a STEP 7 Micro/Win S7-200 program the block-move operation typically looks like the following ladder snippet:
- Source area:
VW10000(fixed start address) - Destination area:
*LD4(indirect, the actual address is supplied by local double-word LD4) - Length: 4 bytes
The S7-200 CPU evaluates *LD4 at runtime, so the destination is dynamic. The S7-300 / S7-400 instruction set removes pointer dereferencing operators from ladder (FBD/LAD). To preserve the same runtime behavior the S7-300 engineer must:
- Build a 10-byte ANY pointer in a temporary DB or in the local stack (LW / P#).
- Call SFC20 BLKMOV and pass that ANY pointer as
SRCBLKorDSTBLK. - Handle the
RET_VALword to detect parameter errors at runtime.
POINTER / ANY data type and an AT view.2. Prerequisites
- STEP 7 V5.5 or STEP 7 Professional (TIA Portal V13+ for S7-300/S7-400 with the S7-300/S7-400 option package). See the STEP 7 V5.5 SP2 download page.
- S7-300 CPU 31x or 32x, or S7-400 CPU 41x, with firmware that supports SFC20 (all standard S7-CPU firmware).
- S7-200 source program (STEP 7 Micro/Win project) for reference. The Micro/Win IDE is not required for the target S7-300 build.
- Working knowledge of STL bit-slicing (L, T, LAR1, +AR1) or SCL AT-view on STRUCT.
3. Anatomy of the STEP 7 ANY Pointer (10 Bytes)
The ANY pointer is the universal "address + length + repetition" descriptor used by SFCs, SFBs, and FC parameter declarations of type ANY. Layout for the S7-300:
| Byte | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|---|---|---|
| 0 | Syntax ID (10h = area-internal, 11h = area-cross-section via DB no., 16h = area-cross-section via DB no. + memory area, etc.) | |||||||
| 1 | Transport size in bits per element: 01=BYTE, 02=WORD, 04=DWORD | |||||||
| 2,3 | Number of elements (WORD, little-endian, 1..n) | |||||||
| 4,5 | DB number (only when Syntax-ID >= 16h) or 0 | |||||||
| 6 | Memory area ID: 81h=Input (I), 82h=Output (Q), 83h=Bit memory (M), 84h=DB data, 85h=DI data, 86h=L stack, 87h=V (S7-200 legacy) | |||||||
| 7,8,9 | Byte address within the area, 24-bit, byte-granular. Bits 7/8/9 = bits 24/25/26/27 of the address when the transport size is BIT; otherwise the address is bit-shifted by the transport-size field. | |||||||
For SFC20 BLKMOV the transport size must be BYTE (01) and the length must be expressed as a number of bytes; therefore the byte address is used as-is.
3.1 Common ANY combinations
| Area | Byte 0 (SynID) | Byte 1 (Trans) | Byte 2/3 (Qty) | Byte 4/5 (DB) | Byte 6 (Area) | Bytes 7..9 (ByteAddr) |
|---|---|---|---|---|---|---|
| Inputs (I) | 10h | 01h | n (WORD) | 0000h | 81h | P# byte address |
| Outputs (Q) | 10h | 01h | n (WORD) | 0000h | 82h | P# byte address |
| Bit memory (M) | 10h | 01h | n (WORD) | 0000h | 83h | P# byte address |
| Global DB (DBX) | 10h | 01h | n (WORD) | DB no. | 84h | P# byte address |
| Instance DB (DIX) | 10h | 01h | n (WORD) | DI no. | 85h | P# byte address |
| L stack (local) | 10h | 01h | n (WORD) | 0000h | 86h | P# byte address |
DWORD_TO_POINTER semantics on area-cross-section pointers.4. SFC20 BLKMOV Interface
| Parameter | Declaration | Data Type | Description |
|---|---|---|---|
| SRCBLK | INPUT | ANY | Source area. Pointer to first byte of source + length. |
| RET_VAL | OUTPUT (INT) | INT | Return value. 0 = OK, non-zero = error (see section 8). |
| DSTBLK | OUTPUT | ANY | Destination area. Pointer to first byte of destination + length. |
SFC20 copies exactly count bytes starting at the address inside SRCBLK to the address inside DSTBLK. Overlapping source and destination areas are handled correctly: SFC20 uses an internal intermediate buffer to avoid corruption. Maximum length per call is 512 bytes.
5. Step-by-Step Migration Procedure
-
Identify the dynamic operand. In the S7-200 source, locate every usage of
*LD/*VWinside a BLKMOV block. Note the format (DB number, area code, byte offset) of each dynamic pointer. - Choose a work area. Reserve a 10-byte ANY in a global DB or in the local stack of the FC/FB. The local stack is volatile and is the recommended location when the ANY only lives during one call.
- Populate the ANY. Either pre-assemble it in the OB1 startup or assemble it on every call. For runtime-built pointers, place the 10 bytes into a temporary ANY tag using STL byte moves.
-
Call SFC20. Pass the constructed ANY to
SRCBLK(when the source is static and the destination is dynamic) or toDSTBLK(when the destination is static and the source is dynamic). For both dynamic, pass two ANY tags. - Evaluate RET_VAL. Connect the output to a status word and, if desired, raise an OB82 / OB121 fault on a non-zero return.
- Verify. Use a VAT table or PLCSIM to inspect memory before and after the SFC20 call, confirm length, address, and data integrity.
6. STL Example: Dynamic Destination in a Global DB
Task: copy 10 bytes from P#I 0.0 BYTE 10 (inputs IB0..IB9) to a destination inside DB100 whose start offset is supplied by MW200. Length is fixed at 10 bytes.
// FC "Build_Any_And_Move"
// Input parameter: i_destByteOffset (INT) - destination byte offset in DB100
// In/Out parameter: io_status (WORD) - RET_VAL mirror
L P##src_any_static // Pointer to constant source ANY
LAR1
L P##dst_any // Pointer to work ANY in local stack
LAR2
// Build DSTBLK ANY for DB100, offset = i_destByteOffset, length 10
L B#16#10 // Syntax ID = area-internal
T LB 6 // dst_any[0]
L B#16#01 // Transport size BYTE
T LB 7 // dst_any[1]
L 10 // Count
T LW 8 // dst_any[2..3]
L 0 // DB-no field unused for SynID=10h
T LW 10 // dst_any[4..5]
L B#16#84 // Area = DB
T LB 12 // dst_any[6]
L i_destByteOffset // runtime offset
SLD 3 // convert byte offset to bit address
T LD 13 // dst_any[7..10]
CALL SFC 20
SRCBLK := P#I 0.0 BYTE 10
RET_VAL:= MW 1000
DSTBLK := dst_any
Notes on the STL code:
-
P##src_any_staticis a pointer literal to a 10-byte constant declared in the FC's TEMP area, or directly toP#I 0.0 BYTE 10. - The
SLD 3instruction shifts the byte offset left by 3 bits, producing a 24-bit bit-address (bytes 7..9 of the ANY), exactly what SFC20 expects when the transport size is BYTE. - If the destination lies in an instance DB instead of a global DB, set the Syntax ID to
10h, the Area byte to85h, and place the instance DB number in bytes 4..5.
7. SCL Example: AT-View on a STRUCT
SCL hides the byte layout behind the POINTER and ANY data types, but you can still access the inner fields with an AT view. The following FC builds the same destination ANY and calls SFC20.
FUNCTION FC 100 : VOID
VAR_TEMP
t_retVal : INT;
t_srcAny : ANY;
t_dstAny : ANY;
t_dstView : AT VIEW OF t_dstAny : STRUCT
synID : BYTE;
trans : BYTE;
count : WORD;
dbno : WORD;
area : BYTE;
addr : DWORD;
END_STRUCT;
END_VAR
VAR_INPUT
i_destByteOffset : INT; // Destination byte offset inside DB100
END_VAR
BEGIN
// Static source ANY (built once via literal in OB1 or initializer)
t_srcAny := P#I 0.0 BYTE 10;
// Build the destination ANY byte by byte using the AT view
t_dstView.synID := B#16#10; // area-internal
t_dstView.trans := B#16#01; // BYTE
t_dstView.count := 10; // 10 bytes
t_dstView.dbno := 0; // unused for SynID=10h
t_dstView.area := B#16#84; // DB
t_dstView.addr := SHL(IN:=WORD_TO_DWORD(INT_TO_WORD(i_destByteOffset)),
N:=3); // byte -> bit shift
SFC20(SRCBLK := t_srcAny,
RET_VAL:= t_retVal,
DSTBLK := t_dstAny);
IF t_retVal <> 0 THEN
// Save RET_VAL in MW1000 to surface in VAT/PLCSIM
"DB_Status".SFC20_RetVal := t_retVal;
END_IF;
END_FUNCTION
8. RET_VAL Error Matrix for SFC20
| RET_VAL (hex) | Meaning | Recommended action |
|---|---|---|
| 0000h | No error. | None. |
| 8091h | Source ANY pointer out of process image / DB range. | Validate the constructed bytes 6/7/8/9. Check that the Syntax ID and memory area ID match. |
| 8092h | Destination ANY pointer out of process image / DB range. | Same as 8091 but for DSTBLK. |
| 80A1h | Source ANY has transport size != BYTE. | Force byte 1 of the ANY to 01h. |
| 80A2h | Destination ANY has transport size != BYTE. | Force byte 1 of the ANY to 01h. |
| 80B1h | Source area length = 0 or > 512. | Clamp count to 1..512. |
| 80B2h | Destination area length = 0 or > 512. | Clamp count to 1..512. |
| 80C1h | Source byte range overlaps target byte range in unsupported way. | Split into two SFC20 calls. |
| 8xyyh (general) | CPU fault during block copy. | Refer to STEP 7 System Software manual for the xy encoding. |
9. Verification
-
Static VAT check. Create a VAT with the source bytes (e.g.,
IB0..IB9) and the destination DB area (DB100.DBB0..DB100.DBB9or any offset). Trigger the FC and confirm a data-id match. - Monitor control bits. Use PLCSIM or a real CPU. Place a breakpoint inside FC100 (in SCL) or a single-step in STL and inspect the temp ANY bytes after the AT-view population.
-
Edge cases.
- Set
i_destByteOffsetto0,-1(illegal), and the maximum legal value (DB size - 10). The first must succeed, the second must return a non-zero RET_VAL, the third must succeed. - Change the DB number at runtime. Replace
t_dstView.dbnowith another open DB to confirm that the Syntax ID / area combination works for both84h(global DB) and85h(instance DB).
- Set
-
Performance. SFC20 execution time depends on length and CPU. For a 10-byte copy on an S7-315-2 PN/DP expect
< 50 us; on an S7-412 expect< 20 us. Larger copies scale linearly. If a sub-millisecond cycle is required, place the SFC20 call inside OB35 with the appropriate phase offset.
10. Troubleshooting
| Symptom | Likely root cause | Fix |
|---|---|---|
| SFC20 returns 8091h on a DB source. | Syntax ID set to 10h but DB number field non-zero, or DB not loaded. | For area-cross-section pointer, use Syntax ID 16h; for area-internal use 10h with DB number = 0. |
| SFC20 returns 80A1h. | Transport size set to 02h (WORD) by mistake. | Hard-code byte 1 of the ANY to 01h. |
| CPU goes to STOP after the call. | Destination offset beyond DB length, or DB not present. | Validate i_destByteOffset + count against the DB length; ensure the destination DB is loaded in OB100 / OB1 startup with SFC22 / SFC23 or SFC24 / SFC25 as needed. |
| Data copied but offset is 8x too large. | Byte offset was loaded without the SLD 3 / SHL(...,3) shift. |
Always shift the byte offset left by 3 to obtain the bit address required by the ANY pointer. |
| Data scrambled / wrong area. | Memory area byte (offset 6) wrong. | 83h = M, 84h = DB, 85h = DI, 86h = L stack. |
| Single-byte instead of multi-byte copy. | Count word (bytes 2/3) set to 0. | Set count to the actual length BEFORE calling SFC20. |
11. Portability Notes (S7-400 / TIA Portal / S7-1500)
- The same ANY layout applies to S7-400 and to the S7-300 as a derivative. On S7-400 the maximum SFC20 length is still 512 bytes.
- In TIA Portal, SFC20 is exposed as BLKMOV inside the "Basic Instructions" palette and supports the same ANY input format. The legacy AT-view trick still works.
- On S7-1500, the MOVE_BLK_VARIABLE instruction replaces SFC20; it uses the
VARIANTdata type instead of an ANY. The dynamic pointer concept still applies, but at a different syntactic level. If the code must run on both S7-300 and S7-1500, keep the SFC20 call in a dedicated FC and gate it by CPU type, or use a wrapper FB on S7-1500 that accepts a VARIANT.
12. Field-Commissioning Checklist
- [ ] All dynamic offsets used at runtime have been range-checked against the destination DB size before calling SFC20.
- [ ] RET_VAL is wired to a status word that HMI can read.
- [ ] A non-zero RET_VAL triggers a one-shot alarm so that the error is captured even if it clears before HMI polls.
- [ ] A watchdog timer (e.g., OB80) is enabled if the SFC20 is critical-path.
- [ ] All DBs referenced by ANY pointers are loaded and unlinked (avoid download in RUN when DB length changes; use "Download in RUN" with care).
- [ ] A reference S7-200 program (or printout) is archived in the project vault for audit trail.
13. Frequently Asked Questions
Can I keep the S7-200 pointer syntax *LD4 on an S7-300?
No. The asterisk dereference operator is a STEP 7 Micro/Win construct and is not recognized in STEP 7 (LAD/FBD/STL). Replace it with a runtime-built ANY pointer that is passed to SFC20 BLKMOV (or another ANY-aware block).
What is the maximum length that SFC20 BLKMOV copies in one call?
512 bytes. For longer transfers call SFC20 in a loop, incrementing the byte offset of the source and destination ANYs by 512 on each iteration. Verify that the loop counter never exceeds the total length.
Why does SFC20 return 8091h even though the destination DB exists?
The most common reason is that the Syntax ID in the ANY is wrong. Use 10h for area-internal (DB number field must be 0) or 16h for area-cross-section (DB number field set to the target DB). A second cause is the bit-address not shifted left by 3, which yields a misaligned address.
Does SFC20 work on instance DBs (DI)?
Yes. Set the memory area byte (offset 6) of the ANY to 85h and place the instance DB number in bytes 4..5 of the ANY. The Syntax ID remains 10h for area-internal pointers; use 16h only when crossing area boundaries.
How do I migrate the same code to TIA Portal / S7-1500?
For TIA Portal V13+ on S7-300/400 use SFC20 BLKMOV with the same ANY layout. For S7-1500 replace SFC20 with the MOVE_BLK_VARIABLE instruction, which accepts a VARIANT instead of an ANY. Wrap the call in a dedicated FC so that the migration is localized.