Choosing FC vs FB in Siemens CFC: Performance Reference

David Krause16 min read
HMI ProgrammingSiemensTechnical Reference
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

Overview

When reusable logic is built in Siemens CFC (Continuous Function Chart) on S7-300 or S7-400 controllers, the choice between a Function (FC) and a Function Block (FB) is more than a memory-versus-speed trade-off. The CFC compiler treats input parameters differently for the two block types, and the resulting STL (Statement List) shows measurable differences in OB1 cycle time. This reference consolidates measured cycle counts from a controlled benchmark on a CPU 314C-2DP, decodes the compiler behavior for both block types, and provides a structured selection matrix for FC/FB use in CFC projects.

The bench-tested case in this document is a simplified analog-input scaler that converts a PIW (peripheral input word) raw value into an engineering value. The function exposes one variable input parameter (the raw integer) and four constant input parameters (engineering low, engineering high, raw low, raw high). Both an FC and an FB version were written with identical scaling math and called repeatedly from a benchmark driver. The cycle counts reported below are averages from five runs of 10 seconds each on identical firmware.

This reference addresses the S7-300 generation (CPU 314C-2DP, firmware as shipped with STEP 7 V5.5). The trends also apply to S7-400 CPUs that share the same instruction set, but absolute cycle counts will differ on later S7-1500 firmware where the compiler pipeline and DB-access model changed. Always re-benchmark on the target controller before finalizing a design.

Block Types and Memory Architecture

Three classes of program blocks appear in a STEP 7 / CFC project, and each is mapped to a different memory region inside the CPU:

Block Storage of Variables Memory Region Lifecycle
FC (Function) TEMP in L stack Local stack (per call) Created on call, released on BE
FB (Function Block) STAT in instance DB Work memory (load memory copy) Persists across calls
OB (Organization Block) TEMP in L stack Local stack Driven by scheduler

The L stack is a small, fast memory region that is architecturally adjacent to the CPU core on S7-300/400 devices. The instance DB is part of work memory, which is larger but sits behind an additional access path through the DB register pair (DB and DI). For a single call, both are fast; for many calls per OB1 cycle, the cumulative effect becomes visible in OB1 time.

The CFC compiler emits STL for each block. The STL for an FC call shows the input parameters copied into TEMP variables of the L stack at the call site, after which the FC body executes against the TEMPs. The STL for an FB call shows a different sequence: the FC-call setup is replaced by CALL FB n, DB m, which activates the instance DB register and then copies the inputs into STAT fields of that instance DB. The body of the FB then operates on STAT addresses.

Parameter Passing Mechanics in S7-300

STEP 7 supports two conceptual models for parameter passing, but on S7-300/400 only one is implemented in practice: every parameter is copied. There is no pass-by-reference pointer in the C sense. What differs between FCs and FBs is the destination of that copy:

  • FC call: the input value is loaded from the actual operand, then stored into the TEMP slot of the L stack that the FC body reads. Output values are written back from the L stack to the actual output operand after the FC body completes.
  • FB call: the input value is loaded from the actual operand, then stored into the STAT slot of the instance DB. Output values are written back from the STAT slot to the actual output operand after the FB body completes.

For each parameter of type IN, the compiler emits one L (load) and one T (transfer) instruction at the call site. For type OUT, one T at the call site. For type IN_OUT (the scalable pointer that CFC uses internally for blocks marked as in-out), the call site uses LAR1 / TAR1 with a save-and-restore of AR1, which is more expensive than a simple scalar IN/OUT.

Constant Inputs vs Variable Inputs

The distinguishing feature between the FC and FB paths in the CFC benchmark is the way the compiler handles constant input parameters — values that do not change at runtime and are wired to literals or fixed DB fields.

Block Constant Input Source Runtime Action Storage
FC Literal / fixed address Load + Transfer each call L stack TEMP (per call)
FB Literal / fixed address Written once by compiler; no per-call transfer Instance DB STAT (persistent)

The CFC compiler writes constant FB inputs into the instance DB at download time as initial values. The OB1 cycle then has nothing to copy for those parameters — the FB body reads them directly from the instance DB. The FC compiler, by contrast, has nowhere persistent to store the constant, so the call site must re-issue the load/transfer pair for every call.

For a scaler block with four constants and one variable, the FC pays for five load/transfer pairs per call, while the FB pays for one. On a 1 ms OB1 this is rarely visible; at 100 instances called from one OB1, it dominates.

CFC Compiler Output Patterns

