Detecting Cyclic OB Calls in Siemens FB with RD_SINFO

David Krause14 min read
SiemensTIA PortalTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Detecting Cyclic OB Calls in a Siemens FB Using RD_SINFO and QRY_CINT

When a Function Block (FB) is reused across multiple Organization Blocks (OBs) in a Siemens S7-300, S7-400, S7-1200, or S7-1500 program, the runtime behavior of that FB can change dramatically depending on the OB that called it. A block intended for a 100 ms cyclic interrupt will misbehave if a programmer accidentally wires it into OB1 (free cyclic), or into a hardware interrupt OB with a 1 ms phase. The cleanest engineering response is to make the FB self-checking: read the start information of the calling OB at runtime, confirm that it is a cyclic interrupt (OB 30 through OB 38), and optionally verify the configured cycle time against an expected band.

This reference covers the two Siemens system functions that make this possible — RD_SINFO for identifying the calling OB and QRY_CINT for reading the configured cyclic interrupt parameters — along with practical SCL implementations, edge cases, and a fault-handling pattern suitable for production code.

1. Overview: Why an FB Should Know Its Caller

In a typical STEP 7 / TIA Portal project, FBs are written once and instantiated many times. They are called from OB1 (free cyclic), from cyclic interrupt OBs (OB 30 – OB 38), from time-of-day OBs (OB 10 – OB 17), from hardware interrupt OBs (OB 40 – OB 47), and from startup OBs (OB 100 / OB 101 / OB 102). Each of these contexts has a different determinism profile:

OB context comparison for FB execution
Calling OB Type Typical call interval Implication for FB
OB1 Free cyclic / main scan Program-dependent (10 ms – several seconds) Non-deterministic; no fixed period
OB 30 – OB 38 Cyclic interrupt 5 s – 10 ms (configurable) Deterministic period; good for PID and filters
OB 10 – OB 17 Time-of-day Once or on schedule Asynchronous; not suitable for loops
OB 40 – OB 47 Hardware interrupt Event-driven Non-periodic; very low latency
OB 100 / 101 / 102 Startup One-shot FB must not run control logic here

Without an in-block check, a programmer who drops the FB into the wrong OB will not see a compile error, and the resulting control loop will simply run at the wrong rate. The two instructions below give the FB the means to detect this situation and react.

2. Prerequisites

Before applying the techniques in this article, verify the following:

  • STEP 7 (TIA Portal) V13 SP1 or later, or STEP 7 V5.5 SPx for the S7-300/400 classic environment. The instructions RD_SINFO and QRY_CINT are available in both. On S7-1200/1500 the S7-1200/1500 system manual lists RD_SINFO in the basic instructions palette.
  • The target CPU firmware supports the instruction. QRY_CINT requires an S7-300/400 CPU with at least firmware matching the configured OBs, and is fully supported on S7-1500 from firmware V1.5 onward. See the S7-1500 system manual, section "Cyclic interrupt OBs".
  • You have a project with at least one cyclic interrupt OB (OB 30 – OB 38) configured in Device configuration → Properties → Cyclic interrupts.
  • The FB instance DB is generated as a single-instance or multi-instance DB; both work for the checks below.
Note: Do not call QRY_CINT from OB1 expecting a meaningful result — the instruction is only valid inside a cyclic interrupt OB, or it will return error code W#16#8090 ("OB does not exist") when the queried OB slot is unconfigured. RD_SINFO works from any OB.

3. Identifying the Calling OB with RD_SINFO

RD_SINFO (Read OB Start Information) is the standard Siemens mechanism for an FB to ask the operating system: which OB am I currently running in, and which OB ran before me? It returns two structures describing the start events of the current and last OB.

3.1 Instruction Signature

The SCL declaration for the call is:

// SCL
#retVal := RD_SINFO(
    TOP_SI  := #stTopSI,    // UDT "SI_Classic" or "SI_8" depending on CPU family
    START_UP_SI := #stStartupSI // Same UDT type
);

For the S7-1500 (and S7-1200 from firmware V4.x), the relevant UDT is SI_Classic (legacy layout) or the newer SI_8 tag; both expose the OB number at a fixed offset. The exact tag layout is documented in the S7-1500 system manual, chapter "OB start information".

3.2 Extracting the OB Number

On S7-300/400 the relevant field is TOP_SI.OB_CLASS and TOP_SI.OB_NR; on S7-1500 the same fields exist inside the UDT, but the field name may be OB_Number or ob_nr depending on library version. A defensive implementation simply tests the integer value of the second word of the structure. The most portable pattern is:

// SCL - portable OB number extraction
#iCurrentOB := INT_TO_WORD(#stTopSI.OB_NR);   // WORD holding 1..255

