1. Problem Statement: Bulk-Copying Optimized Data Blocks on S7-1519F
A common engineering task in TIA Portal V15 with an S7-1519F CPU is to copy the complete contents of one optimized data block (DB) to another DB that has the same layout. The destination DB typically mirrors the source, but is reserved for downstream logic, an external interface, or a snapshot / history buffer.
Engineers who reach for the familiar SFC20 (BLK_MOV) discover three problems on optimized blocks:
- The
CALL SFC20with an absolute source pointer such asP#DB1.DBX0.0 BYTE 20is rejected by the TIA Portal V15 compiler when the source or destination has the Optimized block access attribute enabled. - Even if the call is accepted at compile time, the runtime returns a status in
RET_VALbecause the internal pointer resolves to a region that the S7-1500 firmware does not expose through absolute addressing. - The classic, byte-counted approach is not safe for a block that contains
STRING,STRUCT,ARRAY,DTL, orWSTRINGelements, because the ANY/POINTER length must match the entire load image, including padding, exactly.
This article documents the supported alternatives, the firmware/build constraints, and the verification steps needed to copy an entire optimized DB to a second optimized DB on TIA Portal V15, S7-1500 / S7-1519F.
2. Why SFC20 (BLK_MOV) Fails on Optimized Blocks
SFC20 is the system function from the standard library that copies a contiguous range of bytes. Its interface is declared in type SFB-set "Standard Library / System Function Blocks":
| Parameter | Declaration | Data type | Meaning |
|---|---|---|---|
| SRCBLK | INPUT | VARIANT (S7-1500) / ANY (legacy) | Source area to be copied |
| RET_VAL | OUTPUT | INT | Error / status code (see Section 9) |
| DSTBLK | OUTPUT | VARIANT (S7-1500) / ANY (legacy) | Destination area |
On S7-1500 firmware, the source and destination must be absolutely addressable data areas. The attribute Optimized block access removes the fixed, contiguous byte layout and replaces it with symbolic slot management in the work memory. Two consequences follow:
- The compiler cannot derive a length from
P#DBx.DBX0.0 BYTE nbecause the symbol is not address-based. - You cannot reach the storage with the
PEEK/POKEsemantics of SFC20 even if the call is forced through.
Symptom in the TIA Portal V15 build log: "The declaration of the actual parameter SRCBLK / DSTBLK is not compatible with the formal parameter." At runtime, the status word carries W#16#8091 (length mismatch) or W#16#80B1 (DB wrong).
For an S7-1200, the situation is even more restrictive: SFC20 itself is not distributed in the S7-1200 instruction set, so the BLK_MOV instruction from the legacy S7-300/400 world is simply unavailable.
3. Prerequisites
- TIA Portal V15 (or V15.1, V16) installed; matching Siemens Industry Online Support service packs applied.
- S7-1500 / S7-1519F CPU with firmware compatible with the TIA Portal version. For TIA Portal V15 the safe baseline is firmware 2.6; for V15.1 the safe baseline is 2.7.
- Project with at least one optimized DB containing the source layout and a destination DB whose structure matches the source. Identical UDTs, identical ARRAY bounds, identical STRING lengths.
- For SCL solutions: an SCL source container or SCL block in the program.
- Online connection to the PLC for verification (PUT/GET is not required).
4. Method 1 — Wrap the Layout in a UDT, Then Copy with SCL ":="
The cleanest, fastest approach is to define one UDT that contains every element of your DB layout, and expose a single tag of that UDT inside the DB. The whole structure then becomes a single variable that the S7-1500 CPU can copy symbolically in one operation.
4.1 Create the UDT
- In the project tree, expand PLC_1 > Program blocks > PLC data types.
- Add a new data type, e.g.
UDT_Recipe. - Inside
UDT_Recipe, recreate exactly the layout of your DB: everySTRING,STRUCT,ARRAY,BOOL,REAL,DTL, etc. - Compile the UDT.
4.2 Add a Single UDT-Typed Tag to Each DB
- Open the source DB (e.g.
DB_Source). Delete all existing tags and add a single tagDataof typeUDT_Recipe. - Open the destination DB (e.g.
DB_Dest). Add a tagDataof typeUDT_Recipewith identical start values (or "---" if the destination is overwritten at runtime). - Compile both DBs.
4.3 Copy with a Single SCL Line
Add an SCL block (e.g. FB_Copy) and place the assignment inside a network:
"DB_Dest".Data := "DB_Source".Data;
That single line copies every byte of UDT_Recipe in a single SCL MOVE operation. The S7-1500 firmware issues a BLKMOV internally; you do not have to count bytes, and you do not have to worry about STRING headers or STRUCT padding.
"DB_Source".Data.sField instead of "DB_Source".sField. The names are one level longer, but the copy is one line, runtime-safe, and survives DB restructuring.5. Method 2 — MOVE in LAD/FBD with Whole-UDT Tags
If you prefer graphical programming, the MOVE box from the basic instructions works for two UDT-typed tags as long as the UDTs are identical in declaration order and length.
- From Basic instructions > Move operations, drag MOVE into the network.
- At
ENconnect a boolean enable if you want to gate the copy. - Wire the source operand to
"DB_Source".Data. - Wire the destination operand to
"DB_Dest".Data. - Download and test.
Internally, the LAD/FBD MOVE on the S7-1500 lowers to a symbolic block-move, exactly like the SCL := from Method 1. There is no length parameter to set; the compiler infers it from the UDT.
This is the equivalent of the SCL := and is the simplest swap for engineers who do not want to use SCL.
6. Method 3 — Keep an Existing DB, Build the UDT After the Fact
The challenge with Method 1 is the one-time migration of an existing DB to a UDT-wrapped DB. Every HMI tag, every block reference, every watch table, and every CFC chart that pointed at "DB_Source".sField must now point at "DB_Source".Data.sField. The following procedure preserves the original DB structure for documentation while still giving you a single UDT for the copy.
- Open
DB_Sourcein the editor and select all the tags (Ctrl+A). - Right-click and choose Copy.
- Open the new PLC data type (e.g.
UDT_Recipe), right-click inside the empty body and Paste. Every tag is recreated inside the UDT, preserving names, data types, ARRAY bounds, and STRING lengths. - Compile the UDT.
- Back in
DB_Source, leave the original tags in place and add a single new tag of typeUDT_Recipeat the bottom (e.g.Shadow). This shadow tag is what Method 1 or 2 will copy from. - In a one-shot SCL block, mirror the legacy layout into the UDT:
"DB_Source".Shadow := UDT_Recipe#(followed by the field-by-field initialisation. Run this once to populate the shadow. - From now on, treat
DB_Source.Shadowas the single source of truth for the copy. Point HMI references at it for new variables, and leave the historical fields intact for documentation.
This hybrid approach costs you one extra UDT-sized shadow tag in memory, but preserves the symbolic bindings of the legacy fields and avoids a global rewire.
7. Method 4 — Array of UDTs, Element-by-Element Copy
When the destination is a buffer — an ARRAY[0..n] of the same UDT — the same MOVE/:= mechanism works at element level:
FOR i := 0 TO 9 DO
"DB_Buffer".Record[i] := "DB_Source".Data;
END_FOR;
This pattern is the canonical way to build a ring buffer, an event log, or a recipe history without ever calling SFC20. It is fully symbolic, has no length math, and scales linearly with the array size.
8. SFC20 Reference (For Non-Optimized Blocks Only)
For completeness, the legacy call works only when the attribute Optimized block access is cleared on both DBs and on S7-1500 (S7-1200 is unsupported):
CALL SFC20
SRCBLK := P#DB_Source.DBX0.0 BYTE 200 // length must be exact
RET_VAL := "DB_Status".retVal // INT
DSTBLK := P#DB_Dest.DBX0.0 BYTE 200;
Length parameter is in bytes. 200 is the byte width of the data area including the initialisation overhead of STRINGs and the alignment padding of STRUCTs. Update the number when the UDT changes.
W#16#8091 at runtime on S7-1500 firmware 2.6.x and above.9. Verification and Diagnostics
9.1 Compile-Time Check
- After each method, perform a full build of the
PLC_1station. The Messages window must show 0 errors and 0 warnings related to the copy block. - Cross-compile the project for the target firmware. TIA Portal V15 / V15.1 supports cross-compile between S7-1500 firmware versions when the source configuration is valid.
9.2 Online Check
- Download the program to the S7-1519F and go online.
- Open the source DB and the destination DB in monitor mode. Force the source fields to recognisable test values (e.g.
REAL=1.234,STRING='TEST'). - Trigger the copy (rising edge on the enable input of the FB).
- Read the destination DB. Every field, every STRING header, every nested STRUCT must equal the source.
- Use a watch table to confirm the SCL / MOVE returned no error tag (where used).
9.3 SFC20 Return Codes (Reference)
| RET_VAL (hex) | Meaning | Typical cause on optimized blocks |
|---|---|---|
| 0000 | No error | — |
| 8091 | Source / destination length differs | Length parameter does not match the UDT byte width |
| 8092 | Source / destination type error | POINTER used on optimized DB |
| 80A1 | Source DB not loaded | DB number does not exist on CPU |
| 80A2 | Destination DB not loaded | DB number does not exist on CPU |
| 80B1 | Source DB wrong | Pointer cannot resolve on optimized DB |
| 80B5 | Source area is write-protected | DB attribute Write-protected is enabled |
| 80B6 | Destination area is write-protected | DB attribute Write-protected is enabled |
| 80B7 | Source area incorrect | Length exceeds DB size |
| 80B8 | Destination area incorrect | Length exceeds DB size |
| 80B9 | Source and destination areas overlap | Two ANY pointers collide |
| 80C1 | SFC already executing | Re-entrant call on a higher-priority OB |
9.4 Performance Sanity
The S7-1500 symbolic MOVE/:= is implemented as a single instruction word and copies the UDT as a contiguous block in work memory. For a typical UDT_Recipe in the 1–4 KB range the operation finishes in a single OB1 scan; for arrays above 64 KB consider breaking the loop into multiple scans to avoid a single-cycle watchdog on the OB.
10. Troubleshooting Matrix
| Symptom | Method | Root Cause | Fix |
|---|---|---|---|
Compiler rejects P#DBx.DBX0.0 BYTE n
|
SFC20 | Source or destination has Optimized block access | Switch to UDT + SCL := (Method 1) or MOVE in LAD/FBD (Method 2) |
RET_VAL = W#16#8091 at runtime |
SFC20 | Byte count does not match UDT width | Recompute BYTE count from UDT or move to symbolic copy |
RET_VAL = W#16#80B5 or 80B6
|
SFC20 | DB attribute Write-protected set | Clear Write-protected on the source/destination DB |
| MOVE in LAD/FBD does not compile | MOVE | Source and destination UDTs differ | Compare UDT versions; recompile both DBs after any UDT change |
| Copy completes but STRING/DTL fields are wrong | SFC20 with wrong length | Length parameter does not include STRING header bytes | Recompute length, or use symbolic copy |
| CPU stops at OB1, SF LED on | Any | DB length differs from declared UDT | Recompile DB; clear Retain mismatch on the target DB |
| Online value visible but HMI shows old value | Any | HMI tag still bound to legacy field name | Update HMI tag list to DB.Data.field
|
| SFC20 unavailable | SFC20 | CPU is S7-1200 | Use Method 1 or 2 — S7-1200 has no SFC20 |
11. Field-Notes and Best Practices
- Single UDT, single tag, single copy. Restructure the DB so the entire payload is one UDT-typed tag. The bulk-copy becomes a one-liner that survives refactoring.
- Use the SAME UDT on both sides. The symbolic MOVE requires identical UDTs. If you need a different layout on the destination (for example, an additional field), create a second UDT and copy field-by-field with SCL.
- Avoid mixing optimized and non-optimized DBs in the same copy path. Mixing requires an intermediate copy into a non-optimized "shim" DB, which is brittle and error-prone.
- Watch STRING length and ARRAY bounds. The symbolic copy moves the full declaration, not the active length. Truncation happens at the producer, not at the copy.
- Retain attribute. If the destination is Retain = true, expect a longer first-scan initialisation, but no impact on the copy itself.
- Know your firmware. TIA Portal V15 with firmware 2.6 is the minimum comfortable baseline for symbolic MOVE on UDT-typed tags. Older firmware combinations can compile the project but fail the download with a compatibility error.
-
Document the migration path. If you adopt the UDT-wrap approach in a running project, keep a list of every HMI / SCADA tag that must move from
DB.fieldtoDB.Data.field. TIA Portal's Cross-reference (Ctrl+Shift+F3) is the fastest way to enumerate them.
12. Reference Documentation
- Siemens Industry Online Support — S7-1500 / TIA Portal manuals
- SIMATIC S7-1500, S7-1500R/H, ET 200MP System Manual
- SIMATIC S7-1500 / ET 200MP — Programming and Operating Manual (SCL)
- S7-1500 List Manual — SFC20 (BLK_MOV) interface and error codes
- TIA Portal V15 / V15.1 Release Notes (firmware compatibility matrix)
13. FAQ
Why does SFC20 compile but the move instruction does not work in TIA Portal V15?
SFC20 is rejected at compile time on optimized DBs because its SRCBLK/DSTBLK parameters are based on absolute pointers (P#DBx.DBX0.0 BYTE n), and optimized blocks are not absolutely addressable. The build either flags the call as incompatible, or it accepts it but returns W#16#8091 at runtime. Switch to a UDT-typed tag and copy symbolically with SCL := or the LAD/FBD MOVE box.
Can I use SFC20 (BLK_MOV) on an S7-1200?
No. SFC20 is part of the S7-1500 / S7-300 / S7-400 instruction set; it is not distributed in the S7-1200 firmware. On S7-1200 the only supported bulk-copy path is the symbolic MOVE on a UDT-typed tag.
Do I have to convert my existing DB to a UDT to enable the copy?
For the cleanest approach, yes. Define a UDT that mirrors the DB layout, expose a single tag of that UDT inside both DBs, and copy with := or MOVE. If you cannot refactor the DB, use Method 3: leave the original tags intact, add a single UDT-typed shadow tag, and copy from the shadow.
Does the symbolic MOVE preserve STRING headers, STRUCT padding, and ARRAY metadata?
Yes. On S7-1500 firmware 2.6+ the symbolic MOVE copies the full UDT load image, including the two-byte STRING length field, the four-byte max-length field, the implicit alignment of STRUCTs, and the ARRAY bounds. You do not have to compute a byte count.
Is the UDT-wrap method safe to use with a fail-safe S7-1519F?
The copy operation only touches standard DBs. F-signatures, F-runtime groups, and F-I/O substitution values are not affected. Validate any F-related DB through the Safety Administration tool to confirm that no qualified safety container is being moved outside its approved scope.
What RET_VAL should I monitor when I must keep SFC20 for legacy reasons?
Monitor RET_VAL for W#16#8091 (length mismatch) and W#16#80B5/80B6 (write protection). On optimized blocks the call should be replaced; the codes appear only when the project was forced to compile on a non-optimized DB.