Search code examples
plctwincatcodesysiec61131-3

In IEC61131-3, what are the semantics of the assignment operator for variables of Function Block type?


In IEC 61131-3 implementations (e.g. CoDeSys, TwinCAT, Unity), what actually happens when you use the assignment operator on variables of Function Block type? I've looked at the TwinCAT and CoDeSys docs without finding any kind of clear description; I assume it's defined by the 61131-3 spec, but I don't have a copy of that.

E.g:

VAR
   fbA, fbB : TON;
END_VAR
   
// Function Block assignment
fbB := fbA;

In this case, memory is statically allocated for both the fbA and fbB instances before the assignment is executed. Does the assignment operator just copy the VAR_INPUT, VAR, and VAR_OUTPUT internal state variables from fbA's memory area to fbB's? Does anything else happen?

What if fbA or fbB are instances of a Function Block type that implements FB_init(), FB_reinit() or FB_exit()? Does assigning fbB := fbA result in calls to any of those methods?


Solution

  • I wrote a bit of test code (TwinCAT 3.1.4024.44)

    MAIN.TcPOU:

    PROGRAM MAIN
    VAR_INPUT
        nInits: LINT;
        nReinits: LINT;
        nExits: LINT;
    END_VAR
    VAR
        fbA, fbB, fbC : FB_Test;
        bRun : BOOL;
        bCallStackFB : BOOL;
        nStackResult : INT;
    END_VAR
    
    
    -------------
    
    IF bRun THEN
        fbA();
        
        fbB := fbA;
        
        fbB();
        
        fbC := fbB;
        
        fbA.nInput := fbB.nOutput;
        bRun := FALSE;
    END_IF
    
    IF bCallStackFB THEN
        nStackResult := UseStackFB(10);
        bCallStackFB := FALSE;
    END_IF
    
    ==============
    
    METHOD UseStackFB : INT
    VAR_INPUT
        nInput : INT;
    END_VAR
    VAR
        fbStackFB : FB_Test;
    END_VAR
    
    ---------------
    
    fbStackFB(nInput := nInput);
    UseStackFB := fbStackFB.nOutput;
    
    

    FB_Test.TcPOU:

    FUNCTION_BLOCK FB_Test
    VAR_INPUT
        nInput : INT;
    END_VAR
    VAR_OUTPUT
        nOutput : INT;
        nRunCount : LINT := 0;
    END_VAR
    VAR
        nVar : INT;
    END_VAR
    
    ---------------
    
    nVar := nInput + 1;
    nOutput := nVar + 1;
    nRunCount := nRunCount + 1;
    
    ================
    
    METHOD FB_exit : BOOL
    VAR_INPUT
        bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
    END_VAR
    
    ----------------
    
    MAIN.nExits := MAIN.nExits + 1;
    
    ================
    
    METHOD FB_init : BOOL
    VAR_INPUT
        bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
        bInCopyCode : BOOL;  // if TRUE, the instance afterwards gets moved into the copy code (online change)
    END_VAR
    
    ----------------
    
    MAIN.nInits := MAIN.nInits + 1;
    
    ================
    
    METHOD FB_reinit : BOOL
    VAR_INPUT
    END_VAR
    
    ----------------
    
    MAIN.nReinits := MAIN.nReinits + 1;
    

    Running the main program in the TC debugger shows that MAIN.nInits starts at 3 (one each for the statically allocated fbA, fbB and fbC instances). Toggling bRun does not increment the init, reinit, or exit counters; so clearly the FB_init(), FB_reinit() and FB_exit() methods are not invoked when one FB instance's data is overwritten by that of another.

    Inspecting the behaviour of fbA, fbB and fbC in the debugger shows that the assignment operation definitely copies all of the FB's VAR_INPUT, VAR, and VAR_OUTPUT state when the FB is assigned.

    Toggling bCallStackFB does increment both MAIN.nInits and MAIN.nExits, so creating a temporary instance of an FB as a stack variable, such as a VAR in a FUNCTION, METHOD or PROPERTY implementation, definitely calls both FB_init() and FB_exit(). (Note: TwinCAT versions before 3.1.4022 and CoDeSys versions before V3.5 SP9 Patch 8 do not call FB_exit() for stack-allocated FB instances.)