Siemens STEP 7 Data Types: TIA Portal Complete Reference

David Krause13 min read
SiemensTechnical ReferenceTIA Portal
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

STEP 7 Data Type Architecture Overview

STEP 7 — Siemens' integrated engineering framework inside TIA Portal — organizes every tag, constant, and block parameter under a typed system. Three families are defined for S7-300/400, S7-1200, and S7-1500 controllers:

  • Elementary data types provided by the system (binary, integer, floating-point, time, character)
  • Complex data types constructed by combining elementary types (DTL, STRING, ARRAY, STRUCT, UDT)
  • Parameter types used to formalize FB/FC interfaces (TIMER, COUNTER, BLOCK_FB, POINTER, ANY, VARIANT)

Each instruction in STL, LAD, or FBD operates on objects of a specific size: bit-logic instructions work on BOOL, move/load/transfer instructions operate on bytes, words, and double words, and math instructions process integers or REAL/LREAL. Choosing the correct type up front prevents silent truncation, sign extension errors, and IEC 61131-3 check warnings at compile time.

The complete type catalog for STEP 7 V14.0 and later is documented in the Siemens entry Overview of the valid data types — STEP 7 Professional V14.0.

Bit, Byte, Word, and Double Word Foundations

Every elementary type resolves to one of four storage widths. The naming follows IEC 61131-3 conventions adopted by STEP 7:

