Siemens S7 L# Literal: DINT Constant Reference and Usage

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

The token L# appearing in a Siemens S7 program — for example, on the input pin of a MOVE box as IN = L#0 — is a language literal prefix, not a pointer, not a memory address, and not a hidden constant. It is STEP 7 / TIA Portal syntax that explicitly marks the constant as a 32-bit signed integer of data type DINT. The suffix value 0 simply means "the integer zero, 32 bits wide." Reading it as an indirect pointer or as a special marker is a common misinterpretation; the L# prefix is purely a typing annotation for the compiler.

This reference explains what L# means, how it compares to other S7 literal prefixes, why a tag such as DB42.BDD0 might still display a non-zero value even when a MOVE box writes L#0 to it, and how to trace the remaining source of the writes.

L# Notation and the DINT Data Type

The letter L in L# is shorthand for Long, the German-engineered term Siemens uses internally for its 32-bit signed integer data type. In modern TIA Portal and STEP 7, this data type is also called DINT (Double Integer). The two names refer to the same 4-byte, two's-complement format:

  • Storage width: 32 bits (4 bytes)
  • Encoding: two's complement, signed
  • Range: -2,147,483,648 to +2,147,483,647
  • Equivalent IEC 61131-3 type: DINT
  • Equivalent mnemonic in classic STEP 7: L# literal prefix or DINT type

When the editor displays L#0, it is equivalent to the typed constant DINT#0 in IEC 61131-3 syntax. The MOVE box then loads the bit pattern 16#0000_0000 into the destination operand. If a programmer had wanted to write a 16-bit zero, the prefix would be W#16#0; for an unsigned 32-bit zero, the prefix would be DW#16#0 or UDINT#0; for a Boolean FALSE, the value would simply be FALSE or BOOL#0.

STEP 7 Literal Prefix Reference Table

The S7 programming languages — LAD, FBD, STL, SCL, and GRAPH — all share a common set of typed literal prefixes. The right-hand side of the table shows the constant that would be written to a DINT-sized operand such as DB42.BDD0:

Prefix Data Type Width Range Example Hex Pattern
L# DINT (signed) 32 bit -2,147,483,648 to +2,147,483,647 L#0, L#123456, L#-1 16#0000_0000
UDINT# UDINT (unsigned) 32 bit 0 to 4,294,967,295 UDINT#0, UDINT#4000000000 16#0000_0000
DW#16# DWORD (hex) 32 bit 0 to 4,294,967,295 DW#16#0, DW#16#DEAD_BEEF 16#DEAD_BEEF
2# Bit pattern 1 to 32 bit 0/1 per bit 2#0, 2#1010_0101 16#A5
INT# INT (signed) 16 bit -32,768 to +32,767 INT#0, INT#-32768 16#0000 (sign-extended)
W#16# WORD (hex) 16 bit 0 to 65,535 W#16#0, W#16#FFFF 16#FFFF
B#16# BYTE (hex) 8 bit 0 to 255 B#16#0, B#16#FF 16#FF
REAL# REAL (float) 32 bit ±3.4e38 REAL#0.0, REAL#1.0E-3 IEEE-754
BOOL# BOOL 1 bit 0 / 1 BOOL#0, TRUE n/a

Note: in classic STEP 7 STL, the legacy form is simply L 0 (load accumulator with integer). The TIA Portal and SCL styles prefer the explicit L# or DINT# prefix to remove ambiguity about operand width.

DB42.BDD0 Address Breakdown

The tag DB42.BDD0 uses the absolute, symbolic-free addressing that S7 programmers see when an online block is opened without its symbol table. Each segment of the name carries a specific meaning:

  • DB42 — Data Block number 42. This is the instance / global DB that owns the byte.
  • B — Byte root. The address is counted in bytes from the start of the DB.
  • D — Double-word modifier. The next token is interpreted as a 32-bit aligned offset.
  • D — Decimal base. The number that follows is a base-10 byte offset.
  • 0 — Byte offset 0. With a 32-bit DWord, the four bytes occupied are DB42.DB[0..3].

