Resolving DB0 Access Errors in S7-1500 OB1 During CPU Restart

David Krause14 min read
SiemensTIA PortalTroubleshooting
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

Problem Overview

On Siemens SIMATIC S7-1500 controllers programmed with TIA Portal V16 and newer, a recurring fault pattern appears during cold or warm restarts where the CPU enters STOP shortly after power-up with a diagnostic buffer entry referencing DB0. The user program, typically executing inside OB1 (main cyclic program) or one of its called Function Blocks (FBs), attempts to open a data block whose block number evaluates to zero. Because DB0 is reserved as the system data block area used internally by SFBs and SFCs and is not loadable as a user DB, the call triggers a programming error and, depending on OB configuration, brings the CPU down.

The symptom is consistent with the field case reported: the CPU runs without faults once the program has been online for some time, but the same fault returns each time the controller is rebooted, regardless of whether it is a power-off/power-on cold restart, a warm restart, or a STOP-RUN transition. Diagnostic buffer entries typically show Event ID 16#2522 (programming error in user program) or 16#3571 (block cannot be loaded / DB not loaded) with the event reference pointing to the instruction that triggered the access, often a fully qualified DB access such as DB[variable].Member or an instruction that interprets a DB-ANY input as a block number.

Understanding why DB0 appears, why it only happens on restart, and which OBs are involved is the foundation of a durable fix. The remainder of this reference walks through the root cause, the diagnostic process, and the validated remediation patterns.

Root Cause: Uninitialized DB_ANY Parameters

The Siemens DB_ANY data type carries a runtime reference to a data block. Unlike DB (static), POINTER, or ANY, the DB_ANY pointer resolves the block number dynamically through the block container of the assigned instance. When a parameter of type DB_ANY is left unconnected at the call site, the variable holds the value 0, which the CPU interprets as a request to load DB0. S7-1500 CPUs do not allow user access to DB0 for read or write; the system responds with a block-not-found or programming error before the operand fetch is performed.

The same failure mode applies to:

  • VARIANT inputs of FBs that internally call WRD_TBL or array-copy instructions
  • Indirect addressing using OPN DI[i] or OPN DB[i] where i is a tag of type DINT not yet initialized
  • Legacy ANY pointers with a zeroed DB field
  • FB multi-instances whose parent DB has not yet completed initialization during startup OBs

The field pattern observed is that the call sequence appears fine after a STOP-RUN because the parent FB has been initialized in a previous cycle, so the DB_ANY tag has been written with a valid block number. On a fresh restart, the first scan of OB1 executes before the user program has had the opportunity to populate that tag, so the value is still the default zero from the load memory image.

Why the Fault Only Appears on Restart

S7-1500 CPUs distinguish three startup types: cold restart (OB100), warm restart (OB101, retained), and hot restart (OB102 on S7-1500R/H redundant systems). The startup OB runs to completion before OB1 begins its cyclic processing. During startup, the CPU:

  1. Clears the process image and initializes all non-retentive M, DB instance tags, and I/O areas to the configured initial values from the offline project.
  2. Loads the system data and the startup OB.
  3. Executes the startup OB once.
  4. Transitions to RUN and begins OB1 execution.

If the project has been compiled with initial values, tags declared without explicit assignment retain the offline initial value, which by default is 0. DB_ANY tags fall into this category. When the startup OB does not write the DB_ANY value, or the FB that holds it is not invoked from the startup OB, then on the first scan of OB1 the uninitialized pointer reaches a block-opening instruction. The fault is reproducible on every restart until the program reaches the scan where the FB finally writes the pointer with a valid block number.

This explains the symptom: fault on reboot, no fault during runtime because subsequent scans contain a valid DB_ANY.

Reading the CAddr in TIA Portal

The diagnostic buffer entries reference a CAddr (cross-reference address) and a BlockNumber. To interpret these correctly in TIA Portal V16, V17, V18, or V19:

  1. Open Online > Online & Diagnostics on the CPU.
  2. Select Diagnostics buffer.
  3. Locate the programming error entry, e.g. 16#2522 "Programming error" or 16#3571 "Block cannot be loaded".
  4. Click the row, then click Open in editor. This jumps directly to the offending STL/SCL line.
  5. Right-click the failing instruction and select Go to > Cross-reference to confirm which FB/OB invokes the instruction path.

