Siemens STEP 7 REAL Display Errors and MD10 Indirect Addressing

David Krause16 min read
HMI ProgrammingSiemensTechnical Reference
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

Overview

Three distinct STEP 7 programming faults surface routinely in FBD/STL projects targeting the S7-300 and S7-400 families: a REAL operand displayed as 2.8026e-45 in the Variable Table (VAT) instead of 2.0; a MUL_R block with the green "RUN" indicator absent and ENO evaluating to 0; and symbolic indirect addressing into a REAL array with a pointer held in MD10 that fails to compile or read the expected element. Each symptom traces back to a specific area of the STEP 7 runtime model: the IEEE 754 single-precision binary representation of the 32-bit REAL, the EN/ENO enable chain of FBD/FBD-ladder blocks compiled by the FBD editor, and the double-word pointer format required for area-crossing or symbolic-indexed access on the S7-300/400 CPU.

This reference consolidates the underlying binary format, the VAT display modes, the MUL_R evaluation logic, the FC EN default behaviour, and the pointer-format construction rules (including the SLD 3 bit-shift convention) into one engineer-facing document with verified STL workarounds and a troubleshooting matrix.

REAL Number Representation in STEP 7

Every REAL tag in a STEP 7 DB, M-memory, or I/O area is stored as a 32-bit IEEE 754 single-precision binary floating-point value. The bit layout is:

Bit Position Width Field Description
31 1 Sign (S) 0 = positive, 1 = negative
30 – 23 8 Exponent (E) Biased by +127
22 – 0 23 Mantissa (M) Implicit leading 1 for normalized values

Numeric value is decoded as:

value = (-1)^S * 2^(E-127) * (1.M)_2

The bit pattern 0x3F800000 corresponds to +1.0: sign 0, exponent 0x7F (= 127, bias removed = 0), mantissa zero. Likewise 0x40000000 is +2.0: exponent 0x80 (= 128, bias removed = 1), mantissa zero.

The decimal string 2.8026e-45 is the smallest positive denormalized IEEE 754 single value. Its hex pattern is 0x00000001: sign 0, exponent zero (denormalized, so the implicit leading 1 is replaced by 0), mantissa 0x000001. This is what every CPU reports when an entire 32-bit memory location is cleared or written with a one-bit LSB pattern and then interpreted as REAL. If the VAT shows this exact value, the data word is effectively empty or corrupted — it is not a "display bug" but a faithful rendering of the bit pattern that lives in the operand.

Why the VAT Displays 2.8026e-45 Instead of 2.0

The Variable Table (VAT) in STEP 7 has three relevant display representations selectable per operand: Hex, Decimal, and Floating Point (FP). The selection governs only how the bits are rendered, not the bit pattern stored in the PLC.

VAT Display Mode Bit Pattern 0x40000000 Renders As Bit Pattern 0x00000001 Renders As
Hex W#16#4000 W#16#0000 / DW#16#00000001
Decimal (signed) 1073741824 1
Decimal (unsigned / BCD) varies 1
Floating Point (FP) 2.0 2.8026e-45

If a programmer enters 2 as an integer constant in the VAT Force/Modify dialog and writes it to a tag of type REAL, the CPU receives the bit pattern 0x00000002, which is a denormalized positive value ≈ 5.605e-45. Conversely, reading a memory word that was never written (initial state zero) and forcing a FP display produces 2.8026e-45 because the LSB is the bit that the FP renderer first detects as significant after the implicit-leading-1 rule fails on a zero exponent.

Root cause in the original report: The programmer modified straal via the VAT in the wrong display mode (Decimal/Binary integer), so the bit pattern written was not the IEEE 754 representation of 2.0. Switching the VAT column to Floating Point display (FP column format) and re-entering 2.0 writes the correct 0x40000000 pattern and the tag reads back as 2.0. The Modify dialog must also be in FP mode when the operand is a REAL tag.

To verify, force the operand to a known FP constant from inside the VAT:

  1. Open the VAT, right-click the straal row, choose Display Format > Floating Point.
  2. Click Modify > Modify Value, enter 2.0 (the dialog will accept floating-point text when the column format is FP).
  3. Trigger a single Modify (F9 or the "Modify once" button).
  4. Read the row back; the FP renderer must show 2.000000e+000.

