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();
mb.magic = el6695_rt::rfm::MB_MAGIC;
}
// Spin-poll REP for the remote-processed payload.
let deadline = Instant::now() + Duration::from_nanos(cycle_ns / 2);
// Spin-poll REP for the remote-processed payload. The RFM hop
// (~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;
loop {
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);
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);
got = true;
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 {
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());
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]);
// Bridge frames (CX 800B payload): apply the same
// f(x)=x*7+3 transform on tc_seq[0..8] that the local
// ticker applies, so the PLC's echo check passes
// 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());
}
// Echo is a dumb reflector: f(x) is applied by the
// D2000 bridge (single transform point, avoids double
// transform).
rep.len = cur_len;
rep.ts_ns = cur_ts;
el6695_rt::rfm::barrier();