FB instances vs. function calls
Definition
In IEC 61131-3 every callable thing is either a Function
(FUN) or a Function Block (FB). The two have the same call
syntax (name(arg, …) or name(p := v, …)) but completely
different storage semantics:
- A Function has no internal storage. The same
ABS(-3)call would always return3regardless of how many times it was called before. Functions are pure mathematical mappings from inputs to a single typed return value. - A Function Block has internal storage that lives across
scan cycles. A
TON(timer-on-delay) keeps its elapsed-time counter between cycles; aCTU(counter-up) keeps its count between cycles; a user-writtenFUNCTION_BLOCKkeeps whatever VAR declarations it has between cycles.
Because of this difference, FBs cannot be “called” the way a function is. You must first declare an instance variable of the FB type — that creates one persistent storage struct per instance — and then call the instance, which routes through its struct.
Syntax
Function call (FUN)
result := function_name ( arg_list );
arg_list := positional_args | named_args
positional_args := expr { ',' expr }
named_args := param_name ':=' expr { ',' param_name ':=' expr }
FB instance declaration + call
(* declaration in the POU's VAR section *)
VAR
instance_name : FB_TYPE;
END_VAR
(* call in the POU body — note: instance_name, not FB_TYPE *)
instance_name ( input_list );
(* read outputs *)
... := instance_name . output_name ;
Examples
Function (FUN) — direct call, no declaration needed
iCount : INT;
rDistance : REAL;
iCount := ABS(-3); (* positional *)
rDistance := SQRT(IN := 2.0); (* named — IN is the canonical pin *)
Function Block (FB) — declare instance, then call instance
VAR
stepTimer : TON; (* one persistent struct per instance *)
edgeRise : R_TRIG;
END_VAR
stepTimer(IN := xStart, PT := T#100ms); (* call the INSTANCE, not TON *)
edgeRise(CLK := xButton);
IF stepTimer.Q THEN (* read outputs through the instance *)
DoStuff();
END_IF;
IF edgeRise.Q THEN (* one-shot pulse on rising edge *)
Counter := Counter + 1;
END_IF;
Semantics
A function call:
- Evaluates its arguments once.
- Computes a return value.
- Has no after-effects — the next call with the same arguments produces the same result.
A function-block call:
- Evaluates its inputs.
- Mutates the instance struct (the timer increments, the edge detector latches the previous CLK, the counter advances).
- Updates the instance’s outputs.
- The outputs remain readable until the next call to the same instance.
IEC reference
- Functions: IEC 61131-3 third edition (2013), clause 6.6.2.1.
- Function blocks: clause 6.6.2.2.
- Both clauses live under “Calling functions and function blocks” (6.6.2).
matiec conformance
matiec implements both call mechanisms exactly as the standard
requires. The two recurring pain points are documented above
in the llm_signals block:
- Calling an FB type (e.g.
TON(IN:=x, PT:=t)) instead of an instance — caught by the local FStCompiler. - Mixing positional / named arguments in a way that doesn’t match the function’s declared parameter list — caught by matiec.
ForgeIEC notes
The Ackersteuerung-2026-05-14 deploy involved exactly this
pattern in FB_MANUAL_TIME:
VAR
tonMt : TON; (* per-step timer *)
END_VAR
tonMt(IN := TRUE, PT := tMtPerBed);
IF tonMt.Q THEN
iMtStep := iMtStep + 1;
END_IF;
Note the instance name tonMt is what is called and what is
queried; the FB type TON never appears in the body.