Resolving SCL REAL Type Errors with Siemens TIA PIW Inputs

David Krause12 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

1. Problem Overview

A recurring failure mode on S7-1200 and S7-1500 controllers occurs when an SCL (Structured Control Language) function block declares analog process variables as REAL (IEEE-754 single-precision, 32-bit) but the peripheral input word (e.g., PIW752, PIW754, IW752) is read as a 16-bit INT. TIA Portal rejects the implicit assignment with a type-mismatch compile error, and downstream users may also encounter the runtime error "A structure without components is not allowed" if a User-Defined Type (UDT) is partially populated.

The typical symptom pattern is:

  • SCL source compiles with red squigglies on y := u1 + u2; when u1, u2 are bound to PIW752/PIW754.
  • Direct assignment of a peripheral word to a REAL input raises "Incompatible types: cannot convert INT to REAL implicitly".
  • If the user inserts a CONV block but the surrounding UDT (e.g., User_data_type_1) is empty, the download fails with "A structure without components is not allowed".
  • Switching all variables to INT removes the compile error but breaks any downstream floating-point math (filtering, scaling, PID, model integration).

This article documents the root cause, the correct conversion chain, and the verified workaround patterns for TIA Portal V16, V17, V18 and V19.

2. Root Cause Analysis

2.1 The PIW data type is INT, not REAL

The peripheral input area of an S7 CPU is always a 16-bit signed integer. The I/O address PIW752 occupies two bytes in the process-image input table (PII) and is logically a WORD at the bus level, but symbolically it is surfaced as INT. The same applies to PQW on the output side (INT) and to the symbolic I/O fields %IW / %QW in IEC 61131-3 view.

REAL is a 32-bit single-precision IEEE-754 floating-point value, occupying four consecutive bytes. S7-1200/1500 hardware does not support any native FLOAT I/O module; the 4-byte analog modules (e.g., AI 8xU/I/RTD/TC ST, 6ES7531-7KF00-0AB0) still deliver one 16-bit channel per word into the PII. The CPU cannot perform an implicit INT → REAL promotion in a peripheral assignment because the bit pattern would be re-interpreted, not scaled.

Symbol Type Size (bytes) Range Bit layout
PIW / IW / %IW INT 2 -32 768 … +32 767 16-bit two's complement
PQW / QW / %QW INT 2 -32 768 … +32 767 16-bit two's complement
REAL (LREAL on 1500) Floating-point 4 (8 for LREAL) ±3.402 823E+38 (REAL) IEEE 754-2008 binary32
DINT Integer 4 -2 147 483 648 … +2 147 483 647 32-bit two's complement

2.2 Why MATLAB Function-block code triggers the error

Code generated by Simulink PLC Coder or by hand-written MATLAB Function blocks typically targets a generic IEEE-754 environment, where the inputs u1, u2 are REAL by default. When the coder emits SCL, the SCL prototype keeps REAL declarations. Downstream the user is forced to feed PIW values into a REAL signature, which the strict TIA Portal type checker rejects:

FUNCTION_BLOCK MATLAB
VAR_INPUT
  u1 : REAL;     // declared REAL
  u2 : REAL;     // declared REAL
END_VAR
VAR_OUTPUT
  y  : REAL;
END_VAR
y := u1 + u2;
END_FUNCTION_BLOCK

Binding u1 := PIW752 at the call site fails because the implicit conversion INT → REAL is not allowed for I/O areas in STEP 7.

2.3 The "structure without components" error

The message "A structure without components is not allowed" (TIA Portal error code 0xFFD2 1A92 / 0xE7B3 depending on version) is raised during download when a STRUCT datatype is referenced but contains no members, or when a UDT has been declared but its data record was never edited. A typical scenario: the engineer adds a CONV instance, creates a new UDT to hold the converted value, and forgets to add a member to the UDT. The compiler treats the empty UDT as STRUCT END_STRUCT with no fields, which is invalid for download.

3. IEC 61131-3 Type Promotion Rules in TIA Portal

IEC 61131-3 (third edition, 2013) and its 2024 revision define the following implicit type promotions in textual languages:

  • BOOL → BYTE → WORD → DWORD → LWORD (bit-string widening)
  • SINT → INT → DINT → LINT (integer widening, sign-preserving)
  • USINT → UINT → UDINT → ULINT (unsigned integer widening)
  • REAL → LREAL (floating widening)

