Overview of S7-400H CPU Validity Diagnostics
The SIMATIC S7-400H is Siemens' high-availability (Hochverfügbar) fault-tolerant PLC platform that pairs two identical CPUs in a hot-standby configuration. Each CPU independently executes the user program and synchronizes its state with the partner through fiber-optic redundancy links. The CPU validity bits reported through SZL W#16#xy71 are the lowest-level heartbeat that tells the application program which redundancy partner's diagnostic snapshot can be trusted at any given moment.
System function block SFC51 "SYSST" (Read SZL) is the canonical method for retrieving this data from any S7-400/H CPU, and SZL W#16#xy71 is the dedicated system status list for H-system module and CPU status. This reference documents the structure of that SZL, the meaning of the B#16#01 / B#16#02 / B#16#03 validity bytes, the operating-state (Betriebszustand, BZ) encoding from W#16#0001 through W#16#000F, and the engineering consequences of consuming invalid data downstream.
S7-400H Redundancy Architecture and Diagnostic Sources
An S7-400H station contains two identical CPUs (CPU 0 in rack 0 and CPU 1 in rack 0 or 1 depending on the configuration), each connected to the partner via two redundant fiber-optic synchronization modules. Two synchronization links must be active for full redundancy; a single link results in a Single-Mode warning but operation continues. Each CPU independently reads its own inputs and writes its own outputs; the active (master) CPU owns the I/O bus and the partner remains in hot standby, executing the program but with outputs inhibited.
Three families of diagnostic information are available to the user program:
- Local CPU diagnostics – diagnostic buffer, OB-startup information, and standard SZLs (for example W#16#0019 for LED status, W#16#001C for component identification).
- H-system diagnostics – SZL W#16#xy71, the topic of this reference, which aggregates redundancy status, CPU validity, and link state.
- Module and DP-slave diagnostics – SZLs W#16#xy00 through W#16#xy0A, surfaced as DP diagnostics in OB82 / OB86 / OB122.
The S7-400H system manual, entry ID 1117397, is the canonical document for redundancy-state interpretation. The FAQ 19537149 on Siemens Support is the original reference for SZL W#16#0071 that engineers in the field most commonly cite.
SFC51 "SYSST" and the System Status List (SZL) Mechanism
SFC51 is the system function every S7-400/H user program uses to query the system status list. The declaration in STEP 7 is:
FUNCTION_BLOCK SYSST / SFC51
VAR_INPUT
REQ : BOOL; // start read on rising edge
SZL_ID : WORD; // SSL_ID (system status list identifier)
INDEX : WORD; // partial-list index
END_VAR
VAR_OUTPUT
BUSY : BOOL;
SZL_HEADER : STRUCT // length / number of records
LENTHDR : WORD;
N_DR : WORD;
END_STRUCT;
SZL_DATA : ARRAY[1..464] OF BYTE; // 232 WORD
RET_VAL : INT;
END_VAR
Calling SFC51 places an asynchronous request; the call must be repeated until BUSY goes FALSE. RET_VAL = 0 indicates success, and the header word LENTHDR / N_DR (read as two separate words inside SZL_HEADER.LENTHDR and SZL_HEADER.N_DR) reports the record length and number of records returned. SZL_ID is the SZL number; for S7-400H H-system status the SZL number is decimal 113 (hex 0x71). The INDEX parameter selects the partial list:
| SZL_ID (W#16#) | Decimal | INDEX | Description |
|---|---|---|---|
| W#16#0071 | 113 | 0 | Header / overall H-system status |
| W#16#0171 | 113 | 1 | CPU 0 redundancy state record |
| W#16#0271 | 113 | 2 | CPU 1 redundancy state record |
| W#16#0F71 | 113 | 15 | All partial lists returned sequentially |
The "xy" in W#16#xy71 is the partial-list selector. Most production code reads W#16#0071 with INDEX=0 once per OB1 cycle and indexes into SZL_DATA by record offset. SFC51 uses the caller's input buffer for SZL_DATA; that buffer must be at least LENTHDR * N_DR bytes long. SFC51 also returns a 16-bit RET_VAL, not a BOOL – RET_VAL = 0 is success, RET_VAL > 0 is warning, RET_VAL < 0 is error.
SZL W#16#0071 Record Layout for S7-400H
When SFC51 returns SZL W#16#0071 (SZL number 113, partial list 0), it delivers one or more fixed-format records. Each record is 16 bytes long. The byte map is documented in the STEP 7 System and Standard Functions reference manual and reproduced here with field-level annotations:
| Offset | Length | Field | Meaning |
|---|---|---|---|
| Byte 0 | 1 | Validity flags | Bit-encoded validity for fields in this record. Bit 0 = bz_cpu_0_valid, bit 1 = bz_cpu_1_valid, bit 2 = both CPU states valid (B#16#03 condition). |
| Byte 1 | 1 | Reserved | Reserved, must be 0. |
| Word 1 (bytes 2-3) | 2 | bz_cpu_0 | Operating state of CPU 0. WORD, value range W#16#0001 ... W#16#000F. Values outside this range are flagged invalid by bit 0 of the validity byte. |
| Word 2 (bytes 4-5) | 2 | bz_cpu_1 | Operating state of CPU 1. Same encoding as bz_cpu_0. |
| Byte 6 | 1 | Role / master-reserve | 0 = no master assignment, 1 = CPU 0 is master, 2 = CPU 1 is master, 3 = undefined (only valid in single-mode or during link-up). |
| Byte 7 | 1 | Link status | Bit 0 = link 1 OK, bit 1 = link 2 OK, bit 2 = both links OK (redundant), bit 3 = fiber-optic modules present. |
| Word 3 (bytes 8-9) | 2 | Reserved | Reserved for future expansion. |
| Word 4 (bytes 10-11) | 2 | Last partner update tick | Time in 10 ms increments since last successful role / program update from partner CPU. 0xFFFF = no update yet. |
| Word 5 (bytes 12-13) | 2 | Sync attempts since last success | Counter of synchronization retries since last successful link event. |
| Word 6 (bytes 14-15) | 2 | Diagnostic counter | Increments on every H-system diagnostic event. Useful as a "new event" edge detector. |
The validity byte (offset 0) is the most important field for the calling code because it tells the program which of the following fields are trustworthy. The byte is interpreted as a set of three named tokens, which is exactly the source documentation's B#16#01, B#16#02, B#16#03 encoding:
| Token | Bit mask | Set when |
|---|---|---|
| B#16#01 (bz_cpu_0 valid) | 0x01 | The bz_cpu_0 word at offset 2 contains a defined value in the range W#16#0001..W#16#000F. Cleared when CPU 0 has lost power, is in an unrecoverable defect state, or has not yet completed the first diagnostic snapshot after startup. |
| B#16#02 (bz_cpu_1 valid) | 0x02 | Same condition for the partner CPU 1. Cleared independently of CPU 0; a defective partner sets B#16#02 = 0 while B#16#01 = 1 remains. |
| B#16#03 (both CPUs valid) | 0x03 | Both bz_cpu_0 and bz_cpu_1 are valid. The "fully redundant" condition. Most user programs gate fail-over logic on this token rather than on either bit individually. |
BZ Operating State Encoding W#16#0001..W#16#000F
The bz_cpu_0 and bz_cpu_1 words report the operating state of each CPU. Siemens reserves the lower four bits for the state code and the upper twelve bits for vendor-specific extension (treated as zero in current firmware). The full encoding is:
| W#16# | Decimal | State | Set by | CPU outputs |
|---|---|---|---|---|
| 0001 | 1 | STOP, update from partner pending | CPU has just been switched to STOP; outputs disabled, role handshake not yet complete. | Inactive |
| 0002 | 2 | STARTUP (cold restart, OB100) | Power-up or reset request; CPU executes startup OBs. | Per OB100 logic |
| 0003 | 3 | STARTUP (warm restart, OB101) | Restart on retained tags. | Per OB101 logic |
| 0004 | 4 | RUN | Normal cyclic operation, OB1 in scan. | Active |
| 0005 | 5 | RUN, partner link-up in progress | CPU in RUN but the partner has not yet reached link-up; outputs reflect single-mode behavior. | Active (single mode) |
| 0006 | 6 | HOLD | Test/debug breakpoint; outputs frozen, scan paused. | Frozen |
| 0007 | 7 | Defect (CPU self-test failed) | Firmware detected internal fault, CPU is in self-test failure state and may not respond to SZL requests on its own bus. | Inactive |
| 0008 | 8 | Defect (redundancy handshake lost) | CPU is running but has lost all synchronization with the partner and is now operating as a stand-alone (single) CPU. Outputs are still active. | Active (single mode) |
| 0009 | 9 | Link down, partner not reachable | Both fiber-optic links broken; CPU is master by default, no partner data is available. | Active (single mode) |
| 000A | 10 | Standby (reserve, link OK) | CPU is the standby partner; program is executing, outputs inhibited, fully in sync with master. | Inhibited |
| 000B | 11 | Standby, partner updating | Standby CPU is processing an update from the master (role swap in progress). | Inhibited |
| 000C | 12 | Synchronization | Initial sync after power-up; both CPUs exchanging role and program state. | Inactive |
| 000D | 13 | Reserved | Reserved by Siemens, must be treated as invalid. | — |
| 000E | 14 | Reserved | Reserved by Siemens, must be treated as invalid. | — |
| 000F | 15 | NO POWER | CPU has no supply voltage or is physically absent. bz_cpu_x validity bit is always 0 in this case. | Inactive |
Any value outside W#16#0001..W#16#000F must be treated as an invalid snapshot, and the corresponding validity bit (B#16#01 or B#16#02) will be cleared by the firmware to signal that condition. This is the primary defensive check the user program must perform before consuming the bz_cpu_x word.
The following state machine summarizes the transitions and the validity flag behavior. The validity bits are the contract the firmware offers to the application: if the bit is set, the value is trustworthy; if cleared, the application must ignore it.
SFC51 Error Codes and RET_VAL Diagnostics
When SFC51 reports an error, RET_VAL contains a hex-coded diagnostic word. The most common values are:
| RET_VAL (W#16#) | Meaning | Typical cause | Recommended action |
|---|---|---|---|
| 0000 | No error, SZL_DATA valid. | Successful read. | None; consume SZL_HEADER and SZL_DATA. |
| 0080 | Header retrieval not yet complete; call again. | First call to a new SZL_ID, firmware is preparing the data. | Poll SFC51 until BUSY = 0; do not read SZL_DATA yet. |
| 0081 | Data retrieval not yet complete; call again. | Subsequent call to same SZL_ID; firmware is still building the data. | Same as 0080; continue polling. |
| 7000 | First call with REQ = 0; no read initiated. | Calling without rising edge on REQ. | Trigger REQ with a one-shot pulse. |
| 8090 | SZL_ID not supported on this CPU / firmware. | SZL W#16#0071 not available on firmware before V3.1 or on non-H CPUs. | Verify CPU is an S7-400H type and firmware supports the SZL. |
| 8092 | INDEX out of range for the requested SZL. | INDEX value not valid for SZL W#16#0071. | Confirm INDEX = W#16#0000 or 0x0F for "all". |
| 80A0 | Negative acknowledgment while reading from the target CPU. | Partner CPU is in NO POWER or DEFECT, target memory area not accessible. | Read the validity byte first; if B#16#02 = 0, ignore the call result. |
| 80A1 | Negative acknowledgment while writing to the target CPU. | Not applicable to SFC51 (read-only) but can appear in SFC51 background diagnostics. | Re-run the call after a 1 s delay. |
| 80B0 | System error (SZL fetch driver fault). | Firmware-level issue, often cleared by STOP/RUN. | Cycle the CPU mode switch; if persistent, update firmware. |
| 80B1 | Requested SZL_ID not configured. | CPU's SZL configuration does not include W#16#0071. | Check HW Config → CPU properties → SZL tab; enable if disabled. |
Programming Examples: STL, SCL, and Ladder
Three reference implementations of the same logic are presented: STL for compactness, SCL for type-safety, and ladder for visualization in STEP 7. All three consume SFC51 output and expose the bz_cpu_x values and validity tokens as symbolic tags.
STL Implementation (STEP 7 V5.5)
FUNCTION_BLOCK FB_H_SSL_READ
TITLE = 'Read S7-400H SZL W#16#0071 and decode validity'
VERSION : '1.0'
VAR
sfc51_req : BOOL := FALSE; // rising edge trigger
sfc51_busy : BOOL;
sfc51_ret : INT;
sfc51_hdr : STRUCT
LENTHDR : WORD; // record length in bytes
N_DR : WORD; // number of records
END_STRUCT;
sfc51_data : ARRAY[0..463] OF BYTE;
END_VAR
VAR_TEMP
rec : INT; // record index
off : INT; // byte offset
END_VAR
BEGIN
NETWORK 1
A "scan_1Hz"; // call once per second
FP "M_H_Trigger";
= sfc51_req;
NETWORK 2
CALL SFC51 (
REQ := sfc51_req,
SZL_ID := W#16#0071,
INDEX := W#16#0000,
BUSY := sfc51_busy,
SZL_HEADER:= sfc51_hdr,
SZL_DATA := sfc51_data,
RET_VAL := sfc51_ret
);
NETWORK 3
L sfc51_ret;
L 0;
<>I ;
JC _err; // 0x80xx range = SFC51 protocol error
NETWORK 4
// Walk all records; for H system one record per CPU
L sfc51_hdr.N_DR;
L 0;
==I ;
JC _done;
L 0;
T #rec;
LOOP: NOP 0;
// record offset = rec * LENTHDR
L #rec;
L sfc51_hdr.LENTHDR;
*I ;
T #off;
// byte 0: validity flags
L sfc51_data[off + 0];
T "DB_H.DBB0_validity";
// bytes 2-3: bz_cpu_0
L sfc51_data[off + 2];
T "DB_H.DBB2_bz_cpu0_lo";
L sfc51_data[off + 3];
T "DB_H.DBB3_bz_cpu0_hi";
// bytes 4-5: bz_cpu_1
L sfc51_data[off + 4];
T "DB_H.DBB4_bz_cpu1_lo";
L sfc51_data[off + 5];
T "DB_H.DBB5_bz_cpu1_hi";
L #rec;
L 1;
+I ;
T #rec;
L sfc51_hdr.N_DR;
<I ;
JC LOOP;
_done:NOP 0;
BEU;
_err: SET;
S "H_SSL_READ_ERROR";
BE;
END_FUNCTION_BLOCK
The decoded values can then be consumed in OB1 with a guard pattern:
NETWORK 10
// Safety gate: only allow role-swap decisions when BOTH bz values are valid
L "DB_H.DBB0_validity";
L B#16#03; // both bits set
==I ;
= "H_Both_CPU_Valid";
A "H_Both_CPU_Valid";
JCN SKIP_FAILOVER;
// safe to read the master/role field
L "DB_H.DBB6_Role";
L 1; // CPU 0 is master
==I ;
= "H_CPU0_Is_Master";
SKIP_FAILOVER: NOP 0;
SCL Implementation (TIA Portal / SCL)
FUNCTION_BLOCK FB_H_SSL_READ_SCL
TITLE = 'S7-400H SZL W#16#0071 Reader (SCL)'
{ S7_Optimized_Access := 'FALSE' }
VERSION : '2.0'
VAR
sSZLHeader : STRUCT
LENTHDR : WORD;
N_DR : WORD;
END_STRUCT;
sSZLData : ARRAY[0..463] OF BYTE;
bTrigger : BOOL;
END_VAR
VAR_OUTPUT
bz_cpu_0 : WORD;
bz_cpu_1 : WORD;
validity_byte : BYTE;
bz_cpu_0_valid : BOOL;
bz_cpu_1_valid : BOOL;
both_valid : BOOL;
bError : BOOL;
wErrorCode : WORD;
END_VAR
VAR_TEMP
sfc51_busy : BOOL;
sfc51_ret : INT;
iOff : DINT;
END_VAR
BEGIN
// Fire SFC51 once per cycle when triggered
IF bTrigger THEN
sfc51_ret := SFC51(
REQ := TRUE,
SZL_ID := W#16#0071,
INDEX := W#16#0000,
BUSY := sfc51_busy,
SZL_HEADER := sSZLHeader,
SZL_DATA := sSZLData
);
END_IF;
IF sfc51_ret <> 0 THEN
bError := TRUE;
RETURN;
END_IF;
IF sfc51_busy THEN
RETURN; // wait for completion
END_IF;
// First record only for a 2-CPU H system
IF sSZLHeader.N_DR = 0 THEN
RETURN;
END_IF;
iOff := 0; // rec=0, LENTHDR ignored for first record
validity_byte := sSZLData[iOff + 0];
bz_cpu_0.%B0 := sSZLData[iOff + 2];
bz_cpu_0.%B1 := sSZLData[iOff + 3];
bz_cpu_1.%B0 := sSZLData[iOff + 4];
bz_cpu_1.%B1 := sSZLData[iOff + 5];
bz_cpu_0_valid := (validity_byte AND B#16#01) <> B#16#00;
bz_cpu_1_valid := (validity_byte AND B#16#02) <> B#16#00;
both_valid := (validity_byte AND B#16#03) = B#16#03;
bError := FALSE;
END_FUNCTION_BLOCK
Ladder Implementation
Network 1: One-shot SFC51 trigger (rising edge of 1-Hz clock)
[ scan_1Hz ] --[P|-- ( sSZL_Trigger )
Network 2: Call SFC51 with H-system SZL ID
[ sSZL_Trigger ] --[ CALL SFC51
REQ := sSZL_Trigger,
SZL_ID := W#16#0071,
INDEX := W#16#0000 ]
Network 3: Watch BUSY; only consume SZL_DATA on completion
[ SFC51.BUSY ] --[/]-- ( sSZL_Done )
Network 4: Read RET_VAL; abort on non-zero
[ SFC51.RET_VAL ] --[== 0]-- ( sSZL_OK )
[ sSZL_OK ] --[/]-- ( sSZL_Error )
Network 5: Move validity byte to a symbolic tag
[ sSZL_OK ] --[ MOVE sSZLHeader.N_DR -> DB_H.N_DR ]
[ sSZL_OK ] --[ MOVE sSZLData[0] -> DB_H.Validity ]
Network 6: Decode B#16#01 - bz_cpu_0 valid
[ DB_H.Validity ] --[ AND B#16#01 ]
--[ <> 0 ]-- ( DB_H.bz_cpu_0_valid )
Network 7: Decode B#16#02 - bz_cpu_1 valid
[ DB_H.Validity ] --[ AND B#16#02 ]
--[ <> 0 ]-- ( DB_H.bz_cpu_1_valid )
Network 8: Decode B#16#03 - both CPUs valid
[ DB_H.Validity ] --[ AND B#16#03 ]
--[ == B#16#03 ]-- ( DB_H.both_valid )
Network 9: Conditional move of bz_cpu_0 (gated on validity)
[ DB_H.bz_cpu_0_valid ] --[ MOVE sSZLData[2] -> DB_H.bz_cpu_0.LB ]
[ DB_H.bz_cpu_0_valid ] --[ MOVE sSZLData[3] -> DB_H.bz_cpu_0.HB ]
Network 10: Conditional move of bz_cpu_1
[ DB_H.bz_cpu_1_valid ] --[ MOVE sSZLData[4] -> DB_H.bz_cpu_1.LB ]
[ DB_H.bz_cpu_1_valid ] --[ MOVE sSZLData[5] -> DB_H.bz_cpu_1.HB ]
Engineering Impact: Why Invalid Data Matters
The user's original question is whether a bz_cpu_x snapshot flagged invalid can affect the system behavior. The answer is yes, in three ways that the application program must defend against.
- Process decisions taken on stale or zero-initialized data. If the bz_cpu_x word is treated as RUN (W#16#0004) when the validity bit is 0, the program may continue driving outputs as if the partner is healthy when the partner is actually NO POWER (W#16#000F) or DEFECT (W#16#0007). The SZL firmware will write W#16#0000 (a value outside the defined range, which is itself an invalid marker) into the word when the validity bit is 0, but the application code is responsible for checking the validity bit before trusting the value.
- Role-swap logic can fire on partial data. Most H-system programs implement a "primary loses, secondary takes over" routine. If that logic is gated only on bz_cpu_0 == 0007 (DEFECT) and ignores the B#16#01 validity bit, a freshly powered-up secondary CPU that has not yet synchronized will report bz_cpu_0 = 0000 and the program will treat it as a defect, possibly triggering an unnecessary role swap.
- Diagnostic buffer entries can flood. Calling SFC51 with SZL_ID = W#16#0071 when the partner CPU is in NO POWER state will return RET_VAL = W#16#80A0 (target CPU does not exist) or, on some firmware versions, a header with N_DR = 0. Code that does not check N_DR or RET_VAL will repeatedly attempt the call and may generate a "SFC51 call without active SZL" diagnostic event on every OB1 cycle.
The defensive pattern in production code is therefore: read the validity byte first, branch on the B#16#01 / B#16#02 / B#16#03 tokens, and never trust bz_cpu_x values when the corresponding validity bit is 0.
Reproducing an Invalid CPU Validity State for Testing
The user also asked how to force an invalid snapshot. There are four practical methods, in order of increasing risk to a live process. Always perform these tests on a bench rig or during a scheduled outage.
| Method | What it does | Expected validity | Risk |
|---|---|---|---|
| Power down CPU 1 with the H-system in RUN | CPU 0 remains master, CPU 1 reports NO POWER (W#16#000F). bz_cpu_1 validity bit (B#16#02) goes 0; B#16#01 remains 1. | B#16#01 = 1, B#16#02 = 0 | Medium – live system continues in single mode; any in-process role swap logic will trigger. |
| Pull both fiber-optic links during RUN | Both CPUs detect link-down, bz_cpu_0 / bz_cpu_1 both go to W#16#0009 (Link down). Validity bits stay 1 because the firmware still has a valid local snapshot. | B#16#01 = 1, B#16#02 = 1, but bz values are 0x0009 | High – master continues running single mode, system may generate OB70 / OB72 errors. |
| Insert a deliberately defective spare CPU | Defective CPU reports W#16#0007 (defect) or refuses to power up, producing W#16#000F (NO POWER). Validity bit is 0. | B#16#01 = 1, B#16#02 = 0 (or vice-versa) | None to process; excellent for testing diagnostic buffer entries. |
| Use a CPU in firmware update / MRES state | CPU is present on the rack but is in MRES / firmware-update mode. Firmware does not publish a valid SZL snapshot, so validity bit is 0. | B#16#0n = 0 for that CPU | None to process; safe bench test. |
Troubleshooting Matrix and Verification Procedure
| Observed SZL byte 0 | bz_cpu_0 | bz_cpu_1 | Diagnosis | Recommended action |
|---|---|---|---|---|
| B#16#00 | 0000 | 0000 | Both CPUs not yet producing snapshots (H-system not in RUN, or SZL_ID mismatch). | Confirm SZL_ID = W#16#0071, INDEX = W#16#0000. Verify the H-system has completed link-up (H LED on CPU front). |
| B#16#01 | 0004 (RUN) | 0000 | CPU 0 healthy, CPU 1 not yet reporting (typical during first 5 s after link-up). | Wait for link-up to complete; if condition persists past 30 s, check fiber-optic cable on the partner side. |
| B#16#02 | 0000 | 0004 (RUN) | CPU 0 invalid (typically NO POWER or DEFECT), CPU 1 master. | Check CPU 0 power supply, mode switch position, and diagnostic buffer (SZL W#16#00A0). |
| B#16#03 | 0004 | 000A (standby) | Healthy redundant pair, CPU 0 master, CPU 1 standby. | None – normal operation. |
| B#16#03 | 000A | 0004 | Healthy redundant pair, CPU 1 master, CPU 0 standby. | None – confirm intended master assignment. |
| B#16#03 | 0009 | 0009 | Both CPUs link-down, each in stand-alone single mode. | Inspect both fiber-optic links and synchronization modules (IF 960 or IF 962). |
| B#16#01 | 0007 | 0000 | CPU 0 in self-test defect state. | Replace CPU 0; check for firmware mismatch with the partner. |
| B#16#00 | FFFF | FFFF | SZL not initialized – call to SFC51 not yet complete, or RET_VAL non-zero. | Check RET_VAL; poll BUSY; verify the SZL_ID partial list number is supported by your CPU firmware. |
When commissioning an S7-400H station, verify the SZL W#16#0071 path is functioning correctly using this 6-step procedure.
- Force SFC51 call in OB100 – call the SZL read once during cold restart, log the raw bytes to a watch table.
- Confirm byte 0 = B#16#03 – both validity bits should be set within 10 s of link-up; if not, both CPUs are not yet synchronized.
- Confirm bytes 2-3 = W#16#0004 – bz_cpu_0 should report RUN.
- Confirm bytes 4-5 = W#16#000A or W#16#0004 – bz_cpu_1 should be standby (W#16#000A) or RUN (W#16#0004) depending on which CPU is master.
- Force a partner power-cycle – disconnect power to CPU 1; byte 0 should drop from B#16#03 to B#16#01, byte 5 should drop to W#16#000F, byte 6 should remain 1 (CPU 0 master).
- Restore power to CPU 1 – byte 0 should recover to B#16#03, byte 5 should recover from W#16#000F to W#16#000A within 30 s of link-up.
If step 5 or 6 fails, the most common root cause is a partially seated fiber-optic connector (link 1 OK, link 2 not OK); the link-status byte at offset 7 confirms the diagnosis.
Integration with TIA Portal and Diagnostic Symbols
On S7-400H CPUs configured in TIA Portal V15 or later, the same diagnostic information is available through three additional paths:
- Online & Diagnostics → Diagnostics status – the H-system tab shows the bz_cpu_0 and bz_cpu_1 values as a live dashboard. The validity token (B#16#01 / B#16#02 / B#16#03) is not exposed directly; it is implicit in the Status column (green = valid, gray = invalid).
- Web server diagnostic pages – the H-CPU's integrated web server exposes the same fields under "Module information → SZL 0x71". This is useful for remote monitoring without a STEP 7 connection.
- Diagnostic symbols in the project tree – TIA Portal renders the H-system state using the standard diagnostic symbol set documented at TIA Portal – Explanation of diagnostics symbols. A gray "no diagnosis" symbol next to the partner CPU indicates that the B#16#02 / B#16#01 validity bit for that CPU is 0.
Even with these GUI aids, the canonical source for redundancy decisions in the user program is the SFC51 path, because the H-system Web server and TIA Portal dashboard run outside the OB1 scan and cannot be polled at the rate a process controller requires.
Safety, OB70 / OB72 / OB82 Handling, and Performance
S7-400H is not by itself a SIL-rated platform. The H-CPU family can be deployed in SIL 2 / SIL 3 applications when paired with an F-CPU (e.g., CPU 416F-3 PN/DP) in a separate rack and a certified safety program. The validity bits in SZL W#16#0071 are not safety-relevant inputs; they are diagnostic-only and must never be used as the primary decision input in a safety path. Safety decisions must be made by the F-CPU's own F-runtime diagnostic, not by reading SZL on a standard H-CPU.
The H-system generates three error OBs that should be wired in production code to correlate with the SZL validity token transitions:
| OB | Fires when | Typical action | Correlates with SZL field |
|---|---|---|---|
| OB70 | Redundancy loss (I/O redundancy error) | Log the event, set a maintenance tag, continue in single mode if possible. | Validity byte B#16#01/B#16#02 transition; role byte (offset 6) flip. |
| OB72 | CPU redundancy loss (standby partner unavailable) | Log, alert, optionally initiate controlled shutdown if safety requires. | Validity byte for the affected CPU; sync-attempts counter (offset 12). |
| OB82 | DP / PN slave diagnostic interrupt (often I/O module health change) | Evaluate the diagnostic frame, isolate the affected module. | Module-level validity, not directly tied to SZL W#16#0071. |
A robust production code pattern: in OB70 / OB72, do not blindly trust the SFC51 SZL read for the first call after the OB fires; wait for the diagnostic counter at offset 14 to increment at least once (which it does within one OB35 cycle, ~100 ms), then read the validity byte and decide.
For performance, the SZL W#16#0071 call on CPU 417-4H typically completes in 4-8 ms, well within an OB35 cycle budget. The SZL_DATA buffer is 464 bytes maximum, more than enough for the 16-byte records. Caching the SZL data in a global DB and reading it from OB1 (rather than calling SFC51 in OB1 directly) reduces OB1 scan time by 1-3 ms on a typical configuration. For a more deterministic cycle, use OB35 at 100 ms to drive SFC51 and a global DB tag (e.g., DB_H.bz_cpu_0) that OB1 can poll without re-issuing the SZL call.
Cross-Platform Redundancy Patterns and Field-Proven Caveats
The cross-platform SZL pattern is not unique to Siemens. High-availability PLCs in general implement similar CPU-validity / partner-validity bit pairs. Mitsubishi Q4ARCPU redundant CPUs in a MELSECNET/H network, for example, report partner status through comparable network diagnostic buffers where the Q4ARCPU's "partner CPU existence" bit maps cleanly onto the S7-400H B#16#01 / B#16#02 concept. Allen-Bradley ControlLogix Redundancy Module (1756-RM2 / 1756-RM) systems expose an analogous qualification bit on each chassis, where the partner chassis must qualify before a switchover is allowed. The universal lesson is the same: a CPU validity bit is a contract from the runtime to the user program, and consuming partner data without checking the contract leads to spurious role swaps and "ghost" partner states.
From hundreds of field deployments, the following pitfalls recur in H-system SZL handling code. Engineers integrating SFC51 should review against this list.
- Forgetting that SFC51 is asynchronous. The call must be repeated every OB1 cycle until BUSY = 0. A single call often returns BUSY = 1 and an empty SZL_DATA buffer.
- Reading SZL W#16#0071 before the first link-up completes. During the first 10 s of H-system startup the SZL returns N_DR = 0. The application code must wait for N_DR > 0 before indexing into the data array.
- Treating the validity byte as a status indicator. The byte does not say "the CPU is bad"; it says "the snapshot in this record is not trustworthy."
- Confusing W#16#0071 with W#16#0019. W#16#0019 is the LED status of a single CPU; W#16#0071 is the H-system status of the pair. S7-400H returns both, but only W#16#0071 contains the BZ operating state for both CPUs.
- Indexing by N_DR instead of by LENTHDR. A bug-fix to the LENTHDR field (for example, after a firmware update that adds a new field) shifts the offsets. Always index by (record index * LENTHDR), not by hard-coded byte offsets.
- Not using OB35 (100 ms cyclic interrupt) for SZL polling. OB1 runs at a variable scan rate, which makes the diagnostic update rate non-deterministic. Use a fixed-rate cyclic interrupt for the SZL read so the diagnostic counter at offset 14 increments at a known rate.
- Migrating from S7-400 single to S7-400H without re-checking the SZL map. Single-CPU S7-400 code that reads W#16#0019 for the LED status will continue to work on an H-system CPU but will not return the bz_cpu_0 / bz_cpu_1 operating state. Add SZL W#16#0071 calls explicitly when porting.
Frequently Asked Questions
What is the difference between B#16#01 and B#16#03 in the SZL W#16#0071 validity byte?
B#16#01 (binary 0000 0001) is set when bz_cpu_0 is trustworthy. B#16#03 (binary 0000 0011) is set when both bz_cpu_0 and bz_cpu_1 are trustworthy. B#16#01 alone means CPU 0 is valid but CPU 1 is not (e.g., partner in NO POWER or DEFECT). B#16#03 is the "fully redundant, both CPUs healthy" condition. B#16#02 alone means only CPU 1 is valid.
Can I rely on bz_cpu_x being zero when the validity bit is cleared?
No. The SZL firmware writes the undefined value W#16#0000 into the bz_cpu_x word when the validity bit is cleared, but this is a firmware implementation detail that may change between firmware versions. Always check the validity bit first, and treat any bz_cpu_x read with the validity bit cleared as undefined data, not as a known state.
Why does SFC51 return N_DR = 0 sometimes?
Either the H-system has not yet completed link-up (typically the first 10 s of startup), or the SZL_ID is not supported by the current CPU firmware. Confirm the SZL_ID is W#16#0071 and the INDEX is W#16#0000, and confirm the H LED on both CPUs is solid green (link-up complete) before debugging further.
How do I reproduce an invalid CPU state for testing without taking the plant down?
The safest method is to insert a known-defective CPU in slot 1 of a bench rig configured as an H-system. The defective CPU will refuse to power up or report W#16#0007 (DEFECT), setting B#16#02 = 0 in the partner's SZL. For a live-plant test, power down CPU 1 only (CPU 0 remains master) and observe the validity byte drop from B#16#03 to B#16#01.
Is the SZL W#16#0071 validity token safe to use for fail-over decisions in OB1?
Yes, for operational fail-over (which CPU takes over as master, which is the standby) the validity token is the canonical input. It is not a safety input and must not be used in F-runtime logic. A safety-rated fail-over must use the F-CPU's own diagnostic mechanism (e.g., F-I/O PASS_OUT, F-runtime diagnostic flags) and must be reviewed against IEC 61508.
What is the difference between SZL W#16#0071 and SZL W#16#0F71?
SZL W#16#0071 (INDEX = 0) returns the partial list 0 of the H-system SZL, which contains the validity token and the bz_cpu_0 / bz_cpu_1 operating states. SZL W#16#0F71 (INDEX = 15) returns the entire SZL 113 in one buffer, which is more efficient for a full snapshot but requires more buffer space. For cyclic operational use, W#16#0071 is preferred because the call is smaller and the response is faster.
What does W#16#000F (NO POWER) actually mean in SZL W#16#0071?
W#16#000F indicates the CPU has no supply voltage or is physically absent from the rack. The bz_cpu_x validity bit for that CPU will be 0 (B#16#01 = 0 if CPU 0 has no power, B#16#02 = 0 if CPU 1 has no power). The remaining CPU, if present, will continue in single-mode operation as long as its own validity bit is 1.