Width Bits Range of Unsigned Values Typical Mnemonics
Bit 1 0 or 1 BOOL, I0.0
Byte (B) 8 0 to 255 (B#16#00 … B#16#FF) BYTE, IB0, MB10
Word (W) 16 0 to 65 535 (W#16#0000 … W#16#FFFF) WORD, MW10
Double word (D) 32 0 to 4 294 967 295 (DW#16#0000_0000 … DW#16#FFFF_FFFF) DWORD, DINT, REAL, TOD
Long word (LW) 64 0 to 2^64 - 1 LWORD, LINT, LREAL, LTOD
Address overlap warning: MW10, MD10, MB10..MB13, and individual bits M10.0..M13.7 all reference overlapping memory. Never mix symbolic and absolute addressing to the same byte range without a comment — S7-1500 will not catch overlapping bit accesses in LAD at compile time.

Elementary Data Types — Binary Numbers

Binary types are unsigned bit-aggregates used for status flags, masks, and I/O mapping:

Type Size Range Constant Notation Typical Use
BOOL 1 bit 0, 1 TRUE, FALSE Digital I/O, flags, FB enable inputs
BYTE 8 bits B#16#00 … B#16#FF B#16#2A, 2#0010_1010 Bit-packed status words, ASCII
WORD 16 bits W#16#0000 … W#16#FFFF W#16#1234, 2#1000_0011_0000_1111 Hex-coded register, 16-bit flag word
DWORD 32 bits DW#16#0000_0000 … DW#16#FFFF_FFFF DW#16#DEAD_BEEF Bit mask, pointer arithmetic, TOD container
LWORD 64 bits LW#16#0 … LW#16#FFFF_FFFF_FFFF_FFFF LW#16#0 S7-1500 high-speed counter presets

Bit-logic instructions in LAD/FBD operate exclusively on BOOL. To work with multi-bit fields use AW (AND word), OW (OR word), XOW (exclusive OR word), AD, OD, XOD for double words.

Elementary Data Types — Signed and Unsigned Integers

STEP 7 distinguishes signed (S prefix) and unsigned (U prefix) integers. The byte width defines the numerical range:

Type Width Signed Range Unsigned Counterpart Unsigned Range
SINT 8 bit -128 … +127 USINT 0 … 255
INT 16 bit -32 768 … +32 767 UINT 0 … 65 535
DINT 32 bit -2 147 483 648 … +2 147 483 647 UDINT 0 … 4 294 967 295
LINT 64 bit -9.22 × 10^18 … +9.22 × 10^18 ULINT 0 … 1.84 × 10^19

Integer arithmetic uses two's-complement representation. When migrating S7-300 STL code that uses accumulators to TIA Portal SCL, replace ITD (Integer to Double Integer) conversions with explicit INT_TO_DINT casts to avoid sign extension surprises. For analog scaling, prefer DINT_TO_REAL / REAL_TO_DINT over implicit LAD/MOVE boxes — implicit MOVE between incompatible widths generates a compile warning in TIA Portal V17+.

Elementary Data Types — Floating-Point Numbers

STEP 7 supports IEEE 754 single and double precision:

Type Width Precision Approx. Decimal Range NaN / Inf Handling
REAL 32 bit ~7 significant digits ±3.402 823 × 10^38 Supported (IEEE 754)
LREAL 64 bit ~15 significant digits ±1.797 693 × 10^308 Supported (IEEE 754)
NaN propagation: Dividing a REAL by zero returns +Inf, -Inf, or NaN depending on the operands. Downstream REAL_TO_DINT conversion of NaN yields 0 with the ENO = 0 status flag — wire ENO into OB80 / a global error word for diagnostics.

Elementary Data Types — Time, Date, and TOD

Time-domain types separate short-duration timers from absolute clocks:

Type Width Content Range Constant Notation
S5TIME 16 bit Legacy timer value (10 ms base) 0 … 9 990 ms (S7-300/400) S5T#1s30ms
TIME 32 bit IEC duration, signed ms -2 147 483 648 ms … +2 147 483 647 ms (≈ ±24.86 d) T#1m30s500ms
LTIME 64 bit High-resolution duration, ns ≈ ±292 years LTIME#1h30m
DATE 16 bit Days since 1990-01-01 D#1990-01-01 … D#2168-12-31 D#2024-08-15
TOD 32 bit ms since 00:00:00.000 (unsigned) TOD#00:00:00.000 … TOD#23:59:59.999 TOD#14:30:00.000
LTOD 64 bit ns since 00:00:00.000 (unsigned) 0 … 86 399 999 999 999 ns LTOD#14:30:00.123456789
DT 64 bit Legacy DATE_AND_TIME (BCD) DT#1990-01-01-00:00:00 … DT#2089-12-31-23:59:59.999 DT#2024-08-15-14:30:00
DTL 12 byte Structured date+time (S7-1500 / S7-1200 FW ≥ 4.4) 1970-01-01-00:00:00.000 … 2554-12-31-23:59:59.999 999 999 Constructed from DT_TO_DTL
CHAR 8 bit ASCII single character 0 … 255 'A'
WCHAR 16 bit UCS-2 single character 0 … 65 535 CHAR#"a"

TOD (Time of Day) Bit Structure and Millisecond Encoding

Unlike DTL, the legacy TOD type is a flat 32-bit unsigned integer. Its semantic value is the number of milliseconds elapsed since midnight (00:00:00.000). The maximum representable time is therefore:

TOD_max = 24 × 60 × 60 × 1000 − 1 = 86 399 999 ms

The exact bit layout is:

Bit Range Field Significance
31 … 27 Hours × 1 000 000 (encoded as ms) Resolved by division, not by bit masking
26 … 22 Minutes × 60 000 (encoded as ms) Resolved by division, not by bit masking
21 … 16 Seconds × 1000 (encoded as ms) Resolved by division, not by bit masking
15 … 0 Milliseconds 0 … 999 Low word of the DWORD
Common misconception: TOD is not BCD-packed; it is a straight binary millisecond counter. Trying to bit-mask hours as a separate nibble field will produce wrong results. Use the divide-and-modulo pattern shown below.

The S7-1500 high-resolution sibling LTOD stores nanoseconds since midnight as a 64-bit unsigned integer and supports up to nine fractional digits in the literal (LTOD#23:59:59.999999999).

Unpacking TOD into Hours, Minutes, Seconds, Milliseconds

Once a TOD value (DWORD, ms since midnight) is in an MD register or DINT tag, splitting it into H / M / S / ms fields uses a cascade of integer divisions and modulo operations. The pure STL snippet from the engineering record multiplies each component into milliseconds and accumulates; the cleaner SCL pattern uses DIV and MOD:

STL pattern (multiplicative)

      L     L#16              // hours = 16
      L     L#60
      *D                        // hours × 60 = minutes-base
      L     L#60
      *D                        // hours × 3600 = seconds-base
      L     L#1000
      *D                        // hours × 3 600 000 ms
      T     #hd1                // intermediate accumulator

      L     L#45               // minutes = 45
      L     L#60
      *D                        // minutes × 60
      L     L#1000
      *D                        // minutes × 60 000 ms
      L     #hd1
      +D
      T     MD 504              // total ms since midnight = 60 600 000

This STL block reconstructs the millisecond count from components. For the inverse operation — given a DWORD of milliseconds, extract H / M / S / ms — use the SCL implementation:

SCL (preferred) for decomposing TOD

// Input:  tod_ms : DWORD   (ms since midnight, 0 .. 86_399_999)
// Outputs: hours, minutes, seconds, millis
hours    := tod_ms / 3600000;
minutes  := (tod_ms MOD 3600000) / 60000;
seconds  := (tod_ms MOD 60000) / 1000;
millis   := tod_ms MOD 1000;

For an S7-1500 with the higher-resolution LTOD, the same logic scales to nanoseconds:

// Input: lTod_ns : LINT    (ns since midnight, 0 .. 86_399_999_999_999)
hours   := lTod_ns / 3600000000000;
minutes := (lTod_ns MOD 3600000000000) / 60000000000;
seconds := (lTod_ns MOD 60000000000)    / 1000000000;
ms      := (lTod_ns MOD 1000000000)     / 1000000;
us      :=  lTod_ns MOD 1000000;
Verification check: For TOD#14:30:45.500: 14 × 3600000 + 30 × 60000 + 45 × 1000 + 500 = 50 645 500 ms. Loading the DWORD into MD504 and stepping through the division cascade must reproduce exactly 14 / 30 / 45 / 500. Any mismatch implies byte-swapping on a Profibus DP slave, which is the most common failure mode when TOD arrives from a third-party device over a byte-swapped protocol.

Complex Data Types

Complex types group elementary types. They are mandatory for structured DBs, recipe handling, and HMI faceplate data exchange:

Type Size Description Available On
DTL 12 byte {YEAR:UINT, MONTH:BYTE, DAY:BYTE, WEEKDAY:BYTE, HOUR:BYTE, MINUTE:BYTE, SECOND:BYTE, NANOSECOND:DWORD, NANOSECOND_VALID:BYTE, … } S7-1200 FW ≥ 4.4, S7-1500
DATE_AND_TIME 8 byte Legacy BCD-packed DT, six decades of range S7-300/400, S7-1500 via compatibility
STRING n + 2 byte ASCII string, n = 0 … 254 declared chars All S7-300/400/1200/1500
WSTRING 2n + 4 byte UCS-2 (UTF-16) string, n = 0 … 16 382 declared chars S7-1500, S7-1200 FW ≥ 4.4
ARRAY [lo..hi] of <type> sum of elements Multi-dimensional indexing permitted (max 6 dimensions) All S7 generations
STRUCT sum of members Named nested record All S7 generations
PLC_UDT (UDT) sum of members User-defined template reusable in DBs / FB static sections All S7 generations; S7-1500 allows up to 65 534 UDTs per CPU

The S7-1200/1500 quota — up to 65 534 PLC data types per CPU, each holding up to 252 components — is documented in Basics of PLC data types — STEP 7 Professional V13.0. Plan UDT libraries accordingly when migrating from S7-300, where each UDT was limited to 252 bytes of declared structure size.

Parameter Types for FB / FC Interfaces

Parameter types exist only in formal-parameter declarations of FBs and FCs. They let a block accept generic objects:

Parameter Type Width What It Carries Typical Caller Pattern
TIMER 2 byte Reference to an IEC timer instance (TP, TON, TOF, etc.) myTon(IN := start, PT := t#5s)
COUNTER 2 byte Reference to CTU / CTD / CTUD cntUp(CU := pulse, PV := 100)
BLOCK_FB / BLOCK_FC 2 byte Reference to an instance of a particular FB / FC number Indirect call via CALL FB[n]
BLOCK_DB 2 byte Any data block number OPN DB[ n ]
BLOCK_SDB / BLOCK_SFB / BLOCK_SFC 2 byte System blocks (loadable / non-loadable) Rarely used outside Siemens library macros
VOID No parameter (default for unused FB / FC outputs) Function blocks without return value
POINTER 6 byte DB number + area + byte offset (legacy 32-bit pointer) Replaced by VARIANT in S7-1500
ANY 10 byte Type-tagged pointer with repetition factor Legacy generic parameter (still present)
VARIANT pointer + type tag Type-safe generic pointer, dereferenceable in SCL via VariantGet / VariantPut S7-1200/1500 strongly preferred
Pointer vs. Variant: POINTER and ANY cannot be checked at compile time for type safety. S7-1500 deprecates them in favor of VARIANT, which carries the run-time type tag inside the block and lets SCL blocks inspect element types before dereferencing.

Number Notation and Constant Formats

STEP 7 accepts multiple literal formats. Choosing the right notation prevents accidental truncation:

Format Widths Prefix Example Resulting Type
Hexadecimal 8, 16, 32, 64 B#16#2A, W#16#1234, DW#16#DEAD_BEEF, LW#16#0 BYTE / WORD / DWORD / LWORD
Binary 8, 16, 32, 64 2#0010_1010, 2#1000_0011_0000_1111 BYTE / WORD / DWORD / LWORD
Decimal (signed) 8, 16, 32, 64 -128, 32767, 123456 SINT / INT / DINT / LINT (compiler picks smallest fitting type)
Octal (legacy) 8#777 Deprecated; do not use in new code
Floating point 32, 64 3.14, 1.0e-6 REAL / LREAL
DATE 16 D#2024-08-15 DATE (days since 1990-01-01)
TIME / TIME-OF-DAY / DATE_AND_TIME 32 / 32 / 64 T#1m30s, TOD#14:30:00.000, DT#2024-08-15-14:30:00 TIME / TOD / DT
Character 8 'A' CHAR

Underscores are permitted inside numeric literals (W#16#1234_5678) and are ignored by the compiler — use them to group high-bit fields. Decimal constants with no explicit prefix default to the smallest signed integer that fits; large constants above 2 147 483 647 must be qualified explicitly as L# for 64-bit literal or as UDINT#/ULINT# unsigned literal.

Symbolic Addressing and PLC Data Types (UDT)

Symbolic addressing binds a name to a tag plus its type. TIA Portal enforces that every symbolic variable in a global DB, instance DB, or FB static section is assigned exactly one elementary or complex type. Three practical guidelines apply:

  1. Allocate UDTs for repeated structures. A UDT ("PLC data type") named, for example, typeAxis with members setpoint (LREAL), actual (LREAL), enable (BOOL), status (WORD) can then be instanced as axis1, axis2, … in a global DB. Any change to typeAxis propagates automatically.
  2. Prefer LREAL over REAL on S7-1500 when scaling analog values — even modest filter chains accumulate error past the 7-digit boundary of REAL.
  3. Bind HMI tags to symbolic DB members. HMI faceplates read only the symbols exported by the PLC, and using symbolic addressing means a DB renumbering never breaks the HMI connection.

Verification, Diagnostics, and Cross-References

Use the following checklist to confirm that a STEP 7 program is type-correct before commissioning:

  • Compile project — zero warnings under "Type conversion / Possible loss of information".
  • Open the watch table, load the binary TOD MD504, and verify the unpack yields the expected H/M/S/ms values. Discrepancy of exactly ±3600 000 ms points to the hours field; ±60 000 ms to minutes.
  • Read OB1 stack information when the CPU enters STOP with SF: confirm no REAL divide-by-zero (event ID 16#2520 area pointer fault) or BCD-conversion error.
  • Cross-reference (Show usage) every UDT instance in the project; any consumer expecting a deprecated member must be migrated.
  • Confirm the S7-1500 CPU firmware supports the requested types: LTOD, DTL, and WSTRING require FW ≥ V2.0 (LTOD) / V4.4 (DTL, WSTRING) on the S7-1200 side.

The authoritative reference for the type catalog remains the Siemens Support entry Overview of the valid data types — STEP 7 Professional V14.0, supplemented by Basics of PLC data types — STEP 7 Professional V13.0 for UDT limits, and the product page STEP 7 — TIA Portal for licensing and version compatibility.

Frequently Asked Questions

What is the exact bit layout of a TOD value in STEP 7?

TOD is a 32-bit unsigned integer storing milliseconds elapsed since midnight (00:00:00.000). The maximum is 86 399 999 ms (23:59:59.999). It is not BCD-packed; hours, minutes, and seconds are recovered by division and modulo, not by bit masking. To recover each field: hours = tod / 3 600 000; minutes = (tod MOD 3 600 000) / 60 000; seconds = (tod MOD 60 000) / 1000; ms = tod MOD 1000.

How do I unpack TOD into separate hour, minute, second, and millisecond variables?

Load the TOD as a DWORD into a tag (or MD register), then apply integer division and modulo. In SCL: hours := tod DIV 3600000; minutes := (tod MOD 3600000) DIV 60000; seconds := (tod MOD 60000) DIV 1000; millis := tod MOD 1000. In STL, multiply each component by its millisecond weight and accumulate, or use the reverse divide/modulo cascade for decomposition.

What is the difference between TOD and DTL?

TOD (32 bit) is a flat unsigned counter of milliseconds since midnight with range 00:00:00.000 … 23:59:59.999. DTL (12 byte) is a structured type with named fields YEAR, MONTH, DAY, WEEKDAY, HOUR, MINUTE, SECOND, and NANOSECOND, available on S7-1500 and S7-1200 firmware ≥ V4.4, with range 1970-01-01 … 2554-12-31. DTL is the recommended type on modern CPUs because it survives byte-swap errors during fieldbus transfers.

How many UDTs (PLC data types) can an S7-1200 or S7-1500 CPU hold?

Per the Siemens entry "Basics of PLC data types — STEP 7 Professional V13.0", each S7-1200 or S7-1500 CPU supports up to 65 534 PLC data types, and each UDT may contain up to 252 components. S7-300/400 CPUs do not have a fixed quota — they are bounded by the CPU's work-memory and load-memory limits instead.

Should I use REAL or LREAL for analog scaling on S7-1500?

Use LREAL (64-bit IEEE 754 double) whenever the calculation chain includes division, integration, or accumulation. REAL (32-bit, ~7 significant digits) drifts visibly when scaling 4–20 mA signals through a filter with a time constant above a few seconds. LREAL gives ~15 significant digits and is supported natively on the S7-1500 CPU without any performance penalty in SCL.

Back to blog