CODESYS Task Time: Measure ADD/AND/EQ Execution on COM600

Erik Lindqvist8 min read
ABBOther TopicTutorial / How-to
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

The CODESYS-derived IEC 61131-3 logic engine inside an ABB COM600 gateway gives you operators such as ADD, AND and EQ, but no data sheet publishes how many microseconds each one costs. That is not an omission. Per-instruction time is a property of the generated code, the CPU, the memory path to the operands, and the other tasks competing for the processor — it is measured on the target, not looked up in a table. This note gives a repeatable differential-loop procedure that yields a defensible number on your own unit, plus the guardrails that keep the benchmark from tripping a watchdog or disturbing an in-service gateway.

Why No Instruction-Time Table Exists

Two identical ADD statements in the same project can differ by an order of magnitude in cost depending on operand type and where the operands live. The variables below all move the result, so any single published figure would be wrong for most projects.

Factor Effect on measured time
Operand data type BOOL/INT/DINT integer operations map to few machine instructions. REAL/LREAL arithmetic is far more expensive if the CPU has no hardware floating-point unit and the runtime emulates it in software.
Operand location Local stack variables are cheapest. Globals, retains, and variables that are mapped into a process image or resolved through the communication/data-model layer add address resolution and memory traffic.
Code generation Constant folding, common-subexpression elimination and dead-code removal can delete the very statement you are trying to time if its result is never consumed.
Preemption Communication stacks, protocol handling, I/O servicing and visualization run as tasks and interrupt handlers you cannot see in the task list. They inflate max and average readings.
Tool and firmware build Any change to the logic tool build or device firmware can change code generation. Record both with every result set.
Terminology check: "task time" is ambiguous in a CODESYS-style task configuration. One field is the configured interval of a cyclic task; another is the measured execution duration of the task body. Before trusting any number, double the loop count and confirm the field you are reading doubles. Execution duration scales with workload; a configured interval does not.

Configure a Dedicated Benchmark Task

Never benchmark inside a production POU. Isolate the code under test so the reported cycle value contains only the benchmark body plus fixed task overhead, which the differential method then cancels.

  1. Open Resources → Task configuration and add a new task used only for benchmarking.
  2. Set the task type to freewheeling. A freewheeling task restarts as soon as the previous pass ends, so the monitored cycle value tracks real execution. A cyclic task with an interval longer than the execution time can mask the value you want.
  3. Assign one program call: the benchmark POU. No other POUs in this task.
  4. Give the task the lowest priority (highest priority number) in the configuration so the benchmark can never starve communication, protocol or visualization work.
  5. Disable the task watchdog, or raise it well above the expected worst-case loop duration. A 10 000-iteration loop will exceed a typical watchdog setting.
  6. Download, start, and switch the task configuration view to online monitoring to read the cycle values.
Do not benchmark an in-service gateway. A COM600 carries station communication and client visualization. A long benchmark loop at the wrong priority, or a watchdog trip, can stop the application and take the station data flow with it. Use a spare unit, a bench unit, or a scheduled maintenance window.

Differential Loop Method

A single measurement is useless because it contains task-switch overhead, monitoring overhead and the FOR-loop control cost. Take two measurements that differ in exactly one variable and subtract. Two independent forms:

Form A - scale the iteration count, fixed body:
  t_op = ( T(N2) - T(N1) ) / (N2 - N1)

Form B - scale the body, fixed iteration count (preferred):
  t_op = ( T(k+1 ops, N) - T(k ops, N) ) / N

Form B cancels loop-control overhead exactly, because both runs execute the same number of loop iterations. Form A cancels task and call overhead. Run both; they should agree within your measurement noise.

Choose N so the measured difference is at least 100 x the resolution of the cycle-time display. If the monitor shows milliseconds, N = 1000 is usually too small — go to N = 10000 or higher and raise the watchdog accordingly.

PROGRAM PRG_Bench
VAR
    i     : DINT;
    N     : DINT := 1000;   (* iterations - change online between runs *)
    ops   : INT  := 1;      (* 0 = control loop, 1 or 2 = ops in body *)
    a, b  : DINT;
    acc   : DINT;
END_VAR
VAR_GLOBAL (* declared in a GVL, read by another POU to defeat dead-code removal *)
    gAcc  : DINT;
END_VAR

a := a + 1;                 (* operands vary, so no constant folding *)
b := 3;
acc := 0;

CASE ops OF
0:  FOR i := 1 TO N DO
        acc := acc;                    (* control loop: loop overhead only *)
    END_FOR;