There is no implicit promotion between the integer hierarchy and the real-number hierarchy. TIA Portal V16+ enforces this strictly at compile time for peripheral accesses, even though the CPU firmware could execute the conversion in one cycle. Therefore, the programmer must insert an explicit conversion.

4. Solution 1 — Direct INT → REAL with CONV

On S7-1200 firmware V4.0+ and S7-1500, the CONV (Convert) instruction directly accepts INT as the source and emits REAL at the output. The intermediate DINT step is not required for S7-1500, but it is still the recommended path on S7-1200 to avoid a deprecation warning in older TIA Portal versions.

4.1 LAD/FBD implementation

  1. Insert Basic Instructions → Conversion operations → CONV in the network.
  2. Set IN to the symbolic name of the analog input (e.g., "AI_Channel_0" bound to %IW752).
  3. Set OUT to a REAL tag (e.g., "u1_REAL").
  4. Compile, then download.

4.2 SCL implementation

// Explicit INT to REAL conversion (TIA Portal V16+)
FUNCTION_BLOCK FB_MatlabSafe : FB_MatlabBase
VAR
  u1_REAL : REAL;
  u2_REAL : REAL;
END_VAR
BEGIN
  u1_REAL := REAL#PIW752;       // not legal, syntax error
  // Correct:
  u1_REAL := INT_TO_REAL("AI_Channel_0");
  u2_REAL := INT_TO_REAL("AI_Channel_1");
  y := u1_REAL + u2_REAL;
END_FUNCTION_BLOCK

The standard conversion operators in SCL are typed functions: INT_TO_REAL, DINT_TO_REAL, WORD_TO_REAL, BYTE_TO_REAL. Avoid the implicit REAL# cast on a peripheral word — the prefix REAL# performs a type literal interpretation of a constant, not a runtime conversion of a tag.

5. Solution 2 — INT → DINT → REAL (legacy-safe chain)

For TIA Portal V13/V14, or when the SCL editor generates a deprecation note on the direct INT_TO_REAL form, use the explicit two-step chain. This is also the path required if the source word is unsigned and the value can exceed 32 767 (e.g., a 0–10 V module set to unipolar 0–27 648, or a 4–20 mA loop read as 0–27 648).

// Two-step conversion chain
VAR
  raw_DINT  : DINT;
  scaled_R  : REAL;
END_VAR
BEGIN
  raw_DINT  := INT_TO_DINT(%IW752);    // step 1: sign-extend
  scaled_R  := DINT_TO_REAL(raw_DINT); // step 2: bit-pattern to float
END_FUNCTION_BLOCK

The intermediate DINT prevents accidental truncation and keeps the editor's strict-type check satisfied on every TIA Portal version from V13 SP1 onward.

6. Solution 3 — Scale to engineering units with NORM_X and SCALE_X

For a real process value (temperature, pressure, flow), convert the raw 16-bit integer to a normalized 0.0–1.0 REAL with NORM_X, then to engineering units with SCALE_X. This is the Siemens-recommended pattern for AI modules; it is documented in the S7-1200 and S7-1500 system manuals (see Siemens Industry Online Support, entry ID 109755220 for S7-1200 and 109478121 for S7-1500).

6.1 Parameter block

Block Input Meaning
NORM_X VALUE Raw peripheral word (INT)
MIN Lower raw limit (e.g., 0)
MAX Upper raw limit (e.g., 27 648)
RET_VAL REAL in range 0.0 … 1.0
SCALE_X VALUE Output of NORM_X
MIN Engineering low scale (e.g., 0.0 °C)
MAX Engineering high scale (e.g., 100.0 °C)
RET_VAL REAL in engineering units

6.2 SCL call

// Scaling: 0–27648 raw -> 0.0–100.0 °C
VAR
  rNorm  : REAL;
  rTempC : REAL;
END_VAR
BEGIN
  rNorm  := NORM_X(MIN := 0,    VALUE := %IW752, MAX := 27648);
  rTempC := SCALE_X(MIN := 0.0, VALUE := rNorm,  MAX := 100.0);
END_FUNCTION_BLOCK

For bipolar inputs (±10 V, ±20 mA) the limits become -27648 to +27648. For 4–20 mA loops use 0 to 27648 only (4 mA → 0 counts is the Siemens convention with a 4-wire AI; for 2-wire modules check the module manual for the underrange/overrange threshold, typically 0 counts at < 3.6 mA).

7. Solution 4 — Eliminate the UDT "empty struct" error

