Overview
The Siemens SIMATIC S5-928B CPU (used in the S5-135U and S5-155U racks) exposes a small set of system OBs that allow user code to read internal diagnostic data structures. OB 170 is the B-Stack (Break Stack) read function: it copies the eight break-stack levels that the CPU captured at the moment of the most recent PLC stop into a user-supplied DB. Each B-Stack level contains the Step Address Counter (SAC), the calling block type, the block number, and a relative return address.
For a customer who experiences random stops and has neither the STEP 5 programmer on hand nor the expertise to operate it remotely, embedding an OB 170 call into the existing program is the most robust way to leave a forensic trail. The catch, and the question that recurs in field work, is that the OB 170 output provides the SAC as a raw 16-bit absolute memory address (e.g. 53F7H) and the only documented way to convert that address to a block symbol is to cross-reference it against the Block Address List (BAL) that the STEP 5 software derives from the loaded program.
This reference describes the B-Stack layout, the OB 170 calling convention, and the deterministic procedure for mapping any SAC value back to the OB, PB, FB, or FX that was active at stop, including a worked example using the values from the field report (53F7H → OB20, 52E7H → FB250, 54C7H → FX0).
S5-928B Hardware and Memory Architecture
The S5-928B executes STEP 5 code from a 16-bit address space, organised in fixed-length blocks. Understanding this layout is the foundation for SAC decoding because the SAC is just an absolute byte/word offset into the same address space that holds the block bodies.
| Address range (typical) | Contents |
|---|---|
0000H–00FFH |
System RAM, flags, timers, counters, I/O image |
0100H–0FFFH |
Reserved / system area |
1000H–FFFFH |
User program blocks (OB, PB, FB, FX, DB, DX, SB) |
Each user block begins with a five-word header that the CPU uses for housekeeping. The header records the block type, block number, block length, author/version identifier, and library attributes. The first executable STEP 5 instruction sits immediately after the header, so:
SAC = BlockBaseAddress + HeaderOffset + InstructionIndex
where BlockBaseAddress is the address of the first word of the block header and InstructionIndex counts words from the start of the body. The S5 software reports this delta as the "block address" (e.g. OB20 address 0002 in the field report).
Block Structure and Address Allocation
STEP 5 defines seven block types. Each block type has its own address pool, but the CPU does not store the type as an address prefix; it stores the type in the block header and in the B-Stack entry as a coded value. The mapping from SAC to block requires both the SAC and the block-type code from the B-Stack level.
| Block type | STEP 5 keyword | Number range | Function |
|---|---|---|---|
| Organization Block | OB | 0–255 | Cyclic, time-of-day, interrupt, error OBs (OB 1–OB 39 cover standard tasks) |
| Program Block | PB | 0–255 | User program, no parameters |
| Function Block | FB | 0–255 | User program, parameterised via DX |
| Extended Function Block | FX | 0–255 | Function block with its own data interface (FX 0–FX 255) |
| Data Block | DB | 0–255 | User data |
| Extended Data Block | DX | 0–255 | Instance data for an FX |
| Sequence Block | SB | 0–255 | Step sequencer support (S5-150U/S5-155U) |
KF +170 into accumulator 1 and then jumping to the system service routine.B-Stack Layout and OB 170 Interface
The B-Stack consists of eight levels, numbered 0 (most recent / innermost) through 7 (outermost). Each level occupies six words. OB 170 writes the eight levels contiguously into the destination DB, giving a 48-word (96-byte) block. The per-level word layout is:
| Word offset in DB | Field | Meaning |
|---|---|---|
| DW 0 | SAC low | 16-bit absolute return address (the value reported in the field case, e.g. 53F7H) |
| DW 1 | Block type code | Coded value identifying OB, PB, FB, FX, DB, DX, or SB |
| DW 2 | Block number | 0–255 in the corresponding type pool |
| DW 3 | Return address (relative) | Offset of the call site within the calling block |
| DW 4 | Status / flags | CPU status word snapshot at stop |
| DW 5 | Reserved | Used by the CPU; do not interpret |
Eight levels × six words = 48 DW. If the destination DB is sized smaller than 48 DW the CPU truncates the write to the configured length. Size the DB to at least 48 DW to capture the full stack. The first field in the report — the SAC — is not by itself enough to identify the block; you also need DW 1 (type) and DW 2 (number) from the same level. The customer in the field case was reading the SAC but the block-type field was being ignored, which is why the address could not be resolved.
Calling OB 170 in STEP 5 Code
The standard call sequence, written in STL (Statement List) so it can be dropped into any existing STEP 5 program, is:
L KF +170 // Function code for B-Stack read
L KB 200 // Destination DB number
L DW 0 // Destination data word (start offset in the DB)
SPA FB 170 // (or call the system service entry directly)
If the existing program does not already have a logging OB, the recommended pattern is to wrap the OB 170 call inside a small FB invoked from OB 1 (cyclic) or from OB 21/OB 22 (restart), and write the result to a DB that the customer can read out with a programming device or upload via the serial port. Add a one-shot flag so the read is performed only on the first scan after a stop–run transition:
// FB 251 — B-Stack capture
// Input: none
// Output: DB 200 populated with eight B-Stack levels
// Flags: F 200.0 "capture requested" (set by user code on stop)
// F 200.1 "capture done"
U F 200.0
UN F 200.1
SPB CAPT
BEA
CAPT: L KF +170
L KB 200
L KF +0
SPA OB 170 // System service call
S F 200.1
R F 200.0
BE
Place the call in the warm restart OB (OB 21) for full coverage of stop–restart cycles. The B-Stack content survives a stop–run transition; OB 170 read on the first restart cycle therefore returns the stack that was live at the moment of the stop.
Decoding the SAC Field
The SAC field (DW 0 of each B-Stack level) is a 16-bit absolute address pointing to the next STEP 5 instruction that the CPU would have executed in the calling block had the stop not occurred. To map it back to a symbol you need three pieces of information:
- The block-type code from DW 1 of the same level.
- The block number from DW 2 of the same level.
- The block address list (BAL) for the loaded program, which gives the absolute start address of every block header.
Once you have those, the relation is:
RelativeAddress = SAC − BAL[BlockType, BlockNumber].BaseAddress
If the program source and the BAL are available (typically generated by STEP 5's Documentation / Block Address List function), the conversion is one subtraction. If the BAL is not available, see the next section for a reverse-engineering procedure.
Mapping SAC to Block: Worked Example
Using the values from the field report:
| Level | SAC (DW 0) | Block type (DW 1) | Block number (DW 2) | Relative address (DW 3) | Decoded symbol |
|---|---|---|---|---|---|
| 0 | 53F7H |
OB | 20 | 0002H |
OB20 address 0002
|
| 1 | 52E7H |
FB | 250 | (per B-Stack) |
FB250 address 52E7H
|
| 2 | 54C7H |
FX | 0 | (per B-Stack) |
FX0 address 54C7H
|
Walking through the first level:
- SAC =
53F7H, block type = OB, block number = 20. - Query the BAL:
BAL[OB,20].BaseAddress = 53F5H. - RelativeAddress =
53F7H − 53F5H = 0002H. - Subtract the five-word header (
000AH) to get the index of the executing instruction:0002H − 000AH = ...— the field value0002is reported as the "OB20 address", which the S5 software prints as the offset of the call site from the start of the block body (post-header), not the header start. Confirm the convention in your STEP 5 build by cross-checking a known call site.
The same procedure recovers FB250 from 52E7H and FX0 from 54C7H: read DW 1 and DW 2 of the matching B-Stack level, look the block up in the BAL, and subtract.
Reverse-Engineering the Block Address List
If the STEP 5 software and the original program are not available at the customer site — exactly the case in the field report — the BAL can be reconstructed on the engineering workstation by uploading the blocks and reading each header. Procedure:
- Connect a PG to the CPU and place it in Stop with restart.
- For each block number reported by the B-Stack levels, upload the block from PLC memory.
- Decode the first five words of the block: the block type and number confirm identity; the length field tells you the next block's start address in the same pool.
- Build a table of
{Type, Number, BaseAddress, Length}. - Apply the formula in the previous section for every SAC in DB 200.
On the S5-928B the block headers are word-aligned and the length field is in words, so the next block's base is the current base plus the length plus the five-word header overhead. Once a single BAL entry is known, the remaining ones in the same pool follow by addition.
Alternative Diagnostics: ISTACK and USTACK
OB 170 (B-Stack) shows the calling context at the moment of the stop. Two related system functions expose other slices of the diagnostic snapshot:
| Function | OB / FC | Contents |
|---|---|---|
| Interrupt Stack | OB 171 (ISTACK) | Stack content at the most recent interrupt, including the instruction that triggered the interrupt and the CPU status register |
| User Stack | OB 172 (USTACK) | Stack snapshot at the most recent user-error stop (e.g. divide by zero, parameter error); shows register contents, accumulators, and SAC |
For random stops with no immediately obvious cause, capture all three on the first restart cycle and compare the SAC values across them. If the B-Stack SAC and the USTACK SAC disagree, the CPU stopped inside a system service, which strongly suggests a hardware fault or a watchdog on an I/O module rather than a STEP 5 logic error. If they agree, the call site is in user code and the BAL lookup above resolves the block.
Field Procedure for Remote Troubleshooting
The customer described in the report has no STEP 5 software at the remote site and limited PLC expertise. The recommended end-to-end workflow is:
- Add OB 170 capture to the program. Embed the FB above; size DB 200 to at least 48 DW.
- Trigger on stop–run. Use OB 21 / OB 22 to call the capture FB on every warm restart, so the stack is recorded for every stop.
- Persist the capture. If the controller retains DBs across power-cycle (battery-backed RAM), the data is durable. If not, add a battery or a memory card.
- Upload by serial port. Have the customer connect a PG to the S5 port, do an Upload to PG of DB 200, and email the file. No STEP 5 license is required for a raw upload.
-
Decode on the engineering workstation. Use the BAL (either from the original program or reverse-engineered) to convert each SAC in DB 200 to
BlockType + BlockNumber + RelativeAddress. - Cross-check with USTACK/ISTACK. Capture the other two stacks on the next stop to differentiate hardware faults from logic errors.
Verification
Confirm the OB 170 capture is working before you wait for a real stop. In the lab, force a PLC stop by:
- Loading a test program that contains a deliberate
STPinstruction inside FB 1. - Running the program until the stop is logged.
- Restarting and reading DB 200.
- Verifying that level 0 of the B-Stack reports
FB 1as the calling block and the SAC matches the BAL entry for FB 1 at the address of theSTPinstruction.
If the SAC and BAL entry disagree by exactly the header length (10 bytes / 5 words), the BAL convention in your STEP 5 build reports addresses relative to the start of the body, not the start of the header. Add the header length to convert.
What does the SAC field in an OB 170 B-Stack read actually contain?
The SAC (Step Address Counter) is the 16-bit absolute address of the next STEP 5 instruction the CPU would have executed in the calling block at the moment of the stop. It is a raw memory address (e.g. 53F7H) and is only meaningful when paired with the block-type and block-number fields from the same B-Stack level.
Can the SAC be mapped to a block symbol without the STEP 5 software?
Yes. Upload the block headers from the PLC, build a Block Address List by reading each block's start address and length, and subtract the block's base address from the SAC. The result is the relative offset within the block body, which the S5 software prints as the "block address" (e.g. OB20 address 0002).
How large must the destination DB be for an OB 170 read?
Size the destination DB to at least 48 data words (six words per B-Stack level times eight levels). Anything smaller truncates the capture; the innermost levels (which are usually the most informative) are written first, so a 12-DW DB still returns the active level but loses the calling context above it.
Why does the B-Stack show the calling block but not the actual failing instruction?
The SAC points to the instruction the CPU was about to execute, not the instruction that faulted. For a divide-by-zero, the SAC is one or two words after the faulting :/D statement. For a parameter error, the SAC is inside the called block, not the call site. Always cross-check with the USTACK (OB 172) which records the exact fault class.
Can OB 170 distinguish a hardware stop from a logic stop?
Not directly. OB 170 returns the B-Stack regardless of the stop class. Compare the B-Stack with the ISTACK (OB 171) and the USTACK (OB 172); a stack mismatch between B-Stack and USTACK is a strong indicator of a hardware or watchdog stop, while agreement across all three points to a STEP 5 logic fault at the resolved call site.