Find a soft PLC runtime cycle time limit with a load sweep

10 min read · Published November 4, 2026

The limit is the shortest cycle time at which the runtime's maximum scan time stays under the margin you choose, usually half the cycle. Find it by sweeping one workload knob across cycle times, logging maximum scan time and overrun count from the runtime's task monitor, with CPU percentage from top as a cross-check only. The article includes a worked sweep, a soak test and the mistakes that void the result.

A soft PLC runtime on a small single-board computer has a hard edge, and an averaged CPU percentage will not find it. A board can look lightly loaded in top and still miss cycles. The bench method below finds that edge before you deploy: one workload knob, one sweep across cycle times, maximum scan time and overrun count read from the runtime's own task monitor, then a soak run that checks the number survives heat and background load. The method sizes any small controller you plan to put in a panel or a test rig. The limit belongs to the controller; the displays that show status and notices on the floor are sized separately in our shop-floor screens range.

What is the cycle time limit of a soft PLC runtime?

The limit is the shortest cycle time at which the worst-case scan time stays under the margin you pick, commonly half the cycle. At a 10 ms cycle that means a maximum scan time of 5.0 ms or less, measured in the runtime's task monitor.

The margin is a design choice; no standard sets it. Half the cycle is the usual pick because a jitter spike as large as the whole scan, plus the operating system's wake-up and the I/O update, still lands inside the period. Write the margin beside every limit you publish. A limit without its margin is a number nobody can reproduce.

The budget has three parts: your program's scan time, the operating system's wake-up latency, and communications overhead. Task documentation for cyclic runtimes is explicit that a continuous task restarts as soon as it completes a full scan, and that communications add scan time to the task; read your runtime's task documentation for the exact charging model it uses. Scan time is your program; wake-up latency and communications are the rest. Together they explain how a runtime overruns a cycle while executing less code than the period seems to allow.

Overrun is a counted event. Runtimes publish per-task statistics in the task monitor: last, average and maximum scan time, plus an overrun or missed-cycle count. Read the exact field names in your runtime's documentation and log those fields, since the limit is written from them.

Why does CPU percentage in top hide the limit?

A cyclic task sleeps between periods, so top reports an average that smooths out the single bad cycle. The same board can read 30% CPU and still post overruns, because overruns are per-cycle events and an averaged percentage never shows them.

Duty cycle and worst case are different questions. The percentage says nothing about the longest single scan; the overrun counter is what moves when that longest scan crosses the period.

Two effects distort the reading further. Jitter has two layers, the operating system's wake-up latency and the variation in your code's execution time, and the sweep has to separate them; that is what the cyclictest baseline in the steps below is for. And a compiler folds arithmetic on constants at build time, so a benchmark written with constant operands measures the optimizer instead of the board.

Use the CPU figure for triage only. Near 100% on one core with overruns every cycle means the load exceeds the cycle budget, while a low percentage with overruns means scheduling or thermal trouble. Both readings send you back to the task monitor.

What do you need before the sweep starts?

You need four things: scan-time statistics from the runtime's task monitor, a load program with a single workload knob, a locked clock under the performance governor, and a temperature and throttle-flag check before and after every run.

Gather the rest before you power anything on:

  • The task monitor fields for last, average and maximum scan time and the overrun count, found in your runtime's documentation.
  • The load program from the next section.
  • cyclictest, from your distribution's rt-tests package, for the OS latency baseline.
  • Board model and OS build written down, and cooling mounted exactly as it will be deployed.
  • Throttle flags and temperature reads. On the common single-board firmware CLI these are vcgencmd get_throttled and vcgencmd measure_temp; a report of 0 means no throttling of any kind has occurred, and bit 0 set means under-voltage was detected at some point in the board's life.
  • The production background workload: network stack, logging, HMI or OPC connections left active. The same mechanism that stalls an image cycle while data logging runs in a loop competes with a cyclic task for CPU time, so a clean idle board predicts the wrong limit.
  • A separate workstation for the IDE. If you are speccing that machine too, what laptop specs PLC programming needs covers it.

How do you build the load program for the sweep?

Drive every operand from a variable that changes each cycle, run one ADD, MUL and DIV chain per loop iteration, and give the chain a single count n so you can scale the work without touching the cycle time.