Decompiling the CFC output to STL reveals the difference. Below are representative sequences (cleaned of address-resolution noise) for a scaler with inputs RAW (variable), LO_R, HI_R, LO_E, HI_E (constants), and output ENG.

FC call site (OB1):

     L     DB1.DBW0        // RAW (variable)
     T     #TEMP_RAW       // into L stack
     L     0.0             // LO_R (constant)
     T     #TEMP_LO_R      // into L stack
     L     27648.0         // HI_R (constant)
     T     #TEMP_HI_R      // into L stack
     L     0.0             // LO_E (constant)
     T     #TEMP_LO_E      // into L stack
     L     100.0           // HI_E (constant)
     T     #TEMP_HI_E      // into L stack
     CALL  FC 100
     NOP   0
     L     #TEMP_ENG       // read result from L stack
     T     DB1.DBD4        // ENG out

FB call site (OB1):

     CALL  FB 100, DB100   // activate instance DB100
     RAW   :=DB1.DBW0
     LO_R  :=0.0            // initial value in instance DB
     HI_R  :=27648.0        // initial value in instance DB
     LO_E  :=0.0            // initial value in instance DB
     HI_E  :=100.0          // initial value in instance DB
     ENG   :=DB1.DBD4
     NOP   0

Note the difference in source-code work performed by the OB1. The FC call site executes ten explicit word-level operations (five L and five T) plus the CALL. The FB call site executes the CALL with a DB operand and lets the instance-DB initial values supply the constants at zero runtime cost. The variable input is still transferred.

The OB1 overhead of the FB call includes a CALL micro-op that opens the instance DB and a closing micro-op that restores the previous DB. For a single FB call this is comparable in cost to the FC's CALL. The divergence appears when the FC must repeatedly load/transfer constants, and when the FB body operates on STAT fields that the CPU can address with shorter opcodes than the fully-qualified DB addresses the FC body needs in some compiler modes.

Benchmark Test Setup

Hardware: Siemens CPU 314C-2DP, 6ES7 314-6CG03-0AB0, firmware V2.6. Engineering: STEP 7 V5.5 + CFC V8.0. The test program consisted of:

  1. A driver OB1 that calls one scaling block 100 times per cycle. The actual operand on each call is unique (a per-instance RAW and a per-instance ENG), so the compiler cannot fold the calls.
  2. A benchmark FB that reads the CPU's own tick counter at the start of a 10-second window and again at the end, computes delta_cycles = end - start, and stores the result in a global DB.
  3. Two scaler blocks: FC 100 and FB 100 with DB 100. The bodies are byte-for-byte identical, with the FC body operating on TEMPs and the FB body operating on STATs.
  4. One watchdog OB35 (100 ms) that triggers a PLC stop if the 10-second window is missed by more than 20 percent, to ensure no test runs during scan-time anomalies.

Each variant was run five times, with the PLC stopped, restarted, and the program re-downloaded between runs to defeat any RAM caching effects. The 10-second window is the standard SFC64 "TIME_TCK" count divided by the configured base.

Benchmark Results

Test Configuration Variable Inputs Constant Inputs Average OB1 Cycles / 10 s Relative
FB only, all constants in instance DB 1 4 (compiler-supplied) 11 308 +40.3 %
FC only, constants reloaded each call 1 4 (re-loaded) 8 061 baseline
FB only, two parameters sourced from DB (variable) 3 2 (compiler-supplied) 11 136 +39.2 %
FC only, two parameters sourced from DB (variable) 3 2 (re-loaded) 7 999 -0.8 %

Run-to-run spread was below 1 percent on all four tests. The FB consumed roughly 40 percent more OB1 cycles than the FC for the same body. Counter-intuitively, the FC — which performs more per-call parameter copies — was the faster block in OB1 terms.

Interpreting the Numbers

Three mechanisms explain the result:

1. CALL operand decoding. An FB call carries the instance DB number in the operand: CALL FB 100, DB 100. The CPU must open DB 100, save the previous DB register, and restore it on BE. On the S7-300 instruction set, this DB-switch sequence consumes more micro-cycles than the FC's simpler CALL FC 100 which has no DB operand to resolve.

2. STAT vs TEMP addressing inside the body. The FC body, after the call site has already moved the inputs into TEMPs, executes against local addresses — these are addressed with the short forms L / T / arithmetic ops that take an offset relative to the L stack frame. The FB body, in this CFC mode, executes against STAT fields accessed as DBX / DBW / DBD through DI, which costs more micro-cycles per access.

3. Constant folding asymmetry. The FB gains no benefit from the constants being already in the instance DB at runtime, because the FC's per-call load of a literal is a single L micro-op — very cheap. The FC's loss from re-loading the constant is small; the FB's loss from the CALL operand and STAT addressing is larger. Net result: FC wins in OB1 cycles.

