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.
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:
- 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). - 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.
- Symbolic references survive future changes to
MyDBlayout. If another tag is later inserted at offset 0, the symbolic reference continues to point to the correctSTRING. An absolute pointer would silently break. - 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.
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:
-
VARIANToccupies 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_STRINGvia 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
IN1is declaredVARIANT, the compiler accepts any tag, but the block must dereference it explicitly withVariantGet/VariantPut— no implicit dereference is provided. - Always test
TypeOf()againstTypeOf(...).Type = Type_STRINGbefore dereferencing to avoid anSTATUSerror at runtime. - The
VARIANTcannot 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:
- Open the classic project in SIMATIC Manager and run File → Save as → TIA Portal compatible (produces a
.ap14archive) or use Project → Migrate project directly from TIA Portal. - After migration, open the Migration log (Project tree → Common data → Logs). Search for entries containing
POINTERor the block nameFC31; the log lists every assignment the tool rewrote and every assignment it could not rewrite. - For each “Manual rework needed” entry, apply Solution 1 or Solution 2 above.
- Run Compile → Software (rebuild all) to confirm the diagnostic is gone before downloading to the target CPU.
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:
- 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.
- 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).
-
Online monitor gate: Open FC31 online and expand
IN1. The value column must show theSTRINGcharacters of the actual operand (e.g.'Hello world'). For aVARIANT, confirm the “Type” column showsSTRINGand the “Pointer” column shows the expected DB number and byte offset. -
Functional gate: Force
STAT7to a known input, forceTEMP20to the substring andTEMP19to the position. ReadSTAT2after one scan and confirm the expected replaced string. Repeat withST2 = '', 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
- Always run the TIA Portal migration tool against the whole project, never Generate blocks from source from STL fragments.
- Read the Migration log end-to-end and resolve every entry marked “manual rework needed” before the first download.
- Convert every
P#DBx.DBXn.moperand in FC/FB input parameters to a symbolic reference during the same migration pass. - Prefer
VARIANTformal parameters for FCs/FBs that are intentionally generic. - Enable optimized block access on every new S7-1200/1500 block to maximize compile-time symbol resolution.
- 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.
- 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.