For S7-1500 in TIA Portal V15+ you can also use the symbolic view in the watch table, which displays OB_Number directly. The S7-1500 system manual shows the UDT layout in detail.

3.3 Mapping OB Number to Class

Once the OB number is in hand, the application decides what to do with it. The canonical Siemens classification is:

Cyclic interrupt OB numbers and default cycle times
OB Class Default period Common use
OB 30 Cyclic interrupt 5 000 ms Slow supervisory tasks
OB 31 Cyclic interrupt 2 000 ms Trending, statistics
OB 32 Cyclic interrupt 1 000 ms Display update
OB 33 Cyclic interrupt 500 ms Slow control loops
OB 34 Cyclic interrupt 200 ms Mid-speed loops
OB 35 Cyclic interrupt 100 ms Standard PID loop
OB 36 Cyclic interrupt 50 ms Fast loops, motion prep
OB 37 Cyclic interrupt 20 ms High-speed regulation
OB 38 Cyclic interrupt 10 ms Very fast loops (CPU-dependent)
Note: The "default" periods above are the TIA Portal defaults. The actual configured period in your project can be set anywhere in the allowed range (for S7-1500: 1 ms to 60 000 ms, with a 1 ms resolution). Always trust the runtime query rather than the default.

The check that the calling OB is a cyclic interrupt is therefore a single range comparison:

// SCL
IF (#iCurrentOB >= 30) AND (#iCurrentOB <= 38) THEN
    // Calling OB is a cyclic interrupt
    #bIsCyclicOB := TRUE;
ELSE
    #bIsCyclicOB := FALSE;
END_IF;

4. Reading the Configured Cycle Time with QRY_CINT

RD_SINFO tells you which cyclic OB is running, but it does not tell you at what period the OS has been configured to call that OB. For that you need QRY_CINT (Query Cyclic Interrupt). The instruction returns the period in milliseconds and the phase offset for any cyclic interrupt OB number you pass in.

4.1 Instruction Signature

// SCL
#iRetVal := QRY_CINT(
    OB_NR     := 35,          // INT 30..38 (S7-1500 also supports higher numbers on some CPUs)
    CYCLE     := #tCycleTime, // DINT, returned in ms
    PHASE     := #tPhaseOffset // DINT, returned in ms
);

Return values from the S7-1500 system manual:

QRY_CINT return codes
Return value Meaning
W#16#0000 No error
W#16#8090 OB does not exist (unconfigured or invalid number)
W#16#8091 Error reading system memory (internal)
W#16#80A1 Parameter error (CPU-specific)

4.2 Combining RD_SINFO and QRY_CINT

Combining the two instructions gives a complete self-check. The recommended pattern is:

  1. Call RD_SINFO; verify return is W#16#0000.
  2. Confirm the OB number is in the cyclic interrupt range (30…38).
  3. Call QRY_CINT with that same number; verify the period is within the band the FB is designed for.
  4. If any check fails, set an output flag and skip the control logic for that scan.

5. Step-by-Step SCL Implementation

The example below is a production-grade pattern. It uses an internal EN gating flag (bOB_OK) so that the rest of the FB body can early-exit on the first scan if the context is wrong. The block can be pasted into any FB as a wrapper around the application code.

5.1 FB Interface

FUNCTION_BLOCK "FB_ContextCheck"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT
      iExpectedPeriodMs   : DINT := 100;      // Expected cycle time, e.g. 100 ms
      iToleranceMs        : DINT := 5;        // Allowed deviation
   END_VAR
   VAR_OUTPUT
      bOB_OK              : BOOL;             // TRUE when caller is a valid cyclic OB
      bPeriod_OK          : BOOL;             // TRUE when period matches expectation
      wCallingOB          : WORD;             // Echo of the calling OB number
      tActualPeriodMs     : DINT;             // Period reported by QRY_CINT
      wErrorCode          : WORD;             // 0 = OK, see mapping below
   END_VAR
   VAR
      stTopSI             : SI_Classic;
      stStartupSI         : SI_Classic;
      iRetRD              : INT;
      iRetQRY             : INT;
   END_VAR
   VAR_TEMP
      // Reserved for system info; no further temps needed.
   END_VAR
BEGIN