The "A structure without components is not allowed" error is independent of the type conversion, but it surfaces in the same workflow. The fix is to populate the UDT body.

  1. Open PLC data types in the project tree.
  2. Select the UDT (e.g., User_data_type_1).
  3. Inside the STRUCT block, add at least one member:
TYPE "User_data_type_1"
  STRUCT
    rValue    : REAL;     // engineering-unit value
    bOverflow : BOOL;     // optional flag
  END_STRUCT;
END_TYPE
  1. Recompile the block that references the UDT.
  2. Re-download to the CPU.

If the UDT was used solely as a placeholder, either delete it from the block's VAR section or convert the placeholder tag to a simple REAL/DINT tag without a UDT wrapper.

8. Working SCL Code Templates

8.1 Safe wrapper for the MATLAB-style adder

FUNCTION_BLOCK "FB_SumReal"
{ S7_Optimized_Access := 'TRUE' }
VAR
  u1_R : REAL;
  u2_R : REAL;
  y    : REAL;
END_VAR
BEGIN
  // Conversions happen at the I/O boundary, not inside the math
  u1_R := INT_TO_REAL(%IW752);
  u2_R := INT_TO_REAL(%IW754);
  y    := u1_R + u2_R;
END_FUNCTION_BLOCK

8.2 Variant with engineering-unit scaling

FUNCTION_BLOCK "FB_ScaledSum"
{ S7_Optimized_Access := 'TRUE' }
VAR
  rTemp1 : REAL;
  rTemp2 : REAL;
  y      : REAL;
END_VAR
BEGIN
  rTemp1 := SCALE_X(MIN := 0.0,  VALUE := NORM_X(MIN := 0, VALUE := %IW752, MAX := 27648), MAX := 100.0);
  rTemp2 := SCALE_X(MIN := -50.0, VALUE := NORM_X(MIN := 0, VALUE := %IW754, MAX := 27648), MAX := 150.0);
  y      := rTemp1 + rTemp2;
END_FUNCTION_BLOCK

8.3 Graph block import pattern (Simulink PLC Coder)

If the FB is generated by Simulink PLC Coder, change the Inport block datatype from single (REAL) to int16, regenerate, then convert in the calling SCL program. This avoids the round-trip issue without modifying the generated code.

9. TIA Portal Version Compatibility Matrix

TIA Portal CPU firmware INT_TO_REAL direct NORM_X / SCALE_X Empty-UDT error text
V13 SP1 S7-1200 V4.0 Limited (use chain) Yes "Structure without components is not allowed"
V14 SP1 S7-1200 V4.2, S7-1500 V1.8 Yes (V14+) Yes Same
V15.1 S7-1500 V2.0 Yes Yes Same
V16 S7-1500 V2.6 Yes (preferred) Yes Same
V17 S7-1500 V2.9 Yes Yes Same
V18 S7-1500 V3.0 Yes Yes Same
V19 S7-1500 V3.1 Yes Yes Same

On S7-1200 firmware V4.0–V4.2, the compiler raises a warning if INT_TO_REAL is used directly without DINT; the runtime behaviour is correct. Update to firmware V4.4 (6ES721x-1xx40-0XB0) or later to remove the warning.

10. Verification Procedure

  1. Open the online block in TIA Portal with the CPU in RUN and force the AI channel to a known value (e.g., 0 V or 10 V from a calibrator).
  2. Open the Monitor & Force table; add the raw peripheral tag %IW752 and the converted tag u1_R.
  3. Confirm that u1_R equals the expected REAL value (e.g., 0.0 for 0 V on a 0–10 V module, 10.0 for 10 V).
  4. Apply a step change (e.g., 5 V) and verify the value tracks within one OB1 cycle (~10 ms default on S7-1500, ~100 ms on S7-1200).
  5. For the scaled engineering value, cross-check with a calibrated reference instrument; tolerance is typically ±0.1 % of full scale at 25 °C for a 16-bit AI.
  6. Check the CPU diagnostic buffer for any overflow / wire-break events; overrange reads 32 767 or 32 511 depending on module family, and triggers a diagnostic interrupt if configured.

11. Error-Code Quick Reference

Symptom Likely cause Fix
"Incompatible types INT to REAL" at PIW Implicit conversion not allowed Insert INT_TO_REAL or CONV block
"Structure without components is not allowed" Empty UDT Add member(s) to STRUCT, or remove UDT
Compile warning "Conversion may lose information" 16-bit INT cast to 8-bit BYTE Use DINT as intermediate step
Runtime value stuck at 0.0 PIW address outside PII Verify module slot and process-image partition (OB1 cycle must include the address)
Value reads as 32767 (overrange) Input signal exceeds module range Check wiring, scaling, and module configuration in device view
Negative real on unipolar 4–20 mA Underrange bit set, INT value interpreted as signed Use WORD_TO_REAL or mask the MSB with AND before conversion

