Attribute 'estimated-stack-usage'

The pragma transfers an estimated value for the stack size requirement.

Methods with recursive calls do not withstand a stack check because the stack usage cannot be determined. Consequently, a warning is issued for these methods. To suppress the warning, you can use the attribute to give the method an estimated value in bytes for the stack size requirement. The method then successfully passes the stack check.

Syntax: {attribute 'estimated-stack-usage' := '<estimated stack size in bytes>'}

Insertion location: First line above the declaration part of the method

Sample:

{attribute 'estimated-stack-usage' := '99'} // 99 bytes
METHOD SampleMethod : INT
VAR_INPUT
END_VAR

Warning issued:

The warning that is issued for recursive methods without the 'estimated-stack-usage' attribute is:

"C0298: Calculation of stack usage incomplete due to recursive calls, starting at '<FB.Method>'"

In a project that is unchanged compared to the last time the project was build, the warning is only issued when the Rebuild project command is used (not when the Build project command is used).

Setting the stack size:

The value of the maximum possible stack size can be configured in the real-time settings of the TwinCAT project (Settings tab > Global Task Config/Maximum Stack Size [KB]). The set maximum value is not completely available to the PLC project, but part of it is used by the TwinCAT runtime system, for example.

Attribute 'estimated-stack-usage' 1:

No stack calculation for stand-alone PLC projects

Due to the decoupling from the System Manager, it is not possible to calculate the stack usage for a stand-alone PLC project.

Recursive method call

Within the associated implementation, a method can call itself: either directly with the help of the THIS pointer or with the help of a local variable for the assigned function block.

Notice

Machine downtime due to possible stack overflow

Unexpectedly deep recursions can lead to a stack overflow and thus to a machine downtime.

  • Recursions are primarily used for processing recursive data types such as linked lists.

Sample: Calculation of the factorial

Within the function block FB_Factorial, the factorial of a number is calculated in different ways. The various calculations are each carried out using a separate method.

When the project is rebuilt, only the method Recursive generates the warning C0298.

Structure ST_FactorialResult for saving the values:

// Contains the data of the factorial calculation of nNumber.
TYPE ST_FactorialResult :
STRUCT
    nNumber    : USINT;
    nIterative : UDINT;
    nRecursive : UDINT;
    nPragmaed  : UDINT;
END_STRUCT
END_TYPE

Function block FB_Factorial for calculating the factorial:

// Factorial calculation in different ways
FUNCTION_BLOCK FB_Factorial
VAR
     nNumberIterative    : UINT;
END_VAR

Property nNr including set function, for transferring the parameter for the iterative method:

{attribute 'monitoring' := 'variable'}
PROPERTY nNr : UINT
nNumberIterative := nNr;

Iterative calculation method:

// Iterative calculation
METHOD PUBLIC Iterative : UDINT
VAR
    nCnt : UINT;
END_VAR
Iterative := 1;
 
IF nNumberIterative > 1 AND nNumberIterative <= 12 THEN
    FOR nCnt := 1 TO nNumberIterative DO
        Iterative := Iterative * nCnt;
    END_FOR;
    RETURN;
ELSE
    RETURN;
END_IF

Recursive calculation method with attribute for suppressing warning C0298:

// Recursive calculation with suppressed warning
{attribute 'estimated-stack-usage' := '200'}
METHOD PUBLIC Pragmaed : UDINT
VAR_INPUT
    nNumber : USINT;
END_VAR
Pragmaed := 1;
 
IF nNumber > 1 AND nNumber <= 12 THEN
    Pragmaed := nNumber * THIS^.Pragmaed(nNumber := (nNumber - 1));
    RETURN;
ELSE
    RETURN;
END_IF

The pragma parameter for specifying the estimated stack size requirement is set to 200 bytes as an example. This is based on the following calculation:

Stack usage per method call:
Return value UDINT + input parameter USINT + method pointer = 4 bytes + 1 byte + 8 bytes = 13 bytes

Stack usage with a maximum of 12 calls:
12 * 13 bytes = 156 bytes, rounded up to 200 bytes

Recursive calculation method:

// Recursive calculation
METHOD PUBLIC Recursive : UDINT
VAR_INPUT
    nNumber : USINT;
END_VAR
Recursive := 1;
IF nNumber > 1 AND nNumber <= 12 THEN
    Recursive := nNumber * THIS^.Recursive(nNumber := (nNumber - 1) );
    RETURN;
ELSE
    RETURN;
END_IF

Main program MAIN:

PROGRAM MAIN
VAR
    fbFactorial : FB_Factorial;
    stFactorial : ST_FactorialResult := (nNumber := 9);
END_VAR
// Iterativ
fbFactorial.nNr        := stFactorial.nNumber;
stFactorial.nIterative := fbFactorial.Iterative();

// Recursive
stFactorial.nRecursive := fbFactorial.Recursive(nNumber := stFactorial.nNumber);

// Pragmaed
stFactorial.nPragmaed  := fbFactorial.Pragmaed(nNumber := stFactorial.nNumber);