5.2 Detection Logic

   // --- Step 1: read calling OB ---
   #iRetRD := RD_SINFO(
                  TOP_SI      := #stTopSI,
                  START_UP_SI := #stStartupSI );

   IF #iRetRD <> 0 THEN
      #wErrorCode := W#16#8001;   // RD_SINFO failed
      #bOB_OK := FALSE;
      #bPeriod_OK := FALSE;
      RETURN;
   END_IF;

   // On S7-1500 the field name is OB_Number inside the UDT.
   // Adapt the field name to your CPU family/library version.
   #wCallingOB := WORD_TO_INT(#stTopSI.OB_NR);   // portable form

   // --- Step 2: verify the OB class is a cyclic interrupt (30..38) ---
   IF (#wCallingOB >= 30) AND (#wCallingOB <= 38) THEN
      #bOB_OK := TRUE;
   ELSE
      #bOB_OK := FALSE;
      #wErrorCode := W#16#8002;   // Wrong OB class
      #bPeriod_OK := FALSE;
      RETURN;
   END_IF;

   // --- Step 3: read the configured period ---
   #iRetQRY := QRY_CINT(
                  OB_NR := INT_TO_WORD(#wCallingOB),
                  CYCLE := #tActualPeriodMs,
                  PHASE := #tActualPeriodMs ); // reuse variable is fine for the check

   IF #iRetQRY <> 0 THEN
      #wErrorCode := W#16#8003;   // QRY_CINT failed
      #bPeriod_OK := FALSE;
      RETURN;
   END_IF;

   // --- Step 4: verify period is within tolerance ---
   IF ABS_DI( #tActualPeriodMs - #iExpectedPeriodMs ) <= #iToleranceMs THEN
      #bPeriod_OK := TRUE;
      #wErrorCode := W#16#0000;
   ELSE
      #bPeriod_OK := FALSE;
      #wErrorCode := W#16#8004;   // Period mismatch
   END_IF;
END_FUNCTION_BLOCK

5.3 Using the Flag in Application Code

Once the wrapper FB above is in place, the application FB can early-exit cleanly:

// Inside the application FB
IF NOT #bOB_OK OR NOT #bPeriod_OK THEN
   // Latch the alarm but do not run the control loop this scan
   "AlarmLog".OB_Context_Invalid := TRUE;
   RETURN;
END_IF;

// ...normal control logic continues...

6. Alternative Approaches and Trade-offs

Reading the OB number and cycle time at runtime is the most general solution, but it is not always the most efficient one. The Siemens S7-1500 CPUs price RD_SINFO at a small but non-zero slice of execution time, and QRY_CINT is similarly modest. In tight 1 ms loops the overhead can be measurable. Consider the alternatives below when the project allows.

6.1 Caller-Supplied Input Parameter

The simplest alternative is to add an iCallingOB input to the FB and let each call site set it explicitly. For instance:

CALL FB_PIDController, "iDB_PID1"
   iCallingOB       := 35,           // programmer fills this in
   iExpectedPeriod  := 100,
   bExecute         := TRUE;

The advantage is zero runtime overhead: the FB does not need to query the OS. The disadvantage is exactly the failure mode the original question is trying to prevent — a programmer who wires the FB into OB1 but forgets to change iCallingOB defeats the check.

6.2 Instance-DB Compile-Time Convention

For high-performance loops on S7-1500, a common pattern is to ship separate FBs per cycle class (FB_PID_100ms, FB_PID_10ms) and to enforce the convention at code review. This eliminates the runtime check entirely, but multiplies the number of FBs to maintain.

6.3 LAD/STL Combined Approach

On S7-300/400 in classic STEP 7, RD_SINFO is also available and works in STL. The benefit of SCL is that the UDT fields are symbolically accessible; in STL/FBD the same access is possible but requires the engineer to know the byte offset. The TIA Portal help for S7-1500 system manual shows the byte-level layout in section "OB start information" for STL users who need it.

Comparison of the three approaches
Approach Runtime cost Failure mode coverage Maintenance
RD_SINFO + QRY_CINT (this article) Low (two system calls per cycle) Strong — catches all wrong-context cases One FB, one set of logic
Caller-supplied input Negligible Weak — relies on programmer Must update at every call site
Per-period FB family None Strong (compile-time) Multiple FBs to maintain

7. Error Code Reference

The following table consolidates all error values the wrapper FB can produce, including the underlying RD_SINFO and QRY_CINT codes from the S7-1500 system manual.

Wrapper FB error code mapping
Code Source Meaning Recommended action
W#16#0000 Self All checks passed Continue normal execution
W#16#8001 Self RD_SINFO returned non-zero Check CPU diagnostics; verify CPU supports instruction
W#16#8002 Self Calling OB is not in 30…38 range Alarm: "FB called from wrong OB class"
W#16#8003 Self QRY_CINT returned non-zero Verify cyclic OB is configured in device config
W#16#8004 Self Cycle time outside tolerance Alarm: "Configure OB period to expected value"
W#16#8090 QRY_CINT OB does not exist (unconfigured) Enable OB in HW config / device configuration
W#16#8091 QRY_CINT Internal SZL read error CPU diagnostic buffer; possible firmware bug

8. Edge Cases and Field-Proven Caveats

8.1 OB38 on Slow CPUs

OB 38 (10 ms default) is not supported on every CPU. The S7-314, for example, supports OB 35 (100 ms) as its fastest cyclic interrupt and will refuse to load the project if OB 38 is enabled. Always cross-check the configured OB number against the S7-1500 system manual (or the equivalent S7-300 manual) for the specific CPU order number before deploying.

8.2 Calling the FB from a Nested Interrupt

If the FB is called from a function that itself is invoked from a hardware interrupt OB, RD_SINFO still returns the outermost OB — the one whose start event the OS processed. The check works correctly in this case; the only consideration is that the period reported by QRY_CINT reflects the cyclic interrupt configuration of that outer OB, which may not be what the programmer intended.

8.3 Multi-Instance Behaviour

Multi-instance FBs share the start information of the calling OB, so the check returns the same result for every instance in a multi-instance tree. There is no need to call RD_SINFO from every level; calling it once at the top of the chain is sufficient and faster.

8.4 S7-1200 Differences

On S7-1200 (firmware V4.0+), the cyclic interrupt OBs are configured under Program blocks → OB1 → Properties → Cyclic interrupt, but the underlying OB numbers and RD_SINFO/QRY_CINT semantics are identical to S7-1500. The minimum cycle time on S7-1200 is 1 ms, with a 1 ms resolution, and is CPU-dependent — check the specific CPU datasheet for the achievable minimum.

8.5 Fail-Safe (F-CPU) Considerations

On F-CPUs (e.g. CPU 1515F, CPU 1518F), the F-runtime is scheduled in a separate cyclic interrupt class. Calling the standard RD_SINFO from an F-FB is allowed, but QRY_CINT queried for the F-runtime slot returns values specific to the safety cycle. Do not reuse the wrapper above inside F-blocks without reviewing the safety manual for the target F-CPU.

9. Verification and Commissioning

After deploying the wrapper FB, verify the detection is working using the following steps in the online watch table:

  1. Force the FB instance DB online and add wCallingOB, tActualPeriodMs, and wErrorCode to the watch table.
  2. With the program running in the correct cyclic OB, confirm wErrorCode = 0 and wCallingOB matches the OB number from the device configuration (typically 30…38).
  3. Change the call site to OB1, recompile, and download. Confirm wErrorCode = W#16#8002 and the alarm bit goes true.
  4. Change the configured period of OB 35 from 100 ms to 200 ms in the device configuration and download. Confirm wErrorCode = W#16#8004.
  5. Restore the original configuration and confirm wErrorCode = 0 again.
Note: The diagnostic above assumes the FB is the only consumer of the cyclic OB. If the OB runs additional logic, the OB-level cycle time is the sum of all consumers; the check still works because the period returned by QRY_CINT is the OS scheduling period, not the FB execution time.

10. Frequently Asked Questions

What is the difference between RD_SINFO and RD_INFO on Siemens S7-1500?

RD_SINFO returns the start information of the current and last OB only, in a compact UDT. RD_INFO returns the start information of any OB in the system (including OBs that are not currently running) but requires a longer UDT and is generally used for diagnostic dashboards. For a "where am I called from" check inside an FB, RD_SINFO is the right choice.

Can I call RD_SINFO from OB1 and still get a meaningful OB number?

Yes — if the FB is called from OB1, RD_SINFO returns 1 (OB1's number) in the OB_NR field, and the cyclic-interrupt range check (30…38) correctly rejects the call. The instruction is safe to call from any OB context, including free cyclic OB1.

How do I read the configured period if the calling OB is OB 35 but my code is in SCL on S7-300?

QRY_CINT is supported on S7-300/400 with the same call signature. Pass OB_NR = 35, declare CYCLE and PHASE as DINT, and compare CYCLE against the expected period using ABS_DI for tolerance, exactly as in the S7-1500 example above.

Does the cycle time reported by QRY_CINT include the time slice used by the OB itself?

No. QRY_CINT returns the OS scheduling period — the time between two consecutive start events of the OB. The OB's own execution time, plus the time the OS spends on higher-priority tasks, is overlaid on top of that period. For a precise cycle-budget analysis, use a runtime measurement block (RUNTIME) inside the OB and add it to the QRY_CINT value.

What is the fastest cyclic OB I can configure on an S7-1516F?

The minimum cyclic interrupt period on the S7-1516F is 500 µs (0.5 ms) from firmware V2.0 onward, but OB 38's default 10 ms is rarely the practical minimum because OB execution time plus communication load typically consumes more than that. Always consult the S7-1500 system manual for the specific CPU and confirm the achievable minimum with a runtime measurement on the target machine.

Back to blog