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
|
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) |
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
|
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;
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 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:
-
Allocate UDTs for repeated structures. A UDT ("PLC data type") named, for example,
typeAxiswith memberssetpoint(LREAL),actual(LREAL),enable(BOOL),status(WORD) can then be instanced asaxis1,axis2, … in a global DB. Any change totypeAxispropagates automatically. -
Prefer
LREALoverREALon S7-1500 when scaling analog values — even modest filter chains accumulate error past the 7-digit boundary ofREAL. - 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
OB1stack information when the CPU enters STOP with SF: confirm noREALdivide-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, andWSTRINGrequire 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.