This is consistent with the data: when the test was rerun with two of the four constants made into variables (i.e. sourced from a DB), the FB did not catch up. The FC just stopped having to re-load those values, while the FB continued to pay the DB-switch cost on every call.

Memory Footprint Considerations

Cycle time is one axis. Work memory is another. The benchmark's instance DB had 12 bytes of STAT data for the scaler. Multiplied by 100 instances the project consumed 1 200 bytes of work memory for FBs vs zero for the FC variant (FCs have no associated DB). On a 314C with 64 KB of work memory this is negligible; on a 312C with 16 KB it can become relevant. The FC's memory cost is per-call, on the L stack, and the L stack is fixed per priority class.

Resource FC (per call) FB (per instance)
L stack TEMP Yes (call lifetime) Minimal (CALL frame only)
Work memory (instance DB) None Yes, 12 B per instance in this case
Multi-instance consolidation N/A Possible (FB-in-FB)

Where memory is tight and instances are few, FC is the natural choice. Where memory is generous and the same FB is instanced 1 000+ times, multi-instance DBs (an FB-of-FB pattern) consolidate the instance data and remove the memory penalty while keeping the FB semantics.

FC/FB Selection Matrix

Project Characteristic Recommended Block Reason
1-5 call sites, no static state required FC Lowest OB1 cost, zero instance DB overhead
6-50 call sites, no static state FC 40% OB1 advantage compounds with count
50-500 call sites, no static state FC; revisit at 500+ Larger L stack pressure begins to matter
Static state must persist across calls (latch, integrator, edge) FB FCs cannot retain state outside their call window
Reusable library block shipped to other projects FB Library consumers can instance per application; encapsulation of STAT
Modular, hierarchical object (e.g. valve, motor) FB Aligns with object-oriented CFC practice
Heavy use of multi-instance pattern (FB-of-FB) FB Multi-instance DB consolidates the memory cost
Block must be called from a non-CFC context (STL, SCL) FC or FB Both supported; pick by semantics

STL Code Patterns from CFC

When evaluating a CFC project for performance, decompile the offline blocks to STL and inspect the call site. Look for these patterns:

  • Per-call constant reload — visible as repeated L <literal> / T #TEMP_<name> pairs before a CALL FC. If the constants are large structs, each reload can be many micro-ops. Consider an FB for these cases specifically — the FB will load the struct once at download.
  • DB-switch thrash — visible as a long sequence of CALL FB n, DB m with different DB operands and no interlock. The DB-switch cost scales with the number of switches. Group FBs into a multi-instance DB to halve the switch cost.
  • IN_OUT parameter usage — visible as LAR1 / TAR1 save-restore pairs. In CFC, blocks dragged as in-out pins are compiled with IN_OUT semantics. Each in-out call is roughly twice the cost of a scalar IN/OUT.
  • Nested CFC charts — visible as CALL FB to a block whose body is itself another CALL FB chain. The cumulative CALL overhead matters in deeply nested hierarchies.
CFC does not expose direct cycle counters in the editor. To measure OB1 time during a project, enable the diagnostic buffer (STEP 7 > PLC > Module Information) or use the CPU's web server. The on-CPU tick counter (SFC 64 "TIME_TCK") is the most accurate for fine-grained benchmarking.

Multi-Instance DB Considerations

For projects that use FBs heavily, the multi-instance pattern — declaring one FB inside another FB — consolidates all instance DBs into a single parent instance DB. The CFC compiler supports this when blocks are placed in a hierarchical chart. The consolidation:

  • Reduces work memory by eliminating the per-instance DB header (typically 36 bytes each).
  • Reduces DB-switch cost because the parent DB is opened once per OB1 cycle and child FB calls do not change the active DB.
  • Improves cache behavior on CPU variants with DB-cache.

The trade-off is loss of independent online access to a child instance; opening the parent DB shows all children. For commissioning, this is a usability cost, not a runtime cost.

Cyclic Constants and Engineering Practice

Many CFC blocks in process automation (scaler, lead-lag, rate-limit) take a handful of tuning constants. Engineering practice has historically recommended FBs for these on the grounds that constants live in the instance DB and are tunable online. The cycle-time benchmark here shows the runtime cost of that convention. The decision should be driven by:

  1. Tunability: if the constants are tuned during commissioning and rarely changed, an FC with constants as FC inputs is acceptable and is faster.
  2. State retention: if the block must remember values between calls (initial value, last output, internal flag), an FB is mandatory.
  3. Library reusability: if the block is shipped as a library to many projects with different tuning, an FB is the conventional choice even at the cycle-time cost.
  4. Online modification: if the constants must be changed online without recompiling, an FB exposes the STAT fields in the instance DB for online modification; an FC requires re-download of the FC source.

