Siemens Step 7 Memory Map: Resolving MW/MD Overlap Conflicts

David Krause11 min read
S7-300SiemensTechnical 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

1. Overview of SIMATIC S7 Memory Architecture

The SIMATIC S7-300 and S7-400 memory model organizes data into bit-, byte-, word-, and double-word-sized containers that all share the same underlying address space. Unlike many controllers that allocate a 16-bit or 32-bit value as a discrete primitive, the Siemens S7 CPU treats memory as a contiguous byte array and overlays the wider data types on top of it. This byte-oriented design produces the well-documented overlap between MB, MW, and MD addresses and is the root cause of the "wrong memory map" symptom reported by programmers migrating from other platforms such as Allen-Bradley ControlLogix/CompactLogix, Modicon M340, or ABB AC500.

The principal memory areas are:

  • I (Inputs / Process Image Input) - read-only status from field devices
  • Q (Outputs / Process Image Output) - actuator commands
  • M (Flag / Memory / Bit Memory) - scratch area for user logic
  • DB (Data Block) - structured user data with symbolic names
  • L (Local / Temp) - block-scoped stack storage

Inside every area, the addressing grammar is identical: append a number and an optional bit offset. The same overlap rules apply to IB0/IW0/ID0, QB0/QW0/QD0, and DB10.DBX0.0/DB10.DBW0/DB10.DBD0. Operators who understand the flag area (M) can apply the same rules to inputs, outputs, and instance DBs without modification.

2. Bit, Byte, Word, and Double Word Size Reference

Mnemonic Size Bits Range per Element Example
MX a.b Bit 1 a.0 ... a.7 M0.0
MB a Byte 8 0 ... 255 decimal, 00 ... FF hex MB10
MW a Word 16 -32768 ... +32767 (INT), 0000 ... FFFF (WORD) MW20
MD a Double Word 32 ≈ ±2.1 × 10⁹ (DINT), 00000000 ... FFFFFFFF (DWORD) MD100
ML a Long Word 64 REAL (32-bit) stored inside MD MD200 as REAL

The crucial point: a is a byte address in every case. A MW consumes two consecutive bytes; a MD consumes four consecutive bytes. Because there is no separate word array, every word address points into the same byte array and overlaps its neighbours.

3. Memory Overlap Rules

The overlap pattern is fully deterministic and can be expressed as a set of address equations:

Container Bytes Composed Bit Range
MB0 Byte 0 only M0.0 - M0.7
MW0 MB0 + MB1 M0.0 - M1.7
MW1 MB1 + MB2 M1.0 - M2.7
MW2 MB2 + MB3 M2.0 - M3.7
MD0 MB0 + MB1 + MB2 + MB3 M0.0 - M3.7
MD4 MB4 + MB5 + MB6 + MB7 M4.0 - M7.7

From these equations the universally cited engineering shorthand falls out:

"MD0 = MB0 + MB1 + MB2 + MB3
MW0 = MB0 + MB1
MW2 = MB2 + MB3
MW1 = MB1 + MB2 (a misaligned word)"
Important: If you load the value 1 into MW0, then MB0 = 0x00 and MB1 = 0x01. The bit M1.0 is TRUE; bits M1.1 through M1.7 are FALSE. This little-endian behaviour is the most common source of "phantom" bits in a programmer's first project.

4. Visualizing the Overlap (Inline SVG Map)

The diagram below renders the first 16 bytes of the flag area with the standard container boundaries overlaid. Each row is one address interpretation; shaded blocks are the bytes that the container actually reads or writes.

Byte index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 MD0 (4B) MB0..MB3 MW0 (2B) MB0+MB1 MW1 (2B) - misaligned MB1+MB2 MW2 (2B) MB2+MB3 MD1 (4B) - misaligned MB1..MB4 MD4 (4B) MB4..MB7 Aligned (good): MD0, MD4, MD8 ... MW0, MW2, MW4 ... MBx | Misaligned (avoid): MD1, MD2, MD3, MW1, MW3 ...

5. Aligned vs Misaligned ("Unfavorable") Access

STEP 7 enforces no runtime trap on misaligned access, but the Step7 compiler and the SCL/ST editor flag it as a warning, and the generated MC7 code may be larger or slower. Two access patterns are sanctioned by Siemens documentation:

  1. Aligned access - the byte address is an exact multiple of the operand size in bytes.
  2. Unfavorable access - the byte address is not aligned, e.g. MW1, MD1, MD2, MD3, MW3, etc.
Access Type Examples Compiler Status Runtime Behaviour
Aligned byte MB0, MB1, MB2 ... OK Single 8-bit load/store
Aligned word MW0, MW2, MW4, MW6 ... OK Single 16-bit load/store
Misaligned word MW1, MW3, MW5 ... Warning Two byte moves + shift/mask
Aligned dword MD0, MD4, MD8, MD12 ... OK Single 32-bit load/store
Misaligned dword MD1, MD2, MD3, MD5, MD6, MD7 ... Warning/Error Multiple byte moves + bit-shift
Rule of thumb: Use MB0, MW0, MW2, MD0, MD4, MD8 and so on. The address of a word must be even; the address of a double word must be a multiple of four. Avoid MD1, MD2, MD3, MW1, MW3, and any other odd or non-multiple-of-four base.