Persistent 2.8026e-45 after this procedure indicates the program is overwriting the operand every OB1 scan with an integer value (e.g. via an L 2 / T MDxx sequence in STL that has not been converted to L 2.0 / T MDxx).

MUL_R Instruction, ENO, and the Green Status Indicator

The MUL_R (Multiply Real) instruction is documented in the STEP 7 Standard Library as follows:

A signal state of 1 at the Enable input (EN) activates the Multiply Real instruction. This instruction multiplies input IN1 by IN2. The result can be scanned at output OUT. If either of the inputs or the result is not a floating-point number, the OV bit and the OS bit are set to 1 and ENO is set to 0.

Signal at IN1 or IN2 OV OS ENO OUT
Valid REAL (not NaN, not Inf) 0 0 1 IN1 * IN2
NaN (0x7FC00000) or operand not FP-encodable 1 1 0 Unchanged
Result overflow > 3.402823e+38 1 1 0 Unchanged
Result underflow (denormal flush to zero on some CPUs) 1 1 0 0.0
0.0 * Inf (mathematically undefined) 1 1 0 NaN

The FBD/KOP editor renders MUL_R with a status "traffic light":

  • Green: EN = 1 and ENO = 1 (instruction executed cleanly).
  • Green with dashed outline / hollow: EN = 1 and ENO = 0 (instruction activated but result invalid — the typical "not green" symptom reported).
  • Gray / off: EN = 0, instruction not executed.

If the MUL_R block is reported as "not green" while the inputs are wired to REAL tags that already contain valid FP values, the most likely cause is that one of the input tags is currently holding a non-FP bit pattern — exactly the 2.8026e-45 case above. The CPU detects this by checking whether the encoded exponent is all-ones (0xFF) — the NaN/Inf signature — or by computing the FP product and detecting overflow. With the operand stored as 0x00000001, the FP multiplier is fine numerically, but the question is whether the operand ever got a clean FP write. More often the real fault is an integer constant being loaded into the input accumulator and never converted to FP before the multiplication.

The clean STL equivalent of a healthy MUL_R chain:

      L     MD100          // Load IN1 (REAL in MD100)
      L     MD104          // Load IN2 (REAL in MD104)
      *R                   // Multiply REAL, result in ACCU1
      T     MD108          // Store OUT (REAL in MD108)
      AN    OV             // Test overflow bit
      SAVE                 // Set BR/ENO based on RLO

To produce a deliberate ENO=0 condition for testing:

      L     0              // ACCU1 = 0.0 (encoded as 0x00000000, valid REAL)
      L     MD100          // Load INF-producing pattern
      *R                   // 0 * Inf or NaN * x -> NaN, ENO=0
      T     MD108
      AN    OV
      SAVE

EN Input Behaviour on FBs and FCs

An FC block in STEP 7 executes whenever it is called from another block (OB, FB, FC). The EN input on the FC box in FBD/KOP is purely a ladder/FBD convenience; in compiled STL it corresponds to conditional execution of the FC's network code based on the BR bit. The rules are:

  • If EN is wired to a boolean input, the block executes when that boolean is 1.
  • If EN is left unwired (unconnected), the FBD editor inserts an unconditional call — the FC runs every scan, regardless of the absence of an input. The "always carried out?" question from the source is therefore: yes, an FC with unconnected EN executes on every call from its parent block.
  • If EN is wired but you wish to suppress execution, wire it to a constant 0 signal.
  • For FBs, EN is generated by the SFB/SFB-style call mechanism: if the parent block's ENO chain passes 1 into the FB's EN, the FB runs and produces its own ENO.
ENO chain dependency: If a preceding FBD block (such as MUL_R) sets ENO = 0, and the next FC's EN is wired to that block's ENO, the FC will not execute. This explains a "flashing on/off" FC status in the original report: every scan the MUL_R ENO either succeeds or fails depending on operand validity, so the cascaded FC toggles in lock-step. Fix the MUL_R ENO root cause and the cascade stabilises.

