Copy Optimized DB to Another DB in TIA Portal V15 (S7-1500)

David Krause13 min read
SiemensTIA PortalTutorial / How-to
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 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:

  1. The CALL SFC20 with an absolute source pointer such as P#DB1.DBX0.0 BYTE 20 is rejected by the TIA Portal V15 compiler when the source or destination has the Optimized block access attribute enabled.
  2. Even if the call is accepted at compile time, the runtime returns a status in RET_VAL because the internal pointer resolves to a region that the S7-1500 firmware does not expose through absolute addressing.
  3. The classic, byte-counted approach is not safe for a block that contains STRING, STRUCT, ARRAY, DTL, or WSTRING elements, 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.

Toolchain assumption: TIA Portal V15 (Update 4 or later recommended), CPU firmware 2.6.x or newer for S7-1519F, SCL compiler integrated in STEP 7 V15. S7-1200 is excluded from the SFC20 fallback because SFC20 is not part of the S7-1200 instruction set.

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:

  1. The compiler cannot derive a length from P#DBx.DBX0.0 BYTE n because the symbol is not address-based.
  2. You cannot reach the storage with the PEEK / POKE semantics 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.

Rule of thumb: SFC20 is reliable for non-optimized DBs on S7-300/400/1500, never for optimized DBs, and never on S7-1200. Switch to a symbolic copy mechanism as soon as a block uses Optimized block access.

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

  1. In the project tree, expand PLC_1 > Program blocks > PLC data types.
  2. Add a new data type, e.g. UDT_Recipe.
  3. Inside UDT_Recipe, recreate exactly the layout of your DB: every STRING, STRUCT, ARRAY, BOOL, REAL, DTL, etc.
  4. Compile the UDT.

4.2 Add a Single UDT-Typed Tag to Each DB

  1. Open the source DB (e.g. DB_Source). Delete all existing tags and add a single tag Data of type UDT_Recipe.
  2. Open the destination DB (e.g. DB_Dest). Add a tag Data of type UDT_Recipe with identical start values (or "---" if the destination is overwritten at runtime).
  3. 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.

Trade-off: Every consumer of the DB now has to reference "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.

  1. From Basic instructions > Move operations, drag MOVE into the network.
  2. At EN connect a boolean enable if you want to gate the copy.
  3. Wire the source operand to "DB_Source".Data.
  4. Wire the destination operand to "DB_Dest".Data.
  5. 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.

  1. Open DB_Source in the editor and select all the tags (Ctrl+A).
  2. Right-click and choose Copy.
  3. 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.
  4. Compile the UDT.
  5. Back in DB_Source, leave the original tags in place and add a single new tag of type UDT_Recipe at the bottom (e.g. Shadow). This shadow tag is what Method 1 or 2 will copy from.
  6. 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.
  7. From now on, treat DB_Source.Shadow as 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.

Tip: When the program must be rebuilt from scratch (e.g. after switching from TIA Portal V15 to V16 or V17), the UDT-first layout is the right starting point. Use Method 3 only on legacy code that cannot be re-tagged without risk.

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.

Do not use this form on optimized blocks. The ANY pointer will be rejected at compile time on TIA Portal V15, or will return 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_1 station. 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

  1. Download the program to the S7-1519F and go online.
  2. 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').
  3. Trigger the copy (rising edge on the enable input of the FB).
  4. Read the destination DB. Every field, every STRING header, every nested STRUCT must equal the source.
  5. 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.field to DB.Data.field. TIA Portal's Cross-reference (Ctrl+Shift+F3) is the fastest way to enumerate them.
Safety note for S7-1519F (F-CPU): The copy mechanisms above operate on standard data blocks. They do not copy F-runtime groups, F-signatures, or F-I/O substitution values. If the DB is part of a failsafe program, validate with the Safety Administration tool that no F-related data is being moved out of its qualified container.

12. Reference Documentation

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.

Back to blog