S7-300 Data Consistency: SFC20, SFC81 UBLKMOV, and OB Interrupts
This technical reference documents how the Siemens S7-300 / S7-400 CPU execution model interacts with user-level data copy operations, and explains the engineering trade-offs between LOAD / TRANSFER, SFC20 BLKMOV, SFC14 / SFC15 (DPRD_DAT / DPWR_DAT), and SFC81 UBLKMOV when higher-priority Organisation Blocks (OB1x, OB4x, OB6x) are present in the program.
L / T pair is a candidate for an OB interrupt window. Block-copy SFCs reduce—but do not by themselves eliminate—that risk. The only mechanically uninterruptible copy primitive in the S7-300 family is SFC81 UBLKMOV (introduced in newer S7-300 CPUs). On S7-400, SFC20 BLKMOV is uninterruptible within its operating range.1. Overview: Data Consistency in S7-300/400 Cyclic Programs
Data consistency means that a logical record (a recipe, a measurement frame, a controller set of state variables) is read or written as an indivisible unit, never as a torn half-and-half snapshot. In a single-tasking PLC this is trivial; in a priority-class, multi-OB S7 CPU it is not. A cyclic OB1 sweep can be preempted at any instruction boundary by:
- Hardware interrupts (OB4x) from digital inputs, counters, or the integrated I/O of the CPU.
- Time-of-day interrupts (OB1x) that wake the CPU on a wall-clock boundary.
- Cyclic / clocked interrupts (OB3x) running a faster fixed period, often 1 ms to 100 ms.
- Time-error, diagnostic, restart, or asynchronous-error OBs (OB80–OB87, OB121, OB122).
- Profibus DP alarm OBs (OB55–OB57, OB82–OB87) generated by distributed I/O.
If any of these OBs read or write the same memory area the cyclic program is mid-way through copying, the consumer downstream will observe a corrupt (torn) record. The CPU will not warn you. The PLC will not throw a diagnostic. The HMI will simply show a one-cycle blip. That is the failure mode this article is designed to prevent.
2. S7 CPU Execution Model and OB Priority Classes
The S7-300 / S7-400 CPU services OBs strictly by priority. When a higher-priority OB is triggered, the CPU:
- Suspends the currently executing OB at the current instruction boundary.
- Pushes the open run-time environment (accumulators ACCU1 / ACCU2 / ACCU3 / ACCU4, address registers AR1 / AR2, DB register, status word, BR bit, opened DB / DI stack frames) onto the interrupt stack (USTACK).
- Loads the interrupt entry for the new OB, opens its local-instance data area (the L stack frame), and starts execution at the first network.
- On OB completion, restores the saved environment and resumes the preempted OB at the next instruction.
The relevant default priority classes (firmware 2.x and later for S7-31x, S7-31xT, and S7-317; S7-400 follows the same convention) are summarised in the table below. Lower numbers = lower priority; the priority is editable in HW Config for most OBs but should not be changed casually.
| OB | Name | Default priority | Typical period / source |
|---|---|---|---|
| OB1 | Main cyclic | 1 | Free cycle, scan-time driven |
| OB10–OB17 | Time-of-day | 2 | Wall-clock, configured in HW Config |
| OB20–OB23 | Delay (SFC32 / SFC34) | 3–6 | Triggered by user program |
| OB30–OB38 | Cyclic interrupt | 7–15 | 1 ms to 60 s, fixed in HW Config (the OB3x family) |
| OB40–OB47 | Hardware interrupt | 16–23 | Digital / counter / PWM edges |
| OB55 | DP status / DPV1 interrupt | 2 | Profibus DP slave diagnosis |
| OB82 | IO fault | 26 (fault) / 28 (peripheral) | Module diagnostic |
| OB85 | OB not loaded / PIO access error | 26 (fault) / 28 (peripheral) | Background priority or fault |
| OB121 | Programming error | Of the OB that caused it | Syntactic / address error |
| OB122 | Peripheral access error | Of the OB that caused it | Missing / faulty module on direct I/O |
Anything in the OB3x–OB4x range is almost always higher priority than OB1, which is why a "simple cyclic program" without interrupts feels safe but quietly stops being safe the first time a hardware interrupt is added.
3. Why LOAD / TRANSFER Are Not Atomic
The mnemonic pair L (load from memory to accumulator) and T (transfer from accumulator to memory) operate on a single operand each. They do not form an atomic block-copy:
// OB1 — main program
L DB10.DBD0 // ACCU1 := temperature (REAL)
T MD100 // shadow copy for HMI
L DB10.DBD4 // ACCU1 := pressure (REAL)
T MD104
L DB10.DBD8 // ACCU1 := flow (REAL)
T MD108
L DB10.DBD12 // ACCU1 := setpoint (REAL)
T MD112
If an OB35 (cyclic interrupt, priority 12) is configured and writes to DB10.DBD4 between the second and third L instructions, the HMI sees a record in which temperature is from cycle N, pressure is from cycle N+1, flow is from cycle N+1, and setpoint is from cycle N. The actuator downstream will see a torn record and behave accordingly.
This is not a bug; it is the documented behaviour of the S7 execution model. The interrupt stack preserves accumulators and DB registers, but it cannot preserve temporal causality across instructions of OB1.
TEMP variables of OB1 are not at risk, because OB1's L stack frame is preserved on interrupt and restored on resume.4. Process Image Partitions and OB6x Assignment
S7-300 / S7-400 CPUs maintain a process image partition (PIP) per I/O submodule. Each PIP can be bound to an OB in the OB3x–OB4x range, which means the input latch is refreshed at the start of that OB rather than at the start of OB1. This is the mechanism that gives OB35 its deterministic 1 ms–100 ms response time on a fast input.
Relevant rules:
- OB1 sees the PIP that is bound to OB1 (the "OB1-PII"). This image is updated once per OB1 cycle.
- An OB4x bound to PIP N will read PIP N at OB entry, and write its process outputs at OB exit. OB1 does not see the in-flight values.
- If a PIP is bound to OB1 and to an OB3x / OB4x, the assignment is exclusive in HW Config: you must pick one.
Process image partitions are a consistency mechanism, but they apply to PIPs, not to user-defined global DBs. The SFC14 / SFC15 restrictions (next section) are the practical consequence of the PIP-and-OB-bound interaction.
5. SFC20 BLKMOV — Capabilities, Behaviour, and Limits
SFC20 BLKMOV (block move) copies a contiguous byte range from one source area to a contiguous byte range in a destination area. The source and destination may be in different memory areas (e.g. DB → M, IB → DB). The interface is:
// SFC20 BLKMOV (STL call)
CALL "BLKMOV"
SRCBLK := P#DB10.DBX0.0 BYTE 16 // source pointer in ANY format
RET_VAL:= MW200 // error code, 0 = no error
DSTBLK := P#DB20.DBX0.0 BYTE 16 // destination pointer in ANY format
5.1 Atomicity of SFC20 — CPU family dependent
| CPU family | SFC20 interruptible by OB3x / OB4x? | Recommended replacement |
|---|---|---|
| S7-300 (most CPUs) | Yes, SFC20 is interruptible | SFC81 UBLKMOV (newer CPUs) or SFC39 / SFC40 to mask |
| S7-400 (all) | No (within its area) — SFC20 runs uninterruptible when operating on M, DB / DI, or PII / PIQ; cannot be interrupted by OBs of higher priority | SFC20 is acceptable |
| ET 200S IM151-7 CPU / ET 200S IM151-8 PN/DP CPU | Behaviour follows the underlying S7-300 firmware generation | Check the manual for UBLKMOV availability |
| S7-1200 / S7-1500 | No SFC20 — use the MOVE_BLK / MoveBlock instruction or, for consistency, the MoveBlock variant configured as "interruptible = no" |
n/a (different platform) |
5.2 Return-value (RET_VAL) error codes for SFC20
| RET_VAL (hex) | Meaning | Engineer action |
|---|---|---|
| 0000 | No error | Continue |
| 8091 | Maximum nesting depth exceeded | Reduce recursion; SFC20 cannot call itself indirectly while open |
| 8152 | Type conflict between ANY and actual operand | Re-check SRCBLK / DSTBLK data type, byte alignment |
| 8xxy | System error (e.g. x = 1 → access fault, x = 2 → operand not loaded) | Inspect USTACK and diagnostic buffer |
6. SFC14 / SFC15 — Consistency Constraints with Clocked Interrupts
SFC14 DPRD_DAT and SFC15 DPWR_DAT are the only user-callable way to read or write the full consistent data set of a Profibus DP slave in a single transaction. On most DP slaves, the data record is up to 32 bytes (64 on S7-400 with the V2 slave profile) and goes beyond the boundaries of the normal PII / PIQ; the slave firmware delivers it as a single bus cycle, and the CPU stores it as a single coherent image in the SFC buffer.
The STEP 7 online manual places an explicit warning in the SFC14 description:
Why this matters: the CPU will refuse to keep the PII consistent for an OB6x if SFC14 / SFC15 is mid-flight, because the OB6x would see a partially refreshed PIP. The DP call will return RET_VAL = W#16#80A0 (negative acknowledgement from the slave) or, depending on the slave's behaviour, the request will be deferred until the OB6x window is closed. The practical engineering consequence is:
- Bind process image partitions used by SFC14 / SFC15 to OB1 only.
- Schedule SFC14 / SFC15 from OB1, not from OB30–OB38.
- If a clocked OB must read the same DP data, structure the application so the OB reads from the image refreshed by OB1 + SFC14, not directly from the PIP.
- For very high deterministic rates, move the DP communication onto Profinet IO with PROFINET IRT, where the same constraint applies but is handled at the IRT-cycle level by the PROFINET stack.
6.1 Typical SFC14 call
// SFC14 DPRD_DAT — read consistent 16 bytes from DP slave 4
CALL "DPRD_DAT"
LADDR := W#16#100 // configured base address of the slave
RET_VAL:= MW202 // 0 = OK; W#16#80A0, W#16#80B0 etc. on error
RECORD := P#DB30.DBX0.0 BYTE 16
6.2 Common SFC14 / SFC15 RET_VAL codes
| RET_VAL (hex) | Meaning |
|---|---|
| 0000 | No error |
| 8090 | Configured base address (LADDR) not found in HW Config |
| 8092 | ANY pointer type conflict (RECORD length ≠ slave declaration) |
| 80A0 | Negative acknowledgement from DP slave; data record rejected |
| 80A1 | DP slave failure (bus fault, station failure) |
| 80B0 | SFC14 / SFC15 not allowed with this I/O area; PIP is bound to an OB6x |
| 80B1 | Target / source area length too short for the data record |
| 80B2 | DP slave inconsistent (reconfiguration pending) |
| 80B3 | DP slave not yet configured / not in OPERATE |
7. SFC81 UBLKMOV — Uninterruptible Block Move
SFC81 UBLKMOV (uninterruptible block move) is the engineered answer to the S7-300 version of the consistency problem. It performs the same operation as SFC20 — copying a contiguous byte range from one area to another — but the CPU commits the entire transfer as a single, uninterruptible instruction. No OB4x, no OB35, no OB55 can break into the copy.
7.1 Availability
UBLKMOV was introduced in S7-300 CPUs in a firmware generation that corresponds to the S7-31x / S7-31xT / S7-317 / S7-319 series shipped from the early-to-mid 2000s onward. S7-400 does not implement SFC81 because SFC20 already runs uninterruptible on S7-400 (see §5.1). The specific availability on each S7-300 CPU order number is documented in the S7-300 Instruction List; verify by inspecting the online help of the CPU in STEP 7 (F1 on a blank STL line, type SFC81) or by reading the "SFCs / SFBs supported" appendix of the relevant S7-300 CPU manual.
7.2 Interface
// SFC81 UBLKMOV — uninterruptible copy
CALL "UBLKMOV"
SRCBLK := P#DB10.DBX0.0 BYTE 32
RET_VAL:= MW204
DSTBLK := P#DB40.DBX0.0 BYTE 32
The interface is identical to SFC20 except for the function number. RET_VAL error codes are the same family as SFC20, plus 0x8091 (nesting depth) and 0x8xxy (system errors). Because the function is uninterruptible, execution time grows linearly with length; budget it against the OB1 watchdog.
7.3 Performance budget
On an S7-315-2 PN/DP at firmware V3.x, SFC81 on 32 bytes in a globally defined DB executes in single-digit microseconds. On 4 KB the time becomes visible in the OB1 scan and the function should be used only for records whose logical size matches the engineering record (e.g. one 32-byte telegram per axis, not a 4 KB shared data block). For very large transfers, break the data into multiple smaller UBLKMOV calls and insert a WAIT-equivalent or yield the OB1 through normal scan logic so the watchdog does not fire.
8. SFC39 / SFC40 / SFC41 / SFC42 — Interrupt Masking and Delay
When UBLKMOV is not available (older S7-300, or external S7-300 with no SFC81), the engineering fallback is to mask the interrupts for the duration of the copy. Siemens provides four SFCs specifically for this.
| SFC | Mnemonic | Effect | Notes |
|---|---|---|---|
| SFC39 | DIS_IRT | Disable specific interrupt OB(s) | Parameter: MODE = 0 disables all, 1 disables a specific OB, 2 disables a priority class |
| SFC40 | EN_IRT | Enable previously disabled OB(s) | Mirror of SFC39 |
| SFC41 | DIS_AIRT | Delay (queue) all interrupts of higher priority | Increments an internal "delay counter"; safe to nest |
| SFC42 | EN_AIRT | Decrement the delay counter; when zero, pending interrupts fire | Always pair with SFC41; the counter is the protection |
8.1 Pair SFC41 / SFC42 (the safe pair)
// Critical section — copy the recipe atomically
CALL "DIS_AIRT" // SFC41 — delay all higher-priority OBs
CALL "BLKMOV" // SFC20 — copy 32 bytes
SRCBLK := P#DB10.DBX0.0 BYTE 32
RET_VAL:= MW200
DSTBLK := P#DB40.DBX0.0 BYTE 32
CALL "EN_AIRT" // SFC42 — release; the counter decrements
The pair is reference-counted. If SFC41 has been called twice, you must call SFC42 twice to release. Always wrap the SFC41 in a __TRY / __FINALLY-equivalent (S7-300 does not have try/finally in STL; use a flag and OB121 / OB122 to detect partial completion, or place the SFC42 in the OB85 fallback path) so a programming error inside the critical section cannot leave interrupts permanently disabled.
8.2 SFC39 / SFC40 — targeted disable
Use SFC39 / SFC40 to disable a specific OB or priority class, e.g. OB35 only, so OB4x (hardware interrupts) still fire. The targeted disable is faster and more debuggable than the global pair:
CALL "DIS_IRT"
MODE := B#16#1 // 1 = disable specific OB
OB_NR := 35 // disable OB35 only
CALL "BLKMOV"
...
CALL "EN_IRT"
MODE := B#16#1
OB_NR := 35
9. Selecting the Right Mechanism: Decision Matrix
| If your CPU is… | And the data is… | And you need… | Then use… |
|---|---|---|---|
| S7-400 | M / DB / PII / PIQ | Uninterruptible copy ≤ 4 KB | SFC20 BLKMOV directly (it is uninterruptible) |
| S7-300 with SFC81 | M / DB | Uninterruptible copy ≤ 1 KB typically | SFC81 UBLKMOV |
| S7-300 without SFC81 | M / DB | Atomic copy under OB3x / OB4x | SFC41 / SFC42 wrap around SFC20, or SFC39 / SFC40 on the offending OB |
| S7-300 / S7-400 | Profibus DP slave data, ≤ 32 B (≤ 64 B on S7-400 V2) | Full consistent DP record | SFC14 / SFC15 (PIP must not be bound to OB6x) |
| S7-1200 / S7-1500 | Any of the above | Atomic copy |
MOVE_BLK / MOVE_BLK_VARIANT with ENO flag, or the PLC's interruptible = no attribute on the block-move box |
10. Implementation Patterns and Sample STL Code
10.1 Pattern A — recipe snapshot to a non-shared shadow DB
// OB1 — copy a 16-byte recipe into a shadow DB that only this OB touches
// The shadow DB is the source for the HMI; the shadow DB is never written
// by OB3x / OB4x, so consumers always see a consistent record.
CALL "UBLKMOV"
SRCBLK := P#DB10.DBX0.0 BYTE 16 // live recipe in DB10
RET_VAL:= MW300
DSTBLK := P#DB50.DBX0.0 BYTE 16 // shadow DB50, HMI source
10.2 Pattern B — pipelined reading of two independent records
If the engineering record is genuinely a tuple, copy the tuple to a buffer first (Pattern A) and let the consumer read the buffer. The buffer becomes the atomic surface. The classic mistake is to copy field A, compute on A, copy field B, compute on B — there is no atomicity once compute is in the loop.
10.3 Pattern C — SFC14 + SFC20 into a shadow
// OB1
CALL "DPRD_DAT"
LADDR := W#16#100
RET_VAL:= MW310
RECORD := P#DB11.DBX0.0 BYTE 32 // live, just refreshed by DP
CALL "UBLKMOV" // or SFC20 on S7-400
SRCBLK := P#DB11.DBX0.0 BYTE 32
RET_VAL:= MW312
DSTBLK := P#DB12.DBX0.0 BYTE 32 // shadow for OB30 to read
OB30 can now read DB12 freely; it never touches DB11 and is therefore safe even if it runs at a higher priority and preempts OB1 mid-DPRD_DAT.
10.4 Pattern D — SFC39 / SFC40 narrow disable of OB35
// Disable OB35 only, perform 16-byte copy, re-enable
CALL "DIS_IRT"
MODE := B#16#1
OB_NR := 35
CALL "BLKMOV"
SRCBLK := P#DB10.DBX0.0 BYTE 16
RET_VAL:= MW320
DSTBLK := P#DB20.DBX0.0 BYTE 16
CALL "EN_IRT"
MODE := B#16#1
OB_NR := 35
11. Diagnostics, Verification, and Watchdog Behaviour
The CPU does not flag a torn record. The relevant signals are:
- OB85 (OB not loaded / PIO access error) — fires when an interrupt is triggered for an OB that is not loaded, or when a PIP read fails because the OB that owns the PIP is currently disabled. A burst of OB85 entries right after a SFC39 call is the diagnostic you should look for: it means the SFC39 disabled a PIP-owning OB and the PIP-owned input was then read.
- OB122 (peripheral access error) — fires if a direct I/O read hits a slot that is currently being serviced by the DP cycle, e.g. if a masked OB4x tries to read the PIP. Treat a rising OB122 count as a regression after enabling a new SFC39 / SFC40 pattern.
- OB80 (time error / watchdog) — fires if the OB1 cycle exceeds the configured maximum cycle time. SFC81 and SFC20 both run inside OB1, so a long UBLKMOV chain can trip the watchdog. Watch the diagnostic buffer for OB80 with the "time exceeded" code; raise the OB1 maximum cycle in HW Config if the new worst-case path is documented and intentional.
-
Diagnostic buffer —
PLC > Diagnostic Bufferin STEP 7 lists the OB85 / OB122 / OB80 events in chronological order. Filter by event ID; sort by frequency.
11.1 Verification procedure
- Bind an OB35 to PIP 1 at 1 ms (the worst-case preemption rate).
- OB35 increments
DB99.DBD0on each entry. - OB1 increments
DB99.DBD4on each cycle. - OB35 copies
DB10.DBD0→DB10.DBD4ifDB99.DBD0 > 1000(artificial mid-copy perturbance). - Add a HMI tag on a shadow DB populated by your chosen SFC20 / SFC81 / SFC14 / SFC15 + masking pattern. The shadow tag must never display values that violate the engineering record invariants (e.g.
temperature > 0whilepressure < 0). - Run for > 24 h with the perturbation active. Inspect the shadow DB trend in WinCC / TIA Portal's trace; any torn record becomes visible as a single point that fails the invariant.
12. CPU Variant Notes (S7-300 vs S7-400 vs ET 200S)
For S7-300 CPUs of the 31x family, SFC81 is generally available in the newer firmware generations. For older S7-31x CPUs (firmware < a generation that supports UBLKMOV), the masking pattern is the only option. For S7-400, SFC20 is the right tool and is uninterruptible in the relevant memory areas. For ET 200S IM151-7 / IM151-8 CPUs, behaviour follows the S7-300 rules, so check the per-CPU instruction list. For S7-1200 / S7-1500, the topic of "interruptible block move" is handled by the PLC's block-move box with an interruptible = no attribute, not by an SFC; engineering docs on the S7-1200 / S7-1500 platform should be consulted for that environment.
MOVE_BLK instruction with the interruptible attribute. Porting from S7-400 to S7-1500 likewise removes the SFC20 call. Keep the engineering pattern (shadow-DB indirection, SFC14/15 where DP consistency is required) even when the underlying instruction changes.FAQ
Is LOAD / TRANSFER in OB1 actually interrupted by an OB3x / OB4x?
Yes. A L / T pair is interruptible at every instruction boundary. The CPU pushes accumulators, AR1 / AR2, the DB register, and the status word to the interrupt stack, runs the higher-priority OB to completion, and resumes OB1. The torn-record window is between any two instructions.
Is SFC20 BLKMOV uninterruptible?
On S7-400, yes — SFC20 runs uninterruptible for memory areas M, DB / DI, and the process image. On S7-300, no — SFC20 is interruptible; use SFC81 UBLKMOV (where supported) or wrap SFC20 in SFC41 / SFC42 (DIS_AIRT / EN_AIRT) to mask higher-priority OBs for the duration of the copy.
Why does the SFC14 help warn about OB6x-bound process image partitions?
If a PIP is bound to a clocked interrupt (OB3x) or a hardware interrupt (OB4x), the PII is refreshed at OB entry. SFC14 reads a consistent DP record that includes process-image state; calling SFC14 from OB1 while a PIP is OB6x-bound forces the CPU to choose between torn DP data and a torn PIP. Siemens forbids the combination: the SFC14 call returns 0x80B0, the request is rejected, and the application has to re-time the call.
When should I prefer SFC81 UBLKMOV over SFC41 / SFC42 + SFC20?
Prefer SFC81 when the CPU supports it and the record size is small (≤ 1 KB). SFC81 is mechanical — the CPU literally does not yield — so there is no risk of leaving a SFC41/SFC42 pair unbalanced. Use SFC41 / SFC42 only on older S7-300 CPUs that lack SFC81.
What is the safest migration path from S7-300 (no SFC81) to S7-1500?
Replace each SFC41 / SFC20 pair with a single MOVE_BLK instruction configured with interruptible = no. Replace each SFC14 / SFC15 with the platform's DP / PN read / write record instruction (RDREC / WRREC on S7-1500, or the symbolic slave_value access for IRT). Keep the shadow-DB indirection pattern — the engineering invariants do not change with the platform.