The fully equivalent S7 notations are DB42.DBD0 (Data Block DWord, Decimal), DB42.DBDW0 in some legacy TIA Portal display styles, and — once a symbol is assigned — the symbolic name declared in the DB42 declaration table (for example, "Machine_External_Stop").

Because the four bytes DBB0, DBB1, DBB2, DBB3 are aliased, writing the DWord at byte offset 0 automatically writes bits DBX0.0 through DBX3.7 and the words DBW0 / DBW2 at the same time. This is the root of a class of overlapping access bugs covered later in this article.

MOVE Function and L#0 in Practice

The MOVE (or MOVE_BLK, UMOVE_BLK) box in LAD/FBD is a type-aware copy. It accepts a source on the IN pin, a destination on the OUT pin, and copies the bit pattern. When the source is wired to a constant input box that displays L#0, the editor is signaling that the constant has been entered as a typed DINT zero, not as a generic integer. The block will produce the same final state in DB42.BDD0 as wiring INT#0 or REAL#0.0 would, because the destination is 32 bits wide and the source is promoted to fit.

What the MOVE box does not do is claim ownership of the destination. It writes a snapshot in a single PLC scan; any other code path that executes in the same cycle, or in a higher-priority OB, or asynchronously from a communication job, can overwrite that snapshot immediately afterward. This is the single most common reason an operator looking at DB42.BDD0 online sees a non-zero value even though the only visible MOVE in the program loads zero.

Why a Non-Zero Value Still Appears

When a DB42.BDD0 != 0 condition is being used to trigger an external machine stop, four classes of writers can still drive the tag even if the local program only writes zero:

1. HMI / SCADA tag connection

The most frequent cause in production programs is a WinCC, WinCC Unified, TIA Panel, or third-party HMI tag that has been bound to DB42.DBD0 with read/write access. Each HMI poll cycle performs a write-back of the displayed value, and recipe downloads from the HMI commonly write raw DINT words into the same area. If the engineering backup is missing (as in the source case), the only way to confirm this is to put a watch table on the tag, force it zero, and watch the value change from a different source.

2. Other OB / FB / FC with absolute access

Cyclic OBs, time-of-day OBs, error / startup OBs, and any FB or FC instance that opens with absolute addressing (the editor will show DB42.DBD0 in light gray) can write the tag. The "only one place where this variable is writable" assumption is true only within the block the user has open; with no symbol table loaded, the user cannot see writes coming from other blocks. Restoring the symbol table from the engineering backup resolves this immediately.

3. Pointer-based or ANY-pointer access

S7 supports indirect addressing via P# pointers and ANY pointers. A piece of code that builds a pointer at runtime and uses DB42.DBD[AR1,P#0.0] or similar can target the same byte offset without the editor showing DB42.BDD0 as a literal. Indirect writes are invisible in the static cross-reference unless the user expands the indirect references list.

4. Communication jobs (PUT/GET, BSEND/BRCV, OPC UA, Modbus, S7 communication)

An S7 PUT instruction triggered by a partner CPU, an OPC UA server write from a SCADA node, or a Modbus TCP master writing holding register 0 mapped to DB42.DBD0 will all bypass the user program entirely from the perspective of cross-reference. They are also invisible unless the program is monitored with a watch table open.

Detecting Overlapping Memory Access

Overlapping access means two or more symbols or absolute addresses refer to the same byte. With a DWord at byte 0, the following aliases all collide on DB42.BDD0:

Alias Width Bytes Conflict Type
DB42.DBD0 32 bit 0-3 Full overlap (self)
DB42.DBW0 16 bit 0-1 Partial overlap on bytes 0-1
DB42.DBW2 16 bit 2-3 Partial overlap on bytes 2-3
DB42.DBB0..DBB3 8 bit each 0 / 1 / 2 / 3 Byte overlap
DB42.DBX0.0..DBX3.7 1 bit each 0.0 - 3.7 Bit overlap (32 distinct bits)