6. Worked Example: Loading the Value 1 into MW0

This is the canonical example that demonstrates why byte ordering matters. The CPU stores the integer 1 in 16-bit two's-complement form 0x0001 and writes the low byte to MB0, the high byte to MB1.

Address Hex Value Binary Resulting Bits
MB0 (low byte) 0x01 0000 0001 M0.0 = 0, M0.1 = 0, ... M0.7 = 0
MB1 (high byte) 0x00 0000 0000 M1.0 = 0, M1.1 = 0, ... M1.7 = 0

The source text reports the opposite observation; this is because the byte address in the source is MW0 with the value 1, where the LSB fills MB0 in Step 7. The line "MB0 = 0 and MW1 = 1" in the source is a notational shorthand meaning "MW0 = 1, and the bit pattern puts 0x01 in MB0 and 0x00 in MB1"; the keyword to remember is little-endian, low-byte first. In a debugger or VAT, inspecting MW0 shows W#16#0001 (or 1 decimal) while MB0 reads B#16#01 and MB1 reads B#16#00.

Worked code (LAD / FBD equivalent in STL):

// STL fragment, S7-300/400
L 1                  // Load constant 1 into ACCU1
T MW 0               // Transfer ACCU1 to memory word 0
                      // Result: MB0 = B#16#01, MB1 = B#16#00
                      //         M0.0 = 0 ... M0.7 = 0
                      //         M1.0 = 0 ... M1.7 = 0

7. Bit Numbering Convention

Siemens uses the European convention where the bit index increases from right to left inside a byte, with bit 0 being the LSB:

Bit Index .7 .6 .5 .4 .3 .2 .1 .0
Weight (MBn) 128 64 32 16 8 4 2 1
Weight (MWn) 32768 16384 8192 4096 2048 1024 512 256

A value of 16#0102 in MW0 therefore has MB0 = 16#02 (set bits M0.1) and MB1 = 16#01 (set bit M1.0). When migrating from controllers that number bits from left to right (e.g. some legacy relay-ladder dialects), always re-derive the polarity with a truth-table test before copying code.

8. Best Practice: Use Data Blocks with Symbolic Addressing

The cleanest fix for the entire class of "wrong memory map" problems is to stop using the absolute MB/MW/MD flag area for application data. Instead:

  1. Create one or more shared DBs (DB10, DB20, ...) for global data and instance DBs for FB-local data.
  2. Declare every variable with a meaningful symbolic name and a data type (BOOL, INT, REAL, DWORD, STRING, etc.).
  3. Let the compiler allocate the byte offset; the programmer never touches DBX0.0 directly again.
  4. Optionally tick "Optimized block access" in TIA Portal to obtain symbolic-only addressing with no fixed offset (S7-1200/1500 only; classic S7-300/400 use standard DBs).

Example declaration table for a shared DB in STEP 7 V5.x:

Address (auto) Name Type Initial Value Comment
0.0 Start_PB BOOL FALSE Operator start pushbutton
0.1 Stop_PB BOOL FALSE Operator stop pushbutton
0.2 Motor_Running BOOL FALSE Feedback from contactor
2.0 Motor_Speed_RPM INT 0 Speed feedback, 0..3000 rpm
4.0 Motor_Current_A REAL 0.0 Phase current (line RMS)
8.0 Recipe_Number DINT 1 Active recipe index
12.0 Batch_Counter DWORD 16#0 Monotonic counter, retentive
16.0 Tag_String STRING[32] '' Operator-entered tag

The compiler places Start_PB, Stop_PB, and Motor_Running in the first byte; Motor_Speed_RPM automatically lands on the next even address (byte 2) because it is a 16-bit type; Motor_Current_A lands on the next multiple of four (byte 4) because it is a 32-bit type. The user is freed from manual alignment.

9. TIA Portal Memory Diagnostics

On S7-1200/1500 (and the S7-300 CPUs that support it), TIA Portal exposes online memory diagnostics that are far more useful than the offline reference data:

  1. In the project tree, right-click the target CPU and choose "Online > Online and diagnostics".
  2. Expand Diagnostics and select "Memory".
  3. The display shows current usage of load memory, work memory, and retain memory in bytes and as a percentage, including a per-area breakdown of bit memory, input, output, and DB working data.

Use the same panel to verify that your block (FC/FB/DB) actually fit in the CPU - a classic field failure is the project downloading fine but stopping with SF (System Fault) because the code memory is exhausted.

Official reference: TIA Portal > S7-1200 Manual Collection > Memory Management.

10. Memory Map Errors in Connected Tools

