1. Overview of S7-1500 Error Handling Architecture
The S7-1500 CPU family (firmware V1.0 through V20) implements a two-tier error model that engineers must understand before commissioning any application involving arithmetic, I/O access, or pointer manipulation:
- Local error handling – the offending block traps the error itself and returns control to the caller.
- Global error handling – the offending block does not trap the error; the CPU invokes the corresponding priority class (OB 121 for programming errors, OB 122 for I/O access errors, OB 80 for time errors, OB 82 for diagnostic errors, OB 83 for module removal/insertion, OB 86 for rack failure, OB 121 for programming, OB 122 for I/O access).
When no global error OB of the appropriate class is loaded, the S7-1500 CPU transitions to STOP with diagnostic buffer entry 3581 / 3582 (firmware-dependent). This is the documented fail-safe behavior. See the official Siemens TIA Portal V20 Tips for Error Avoidance and Error Handling for the complete rules matrix.
2. Block Attribute: "Handle Errors Within Block"
The toggle that selects between local and global handling lives in the block properties of every compiled block (OB, FB, FC, DB, UDT, PLC data type). In TIA Portal V14 through V20:
- Right-click the block in the project tree → Properties.
- Open the Attributes tab.
- Locate the checkbox "Handle errors within block".
- Unchecked (default) → global error handling is active for that block.
- Checked → the block uses local error handling via the system function
GET_ERRORorGET_ERR_IDand OB 121 will not be called.
| Setting | OB 121 Called? | CPU Stop on Error? | Use Case |
|---|---|---|---|
| Handle errors within block = FALSE (default) | Yes, if OB 121 exists | Yes, if OB 121 absent | Process-wide diagnostic collection |
| Handle errors within block = TRUE | No | No | Library blocks that must self-recover |
| OB 121 programmed + global handling | Yes | No (OB decides) | Logged recovery, set flags, return to scan |
| OB 121 absent + global handling | N/A | Yes | Safety-critical STOP on any programming fault |
3. Programming Error OB (OB 121) Behavior on S7-1500
Per the official Siemens documentation entry ID 109742272 – Programming error OB (S7-1500):
"Global error handling must be set if you want to enable the S7-1500 CPU to call the programming error OB."
The trigger sources for OB 121 include, but are not limited to:
- Arithmetic overflow on a typed conversion (e.g.,
INT_TO_REALwith NaN input). - Division by zero on integer and real operands.
- Invalid array index / range violation on a fully-qualified DB access.
- Invalid pointer / Variant unwrap.
- Type conversion error in SCL
MOVEor assignment.
The OB 121 interface provides the temporary OB_EV_CLASS, OB_SW_FLT, OB_FLT_ID, OB_PRG_ADDR, and block-type information (OB_BLOCK_TYPE) tags. Sample read:
#iFaultId := OB_FLT_ID; // 16#80xx subevent code
#iBlockType := OB_BLOCK_TYPE; // 16#38 = OB, 16#08 = FB, 16#0C = FC
#iPrgAddr := OB_PRG_ADDR; // offset inside block
4. Local vs Global Error Handling – Detailed Comparison
| Aspect | Global (default) | Local (Handle within block = TRUE) |
|---|---|---|
| Error OB called | OB 121 (programming), OB 122 (I/O) | None |
| Detection primitive | CPU scan |
GET_ERROR / GET_ERR_ID in caller |
| SCL behavior | OB 121 raised; status bits set |
GET_ERROR returns the error struct |
| LAD/FBD behavior | ENO = 0 on the failing instruction | ENO still propagated; must check downstream |
| STL behavior | Status bits OV, OS, CC0/CC1 set | Same flags, no OB |
| Diagnostic buffer entry | Always written | Optional |
| CPU default on absence | STOP | RUN |
5. Language-Specific Error Behavior
5.1 LAD and FBD
When a math box (ADD, SUB, MUL, DIV, MOVE with type conversion) detects an error, the Enable Output (ENO) of that box is cleared to FALSE. Downstream operations that branch on ENO can be skipped. The diagnostic buffer also receives a non-OB event if OB 121 is present.
5.2 STL (Statement List)
Arithmetic errors set the CPU status bits in the same way as S7-300/400. Read these in the user program:
// Standard bit addresses (S7-1500, word-serial view):
OV == "OV" // Overflow (latched)
OS == "OS" // Stored overflow (sticky)
CC0, CC1 // Condition codes after comparison/arithmetic
Conditional jumps use JP (positive), JN (negative), JZ (zero), JN, JO (overflow), JOS (latched overflow). A division-by-zero result is reported as invalid rather than as a fatal condition—check OV or OS after the operation. See the STEP 7 Professional manual "Jump if calculation is invalid" reference for the canonical flow.
5.3 SCL (Structured Control Language)
SCL does not implicitly branch on ENO. A failing arithmetic expression will:
- Raise the event OB 121 if global handling is active and OB 121 exists.
- Otherwise place a result-dependent invalid value (often
0or last valid) in the target variable. - Set status bits that can be polled with
OV/OS.
This is the source of the "I divide by zero and no error is detected" symptom reported by users: with global handling unchecked and OB 121 present, the error is silently absorbed.
6. Division-by-Zero in SCL – The CONTINUE Pattern
The recommended SCL idiom to avoid a division-by-zero programming error mirrors the official S7-1200 system manual example. Place the guard before the division:
FOR x := 0 TO 10 DO
IF value[x] = 0 THEN
CONTINUE; // skip this index, do not call OB 121
END_IF;
p := part / value[x] * 100;
s := INT_TO_STRING(p);
percent := CONCAT(IN1 := s, IN2 := "%");
END_FOR;
Why CONTINUE rather than EXIT?
-
CONTINUEskips the remainder of the current loop body and proceeds with the next iteration. This is the safe path when only one element is bad. -
EXITterminates the entire loop, which abandons all remaining indices—including valid ones.
An alternative pattern is to pre-filter zero entries into a "zero-encounter" counter:
iZeroCount := 0;
FOR x := 0 TO 10 DO
IF value[x] = 0 THEN
iZeroCount := iZeroCount + 1;
ELSE
p := part / value[x] * 100;
// store p
END_IF;
END_FOR;
IF iZeroCount > 0 THEN
"dbDiag".ZeroDivSeen := TRUE; // diagnostic flag for HMI
END_IF;
7. Status Bits for Calculation Errors
The S7-1500 inherits the S7-300/400 status-bit model. After any integer or floating-point math instruction, evaluate these tags:
| Tag | Symbol | Meaning | Latched? |
|---|---|---|---|
OV |
Overflow | Result out of range | No (cleared on next math op) |
OS |
Stored overflow | Overflow seen at least once | Yes, until cleared with CLR
|
CC0 / CC1
|
Condition codes | ==, <, >, <> | No |
BR |
Binary result | ENO of FB box | No |
To centralize detection, route all critical math through a wrapper FB whose first instruction is A OV; S "dbDiag".OverflowSeen; CLR;.
8. Configuring OB 121 in TIA Portal V20
- Project tree → PLC_x → Program blocks → Add new block.
- Choose Organization block → Programming error OB. Default name: ProgrammingError (OB 121).
- Inside the OB, write the recovery logic. Three patterns are common:
// Pattern A: Log and continue (logged recovery)
"dbDiag".LastFaultId := OB_FLT_ID;
"dbDiag".LastPrgAddr := OB_PRG_ADDR;
"dbDiag".FaultCount := "dbDiag".FaultCount + 1;
RETURN;
// Pattern B: Hard stop after N faults
IF "dbDiag".FaultCount >= 10 THEN
STP(); // explicit STOP request from OB 121
END_IF;
RETURN;
// Pattern C: Bypass and resume
CASE OB_FLT_ID OF
16#8001: "dbDiag".MathFault := TRUE; // DIV by 0
16#8002: "dbDiag".RangeFault := TRUE; // range error
ELSE "dbDiag".OtherFault := TRUE;
END_CASE;
- Compile and download. Confirm in the device configuration: Properties → General → System and clock memory that OB 121 is enabled in the runtime profile.
STP() inside OB 121 transitions the CPU to STOP after the OB body completes. The diagnostic buffer will record "Programming error OB has requested STOP" – useful for forensic replay.9. I/O Access Errors vs Programming Errors
A subtle source of confusion: I/O access errors raise OB 122, not OB 121. The block-attribute Handle errors within block affects both. Field tests show that with global handling the CPU correctly invokes OB 122 on a missing module, while a divide-by-zero in the same scan may go unnoticed because OB 121 is unprogrammed and the user has not implemented the CONTINUE guard. Plan OB programming per fault class:
| OB Number | Fault Class | Default without OB |
|---|---|---|
| OB 80 | Time error | STOP |
| OB 82 | Diagnostic interrupt | IGNORE |
| OB 83 | Module pull/plug | STOP |
| OB 121 | Programming error | STOP |
| OB 122 | I/O access error | STOP |
10. Verification and Diagnostic Procedure
After commissioning, validate global error handling with this 6-step procedure:
-
Force a known divide-by-zero: write a temporary SCL FB that divides an
LREALby0.0with global handling unchecked. -
Observe OB 121: if OB 121 is present and non-empty, the CPU stays in
RUNand the diagnostic counter in your OB body increments. -
Delete OB 121 from the project, recompile, download. Re-trigger the same division. The CPU must transition to
STOPwith buffer entry 3581. -
Re-create OB 121 with
STP()inside. Re-trigger: CPU transitions toSTOPdeliberately, buffer shows "OB 121 STP request". -
Enable Handle errors within block on the test FB and re-trigger. CPU stays in
RUN, OB 121 is not called. - Read the diagnostic buffer: Online & diagnostics → Diagnostics buffer. The buffer will record whether OB 121, OB 122, or neither fired.
10.1 Online Diagnostic Read Example
// Inside OB 1 start-up, tag reads:
#iOB121Count := "dbSysDiag".OB121Count;
#iOB122Count := "dbSysDiag".OB122Count;
IF #iOB121Count > 0 THEN
"dbHmi".LastFaultClass := 'ProgrammingError';
"dbHmi".LastFaultTime := RD_SYS_T();
END_IF;
11. Common Pitfalls and Edge Cases
-
LREAL divide by 0.0 in SCL on a CPU with firmware < V2.0 silently returns
+Infor-Infrather than raising OB 121. Update firmware or add explicitIF divisor = 0.0 THEN ... END_IF. -
Typeless constants:
result := part / 0;is treated as integer by default and may not trigger floating-point exceptions. Cast explicitly:result := part / LREAL#0.0;. - Library blocks with Handle errors within block = TRUE imported from older S7-300 libraries will suppress OB 121; copy the source and reset the attribute if global handling is required.
-
DB access with absolute addressing: invalid slice offsets trigger OB 121 even with Handle errors within block = TRUE. Always validate pointers in
PEEK/POKEsequences. - Circular reference between OBs: an OB 121 that itself faults will escalate to STOP.
12. Replacement CPU Best Practice
Per the Siemens guidance linked above, always have the configured program archived offline. When replacing a defective S7-1500 CPU:
- Download the complete project, not just the changes.
- Confirm that OB 121, OB 122, and OB 82 are present and match the expected block-level attribute state.
- Verify the device configuration fingerprint matches (article number, firmware version, I/O layout).
- Run the divide-by-zero verification above before resuming production.
13. Quick Reference – Decision Flowchart
Program fault occurs
|
Handle errors within block?
/ \
YES NO
| |
GET_ERROR OB 121 present?
returns fault / \
NO YES
| |
CPU -> STOP OB 121 body runs
|
STP() inside?
/ \
NO YES
| |
Return to scan CPU -> STOP
14. Frequently Asked Questions
Why does my S7-1500 CPU stay in RUN after a divide-by-zero even though I have not programmed OB 121?
Because the default block attribute is "Handle errors within block = FALSE" (global), and if OB 121 is present the error is absorbed silently. Either delete OB 121 to force a STOP, add a CONTINUE guard in SCL, or call STP() from inside OB 121 to halt deliberately. See the Programming error OB entry ID 109742272.
How do I detect a divide-by-zero in SCL without stopping the CPU?
Use the IF divisor = 0 THEN CONTINUE; END_IF; pattern before the division, or increment a counter and post-process. Alternatively, enable "Handle errors within block" on the FB and call GET_ERROR after the divide to read the local error structure. See the SCL CONTINUE example above.
What is the difference between global error handling and "Handle errors within block"?
Global error handling (default) makes the CPU call OB 121 for programming faults or OB 122 for I/O faults when the block attribute is unchecked. With "Handle errors within block = TRUE" the calling block must interrogate the error itself using GET_ERROR or GET_ERR_ID; no OB is called.
Does the S7-1500 behave like the S7-300/400 with status bits OV, OS, CC0, CC1?
Yes. The status bit model is preserved. A divide-by-zero sets OV and OS, with the result marked invalid. Read these in STL/SCL after the arithmetic instruction and use JOS/JO jumps in STL. Refer to the STEP 7 Professional manual section "Jump if calculation is invalid".
Which firmware version first supported the current OB 121 behavior on S7-1500?
OB 121 has been supported since the first S7-1500 CPUs (firmware V1.0, 2013). The block attribute "Handle errors within block" and the GET_ERROR/GET_ERR_ID semantics are documented from STEP 7 Professional V14 onward and remain consistent in TIA Portal V20.