diff --git a/src/main.rs b/src/main.rs index 0c6478c..1f24c22 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2387,6 +2387,7 @@ async fn rfm_async_loop( let mut phase_st = OnlineStats::new("fresh_phase_us", 200, -1_500_000, 1_500_000); let mut phase_est: i64 = 0; // estimated TwinCAT frame boundary phase (ns within 1ms) let mut wphase_st = OnlineStats::new("write_phase_us", 200, -1_500_000, 1_500_000); + let mut j2cx_pending: Option = None; while !STOP.load(Ordering::Relaxed) { if duration_sec > 0 && start.elapsed().as_secs() >= duration_sec { @@ -2549,16 +2550,26 @@ async fn rfm_async_loop( while (rt::now_ns() as i64) < target { core::hint::spin_loop(); } wphase_st.push(((rt::now_ns() % 1_000_000) / 1000) as i64); } - let tx_ok = ethercrab::Command::fpwr(station, SM_TX_ADDR) - .send(maindevice, &slot[..PAYLOAD_LEN]) - .await - .is_ok(); - n_fpwr += 1; - if tx_ok { - j2cx_st.push(t_detect.elapsed().as_nanos() as i64); - } else { - n_fpwr_err += 1; - } + // PIPELINE v2: mirror the fprd's working pattern - a short + // yield-kick lets the executor run the TX task (frame goes on + // the wire), then store for poll-based completion at reap. + let mut f: FpwrFut<'_> = Box::pin( + ethercrab::Command::fpwr(station, SM_TX_ADDR).send(maindevice, &slot[..PAYLOAD_LEN]), + ); + let t_kick = Instant::now(); + let _ = futures_lite::future::or( + async { f.as_mut().await.is_ok() }, + async { + while t_kick.elapsed().as_micros() < 20 { + futures_lite::future::yield_now().await; + } + false + }, + ) + .await; + pending_fpwr = Some(f); + j2cx_pending = Some(t_detect); + n_fpwr += 1; // issued; result counted at reap } else { n_ovf += 1; // wire busy; newer tx_latest will go out after reap } @@ -2635,6 +2646,9 @@ async fn rfm_async_loop( if res.is_err() { n_fpwr_err += 1; } + if let Some(td) = j2cx_pending.take() { + j2cx_st.push(td.elapsed().as_nanos() as i64); + } } } { let n = Instant::now(); seg_fpwr_us = n.duration_since(cursor).as_micros() as u64; cursor = n; }