#5987·fprime

Svc::RateGroupDriver: rollover product overflows with many rate groups, LCM would not

Author: ephraim71Created Sep 16, 2026Updated Sep 16, 2026
F´ Version v4.3.0
Affected Component Svc::RateGroupDriver

Feature Description

This commit added a check to ensure we don't overflow when calculating m_rollover. (cc @LeStarch )

The check itself is great. However, the way m_rollover is calculated can be improved considerably. Instead of multiplying every divisor together, we can take the LCM.

As an example, consider a 1 ms base tick with rate groups at 1 ms, 2 ms, 4 ms, 5 ms, 10 ms, 20 ms, 50 ms, 100 ms, 200 ms, 500 ms, 1 s, 2 s, 5 s and 10 s:

cpp
Svc::RateGroupDriver::DividerSet rateGroupDivisorsSet{
    {{1,0},{2,0},{4,0},{5,0},{10,0},{20,0},{50,0},
     {100,0},{200,0},{500,0},{1000,0},{2000,0},{5000,0},{10000,0}}};

Rollover as a product comes to:

1*2*4*5*10*20*50*100*200*500*1000*2000*5000*10000 = 400,000,000,000,000,000,000,000,000 (4.0e26) which is far past the 64-bit FwSizeType max of 18,446,744,073,709,551,615 (1.8e19). The Assert asserts (rightfully).

The LCM of the same set comes to: 10000

Right now, one is forced to split this into two rate group drivers (a fast driver whose last output clocks a second, slower driver) purely because the rollover product hits the limit. With this change, a single rate group driver would be enough.

Suggested change, will send in a PR separately.

  // rollover value should be the LCM of all dividers to make sure integer rollover doesn't jump cycles
  if (dividerSet.dividers[entry].divisor != 0) {
      const FwSizeType divisor = dividerSet.dividers[entry].divisor;
      const FwSizeType reduced = this->m_rollover / gcd(this->m_rollover, divisor);
      // Ensure that rollover will not overflow
      FW_ASSERT((std::numeric_limits<FwSizeType>::max() / divisor) >= reduced,
                static_cast<FwAssertArgType>(reduced),
                static_cast<FwAssertArgType>(divisor));
      this->m_rollover = reduced * divisor;
  }

The overflow assert is kept exactly as-is, just applied to the reduced value.

Rationale

  1. It removes an artificial limit on the number of rate groups. The product grows multiplicatively with every port, so the current scheme runs out of range after a modest number of groups even though the schedule itself is completely reasonable. In the example above, 14 groups with ordinary periods are already unschedulable. The LCM of harmonically related periods, which is what a real rate schedule almost always looks like, stays small no matter how many groups you add.
  2. There will be NO runtime cost for this change. The calculation happens once in configure().
  3. It makes the new overflow assert much harder to hit.