rfm-bridge: fix FPWR gating (was inside Mode::Ticker so bridge never wrote SM2), single-point f(x) on D2000, dumb reflector echo, REP budget 1.5ms

This commit is contained in:
Tony Cao 2026-09-24 15:12:43 +08:00
parent b92484a86c
commit 3f29a86239

View File

@ -1116,8 +1116,11 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
el6695_rt::rfm::barrier(); el6695_rt::rfm::barrier();
mb.magic = el6695_rt::rfm::MB_MAGIC; mb.magic = el6695_rt::rfm::MB_MAGIC;
} }
// Spin-poll REP for the remote-processed payload. // Spin-poll REP for the remote-processed payload. The RFM hop
let deadline = Instant::now() + Duration::from_nanos(cycle_ns / 2); // (~30-800us depending on scheduling) needs a budget larger
// than one PLC cycle; allow up to 1.5ms so the full-chain RTT
// is actually measured instead of timing out to local echo.
let deadline = Instant::now() + Duration::from_nanos(1_500_000);
let mut got = false; let mut got = false;
loop { loop {
unsafe { unsafe {
@ -1129,6 +1132,15 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
{ {
let m = SM_RX_PLD_LEN.min(el6695_rt::rfm::MB_MAX_PAYLOAD); let m = SM_RX_PLD_LEN.min(el6695_rt::rfm::MB_MAX_PAYLOAD);
el6695_rt::rfm::dev_read(&mut tx_buf[..m], rep.payload.as_ptr()); el6695_rt::rfm::dev_read(&mut tx_buf[..m], rep.payload.as_ptr());
// Apply f(x)=x*7+3 to the echoed frame's tc_seq
// here on the D2000 side (single transform point,
// robust): the remote just reflects the frame, the
// PLC's echo check needs [0..8] = f(rx tc_seq).
if m >= 280 {
let rx_seq = u64::from_le_bytes(rx_buf[..8].try_into().unwrap_or([0; 8]));
let processed = rx_seq.wrapping_mul(7).wrapping_add(3);
tx_buf[..8].copy_from_slice(&processed.to_le_bytes());
}
bridge_rtt_st.push(el6695_rt::rfm::now_ns().saturating_sub(rep.ts_ns) as i64); bridge_rtt_st.push(el6695_rt::rfm::now_ns().saturating_sub(rep.ts_ns) as i64);
got = true; got = true;
break; break;
@ -1194,6 +1206,11 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
} }
} }
} }
}
// TX (FPWR) must run for BOTH ticker and rfm-bridge modes (it was
// previously gated inside Mode::Ticker, so rfm-bridge never wrote
// SM2 and the PLC read zeros). Probe mode stays read-only.
if cfg.mode != Mode::Probe {
if PAYLOAD_LEN >= 16 { if PAYLOAD_LEN >= 16 {
tx_buf[8..16].copy_from_slice(&cyc.to_le_bytes()); tx_buf[8..16].copy_from_slice(&cyc.to_le_bytes());
} }
@ -1615,18 +1632,9 @@ async fn rfm_mode_main(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
el6695_rt::rfm::dev_read(&mut buf[..n], req.payload.as_ptr()); el6695_rt::rfm::dev_read(&mut buf[..n], req.payload.as_ptr());
let rep = el6695_rt::rfm::Mailbox::at(mem_ptr, el6695_rt::rfm::MB_REP_OFF); let rep = el6695_rt::rfm::Mailbox::at(mem_ptr, el6695_rt::rfm::MB_REP_OFF);
el6695_rt::rfm::dev_write(rep.payload.as_mut_ptr(), &buf[..n]); el6695_rt::rfm::dev_write(rep.payload.as_mut_ptr(), &buf[..n]);
// Bridge frames (CX 800B payload): apply the same // Echo is a dumb reflector: f(x) is applied by the
// f(x)=x*7+3 transform on tc_seq[0..8] that the local // D2000 bridge (single transform point, avoids double
// ticker applies, so the PLC's echo check passes // transform).
// through the RFM hop. Small ping frames pass through
// verbatim.
if (cur_len as usize) >= 280 {
let mut seq_bytes = [0u8; 8];
el6695_rt::rfm::dev_read(&mut seq_bytes, rep.payload.as_ptr());
let seq_val = u64::from_le_bytes(seq_bytes);
let processed = seq_val.wrapping_mul(7).wrapping_add(3);
el6695_rt::rfm::dev_write(rep.payload.as_mut_ptr(), &processed.to_le_bytes());
}
rep.len = cur_len; rep.len = cur_len;
rep.ts_ns = cur_ts; rep.ts_ns = cur_ts;
el6695_rt::rfm::barrier(); el6695_rt::rfm::barrier();