To audit for these conflicts in TIA Portal:

  1. Open the project and navigate to the DB42 editor.
  2. Switch to the "Cross-reference" view (right-click block → Cross-references).
  3. Right-click the column header and enable "Indirect access" and "Multi-instance access" so that P# and ANY-style writes are also listed.
  4. Filter on byte offset 0..3; any access from a different code block indicates a second writer.

In classic STEP 7 (SIMATIC Manager), the same audit runs via Options → Cross-reference → Display, then sorting on the address column for the DB42 byte range.

Monitoring and Forcing Variables in TIA Portal

To prove the source of a non-zero value in real time:

  1. Open the project online with the PLC in STOP or RUN as required by site safety procedures.
  2. From the project tree, add a Watch table and drag "DB42".BDD0 (or the symbolic name) into the table.
  3. Set the display format to DEC for a clean read of the integer value and add a second column formatted as HEX to inspect the raw bit pattern.
  4. Click Monitor all (the eyeglasses icon). The displayed value updates each cycle.
  5. To test the MOVE box path, force the tag to 0 via Modify → Force; immediately release the force and observe whether the value reverts to a non-zero state on its own. If it does, a second writer is active.
  6. For an indirect-write hunt, add DB42.DBW0 and DB42.DBW2 as separate watch rows. A jump in either of those words while DBD0 remains zero means the writes are reaching the same memory through a partial-overlap path.
Safety note: Forcing production tags in a running machine can cause unintended motion. Use a controlled environment, follow site lockout/tagout, and prefer monitor-only mode until the cause is understood.

Cross-Platform Compatibility

The L# literal prefix is supported across the entire SIMATIC S7 line. The exact firmware versions where typed literals were stabilized are summarized below; in practice any S7 CPU currently in production supports L# unconditionally.

Platform Firmware Family L# Support Notes
S7-300 Firmware ≥ V2.x (all current) Full Use STEP 7 V5.x or TIA Portal V13+
S7-400 All current firmware Full STEP 7 V5.x recommended for older projects
S7-1200 Firmware ≥ V1.0 (all) Full TIA Portal only; L# accepted in LAD/FBD/SCL
S7-1500 Firmware ≥ V1.0 (all) Full DINT# also accepted as IEC 61131-3 alias
ET 200SP CPU S7-1500 compatible Full Same rules as S7-1500
WinAC / SoftPLC Current Full Same compilation rules as S7-1500

For SCL, the IEC 61131-3 form DINT#0 is preferred because it survives export to non-Siemens IEC editors. In LAD/FBD, the editor inserts L#0 automatically when the user types 0 into a constant box that is wired to a DINT input.

Symbolic vs. Absolute Access

When the engineering backup is lost and only an uploaded program is available, the symbol table is stripped and every reference becomes absolute. This is the state described in the source. To recover the symbol table:

  1. Contact the original machine builder (OEM) and request the .ap13 / .ap14 / .ap15 / .ap16 / .ap17 / .ap18 / .ap19 / .ap20 TIA Portal archive, or the *.s7p STEP 7 project.
  2. If the OEM is no longer available, export the DB42 declaration as CSV from the upload (DB → Export → CSV) and re-import the symbols into a fresh project.
  3. Re-link the HMI tags: the HMI's Tag simulation / WinCC tag export contains the original symbol names. Cross-referencing these to the upload reveals writers that are invisible in the PLC code alone.

Troubleshooting Matrix for DB42.BDD0

Observed Symptom Likely Cause Diagnostic Step Resolution
Value is always zero even on a real stop event Logic is reading a different byte offset (DBD4, DBD8) Watch DBD0, DBD4, DBD8 in parallel Correct the absolute address or restore the symbol table
Value briefly zero after a cold restart, then non-zero HMI tag is writing on first poll Disconnect HMI and observe Change HMI tag access from RW to RO, or stop the write from the HMI
Value non-zero only during recipe download Recipe data block overwrites DBD0 Compare HMI recipe to DB42 layout Remap the recipe to a different byte range
Value non-zero on every cycle, same number Other FB/FC writing via absolute access Cross-reference with indirect access enabled Consolidate writers or move the tag to a different DB
Value flickers between zero and non-zero on heavy network traffic S7 PUT/GET or OPC UA write from a partner Disable partner connection and observe Remove the partner write or move data to a separate DB
Value looks correct but external stop never trips Comparison is being done in INT (16-bit) and the upper word is non-zero Check the data type of the comparator input Change the comparator to DINT or mask the upper word
Bits within DBD0 toggle independently Another tag (DBB0..DBB3, DBW0, DBW2) is also used Watch all four byte aliases simultaneously Rename / move the overlapping symbols

