Variable declarations

The eight scope blocks

BlockScopePersistent?Used in
VARPOU-localAcross cycles (FB only); per call (FUN)All POU types
VAR_INPUTRead-only input parametern/aAll POU types
VAR_OUTPUTOutput parameterAcross cycles (FB)FB and FUNCTION (FUN return is separate)
VAR_IN_OUTPass-by-reference parametern/aAll POU types
VAR_TEMPThrowaway, only valid this callNoAll POU types
VAR_GLOBALProject-wideAcross cyclesGVL POUs (Global Variable Lists)
VAR_EXTERNALReference to a VAR_GLOBALAcross cycles (the source’s lifetime)All POU types
VAR CONSTANTCompile-time constantn/aModifier on any block

In ForgeIEC, declarations are typically created via the editor’s Variables panel (or via project.write.add_variable over MCP), not as raw VAR ... END_VAR text in the body. The XML save format does emit them as IEC blocks for round-trip compatibility with other tools, but the editor never asks you to type them.

Block syntax

VAR [CONSTANT | RETAIN | NON_RETAIN] [PUBLIC | PRIVATE]
    name1 : TYPE1 [:= initial_value];
    name2, name3 : TYPE2;
END_VAR

Modifiers:

  • CONSTANT — compile-time constant, can’t be assigned at run-time.
  • RETAIN — value survives a power cycle (persisted).
  • NON_RETAIN — explicit non-persistent (default for most types).
  • PUBLIC / PRIVATE — for FB members; controls whether external code can read/write through the FB instance.

VAR (POU-local)

Inside a FUNCTION_BLOCK or PROGRAM, VAR declarations persist across scan cycles. Inside a FUNCTION they’re re-initialised on every call (functions have no state).

PROGRAM PLC_PRG
    VAR
        iCount   : INT := 0;
        tonStep  : TON;
        sMessage : STRING(80) := 'Init';
    END_VAR
    (* body *)
END_PROGRAM

VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT

Function-block and function parameters:

FUNCTION_BLOCK FB_Counter
    VAR_INPUT
        xCU : BOOL;
        iPV : INT;
    END_VAR
    VAR_OUTPUT
        xQ : BOOL;
        iCV : INT;
    END_VAR
    VAR
        xPrevCU : BOOL;          (* internal state *)
    END_VAR
    (* body *)
END_FUNCTION_BLOCK

VAR_INPUT is read-only inside the body (you can’t assign to it). VAR_OUTPUT is read-write but its lifetime ties to the FB instance.

VAR_IN_OUT passes by reference — the callee mutates the caller’s variable directly. Useful for modifying caller-owned data structures without copying:

FUNCTION_BLOCK FB_Sort
    VAR_IN_OUT
        arr : ARRAY[1..100] OF INT;
    END_VAR
    (* sorts arr in place — caller sees the result *)
END_FUNCTION_BLOCK

VAR_GLOBAL

In a GlobalVarList POU:

VAR_GLOBAL
    gxRunning   : BOOL := FALSE;
    giCounter   : DINT;
    gtonStartup : TON;
END_VAR

To use a VAR_GLOBAL from another POU, you can either:

  1. Declare a VAR_EXTERNAL block in the consuming POU that names the same identifier and type — the standard’s official way.
  2. Reference it via the three-level form GVL.<Group>.<Name> directly — ForgeIEC’s editor handles this automatically and the matiec generator emits the VAR_EXTERNAL for you.

The three-level form is preferred in ForgeIEC; it keeps each POU’s VAR blocks short and makes the dependency obvious in the source.

VAR_TEMP

Function-local scratchpad that’s reset at the start of every call. Useful inside FBs when you don’t want a value to persist across cycles:

FUNCTION_BLOCK FB_Avg
    VAR_INPUT iValues : ARRAY[1..10] OF INT; END_VAR
    VAR_OUTPUT iAvg : INT; END_VAR
    VAR_TEMP
        iSum : DINT;       (* zeroed at every call *)
        i    : INT;
    END_VAR
    iSum := 0;
    FOR i := 1 TO 10 DO
        iSum := iSum + INT_TO_DINT(iValues[i]);
    END_FOR;
    iAvg := DINT_TO_INT(iSum / 10);
END_FUNCTION_BLOCK

RETAIN — power-cycle persistence

VAR RETAIN variables keep their value across a PLC restart (stored in retentive memory; on real hardware this is usually battery-backed RAM or flash).

VAR RETAIN
    diLifetimeCycles : DINT;
END_VAR

In ForgeIEC, the RETAIN modifier maps to the is_retain flag on the pool variable. See Memory: project_persistvarlist_to_attribute for the model rationale.

IEC reference

IEC 61131-3 third edition (2013), clause 6.5 — “Variables”, subclauses 6.5.1 through 6.5.7 for the individual blocks.

matiec conformance

matiec implements all eight blocks per the standard. The strict separation between declaration and body is the main ForgeIEC-side wrinkle (VAR blocks live in the editor’s declaration panels, not in set_text_body payloads) — see the second llm_signals entry above.