The compiler is the opponent: x := 2 + 3; costs nothing after the build. The loop below uses illustrative variable names of my own; every operand changes, so the work survives compilation:

VAR
  n   : DINT := 100;  // workload knob: the sweep changes this
  i   : DINT;
  acc : DINT := 1;    // operand that changes every cycle
  r   : REAL := 1.0;  // same chain in floating point
END_VAR
FOR i := 1 TO n DO
  acc := (acc + 7) MOD 1000003;   // ADD
  acc := (acc * 31) MOD 1000003;  // MUL
  r := r / 7.0 + 0.5;             // DIV
END_FOR

Build one variant per data type the production program uses, at minimum DINT and REAL. Integer and floating-point chains cost different amounts, so a single-type benchmark predicts the wrong program. The test that proves the benchmark measures anything is proportionality: double n, and the average scan time should roughly double. If it stays flat, the compiler folded the chain and the sweep numbers are worthless.

How do you run the sweep step by step?

Start with a long cycle time and a small workload, raise the workload until the line is known, then shorten the cycle time and repeat. Log average and maximum scan time, overrun count and temperature at every point.

  1. Lock the platform. Set the governor to performance with echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor, write down the board model and OS build, mount the cooling as deployed, and read the throttle flags and temperature before the first run.
  2. Baseline the OS with the runtime stopped: cyclictest -m -p 90 -i 1000 -l 100000 locks memory, runs at FIFO priority 90 with a 1,000 µs interval for 100,000 loops. Record the maximum latency; that is the wake-up floor your scan-time jitter sits on.
  3. Start long and light: a 20 ms cycle with the workload knob at its smallest step. Confirm zero overruns, then log average and maximum scan time.
  4. Raise the workload at that fixed cycle in equal steps. Maximum scan time should climb in a straight line; where it bends upward, something other than your code is competing for the CPU.
  5. Shorten the cycle in halving steps, 10 ms, 5 ms, 2.5 ms, and repeat the workload ladder at each one. Bracket the first overrun: one point with a clean overrun counter, one with it moving.
  6. Repeat the bracket with the runtime thread pinned to a core (taskset -cp 2 <pid>) and, where the runtime allows it, at a real-time policy (chrt -f -p 90 <pid>). Record both configurations; the pinned one is what a production board runs.
  7. After each run, read top -H -d 1 for the runtime thread's per-core percentage as a cross-check, then the throttle flags and temperature again. A report other than 0, or bit 0 set, voids the run: fix power or cooling and sweep again.

What does a worked example of a cycle time limit look like?

At a 10 ms cycle with a 50% margin, the cap is a 5.0 ms maximum scan time, and the sweep fit below puts the workload limit at 500 loop iterations, backed off to 450 for the soak run.

The numbers are illustrative bench values; your slope and intercept will differ. Three workload points and one overrun bracket:

Workload n (loops per cycle) Average scan time Maximum scan time Overruns
200 1.9 ms 2.3 ms 0
400 3.4 ms 4.1 ms 0
600 4.9 ms 5.9 ms 0
1,100 8.7 ms 10.4 ms counter moving

Fit the maximum-scan line through the first three points. Slope: (5.9 - 2.3) ms / (600 - 200) = 3.6 ms / 400 = 9 µs per loop. Intercept: 2.3 ms - (9 µs x 200) = 2.3 ms - 1.8 ms = 0.5 ms. Worst case is therefore max(n) = 0.5 ms + (9 µs x n). At the 5.0 ms cap: n = (5.0 - 0.5) ms / 9 µs = 500 loops.

Watch the CPU cross-check while the margin disappears. At n = 600 the thread's duty is 4.9 ms / 10 ms = 49%, an easy-looking top, and the maximum already sits 0.9 ms past the cap. The two readings agree only at n = 1,100, where top shows 8.7 ms / 10 ms = 87% and the counter finally moves.

The same fit answers every cycle time, with cap = half the period: at 5 ms, n = (2.5 - 0.5) ms / 9 µs = 222 loops; at 20 ms, n = (10 - 0.5) ms / 9 µs = 1,055 loops.