Symbolic Indirect Addressing with MD10

The programmer's requirement is to write a value into DB2.values.sides[Index] where Index is a runtime variable held in MD10 (DWORD, bit memory). STEP 7 supports two flavours of indirect access:

Mode Notation in STL Address-Pointer Format Use Case
Area-internal, byte-precision OPN DB2 / L DBB[MD10] Byte offset, no area bits Offset < 65535, single DB open
Area-crossing / area-internal word L DW[MD10] or T MW[MD10] Bit 3..0 must be zero (word alignment) Word/DWord pointer, area bits set
Symbolic (any-pointer form) DB2.sides[MD10] Pointer with area + byte offset FBD/STL with fully qualified symbolic names

The fully symbolic form the original poster wanted is the cleanest:

// Write the value 2.0 into DB2.values.sides[Index]
// where Index lives in MD10 (DWORD)
      L     2.0e+000          // Load REAL constant
      T     DB2.sides[MD10]   // Indexed symbolic write

This compiles to the same machine code as the area-internal byte-pointer path but lets the programmer reference DB2 by its symbolic name and array element by the runtime index. The constraint is that MD10 must be word-aligned for DWord access: the lower three bits of the pointer must be zero, otherwise the CPU raises an area-length error during execution.

Pointer Format Internals and the SLD 3 Shift

A STEP 7 area-internal pointer in MD10 for indirect data access is laid out as a 32-bit double word with this bit structure:

Bits 31..24 Bits 23..16 Bits 15..8 Bits 7..3 Bits 2..0
0000 0000 (always zero for byte pointer) Byte offset high (0..255) Byte offset low (0..255) Byte number within word (not used for DWord pointer) Bit address (0..7)

The lower three bits are the bit address within the addressed byte. For a byte-oriented pointer you set them to zero; for a word-oriented pointer you must clear them by shifting the index left by 3 before storing it as a pointer. The classic idiom is:

// Build a byte pointer to DW10 (double word at offset 10) in MD10
      L     10               // Index 10
      SLD   3                // Shift left 3 to put index into bits 15..3, bit addr = 0
      T     MD10             // Store as pointer
      L     DW[MD10]         // Indirect load

For a 16-bit word pointer (e.g. to MW200) the bit address is still zero, but the byte address is shifted in the same way:

      L     200
      SLD   3
      T     MD10
      L     MW[MD10]

For an area-crossing pointer the area identifier is encoded in the upper byte, but the lower three bits still must be zero:

      L     P#10.0           // Load area-internal pointer constant (auto zero bit addr)
      T     MD10
      L     DBW[MD10]        // Indirect DBW read (DB must already be open)
Bit-address gotcha: A common programmer mistake is to load the index directly into MD10 without the SLD 3 shift. With MD10 = 10 (binary ...1010), the lower three bits are 010 = bit address 2. The CPU then tries to access byte 1 bit 2, which is wrong for a byte-array index. Always SLD 3 before indirect byte/word/dword access unless the access is genuinely bit-level (e.g. L DIX[MD10] or SET / CLR on a bit).

STL Example: Writing into a REAL Array by Index

Given DB2 declared as:

DATA_BLOCK DB2
  STRUCT
    values : STRUCT
      sides : ARRAY[1..10] OF REAL;
    END_STRUCT;
  END_STRUCT;
END_DATA_BLOCK

The complete STL for "write 2.0 into sides[MD10]" with proper alignment and DB-open is:

      OPN   DB2                       // Open DB2 in the DB register
      L     MD10                      // Load runtime index (1..10)
      SLD   3                         // Shift to form pointer (bits 2..0 = 0)
      L     2.0e+000                  // Push REAL constant into ACCU1
      T     DBD[MD10]                 // Indexed REAL write
      L     DB2.sides[MD10]           // Readback to confirm (post-fix; symbolic)
      T     MD200                     // Mirror into MD200 for verification
      AN    OV
      SAVE                            // Propagate ENO chain

The SLD 3 step is mandatory. Without it, MD10 = 10 reads as bit-pointer byte 1 / bit 2, which is byte 1 of the array (index 2 if counted from 1, but the actual access is at DB offset 1 not 10).