The CAddr field encodes the priority class (OB), block number, and offset within the block where the fault was detected. A typical line reads:

Programming error (16#2522)
CAddr: OB1 / FB34 / Offset 0x0046
BlockNumber: 0

Reading BlockNumber: 0 is the smoking gun: the instruction resolved a block number of zero. Click the Open in editor button to navigate to offset 0x0046 inside FB34; the line will be either an explicit OPN DB 0 or, more commonly, an AUF DB [tag] where tag is the DB_ANY input parameter.

Diagnostic Buffer Error Code Reference

Event ID (hex) Meaning Relevant OB
16#2522 Programming error in user program OB121 (Programming error OB)
16#2523 Programming error in process image update OB121
16#2942 I/O access error when reading OB122 (I/O access error OB)
16#2943 I/O access error when writing OB122
16#3571 Block cannot be loaded / DB not loaded OB121 (called when OPN references DB0)
16#39C1 Startup information invalid OB100/OB101
16#4301 Mode transition RUN to STOP due to programming error OB1 / OB121
16#8186 User break in startup Startup OB

Note that DB0 accesses are surfaced as 16#3571 in the diagnostic buffer of the CPU when the resolution fails prior to evaluation; in newer firmware (V2.9 and later on S7-1500), the event is reported as 16#2522 with detailed information about the instruction pointer.

Startup Organization Blocks on S7-1500

Understanding which startup OB is selected in the CPU properties is essential. From the TIA Portal project tree:

  1. Open Device configuration on the CPU.
  2. Select Properties > General > Startup.
  3. Confirm the startup type: cold restart (OB100), warm restart (OB101), or hot restart (OB102 for R/H systems).

If the user has not added a startup OB, the CPU performs its internal startup and skips directly to OB1. Adding OB100 with the parameter StartInfo makes the first-scan detection trivial because StartInfo.PrioClass can be queried or, more cleanly, because OB100 can set a flag before the cyclic program runs.

Solution 1: First-Scan Skip Pattern (Recommended)

The most robust and least invasive fix is to detect the first scan of OB1 and skip the block call that depends on the DB_ANY until the user program has populated the pointer. Two patterns are field-proven.

Pattern A: Use a flag set in the startup OB

Add OB100 to the project (or use the existing startup OB). At the very start of OB100, set a global marker:

// SCL in OB100 - Startup
"StartupComplete" := FALSE;
"FirstScanOfOB1" := TRUE;

Inside the parent FB that calls the block referencing the DB_ANY, gate the call:

// SCL inside the parent FB
IF "FirstScanOfOB1" THEN
    RETURN;
END_IF;

// normal call to FB34 with the DB_ANY parameter
FB34_DB(inAny := MyDB_AnyInput, outValue => result);

At the end of OB1, reset the flag:

// SCL in OB1 - last line
"FirstScanOfOB1" := FALSE;

This guarantees that the offending instruction is not executed on the first scan after a restart, even if the startup OB does not initialize the pointer. Combined with a properly initialized DB_ANY, this is a belt-and-suspenders fix.

Pattern B: Query OB1_START_INFO in SCL

If you cannot modify the startup OB, use the system <<OB1_START_INFO>> STRUCT via RD_SINFO / RD_SYSINF and check whether the current OB priority class indicates a startup. The cleanest path is to check a custom one-second timer that has not yet expired:

// SCL at the top of the parent FB
IF "BootTimeElapsedMs" < 1000 THEN
    RETURN;
END_IF;

Where BootTimeElapsedMs is incremented inside a cyclic interrupt OB (e.g., OB30 at 100 ms) and starts at zero on restart.

Pattern C: Defensive DB_ANY guard

Validate the DB_ANY input before the instruction that uses it:

// SCL
IF inAny.Number = 0 OR inAny.Number = 16#FFFF THEN
    RETURN; // guard
END_IF;

This protects against any future case where the upstream assignment is incomplete and provides a fast, self-documenting safeguard.

Solution 2: Properly Wire the DB_ANY Inputs

The first line of defense is to ensure that every DB_ANY input at the call site of FB34 is wired to a concrete DB or to a tag that holds a valid block number. TIA Portal exposes the DB_ANY data type for FBs and FCs and accepts either:

  • A direct DB symbol, e.g. "DataBlock_1" at the block input
  • An Array of DB element, e.g. MyDBs[3]
  • A tag of type DB_ANY declared in the parent DB

When wiring, select the input in the FB interface and drag a DB from the project tree onto the input. The compiler will accept the assignment only if the type matches; however, leaving the input empty (the default) generates a default value of zero. Re-audit every call site:

  1. Open the project tree and navigate to every call of the FB that contains the failing path.
  2. Expand the call and verify all DB_ANY inputs are bold and show a DB symbol, not "..." or empty.
  3. Compile the project. TIA Portal V17+ emits warning W:1509 for unconnected DB_ANY parameters; treat this as an error.

Solution 3: Initialize the DB_ANY in the Parent FB's Static Section

If the DB_ANY is held in the static area of a parent FB, set an initial value in the FB declaration:

  1. Open the parent FB, select the Static tab.
  2. Locate the DB_ANY tag.
  3. Set the initial value column to the desired concrete DB, e.g. "DataBlock_5".
  4. Recompile and download.

This guarantees that the runtime instance DB created for the parent FB contains a valid block number from the moment the instance is materialized, including the very first scan after a restart.

Solution 4: Use a Symbolic Reference Instead of DB_ANY

Where the project permits, replace DB_ANY with a fully qualified data block access. This avoids runtime block-number resolution entirely. For example, instead of:

// Pattern to avoid
FB34(inAny := MyArrayOfDBs[i], ...);

Use a CASE structure or a multi-instance approach where each block reference is statically declared:

CASE i OF
    0: FB34(inData := "DB_A".Field, ...);
    1: FB34(inData := "DB_B".Field, ...);
else
    ;
END_CASE;

Symbolic access eliminates the failure mode entirely and improves readability. Reserve DB_ANY for cases where the block number is truly dynamic at runtime, e.g., recipe selection from a list of 30 DBs.

Solution 5: Add a Programming Error OB

Adding OB121 (Programming error OB) and OB122 (I/O access error OB) keeps the CPU in RUN even if a programming error occurs. Add them to the project tree under Program blocks > Add new block > Organization block, select Programming error OB. The default empty OB is sufficient; the CPU will call it on the fault and continue.

Caution: OB121/OB122 mask the symptom but do not fix the root cause. Use them as a safety net during commissioning to keep the process running while the upstream issue is investigated. In production, the fault should be logged and the offending path corrected.

For I/O-related faults, consult the official Siemens documentation on I/O access error OB and the parallel "Programming error OB" topic in the SIMATIC S7-1500 manual collection.

Solution 6: Diagnostic Buffer Forwarding

Add diagnostics so the next fault is immediately localized. In OB121, capture the block number, offset, and event ID and write them to a non-retentive buffer:

// SCL in OB121
"DiagBuffer".EventID   := #OB121_EV_CLASS;
"DiagBuffer".FaultOB   := #OB121_FLT_ID;
"DiagBuffer".BlockNum  := #OB121_BLNumber;
"DiagBuffer".Offset    := #OB121_FLT_PRG_ADDR;

Expose these tags to the HMI so that a fault is visible without a direct TIA Portal connection.

Verification Procedure

  1. Apply one or more of the solutions above and download the project to the CPU in STOP mode.
  2. Power-cycle the controller three times consecutively to verify cold-start behavior.
  3. After each restart, observe the diagnostic buffer; no 16#2522, 16#3571, or DB0-related entries should appear.
  4. Trigger a STOP-RUN transition via the TIA Portal Online > CPU operating panel > RUN button.
  5. Watch the first 5-10 seconds of cyclic execution; the program should reach steady state without entering STOP.
  6. Force a power-off, wait 10 seconds, and confirm clean recovery.
  7. Open Watch table and confirm the first-scan flag transitions correctly: TRUE on first scan, FALSE thereafter.
  8. Repeat with the project downloaded from another engineering station to rule out local cache differences.

Troubleshooting Matrix

Symptom Likely Cause Fix
Fault on every cold restart, clean on STOP-RUN Uninitialized DB_ANY on first scan Solution 1 first-scan skip or Solution 3 initial value
Fault on cold restart and warm restart, clean on STOP-RUN Static instance not initialized because no startup OB sets initial values Add OB100 and initialize pointers
Fault only after recipe change Indirect DB reference via index array not bounds-checked Validate index, validate DB_ANY number before OPN
Fault intermittent, depends on operator action Operator HMI writes a tag of type DB_ANY that is momentarily invalid Add PLC-side validation before accepting the HMI value
Fault only after firmware update Firmware now strictly enforces DB0 access; previous firmware masked it Refactor uninitialized DB_ANY inputs; refer to firmware release notes
CPU goes to STOP and OB121 does not run Programming error has not been masked and is not handled Add OB121 as a safety net; fix root cause independently

Related Concepts

  • DB_ANY: A pointer to a data block used at FB/FC interface boundaries. Resolved at runtime via the block container.
  • VARIANT: A flexible pointer to any data type or DB. Encodes type, length, and block number.
  • ANY: Older pointer type; stores area, DB number, byte offset, and length explicitly.
  • OB1: Main cyclic program, priority class 1 (configurable to higher on S7-1500).
  • OB121: Programming error OB; called when an instruction cannot execute due to a programming fault.
  • OB122: I/O access error OB; called when an instruction references I/O that is not present or not accessible at the time of access.

Field-Proven Caveats

  • S7-1500 firmware V2.9 tightened the runtime check on indirect DB access; a project that ran without fault on V2.6 may fault on V2.9 after a restart.
  • Adding OB121 without fixing the cause hides the fault and may cause data integrity issues that surface elsewhere, e.g., the wrong DB being updated silently.
  • The DB_ANY tag must reference a DB that exists in the active project; referencing a DB that is present only on an HMI engineering station is not sufficient.
  • Do not use OPN DB 0 deliberately. The system data block area is internal to the CPU and cannot be a target for user instructions.
  • Always cross-reference the failing instruction via the Open in editor button in the diagnostic buffer; manual offset navigation is error-prone.

References and Standards

For deeper context, refer to the SIMATIC S7-1500 system manual and the TIA Portal help for Organization Blocks. The official Siemens documentation on I/O access error OBs is available at docs.tia.siemens.cloud - I/O access error OB. The Programming error OB topic and Startup OBs are documented in the same collection; cross-reference the SIMATIC S7-1500 manual collection on the Siemens support portal for the firmware version in use (V2.6, V2.9, V3.0, or V3.1).

Why does my S7-1500 only fault on restart and not during normal operation?

On a fresh restart, all non-retentive tags, including any DB_ANY parameter not explicitly initialized, default to 0. The first scan of OB1 executes before the user code has a chance to populate the pointer, so the instruction attempts to open DB0. After the first scan, the parent FB typically writes the pointer with a valid block number and the fault clears. Adding a startup OB (OB100) or a first-scan skip eliminates the fault.

Is DB0 a valid data block I can read or write in S7-1500?

No. DB0 is a reserved identifier in the S7-1500 system and is not accessible to user code. The system uses the same numeric identifier internally for SFB and SFC working data. Any reference to DB0 from user code is treated as a programming error and triggers OB121 or brings the CPU to STOP if OB121 is not loaded.

How do I interpret the CAddr field in the diagnostic buffer?

The CAddr field encodes the priority class (OB), the block number, and the offset within the block where the fault was detected. In TIA Portal, click the diagnostic entry and select "Open in editor" to jump directly to the failing instruction. Look at the BlockNumber sub-field; a value of 0 confirms an uninitialized DB reference.

Can I just add OB121 to keep the CPU in RUN?

Adding OB121 (and OB122 for I/O faults) prevents the CPU from entering STOP and is useful as a commissioning safety net, but it does not fix the root cause. The fault will continue to occur on every restart, and downstream logic that depended on the data being read from a valid DB may execute against incorrect or empty memory. Always pair OB121 with a real fix such as a first-scan skip or a properly initialized DB_ANY.

What firmware versions enforce DB0 protection more strictly?

S7-1500 firmware V2.9 introduced stricter runtime checks for indirect and DB_ANY-based block access; projects that ran without fault on earlier firmware versions may begin to fault on V2.9 and later after a restart. When upgrading firmware, run a controlled restart test and review the diagnostic buffer for new 16#2522 or 16#3571 entries before returning the controller to production.

Back to blog