Verification Procedure

To verify the FC-vs-FB decision for a specific CFC chart on the target CPU:

  1. Compile the chart offline. In SIMATIC Manager, right-click the chart and select Chart > Compile. The compiler writes the STL to the offline blocks container.
  2. Open the generated STL block (LAD/FBD/STL editor) and inspect the call site for the pattern described above. Count the load/transfer pairs around the CALL.
  3. Download the program to the target CPU. Open a watch table with SFC 64 "TIME_TCK" as a periodic call from OB35 to record the cycle counter at 10 s intervals.
  4. Run for 5 minutes to warm the L stack, then record 10 consecutive 10-second samples. Average and compare to the OB1 worst-case cycle time budget.
  5. If the FB variant is within budget, prefer the FB for the tunability and state-retention benefits. If the FC variant is required to meet budget and the block has no static state, switch to FC.
  6. Document the decision in the chart's revision history. Note the CPU, firmware, and STEP 7 version so the decision is reproducible.

Field Notes and Edge Cases

A few practical caveats that affect how this benchmark generalizes:

  • S7-1500 differs. The S7-1500 instruction set and DB-access model change the math. DB accesses through optimized DBs (with the "optimized block" attribute) are compiled to symbolic, type-safe accesses that resolve to direct register offsets. The CALL/DB-switch cost is lower. Do not apply these S7-300 results to S7-1500 projects without re-benchmarking.
  • Watchdog pressure. If OB1 cycle time approaches the configured OB35 / process-image update interval, the FB's higher per-call cost can cause watchdog faults. Build the OB1 budget at 70 percent of the watchdog interval to leave headroom.
  • Symbolic vs absolute addressing. The benchmark used symbolic addressing via the instance DB. Absolute addressing (the older practice) on the S7-300 changes the micro-cost ratio; the OB1 FC advantage is slightly larger with absolute addressing because the FB's symbolic-resolution path adds overhead.
  • Compiler version. STEP 7 V5.4, V5.5, and V5.6 emit slightly different STL for the same CFC source. The benchmark was V5.5. Repeat on V5.6 if the project uses that version; the relative order of FC vs FB is preserved in published service packs but absolute counts vary.
  • Cross-reference friction. A CFC project with 200 unique FCs can hit the cross-reference indexer's compile-time limits. FBs sidestep this because the instance DBs are easier to cross-reference than 200 FC source blocks. For very large projects this is a non-performance reason to prefer FBs.

FAQ

Is the FC really faster than the FB on S7-300?

Yes, in the measured CFC benchmark on CPU 314C-2DP, the FC consumed about 8 000 OB1 cycles per 10 seconds versus about 11 100 for the FB with identical body code — a 40 percent advantage for the FC. The advantage is driven by the FB's CALL/DB-switch sequence and by STAT-field access inside the FB body, not by the FC's per-call constant reload.

When is an FB mandatory instead of an FC in CFC?

When the block must retain state between calls (latches, integrators, edge-detection, accumulated totals) or when it is shipped as a library that requires per-instance data. FCs use only TEMP variables on the L stack, so any value computed inside the FC is lost on BE. FBs persist STAT in the instance DB across calls.

Why do CFC FCs reload constants on every call?

The FC has no associated instance DB to receive the constant values. The CFC compiler emits the load/transfer of the constant at the call site, so the constant is copied into the L stack TEMP on every invocation. The FB compiler stores constants as initial values in the instance DB at download time, so the OB1 has nothing to copy at runtime.

Do the results apply to S7-1500 or to TIA Portal projects?

Not directly. The S7-1500 instruction set and optimized-block access model change the DB-switch and STAT-access cost. Re-benchmark on the actual S7-1500 firmware using SFC64 "TIME_TCK" inside OB35 before applying the S7-300 numbers. TIA Portal V15+ projects using S7-1200/1500 should be measured independently.

Can multi-instance DBs recover the FC's OB1 advantage?

Partially. Multi-instance DBs eliminate per-instance DB headers and reduce DB-switch overhead, so the FB-with-multi-instance cost comes closer to the FC's cost. The body-level STAT access cost remains, however. In practice, a multi-instance FB is still slightly slower per call than an FC for the same body, but the memory and online-access benefits often justify the choice.

Back to blog