TIA Portal · SIMATIC S7-1500 · how the CPU thinks

The execution model

Before writing contacts in TIA Portal, understand when the Siemens CPU executes your code: once during startup, in the main cycle, or in an interrupt that preempts the scan. Correct logic can still behave incorrectly when it runs at the wrong time. This simulation uses a railway level crossing to show those timing differences. It is a teaching example, not a validated railway safety design. Any safety function belongs to a safety relay or safety PLC, not this standard control-logic example.

⏱️ OB1 main cycle🔁 OB30 cyclic interrupt⚡ OB40 hardware interrupt
01 · The cycle

Startup, cyclic execution and interrupts

Before OB1 starts, the CPU runs any startup OB in the program, such as OB100 (Startup), once. It then repeats the three steps below. A loop takes a few milliseconds, with its duration changing as different code executes. Code that needs exact timing, or a reaction faster than one loop, cannot live here alone.

STEP 1 · PII
Read all inputsThe CPU snapshots every input into the Process Image of Inputs. Your code reads the copy, not the live wire.
→
STEP 2 · OB1
Run your blocksOB1 (and any FCs/FBs it calls) executes top-to-bottom on that snapshot. This is where your machine logic lives.
→
STEP 3 · PIQ
Write all outputsThe Process Image of Outputs is pushed to the real terminals at the end of this simplified cycle. The sequence then repeats.

🚆 Problem 1: reacting before the next scan. A train trips the approach sensor while OB1 may already be busy elsewhere in its scan. If the reaction is handled only in the main cycle, it must wait for execution to reach that code. The crossing simulation lets you compare that wait with an interrupt response.

🔁 Problem 2: maintaining the interval. The warning lights must flash at a precise, regulated rate. If their blink depends on a scan time that drifts from 4 ms to 9 ms, the cadence drifts too. Unacceptable for a signal.

Siemens uses Organization Blocks as entry points that the operating system calls. Interrupt OBs run at a higher priority than OB1. Follow the three blocks below to see when each one executes in the crossing simulation.

02 · Follow the execution

Three blocks controlling a crossing

Below is the crossing up top and a logic-analyzer view of the CPU itself underneath. The display shows which block is executing in this simulation. OB1 runs the gate sequence in the gaps. OB30 runs on a 500 ms interval to flash the lamps. Press 🚆 Send a train and OB40 preempts OB1 when the sensor triggers the hardware interrupt. Follow the lanes to see how higher-priority work interrupts the main cycle.

OB1 · main cycle
gates: OPEN
CPU execution: newest on the rightcycle 0
OB40 HW int
OB30 cyclic
OB1 main
speed

👀 The lamps change on the yellow OB30 ticks. That block follows the OS clock rather than the variable duration of the main scan. When the train reaches the sensor, the red OB40 bar appears on top of the petrol OB1 bar. The diagram shows OB1 paused while the higher-priority interrupt handles the sensor event, before cyclic execution continues.

03 · Reuse with memory

Reuse the code with separate crossing data

A rail line has many crossings with the same behaviour. Write the common logic once as a Function Block, FB_Crossing, then call it twice. Each call gets its own instance DB: storage for that crossing's state. Both crossings use the same code with separate memory. Send a train through one crossing and watch which instance data changes.

// written ONCE — the behaviour of any crossing
FUNCTION_BLOCK "FB_Crossing"
  VAR  gateState : Int;  stepTimer : Time;  lampPhase : Bool;  trainPresent : Bool;  END_VAR
// called TWICE — each call carves out its own instance DB
"Main_St"();  // instance DB10 — Hauptstraße crossing
"Bahnhof"();  // instance DB11 — Bahnhofweg crossing

With these two FB instances, the code is shared, the data is not. Fix a bug in FB_Crossing once and every crossing using it gets the fix. Each instance keeps its own gateState, timer and train flag. An FC has no instance DB of its own to hold that state between calls.

04 · Data-block addressing

Optimized vs. standard data blocks

If you have worked with older Siemens PLCs, check the access setting when moving to the S7-1200/1500. Data blocks are optimized by default: variables have no fixed byte address. You access them by name while the compiler arranges their storage for speed. Switch a DB to standard access and variables use a fixed byte.bit offset for absolute addressing. Adding a variable can shift the following offsets. Toggle the setting below and compare the resulting memory map.

📦 DB10 "Main_St"

Memory map

An HMI tag pointing at "Setpoint"

✅ Optimized: no offsets and symbolic access only ("Main_St".gateState). The CPU arranges the layout for fast access; on an S7-1500 each BOOL takes a whole byte. With a memory reserve set on the block, you can add variables and download without reinitializing its values. This is Siemens' default and recommended access setting.

⚠️ Standard: fixed offsets support absolute addresses such as %DB10.DBX4.0 and the tools that require them. BOOLs pack eight to a byte, but the next INT starts on an even byte, so padding bytes appear, and inserting a variable renumbers the addresses below it. An HMI tag that still uses the old address can then read the wrong value.

05 · Check yourself

Two that catch people out

1 · Your warning lamps must flash at a dead-steady 1 Hz no matter how heavy the program gets. Where does that logic belong?

2 · You add one BOOL to the top of a standard DB that an HMI reads by absolute address %DB10.DBX4.0. After download, what happens?