1:  FOR i := 1 TO N DO
        acc := acc + (a + b);          (* 1 x ADD under test *)
    END_FOR;
2:  FOR i := 1 TO N DO
        acc := acc + (a + b) + (a + b);(* 2 x ADD under test *)
    END_FOR;
END_CASE;

gAcc := acc;                (* sink: result must be consumed somewhere *)

Variants for the other operators — keep the same loop skeleton and only swap the body:

(* AND on BOOL *)      xAcc := xAcc AND (xA AND xB);
(* AND on DINT bits *) acc  := acc + (a AND b);
(* EQ comparison *)    xEq  := xEq XOR (a = b);
(* REAL add - expect a very different result *)
                       rAcc := rAcc + (rA + rB);

The XOR and accumulate patterns exist purely to force the compiler to keep the operation and to keep the result live all the way to gAcc.

Read, Verify and Interpret the Numbers

Log the monitored values for each run. Use the minimum cycle value as the cleanest estimate of uncontended execution, the maximum as the preempted worst case, and the average for cycle budgeting.

Run N Ops/iteration Min cycle (example only) Derived
Control loop 1000 0 950 us loop overhead ~0.95 us/iter
1 x ADD 1000 1 1300 us
2 x ADD 1000 2 1650 us t_add = (1650-1300)/1000 = 0.35 us
1 x ADD 10000 1 13000 us Form A cross-check, slope linear

The figures above are placeholders that demonstrate the arithmetic only. Substitute your own readings; do not carry these numbers into a design.

Verification checklist before you trust a result:

  1. Linearity: measure at N = 100, 1000, 10 000. Plotted time versus N must be a straight line. A knee indicates watchdog interference, memory effects or a task-scheduling artefact — investigate before using the slope.
  2. Body scaling: going from 1 op to 2 ops must add the same increment as going from 2 to 3. If it does not, the compiler is optimizing part of the body away.
  3. Field identity: double N and confirm the monitored value doubles, proving you are reading execution duration and not a configured interval.
  4. Independent clock cross-check: on a freewheeling task, note the cycle counter and wall-clock time at two points. Average period = elapsed time / cycle delta. It should match the monitored average.
  5. Loaded versus unloaded: repeat the whole set with representative communication traffic and visualization clients connected. The spread between min and max under load is the jitter your design must tolerate.

Convert the Result Into a Cycle-Time Budget

The per-operation number is only useful as a budget input. For a task that must complete within an interval T:

N_max = ( T * U - T_fixed ) / t_op

  T       = task interval or required response time
  U       = fraction of that interval you allow the logic task to consume
  T_fixed = measured overhead of the task with no logic in it
  t_op    = per-operation time from the differential measurement

Keep U conservatively below 1 so communication, protocol and visualization tasks retain headroom; the exact figure is an engineering decision, and the loaded max/min spread from the verification step tells you how much margin the platform actually needs. Size against the maximum observed value, not the average, whenever the logic feeds a time-critical output.

Re-measure whenever any of the following changes: device firmware, logic tool build, data types used in the hot path, task priorities or intervals, the number of connected visualization clients, or the protocol/communication load. A per-instruction time captured on one build is not portable to another.

Restore the original watchdog settings and delete or permanently disable the benchmark task before returning the device to service. A freewheeling benchmark task left running consumes every spare CPU cycle by design.

FAQ

Does ABB or CODESYS publish execution times for ADD, AND and EQ?

No per-instruction timing table is published for the logic engine in COM600, because the cost depends on data type, operand storage, generated code and competing tasks. Measure it on your own unit with a differential loop and record the firmware and tool build alongside the result.

Why use a freewheeling task instead of a cyclic task for benchmarking?

A freewheeling task restarts immediately after each pass, so the monitored cycle value follows the actual execution duration. With a cyclic task whose interval exceeds the execution time, you can end up reading the configured interval instead of the work you are trying to time.

How many loop iterations should I use?

Pick N so the difference between two runs is at least 100 times the resolution of the cycle-time display — typically 1000 for microsecond resolution and 10 000 or more for millisecond resolution. Always take at least two values of N and confirm the response is linear.

Will a 10 000-iteration benchmark loop trip the task watchdog?

Yes, that is a common failure. Disable or substantially raise the watchdog on the benchmark task only, keep the task at the lowest priority, and restore the original settings before the device returns to service.

Why does my measured time change between runs on the same device?

Communication stacks, protocol handling, I/O servicing and visualization run as higher-priority tasks and interrupt handlers that preempt your benchmark. Use the minimum value for uncontended instruction cost and the maximum under representative load for worst-case cycle budgeting.

Back to blog