Decision: at a 10 ms cycle this board runs 500 loops by the fit, and the soak runs at 450 loops, where the predicted maximum is 0.5 ms + (9 µs x 450) = 4.55 ms and the measured maximum came in at 4.6 ms. Publish the limit as: 10 ms cycle, 450 loops, maximum scan time 4.6 ms, margin 50%, performance governor, board model and OS build as recorded.

How do you check the limit holds before you deploy?

Run the chosen operating point for hours with the enclosure closed and the production software active. If the maximum scan time at hour 4 matches hour 1 and the throttle flags still read 0, the limit was measured on a board you can ship.

A short sweep finds the edge on a cold board; the soak run validates it in the configuration that ships.

  • Soak the operating point for 4 hours minimum, enclosure closed, with the network stack, logging and any HMI or OPC connections active.
  • Compare the final maximum scan time to the first hour's. Growth between them means the board was still heating when you measured, or a background task is creeping.
  • Read the throttle flags at the end. The report must still be 0; bit 0 set at any point means under-voltage happened during the soak, usually a supply or cable problem, and the run is void.
  • Stress the operating system once: bulk file writes, a network transfer, an active SSH session. If the maximum jumps, the runtime thread shares a core with other work; fix scheduling and pinning and sweep again.

Publish the limit only from a soaked, stressed configuration. Keep one line in the design file beside it: if the production program ever needs more than the measured limit, change the cycle time, the workload or the hardware. A runtime that overruns on a machine that will run is a safety defect in the making; fix the budget before the panel ships.

If you want the numbers visible on the floor afterward, live factory data display: signage or dashboard? covers which presentation fits a shop floor.

Which mistakes void the measurement?

Seven mistakes account for most worthless limits: reading only top, benchmarking constants, sweeping a cold board, ignoring the power supply, leaving the default governor on, skipping the cyclictest baseline and sweeping with debug attached.

  • Reading only top. It hides the worst cycle; read the task monitor first and use top as the cross-check it is.
  • Benchmarking constants. The compiler folds them; the proportionality test in the load-program section catches it.
  • Sweeping a cold board. Throttling appears only after heat soak, so a limit measured in the first minutes belongs to a board that no longer exists by the afternoon.
  • Ignoring the power supply. Under-voltage lowers the clock, and the throttle bits keep the history, so a weak supply or a long cable reads as a mysterious limit.
  • Leaving the default governor on. Frequency scaling makes identical runs differ; lock performance and write it down.
  • Skipping the cyclictest baseline. Without it you have no way to split kernel wake-up latency from your program's cost.
  • Sweeping with debug attached. Watch windows and traces add load the production system will never carry; take the final numbers in the mode you deploy.

Recommendation: define the limit as the shortest cycle time whose measured maximum scan time stays at or below half that cycle, record the margin, governor, cooling, thread pinning, board model and OS build beside the number, and hold the deployed operating point about 10% under the fitted cap. If a soak or stress run moves the maximum, fix scheduling, cooling or power and sweep again before the board ships.

Questions

How do you measure period jitter outside the runtime?

Toggle one digital output at the top of every cycle and capture the edges on an oscilloscope or logic analyzer. Take the maximum period minus the minimum across the run; that figure includes the I/O path the real rig uses, which the task monitor never sees. Compare it against the runtime's maximum scan time and the cyclictest baseline: wake-up latency belongs in the margin, and the workload number carries execution cost only.

What do you do when the runtime shows only an average scan time?

Log the average at every sweep point and fit the line as usual, then find the worst case with the output-toggle method. Most runtimes keep an overrun counter even where the maximum is hidden; treat the first overrun as the hard edge and back off from it. If neither figure exists, the average fit plus a longer soak run is the only honest limit you can publish.

Does a measured limit transfer to a different board or software build?

It stays on the bench where you measured it. The slope and intercept belong to one board model, one OS build, one governor setting and one cooling arrangement. A kernel or runtime update, a different supply or a removed heatsink moves both numbers. Re-run the sweep after any of those changes, and write the board model and OS build beside every limit you publish.

How many sweep points does a trustworthy fit need?

Three workload points at one cycle time define the line, and a fourth halfway between two of them confirms it is straight. Sweep cycle times in halving steps from twice your target down to the shortest your I/O update allows, and bracket the first overrun with one clean point and one where the counter moves.

Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.

Keep reading

Build your signAsk for a quote