Engineer Computer System study materials
Engineer Computer System study resources

Fixed-Point Overflow in a Servo Position Path

Engineer Computer System · Put your knowledge to work in context.

All case studies

Fixed-Point Overflow in a Servo Position Path

A three-axis motion controller runs a 10 kHz servo loop on a 32-bit MCU with no FPU. Position is kept in signed Q16.16 fixed point (1 LSB = 2⁻¹⁶ mm). Each cycle the controller forms err = target − actual in Q16.16 and computes a proportional velocity command with gain Kp = 50 (dimensionless, stored as a plain integer): int32_t cmd = err * Kp;. Axis travel is 1200 mm. During a merge, one path kept the original (int32_t)(((int64_t)err * Kp)) form while a refactored copy used the plain int32 multiply. Field symptom: on full-travel homing moves the drive intermittently lurches and trips a following-error fault; measured following error peaks near 700 mm mid-move on a full-travel move but only about 60 mm on 100 mm moves, which never fault. The fault threshold is 5 mm. A junior engineer proposes raising the threshold to 50 mm so the machine stops tripping.

1. Compute the error magnitude at which the plain int32 multiply overflows, and test that number against the field reports.

Read the worked answer

The raw error in Q16.16 is mm × 65536. Full travel is 1200 mm, so err_raw reaches 78,643,200 - comfortably inside int32 on its own. The multiply is the problem: 78,643,200 × 50 = 3,932,160,000, which exceeds INT32_MAX of 2,147,483,647.

The overflow boundary is 2³¹ / 50 = 42,949,673 raw units, i.e. 42,949,673 / 65536 ≈ 655.4 mm of following error. Errors below 655 mm are safe; above that, the product wraps. At the full-travel peak of 700 mm the wrapped value is 3,932,160,000 − 2³² = −362,807,296 raw, about −5536 mm of equivalent command - a full-scale command in the wrong direction, which is exactly the lurch described.

This matches every reported symptom: 100 mm moves peak at 60 mm of error, three orders of magnitude below the boundary, so they can never fault; full-travel moves cross 655 mm only near mid-move, when the commanded profile has outrun the actual position, which is why the fault appears mid-move rather than at the start. The threshold value 655.4 mm is the check that turns a plausible story into a diagnosis.

2. The junior engineer wants the following-error threshold raised to 50 mm. Evaluate that proposal and state what it would and would not achieve.

Read the worked answer

Raising the threshold from 5 mm to 50 mm would not stop the faults, because the events are at roughly 700 mm of error - more than an order of magnitude beyond the proposed limit. So the change fails on its own terms; the machine keeps tripping.

Worse, it is aimed at the wrong layer. The 5 mm threshold is a protection function: it detects that the axis is not following its command and removes torque before the mechanism or the workpiece is damaged. Raising it to accommodate a software defect enlarges the window in which a genuinely uncontrolled axis can travel. The 50 mm figure is also unreachable in normal operation, which means the protection would only ever trip after something has already gone badly wrong.

The proposal does contain a correct instinct - the threshold is not the cause - but the action is backwards. The correct response is: fix the multiply (int64 intermediate cast before multiplication), then confirm that observed error never approaches 655 mm again. If a review shows other paths with the same pattern, treat them as the real defect class. Changing a safety limit to hide a bug converts a diagnostic failure into an undetected one, and is the kind of change that must never be made without a hazard analysis and recorded justification.

3. Specify the fix and the verification that would convince you the defect class is closed.

Read the worked answer

The immediate fix is mechanical: both paths use (int32_t)(((int64_t)err * Kp)), so the intermediate is 64-bit and the result is representable for the entire 1200 mm envelope. Confirm the cast placement - (int64_t)(err * Kp) casts after the overflow already happened and fixes nothing.

Hardening: add a saturating helper for the command output, and compile safety-relevant translation units with -ftrapv or UBSan in a test build so signed overflow aborts rather than wrapping silently. Unit tests should include err = ±1200 mm, and boundary values at ±655.36 mm and one LSB either side.

Verification must be empirical as well as unit-level. Run full-travel moves on all three axes at maximum velocity and acceleration, logging peak following error and the raw command; confirm the command never changes sign abnormally. Repeat with the fault threshold temporarily lowered to its real 5 mm value so any residual excursion is caught. Static analysis (MISRA-C rule 10.x on essential type, or a -Wconversion sweep) should be run over the whole motion library to find sibling instances of int32 multiplication with Q16.16 operands, because one merged copy implies others exist. Record the boundary calculation in the test plan so a future gain change forces the check to be redone: at Kp = 50 the limit is 655 mm, but at Kp = 200 it drops to 164 mm and the envelope is no longer safe.