Practical Reconstruction Example

Given a stripped upload, the following SCL snippet reconstructs the typical pattern behind an external-stop tag and demonstrates the L#0 path:

// DB42 declaration (reconstructed)
TYPE "DB42_type"
  STRUCT
    ExternalStopCode : DINT;   // BDD0, DBD0, alias of bytes 0..3
    bBit00           : BOOL;  // DBX0.0, low bit of DBD0
    wWord0           : INT;    // DBW0, low 16 bits of DBD0
  END_STRUCT;
END_TYPE

// Cyclic OB logic — typical OEM pattern
IF "HMI_Ack_Stop" THEN
    "DB42".ExternalStopCode := L#0;   // clear the stop code
END_IF;

IF "DB42".ExternalStopCode <> L#0 THEN
    "Machine".ExternalStop := TRUE;   // drive the external stop output
END_IF;

In this pattern, the only program-side writer of DB42.BDD0 is the conditional MOVE that loads L#0. Any non-zero value observed online is being written by something outside this OB — most commonly the HMI tag bound to the same offset.

Common Pitfalls

  • Reading L#0 as a pointer because of the leading L. The L is for Long integer, not for Load or List.
  • Believing an absolute upload is complete. It is not: S7 uploads strip the symbol table, the comments, and the HMI tag bindings.
  • Assuming a single MOVE in one block is the only writer. Without the cross-reference with indirect access enabled, you cannot see P#-based writers.
  • Using a 16-bit comparator (INT#) on a 32-bit DWord tag. The upper 16 bits can hold a value that the comparator never sees, leaving the machine in a permanent stop state.
  • Forgetting that the PLC cycle runs to completion before the HMI sees the new value. A tag forced to zero at the start of OB1 can be overwritten later in the same cycle.

Related Siemens References

What does L#0 mean in a Siemens S7 program?

L#0 is a typed literal in STEP 7 / TIA Portal. L# marks the constant as a 32-bit signed DINT (Long integer), and the trailing 0 is the integer value. It is the same as the IEC 61131-3 form DINT#0, and it stores the bit pattern 16#0000_0000 in the destination operand.

Is L# a pointer in S7?

No. L# is a literal prefix, not a pointer. S7 pointers use the P# prefix (for example, P#DB42.DBX0.0 BYTE 4). Mistaking L# for a pointer is a common reading error caused by the leading L; the letter stands for "Long" in the German Siemens data type naming.

Why does DB42.BDD0 show a non-zero value when my MOVE writes L#0?

Other code paths or external systems are still writing the tag. The most common causes are HMI/SCADA tag bindings, partner CPU PUT/GET jobs, OPC UA writes, or another FB/FC that uses absolute access with the symbol table stripped. Open a watch table in TIA Portal, force the tag to zero, release the force, and observe whether the value reverts to non-zero on its own.

What is the difference between DB42.BDD0 and DB42.DBD0?

None in practice. BDD0 uses the older German root (Byte-DWord-Decimal) and DBD0 uses the modern English-style root (Data Block DWord, Decimal). Both address the same four bytes at byte offset 0 of DB42 and accept the same data type widths.

Which Siemens platforms support the L# literal prefix?

All current SIMATIC S7 platforms support L#, including S7-300, S7-400, S7-1200, S7-1500, ET 200SP CPU, and WinAC. In TIA Portal you can also use the IEC 61131-3 form DINT#0, which is equivalent and survives export to third-party IEC editors.

Back to blog