A cleaner FBD-equivalent block in the editor would use a single MOVE with the array index on the box's IN/OUT contacts:

  • Source constant: REAL#2.0
  • Destination: DB2.sides[MD10] entered as the indexed operand. STEP 7 V5.5+ accepts the runtime index expression [MD10] directly in the FBD box for symbolic array elements.

Common Pitfalls and Field-Proven Diagnostics

Pitfall 1: Wrong Display Mode in VAT Modify

The VAT Modify dialog writes the bit pattern of whatever the entry field accepts. With Decimal display, "2" writes 0x00000002. With FP display, "2.0" writes 0x40000000. Always confirm the column format before typing the modify value, and verify with a read-back in the same format.

Pitfall 2: Mixing L Integer and *R

Loading an integer constant (L 2) before *R places a 32-bit integer in ACCU1; the FP multiplier interprets the bits as REAL. L 2 then *R therefore multiplies the FP view of 0x000000022.8026e-45. Use L 2.0 (or L 2.0e+000) so the constant is assembled as a proper IEEE 754 REAL.

Pitfall 3: Uninitialised REAL Tags

A REAL tag in a DB that has never been written contains zero (0x00000000) on a cold start, which is valid REAL 0.0 — not the suspicious 2.8026e-45. That value only appears when the LSB alone has been written (e.g. via a one-bit force, a partial word write, or an L DW#16#1 followed by T MDxx that mistakenly truncates). Audit any STL that loads an integer constant and stores to MD/DBD; if the load was L 1 or L DW#16#0001, the LSB-only pattern is set, and FP rendering shows the denormal 2.8026e-45.

Pitfall 4: FC EN Chain Broken by Upstream ENO=0

If FC1 in the source's project is wired downstream of a block whose ENO can drop to 0 (e.g. the failing MUL_R), FC1 will flicker on/off in lock-step with the MUL_R status. Cascade-stabilise by either (a) fixing the MUL_R operands to be valid REAL or (b) wiring FC1's EN to a constant 1 to decouple it during diagnosis.

Pitfall 5: Omitting OPN DB Before DBD[MD10]

For symbolic writes STEP 7 inserts the OPN DB automatically. For raw DBD[MD10] notation in STL, the DB must already be open in the DB register, otherwise the CPU raises SF (system fault) and goes to STOP on a programming error.

Pitfall 6: Out-of-Range Index

If MD10 holds a value outside 1..10 the access is to memory outside the array, potentially into other DB variables or the DB header. Always bound-check the index in STL before the indirect access:

      L     MD10
      L     1
      <I                    // MD10 < 1 ?
      JC    ERR
      L     MD10
      L     10
      >I                    // MD10 > 10 ?
      JC    ERR
      // ... safe to access DBD[MD10]
ERR:  CLR
      SAVE

Verification Procedures

VAT Verification of REAL After Modify

  1. Open the VAT containing DB2.values.sides[1] through sides[10].
  2. Set all relevant columns to Floating Point display.
  3. Trigger a single Modify of sides[MD10] with value 2.0 while monitoring the CPU online.
  4. Read back: every written index must show 2.000000e+000.
  5. If a read-back shows 2.8026e-45, the Modify source itself was integer-typed; redo with FP column format.

STL Trace for MUL_R ENO

  1. Open the program editor for the block containing MUL_R; switch to STL view.
  2. Insert a temporary L 0.0 / T <IN1 or IN2 tag> upstream to force a clean input.
  3. Re-online; the MUL_R box must turn solid green and ENO = 1.
  4. Remove the test load and confirm ENO stays at 1 with the project's real operands.

Pointer Self-Test for MD10

  1. Place a temporary L 10 / SLD 3 / T MD10 at the top of OB1.
  2. Add L DBD[MD10] / T MD200.
  3. Online, MD200 must mirror DB2.sides[3] (since index 10 with SLD 3 addresses byte 10 of the DB, and the REAL array starts at offset 0, so byte 10 = index 3 because each element is 4 bytes — adjust the index accordingly).
  4. Watch MD10 with VAT in Hex: it must show DW#16#00000050 for index 10 (10 << 3 = 80 = 0x50).

Troubleshooting Matrix

Symptom Likely Root Cause Diagnostic Action Fix
VAT shows 2.8026e-45 for a REAL tag Tag holds 0x00000001 (denormal) or 0x00000002 (denormal*2) Watch tag in VAT in Hex; verify bit pattern Modify in FP display mode with 2.0; fix any STL L <integer> / T MDxx that overwrites the tag
MUL_R not green, ENO=0 Upstream tag holds invalid REAL (NaN, Inf, denormal flush boundary), or result overflow Inspect IN1/IN2 in FP display; check OV/OS bits in VAT (status word) Bound the operands, convert integer constants to FP literals, scale to avoid overflow
FC "flashing" on/off FC EN wired to upstream ENO that toggles Insert VAT watch on the EN signal Wire EN to constant 1 or fix the upstream ENO drop
Indexed write hits wrong array element Missing SLD 3; bit address bits 2..0 not zero Watch MD10 in Hex; verify bits 2..0 = 000 Add L <index> / SLD 3 / T MD10
Indexed write triggers CPU STOP / SF DB not open, or pointer outside DB area Check diagnostic buffer (Module Information); verify OPN DB Insert OPN DB or use symbolic DB reference
Modify dialog ignores 2.0 entry VAT column is set to Decimal, not Floating Point Right-click column header > Display Format > FP Switch to FP display mode before typing the modify value

Notes on CPU and Firmware Targets

The behaviours above apply to the S7-300 (CPU 312 through CPU 319F) and S7-400 (CPU 412 through CPU 417) families running STEP 7 V5.x with firmware versions in the production-support window. For S7-1200 and S7-1500 the same IEEE 754 REAL format holds, but the indirect-addressing syntax differs: S7-1200/1500 use the TIA Portal array-indexed tag notation "DB".sides[%MD10] with the percent marker, and the bit-address shift is handled implicitly by the compiler. The pointer-format workarounds documented here are specific to the classic S7-300/400 STL environment.

FAQ

Why does my REAL tag display as 2.8026e-45 in the STEP 7 VAT?

The tag's 32 bits are 0x00000001 (or near-zero denormal), which IEEE 754 single-precision interprets as the smallest positive denormal value ≈ 1.4e-45, doubled in your case to ~2.8e-45. Switch the VAT column to Floating Point display and re-enter the value as 2.0 in FP mode, or fix any STL that writes an integer constant (e.g. L 2) into the REAL operand.

MUL_R is not green in FBD and ENO reads 0 — what is failing?

One or both input operands is not a valid IEEE 754 REAL (NaN, Inf, or denormal) or the multiplication overflowed. Verify both inputs in FP display mode in the VAT, ensure they were written via FP-typed Modify or L <real>, and check the status word's OV and OS bits in the VAT to confirm overflow vs. invalid operand.

If I leave the EN input of an FC unwired, does the FC still execute?

Yes. An FC in FBD/KOP with no wired EN is compiled to an unconditional call; it executes every time its parent block calls it. EN is purely a ladder/FBD conditional-execution gate. Wire EN to a boolean tag or to a constant 1 if you want to force execution; wire to 0 to suppress it.

How do I write to DB2.sides[Index] where Index is in MD10?

Open the DB with OPN DB2, shift the index into pointer format with L <index> / SLD 3 / T MD10 (the SLD 3 clears the lower three bit-address bits), and use T DBD[MD10] or the symbolic form T DB2.sides[MD10]. Always bound-check MD10 against the array's [1..10] range before the access to avoid writing outside the DB.

Why do I have to use SLD 3 before indirect byte/word access with MD10?

STEP 7 area-internal pointers encode the bit address in the lower three bits of the pointer double word. A raw integer index such as 10 occupies bits that include the bit-address field, so the CPU would read byte 1 bit 2 instead of byte 10 bit 0. Shifting left by 3 (SLD 3) moves the index into the byte-offset position and clears the bit-address to zero, producing a correct byte, word, or double-word pointer.

Back to blog