Re-implementation of rustbootd's ecat_el6695_* examples as a single binary with fixes found in review and on hardware: - DC-follow PLL hardened against period-2 hunting: slew-limited anchor (+/-50us/cycle), bistable-trap snap re-anchor, re-prime on stale deadline - drift-free absolute-grid ticker mode; probe mode for timestamp forensics - bounded-memory online stats (histograms), graceful SIGINT/SIGTERM shutdown with full report, error-streak abort - timestamp plausibility filter comparing against the previous raw sample (avoids the deadlock after a startup outlier) - XFC scope waveform options: --el2202 (with --el2202-dual), --el2262, --el1252 latch timestamp readback with per-channel edge statistics - register access unified in regs.rs: named bit constants everywhere, read-modify-write for enable/activation bytes - vendored patched ethercrab 0.7.1 (sdo_write_complete, send_raw_coe) Verified on J1900 (PREEMPT_RT 6.6.135): 600k cycles/600s exact 1 kHz, tx/rx zero errors, phase_err p50=164us std=5us; EL2202<->EL1252 loopback edge interval mean 2000.24us std=24.65us.
91 lines
2.3 KiB
Rust
91 lines
2.3 KiB
Rust
use core::future::poll_fn;
|
|
use core::task::Poll;
|
|
use criterion::{Bencher, Criterion, Throughput, criterion_group, criterion_main};
|
|
use ethercrab::{Command, MainDevice, MainDeviceConfig, PduStorage, Timeouts};
|
|
use std::{pin::pin, time::Duration};
|
|
|
|
const DATA: [u8; 8] = [0x11u8, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd];
|
|
|
|
fn do_bench(b: &mut Bencher) {
|
|
const FRAME_OVERHEAD: usize = 28;
|
|
|
|
// 1 frame, up to 128 bytes payload
|
|
let storage = PduStorage::<1, { PduStorage::element_size(128) }>::new();
|
|
|
|
let (mut tx, mut rx, pdu_loop) = storage.try_split().unwrap();
|
|
|
|
let maindevice = MainDevice::new(
|
|
pdu_loop,
|
|
Timeouts {
|
|
pdu: Duration::from_millis(1000),
|
|
..Timeouts::default()
|
|
},
|
|
MainDeviceConfig::default(),
|
|
);
|
|
|
|
let mut written_packet = [0u8; { FRAME_OVERHEAD + DATA.len() }];
|
|
|
|
b.iter(|| {
|
|
// --- Prepare frame
|
|
|
|
let mut frame_fut =
|
|
pin!(Command::fpwr(0x5678, 0x1234).send_receive::<()>(&maindevice, &DATA));
|
|
|
|
// Poll future once to register it with sender
|
|
cassette::block_on(poll_fn(|ctx| {
|
|
let _ = frame_fut.as_mut().poll(ctx);
|
|
|
|
Poll::Ready(())
|
|
}));
|
|
|
|
let frame = tx.next_sendable_frame().expect("Next frame");
|
|
|
|
frame
|
|
.send_blocking(|bytes| {
|
|
written_packet.copy_from_slice(bytes);
|
|
|
|
Ok(bytes.len())
|
|
})
|
|
.expect("TX");
|
|
|
|
// --- Receive frame
|
|
|
|
// Turn master sent MAC into receiving MAC
|
|
written_packet[6] = 0x12;
|
|
// Bump working counter so we don't error out
|
|
written_packet[written_packet.len() - 2] = 1;
|
|
|
|
rx.receive_frame(&written_packet).expect("RX");
|
|
|
|
let _ = cassette::block_on(frame_fut);
|
|
})
|
|
}
|
|
|
|
pub fn tx_rx(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("pdu_loop");
|
|
|
|
group.throughput(Throughput::Elements(1));
|
|
|
|
group.bench_function("elements", do_bench);
|
|
|
|
let overhead = {
|
|
// Ethernet header
|
|
(6 + 6 + 2) +
|
|
// EtherCAT header
|
|
2 +
|
|
// PDU header
|
|
10 +
|
|
// Working counter
|
|
2
|
|
};
|
|
|
|
group.throughput(Throughput::Bytes(DATA.len() as u64 + overhead));
|
|
|
|
group.bench_function("ethernet frame bytes", do_bench);
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(pdu_loop, tx_rx);
|
|
criterion_main!(pdu_loop);
|