Final yield only tells you whether a unit passed at the end of the line. Rolled Throughput Yield (RTY) tells you the probability that a unit passes every step without rework or retesting. It's the metric that exposes hidden factory losses.
Why RTY Matters More Than Final Yield
Final yield counts a unit as "pass" even if it failed three times before someone reworked it. RTY doesn't. It multiplies the first-pass yield of every process step together:
RTY = FPY₁ x FPY₂ x ... x FPYₙ
Consider a five-step process where each step has 95% FPY. Final yield might show 99% because rework catches most failures. But RTY tells the real story:
RTY = 0.95 x 0.95 x 0.95 x 0.95 x 0.95 = 77.4%
That means nearly 1 in 4 units needed rework somewhere. Each rework event costs time, labor, and materials that final yield hides completely.
RTY Interpretation
| RTY Range | What It Means |
|---|---|
| > 95% | Process is well-controlled, minimal hidden rework |
| 85-95% | Some steps need attention, review failure Pareto |
| 70-85% | Significant hidden losses, prioritize worst FPY steps |
| < 70% | Process is unstable, rework costs are likely substantial |
The power of RTY is that it pinpoints which step drags down the whole line. If Step 3 has 88% FPY while others sit at 98%, you know exactly where to focus.
Structuring Your Tests for RTY Tracking
Each process step should be a separate test procedure in TofuPilot. This gives you per-step FPY that feeds directly into RTY calculations. Here's a multi-phase OpenHTF test that represents a PCB assembly process with distinct steps:
# Step 1: SMT component placement verification
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
@htf.measures(
htf.Measurement("placement_offset_x")
.in_range(minimum=-0.05, maximum=0.05)
.with_units(units.MILLIMETRE),
htf.Measurement("placement_offset_y")
.in_range(minimum=-0.05, maximum=0.05)
.with_units(units.MILLIMETRE),
htf.Measurement("component_rotation")
.in_range(minimum=-2.0, maximum=2.0),
)
def smt_placement_check(test):
test.measurements.placement_offset_x = 0.02
test.measurements.placement_offset_y = -0.01
test.measurements.component_rotation = 0.5
def main():
test = htf.Test(smt_placement_check)
with TofuPilot(test):
test.execute(test_start=lambda: "PCB-2024-0042")
if __name__ == "__main__":
main()# Step 2: Reflow soldering thermal profile check
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
@htf.measures(
htf.Measurement("peak_temperature")
.in_range(minimum=235, maximum=250)
.with_units(units.DEGREE_CELSIUS),
htf.Measurement("time_above_liquidus")
.in_range(minimum=60, maximum=120),
htf.Measurement("cooling_rate")
.in_range(maximum=3.0),
)
def reflow_profile_check(test):
test.measurements.peak_temperature = 242
test.measurements.time_above_liquidus = 85
test.measurements.cooling_rate = 2.1
def main():
test = htf.Test(reflow_profile_check)
with TofuPilot(test):
test.execute(test_start=lambda: "PCB-2024-0042")
if __name__ == "__main__":
main()# Step 3: Functional test after assembly
import openhtf as htf
from openhtf.util import units
from tofupilot.openhtf import TofuPilot
@htf.measures(
htf.Measurement("supply_voltage")
.in_range(minimum=4.9, maximum=5.1)
.with_units(units.VOLT),
htf.Measurement("quiescent_current")
.in_range(maximum=15.0)
.with_units(units.AMPERE),
htf.Measurement("clock_frequency")
.in_range(minimum=7.99, maximum=8.01)
.with_units(units.HERTZ),
)
def functional_check(test):
test.measurements.supply_voltage = 5.02
test.measurements.quiescent_current = 0.0113
test.measurements.clock_frequency = 8003000
def main():
test = htf.Test(functional_check)
with TofuPilot(test):
test.execute(test_start=lambda: "PCB-2024-0042")
if __name__ == "__main__":
main()Each test procedure runs independently and uploads results to TofuPilot with the same serial number. TofuPilot links all three procedures to the same unit, giving you a complete process history.
Tracking RTY in TofuPilot
Once your test procedures upload results, TofuPilot's dashboard gives you what you need to compute and track RTY:
- Per-procedure FPY trends show first-pass yield for each process step over time. You can spot degradation in a single step before it affects final yield.
- Failure Pareto charts break down which measurements fail most often within each step, so you can prioritize root cause analysis.
- Unit history shows the complete journey of any serial number across all procedures, including retests. This is the ground truth for whether a unit passed first time at every step.
You don't need to build RTY calculations in Python. Structure your tests as separate procedures, upload results with consistent serial numbers, and TofuPilot gives you the per-step visibility that makes RTY actionable.
Key Practices for Accurate RTY
-
One procedure per process step. Don't combine SMT placement and reflow soldering into a single test. Separate procedures give you separate FPY numbers.
-
Use consistent serial numbers. Every procedure for the same unit must reference the same serial number. This is how TofuPilot links the full process chain.
-
Don't filter out retests. Upload every test execution, including failures and retests. RTY only works when you have the complete picture.
-
Set measurement limits in code. Limits defined in OpenHTF flow directly into TofuPilot's analytics. Don't rely on post-hoc analysis to determine pass/fail.