12. Common Pitfalls and Field Notes

  • Mixing optimized and non-optimized access. With Optimized block access enabled (default in V15+), symbolic peripheral tags are addressed via the symbol table, not the absolute PIW. Direct PIW752 in SCL still works if the symbolic name resolves to the same address.
  • Process-image partition mismatch. If the analog module is assigned to PIP 1 (e.g., for fast OB6x update) and the SCL FB runs in OB1, the value will be frozen at the OB1 update. Either move the FB to OB6x or assign the module to PIP 0.
  • Endianness in IEC 61131-3 vs. S7 convention. S7-1200/1500 stores REAL little-endian on the byte order shown in the monitor, matching the IEEE 754 binary32 layout. No byte-swap is required when converting from a 16-bit INT (only the lower 16 bits are used).
  • LREAL on S7-1500. For high-precision math, use LREAL (64-bit) and DINT_TO_LREAL. REAL is 32-bit and loses precision beyond 7 significant digits.
  • CONV block vs. type-conversion function. In SCL, prefer the typed function INT_TO_REAL(...) over the CONV box; the function form produces cleaner error messages and is supported in all language editors.
  • MATLAB/Simulink-generated code. Always inspect the generated SCL for hidden REAL ports. If the upstream model cannot be changed, add a Signal Specification block in Simulink forcing the Inport to int16, then regenerate.

13. Safety and Diagnostics

Safety note: Analog inputs feeding safety functions (F-CPU, F-I/O) must be processed through the certified F-library blocks (e.g., F_REAL_TO families) and the conversion must occur inside the F-runtime group. Generic CONV is not acceptable for SIL 2/3 paths.

For diagnostic monitoring, evaluate the SFC 51 / RD_SINFO (read system status) and the standard SFC 59 (RDREC) to obtain module diagnostics. Overrange and wire-break bits from the AI module's diagnostic record (record index 0) can be mapped to a BOOL tag and used in the same FB that performs the conversion.

14. Standards and References

  • IEC 61131-3:2013, Programmable controllers — Part 3: Programming languages (data-type hierarchy, conversion rules).
  • IEEE 754-2008, Standard for Floating-Point Arithmetic (REAL/LREAL binary layouts).
  • Siemens S7-1200 Programmable Controller — System Manual, entry ID 109755220 on Siemens Industry Online Support (support.industry.siemens.com).
  • Siemens S7-1500 / ET 200MP — System Manual, entry ID 109478121.
  • Siemens STEP 7 SCL V16 — Programming and Operating Manual, entry ID 109751618.

Why does TIA Portal refuse to assign PIW752 directly to a REAL variable?

PIW is a 16-bit signed integer, while REAL is a 32-bit IEEE-754 float. The strict type checker in TIA Portal (per IEC 61131-3 promotion rules) does not allow an implicit INT-to-REAL promotion for peripheral I/O. Insert an explicit INT_TO_REAL function or a CONV block to perform the conversion.

Do I always need the INT → DINT → REAL chain, or is INT → REAL enough?

On S7-1500 CPUs and S7-1200 firmware V4.4 or later with TIA Portal V14+, INT_TO_REAL works directly and is the recommended form. Use the two-step INT → DINT → REAL chain on legacy S7-1200 firmware or when the compiler emits a precision/deprecation warning.

How do I scale a 0–27648 raw value to 0–100 °C engineering units?

Use NORM_X(MIN := 0, VALUE := %IW752, MAX := 27648) to obtain a normalized 0.0–1.0 REAL, then SCALE_X(MIN := 0.0, VALUE := <norm>, MAX := 100.0). The two blocks are documented in the S7-1200 and S7-1500 system manuals.

What causes the "A structure without components is not allowed" error?

The error is raised when a UDT (PLC data type) is declared with an empty STRUCT body. Add at least one member inside the STRUCT, or remove the UDT reference from your block's VAR section, then recompile and download.

Can I use a CONV block on a peripheral input that is configured for 4–20 mA?

Yes. Configure the AI module for 4–20 mA in device view (TIA Portal), then read the raw 0–27648 integer from the PII and convert with INT_TO_REAL. For underrange protection, mask the sign bit or use WORD_TO_REAL if your value never goes negative.

Back to blog