Resolving TIA Portal Pointer to STRING Type Mismatch in Migrated

David Krause14 min read
SiemensTIA PortalTroubleshooting
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

Resolving TIA Portal V14 Pointer-to-STRING Type Mismatch in Migrated S7-1200/1500 Code

When a STEP 7 V5.6 project is migrated to TIA Portal V14 and compiled against an S7-1200 or S7-1500 CPU, classic block calls that pass a POINTER (P#DBx.DBXn.m) into a formal parameter declared as STRING will fail compile with the error:

"The data type POINTER of the actual parameter does not match the data type STRING of the formal parameter."

The error is raised by the TIA Portal compiler because the S7-1200/1500 POINTER data type can only reference elementary data types and cannot carry the 2-byte header that precedes a STRING value in the operand area. The implicit dereference that worked under STEP 7 V5.6 and the legacy S7-300/400 instruction set is not part of the S7-1200/1500 type system. The same source code that compiled for an S7-300 (e.g. 6ES7315-2EH14-0AB0, firmware V3.3) will be rejected after migration to a CPU 1515-2 PN (6ES7515-2AM02-0AB0, firmware V2.0+) or CPU 1214C DC/DC/DC (6ES7214-1AG40-0XB0, firmware V4.x).

This reference documents the root cause, the two engineering-grade remedies (symbolic tag addressing and the VARIANT pointer), and the verification workflow for engineers migrating classic STEP 7 sources into TIA Portal.

1. Problem Definition: Migrated FC31 REPLACE Call

The reported pattern is a user-defined FC31 carrying the REPLACE semantics imported from a STEP 7 V5.6 source:

CALL  FC    31
IN0   :=#STAT7
IN1   :=P#DB206.DBX 0.0
IN2   :=#TEMP20
IN3   :=#TEMP19
RET_VAL:=#STAT2

Under STEP 7 V5.6, IN1 was declared STRING, and the actual operand P#DB206.DBX 0.0 was accepted because the classic STL/ST editor performed an implicit pointer-to-string dereference at runtime. The compiler warning was suppressed or absent on S7-300/400 because the POINTER there was a 6-byte structure that could be loaded into AR1/AR2 and walked through any operand layout.

After the TIA Portal migration tool (Project → Migrate project) ports the S7-300/400 program to an S7-1200/1500 CPU, the compiler performs strict type-checking and raises:

Field Value
Compiler diagnostic The data type POINTER of the actual parameter does not match the data type STRING of the formal parameter
Block FC31 (user REPLACE wrapper)
Formal parameter IN1 : STRING
Actual operand P#DB206.DBX 0.0 (POINTER)
Failing assignment IN1 ← P#DB206.DBX 0.0
CPU target S7-1200 / S7-1500 (any firmware)
TIA Portal version observed V14.0 / V14 SP1 / V15.x

2. Root Cause: S7-1200/1500 POINTER Semantics

The S7-1200/1500 family implements three address-pointer data types with different capabilities. The official definition is published in the TIA Portal S7-1200 manual collection — Variant pointer data type and the S7-1500 system manual.

Pointer type Size (bytes) May point to May point to STRING/STRUCT/UDT/ARRAY Use as FB/FC IN/OUT/IN_OUT
POINTER (legacy 6-byte) 6 Elementary tags only (BOOL, INT, REAL, BYTE, WORD, DWORD, SINT, USINT, DINT, UDINT, LREAL, CHAR, ...) No Only to elementary tags
ANY (legacy 10-byte) 10 Elementary tags and arrays of elementary tags No (STRING, STRUCT, UDT forbidden) Only to elementary tags / arrays
VARIANT (TIA Portal) 0 (compile-time only) Any tag including STRING, WSTRING, STRUCT, UDT, ARRAY, FB instance DB, multi-instance Yes Yes (replaces POINTER and ANY for parameter passing)

Because the legacy POINTER cannot address a STRING, the compiler rejects the assignment in the migrated block. The runtime behavior that the original S7-300 code relied on — AR1 load, indirect addressing through the DB number, hand-walking the 2-byte STRING header — is no longer exposed to user code on S7-1200/1500.

Important: Even if P#DB206.DBX0.0 is "where the string lives" in the operand area, passing the pointer is not equivalent to passing the string. The classic STL behavior (dereferencing AR1 to compute the operand address and reading the max-length and actual-length words before the character buffer) is a hidden contract that the S7-1200/1500 compiler does not honor and that the optimized block access in TIA Portal blocks out entirely.

3. Affected Versions, Firmware, and CPUs

The error is observed on any TIA Portal project that compiles an FC/FB whose interface declares a formal parameter as STRING, WSTRING, STRUCT, UDT, or ARRAY, and whose caller passes a P# pointer. The conditions that produce it:

Item Affected values
TIA Portal V13 SP1, V14, V14 SP1, V15, V15.1, V16, V17, V18, V19
S7-1200 CPU family CPU 1211C / 1212C / 1214C / 1215C / 1217C (article numbers 6ES721x-1...40-0XB0 and later)
S7-1200 firmware V4.0 — V4.6 (block type POINTER restricted since V4.0)
S7-1500 CPU family CPU 1510/1511/1512/1513/1515/1516/1517/1518, ET200SP CPU, Software Controller
S7-1500 firmware V1.5 — V3.1 (POINTER semantics unchanged across firmware revisions)
STEP 7 V5.x source V5.4, V5.5, V5.6, V5.7
S7-300/400 source CPU (pre-migration) Any (CPU 312…CPU 319F, CPU 412…CPU 417)

The condition is not firmware-version dependent. It is a compiler-level decision in the TIA Portal offline editor. Firmware updates to the target CPU do not change the diagnostic.

4. Solution 1: Replace the POINTER with a Symbolic Tag Reference

The first, and recommended, remedy is to pass the STRING directly using its symbolic DB tag name. TIA Portal resolves the symbolic name to the DB number and the byte offset internally and dereferences the STRING correctly, including its 2-byte header (max-length and actual-length words).

If DB206 is named MyDB and its first tag is a STRING[80] named MyString, the migrated call becomes:

CALL  FC    31
IN0   :=#STAT7
IN1   :="MyDB".MyString
IN2   :=#TEMP20
IN3   :=#TEMP19
RET_VAL:=#STAT2

Notes on the rewrite:

  1. The P# prefix is removed. TIA Portal accepts either a fully qualified symbolic tag ("MyDB".MyString) or, for a local variable in the same block, a simple symbolic name (#MyString).
  2. The DB must be know-how protected compatible — i.e. its tags must be defined in the DB declaration, not generated by STL into the data area. Run Project → Compile → Software (rebuild all) before rewriting so the DB tags are visible to the FC editor.
  3. Symbolic references survive future changes to MyDB layout. If another tag is later inserted at offset 0, the symbolic reference continues to point to the correct STRING. An absolute pointer would silently break.
  4. Set the FC's access mode to Standard only if you also need to mix optimized DB access. For an S7-1500 with optimized block access (the default), the compiler resolves the symbolic tag through the compile-time symbol table without requiring a runtime DB number lookup.
Tip: After the rewrite, run Edit → Find and replace on the migrated project to convert any remaining P#DBxxx.DBX0.0 operands in calls to FC31 into the symbolic form. The migration tool does not always rewrite caller blocks, only the called block interface.

5. Solution 2: Use the VARIANT Pointer

Where the source code intentionally uses indirect addressing — e.g. one FC must accept a string from any of several DBs depending on a mode input — declare the formal parameter as VARIANT instead of STRING. The TIA Portal S7-1200 manual — Variant pointer data type documents the rules:

  • VARIANT occupies 0 bytes in the instance / work memory. It is a compile-time descriptor only.
  • It can point to any tag including STRING, WSTRING, STRUCT, UDT, ARRAY, FB instance DBs, and partial slices (e.g. "MyDB".MyStruct.Field).
  • Inside the called FC, use the VariantGet, VariantPut, and type-conversion instructions (STRING_TO_VARIANT / VARIANT_TO_STRING via the move/cast blocks) to read or write the referenced tag.

Example interface declaration for FC31 in SCL:

FUNCTION FC31 : String[80]
VAR_INPUT
  IN0  : DInt;
  IN1  : Variant;        // replaces the old STRING formal parameter
  IN2  : DInt;
  IN3  : DInt;
END_VAR
VAR_TEMP
  sTarget : String[80];
END_VAR
BEGIN
  // Validate the variant type at runtime before dereferencing
  IF TypeOf(IN1) <> Type_STRING THEN
    FC31 := '';
    RETURN;
  END_IF;

  // Move the STRING payload out of the variant
  VariantGet(SRC := IN1, DST := sTarget);

  // Perform the REPLACE semantics against sTarget using IN2/IN3
  // … user logic …
  FC31 := sTarget;
END_FUNCTION

And the call site, where the original P#DB206.DBX 0.0 is now any symbolic tag:

CALL  FC    31
IN0   :=#STAT7
IN1   :="MyDB".MyString        // symbolic tag, accepts any STRING
IN2   :=#TEMP20
IN3   :=#TEMP19
RET_VAL:=#STAT2

Rules and limits of the VARIANT approach:

  • If IN1 is declared VARIANT, the compiler accepts any tag, but the block must dereference it explicitly with VariantGet/VariantPut — no implicit dereference is provided.
  • Always test TypeOf() against TypeOf(...).Type = Type_STRING before dereferencing to avoid an STATUS error at runtime.
  • The VARIANT cannot be used directly in arithmetic or string-function expressions; always extract to a typed temporary first.
  • Supported on S7-1200 firmware V4.0+ and S7-1500 firmware V1.5+ (i.e. all CPUs TIA Portal V14 targets).

6. Solution 3: Let the TIA Portal Migration Tool Handle It

Siemens' migration toolchain (Project → Migrate project → S7-300/400 → S7-1500) is the prescribed workflow and frequently rewrites the affected P#DBx.DBXn.m operands into symbolic references automatically. Engineering notes from the migration documentation:

  1. Open the classic project in SIMATIC Manager and run File → Save as → TIA Portal compatible (produces a .ap14 archive) or use Project → Migrate project directly from TIA Portal.
  2. After migration, open the Migration log (Project tree → Common data → Logs). Search for entries containing POINTER or the block name FC31; the log lists every assignment the tool rewrote and every assignment it could not rewrite.
  3. For each “Manual rework needed” entry, apply Solution 1 or Solution 2 above.
  4. Run Compile → Software (rebuild all) to confirm the diagnostic is gone before downloading to the target CPU.
Common pitfall: If you import STL sources via External sources → Generate blocks from source, the migration tool's automatic rewrites are skipped. Always migrate the whole project so the tool sees the original block interfaces, not just the STL text.

7. Comparison of the Three Remediation Strategies

Strategy Code change Runtime cost Maintainability Indirection support Recommended when
Symbolic tag reference Rewrite the actual operand from P#DB206.DBX0.0 to "MyDB".MyString None (compile-time symbol resolution) High — DB layout changes are absorbed None (caller must know the tag) The DB layout is fixed and the same FC is always called with the same tag
VARIANT formal parameter Change IN1 from STRING to VARIANT and add VariantGet inside FC31 Small (runtime type check + dereference) High — FC accepts any tag Yes — caller can pass any DB tag The FC must accept strings from multiple DBs or different tag names
Migration tool automatic rewrite None (tool performs the rewrite) None High — symbolic form Limited — tool only resolves fixed P# operands The field report already references a fixed DB tag

8. Verification Steps After the Fix

Apply the four-step verification gate after either solution:

  1. Compile gate: Project tree → CPU → right-click → Compile → Software (rebuild all). No diagnostics of type “data type ... does not match” must remain on FC31 or any caller.
  2. Download gate: Online → Download to device. TIA Portal must show “Download successful” without converting the block to address-only access (a sign the symbolic info is lost).
  3. Online monitor gate: Open FC31 online and expand IN1. The value column must show the STRING characters of the actual operand (e.g. 'Hello world'). For a VARIANT, confirm the “Type” column shows STRING and the “Pointer” column shows the expected DB number and byte offset.
  4. Functional gate: Force STAT7 to a known input, force TEMP20 to the substring and TEMP19 to the position. Read STAT2 after one scan and confirm the expected replaced string. Repeat with ST2 = '', with a substring that does not occur, and with a position outside the source string length to confirm error handling.

9. Related Compiler Diagnostics and Their Root Causes

The same root cause produces a family of diagnostics. Use the matrix to triage:

Compiler message Root cause Remedy
The data type POINTER of the actual parameter does not match the data type STRING of the formal parameter P#DBx.DBXn.m passed to STRING formal parameter Solution 1 or 2 above
The data type POINTER of the actual parameter does not match the data type STRUCT of the formal parameter Pointer passed to STRUCT formal parameter Use symbolic UDT tag or change formal parameter to VARIANT
The data type POINTER of the actual parameter does not match the data type UDT of the formal parameter Pointer passed to UDT tag Symbolic UDT tag or VARIANT
The data type ANY of the actual parameter does not match the data type ARRAY of the formal parameter P#DBx.DBXn.m WORD 10 passed to ARRAY[..] of ... Symbolic array tag or VARIANT
Inconsistent block interface — the formal parameter IN1 has the data type STRING, the actual parameter has the data type POINTER Same as row 1, reported in cross-reference view Same as row 1
STRING functions only support variables of data type STRING or WSTRING VARIANT containing STRING passed directly to REPLACE etc. without VariantGet Extract with VariantGet to a typed temporary first

10. Edge Cases and Field-Proven Caveats

Optimized block access. On S7-1500 with optimized block access (the project default), the symbolic tag is resolved at compile time and no DB number is loaded at runtime. This is the fastest and most maintainable choice.

Non-optimized block access. If the FB/FC has been toggled to non-optimized (compatibility with classic S7-300/400 code), the symbolic reference still works but the compiler emits a DB-OPEN around the access. Performance is slightly lower; tooling diagnostics remain identical.

STRING length truncation. When the actual operand is a STRING[80] and the formal parameter is declared STRING[254], the compiler truncates silently in some tool versions and warns in others. Set both ends to the same length or use WSTRING to avoid truncation.

WSTRING versus STRING. A P#DBx.DBXn.m operand cannot address a WSTRING either — same rule applies. The first two bytes of a WSTRING are also a length header, but each character is 2 bytes wide. Symbolic references work the same way.

Multi-instance and instance DBs. VARIANT can point to instance DBs of FBs (e.g. "Motor_1".Setpoint). A legacy POINTER cannot.

Know-how protection. If the migrated FC is know-how protected, the FC interface is invisible and symbolic rewriting inside it cannot be performed by you. Either obtain the unprotected source from the OEM or replace the FC with an unprotected equivalent. The compiled runtime behavior is identical.

S7-1500 software controller (CPU 1507S / CPU 1508S). The same rule applies. There is no POINTER-to-STRING implicit dereference on the software controller either.

S7-300/400 retain on the target. Some legacy P# operands rely on the S7-300/400 ability to walk into a multi-DB area. If the S7-1500 DB layout is consolidated (e.g. several classic DBs merged by the migration tool), symbolic references must be re-anchored against the new DB layout. The compiler does not catch this.

11. Best Practices for Future S7 → TIA Portal Migrations

  1. Always run the TIA Portal migration tool against the whole project, never Generate blocks from source from STL fragments.
  2. Read the Migration log end-to-end and resolve every entry marked “manual rework needed” before the first download.
  3. Convert every P#DBx.DBXn.m operand in FC/FB input parameters to a symbolic reference during the same migration pass.
  4. Prefer VARIANT formal parameters for FCs/FBs that are intentionally generic.
  5. Enable optimized block access on every new S7-1200/1500 block to maximize compile-time symbol resolution.
  6. Use the F1 help context for “POINTER data type” and “VARIANT data type” in TIA Portal for the current revision — Siemens revises the type rules between V14 and V18/V19 in subtle ways.
  7. Run Project → Archive before and after the migration to compare offline if a regression appears.

12. FAQ

Why does TIA Portal V14 reject P#DB206.DBX0.0 when STEP 7 V5.6 accepted it?

The S7-1200/1500 POINTER data type is restricted to elementary tags and cannot address a STRING (which has a 2-byte length header). The compiler enforces this strictly, while STEP 7 V5.6 allowed implicit pointer-to-string dereference through AR1 on S7-300/400. Replace P#DB206.DBX0.0 with the symbolic DB tag (e.g. "MyDB".MyString) or change the FC's formal parameter to VARIANT.

Which CPU firmware versions are affected by this pointer-to-STRING mismatch?

All S7-1200 firmware V4.0+ and all S7-1500 firmware V1.5+ show this compiler diagnostic. The diagnostic originates in the TIA Portal offline editor, not in the target firmware, so updating the CPU firmware does not change the outcome.

Can I keep the FC interface as STRING and just change the actual operand?

Yes, and that is the simplest fix. Replace P#DB206.DBX0.0 in the CALL with the symbolic tag "MyDB".MyString (assuming MyString is the first STRING in DB206). The FC interface stays STRING, the runtime behavior is identical to the original S7-300/400 code, and DB layout changes are absorbed automatically.

When should I use VARIANT instead of a symbolic tag reference?

Use VARIANT when one FC must accept a string from any of several DBs at runtime, when the caller's source DB is selected by a mode input, or when the FC is part of a reusable library that must work against customer-specific DB layouts. Declare the formal parameter as VARIANT and dereference with VariantGet after a TypeOf check.

Does the TIA Portal migration tool fix this automatically?

Can a VARIANT point to a WSTRING or UDT?

Yes. The VARIANT data type in TIA Portal supports STRING, WSTRING, STRUCT, UDT, ARRAY, FB instance DBs, and individual structural components. The legacy POINTER and ANY cannot. See the TIA Portal S7-1200 manual — Variant pointer data type for the complete capability matrix.

Back to blog