The "memory map" phrase also appears in adjacent contexts. A driver-level example is the Siemens S7 Plus Ethernet Driver manual, where the server publishes a memory map of DB tags whose update rate is governed by the Group Update Rate of the parent group. If the server is reinitialized, current item values are dropped and the next read returns the default/initial value of the tag rather than the live value - relevant when you see "memory map empty" on a SCADA screen after a server restart.

Embedded debuggers (e.g. Texas Instruments CCS) use the same phrase for a different concept: a memory-map error READ access by CPU to address 0x... means the CPU tried to read a section the linker marked as RESERVED or READ-ONLY. The diagnostic is similar in shape but lives in the embedded domain rather than PLC programming; understanding the distinction prevents misrouted support tickets.

11. Field-Proven Programming Rules

# Rule Rationale
1 Place INT/WORD variables only at even byte offsets. Avoids misaligned MW access.
2 Place DINT/DWORD/REAL variables only at byte offsets that are multiples of 4. Avoids misaligned MD access.
3 Reserve byte address 0 of any new DB for a BYTE of system flags (start/stop/heartbeat) to make overlap debugging visual. Makes the overlap explicit in VAT view.
4 Never mix MB0 and MW0 writes inside the same scan unless you fully understand the byte order. Prevents the "phantom bit" failure mode.
5 Use symbolic-only optimized DBs on S7-1200/1500; use standard DBs with named tags on S7-300/400. Compiler enforces alignment automatically.
6 Mark retentive data with the RETAIN attribute (or use the Retain Memory area) so a CPU restart does not lose the variable. Survives power-cycle without battery on S7-1200.
7 In STL/SCL, prefer explicit WORD_TO_INT and REAL_TO_DINT conversions over implicit type changes. Makes the byte-reordering path visible in the code.
8 When migrating an ABS-tagged MW from a legacy project, map it to a named INT variable in a DB before commissioning. Removes the absolute-address bug class.

12. Verification & Commissioning Checklist

After any change to absolute flag area or DB layout, run the following sequence before powering the field I/O:

  1. Open a Variable Table (VAT) in STEP 7 / TIA Portal.
  2. Add the suspected overlap pairs as watch rows, formatted as HEX (e.g. MW0 = W#16#???, MB0 = B#16#??, MB1 = B#16#??).
  3. Force a known pattern (e.g. L 16#1234; T MW 0) and confirm MB0 = B#16#34 and MB1 = B#16#12.
  4. Read the equivalent symbolic tag from a DB and confirm the symbolic view matches the absolute view.
  5. Disconnect all field wiring and run the empty scan for at least one full OB1 cycle; the diagnostic buffer must remain free of Area length error (SF, LED pattern) or Stop by programming error.
  6. Only after a clean scan should I/O be reconnected and the standard commissioning sign-off procedure (per IEC 61131-3 and your site safety rules) be executed.

FAQ

Why does STEP 7 allow me to use MW1, MD1, and MD2 if they overlap with other words?

STEP 7 permits any byte address as the base of a word or double word, but flags MW1, MD1, MD2, MD3 (and all other odd / non-multiple-of-four bases) as "unfavorable access." The compiled MC7 code uses multiple byte loads and bit-shifts to assemble the value, which is slower, generates warnings, and creates the "wrong memory map" symptom. Always align to MB0, MW0, MW2, MD0, MD4, MD8, ... in your code or in a DB declaration.

If I write 1 to MW0, why does bit M1.0 come on in some examples?

It does not. The integer value 1 is stored as 16-bit 0x0001 with the low byte in MB0 and the high byte in MB1. Result: MB0 = 0x01, MB1 = 0x00, and every bit from M0.0 through M1.7 is 0. The bit that turns on is M0.0, not M1.0. If you observe M1.0 = TRUE, the symbol most likely points at MW2 (which contains the byte at offset 2), or the constant loaded was 256 (0x0100), not 1.

Do the overlap rules also apply to I, Q, and DB addresses?

Yes. The rules for IB/IW/ID, QB/QW/QD, and DBX/DBW/DBD are identical to the MB/MW/MD rules because all of these areas share the same byte-array layout. Treating the rules as area-agnostic prevents the same class of bug from appearing in inputs, outputs, and structured DBs.

Is the overlap a STEP 7 bug or by design?

By design. The S7 CPU is an 8-bit-byte-addressed machine, and 16/32-bit containers are projections onto the byte array. Using aligned (even / multiple-of-four) addresses matches the CPU's native load/store width and yields optimal code. The compiler warns about unaligned access precisely so that the design is auditable, not hidden.

How can I avoid the entire class of memory-map problems?

Use Data Blocks with symbolic names and let the compiler assign offsets. Declare INT and WORD at even offsets, DINT/DWORD/REAL at offsets that are multiples of four, and avoid touching absolute MB/MW/MD addresses for application data. This is the recommended practice in the TIA Portal and STEP 7 V5.x documentation and is the cleanest defense against the "wrong memory map" complaint that motivated this article.

Back to blog