### Linux Profiling Setup for Rust Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Instructions for setting up profiling tools on Linux, including installing necessary packages, configuring kernel parameters for symbol visibility, and running `perf` for recording and reporting. ```bash # Ubuntu apt install linux-tools-common linux-tools-generic linux-tools-`uname -r` # Debian sudo apt install linux-perf RUSTFLAGS="-C force-frame-pointers=yes" cargo build --example --profile profiling # # To get kernel symbols in the perf output echo 0 | sudo tee /proc/sys/kernel/kptr_restrict # OR sudo sysctl -w kernel.kptr_restrict=0 # Read current value sudo sysctl kernel.kptr_restrict # This should show non-zero addresses now. Will show zeros without sudo. sudo cat /proc/kallsyms # Might need sudo sysctl kernel.perf_event_paranoid=-1 # Might need sudo sysctl kernel.perf_event_mlock_kb=2048 sudo setcap cap_net_raw=pe ./target/profiling/examples/ sudo perf record --call-graph=dwarf -g ./target/profiling/examples/ # To record benchmarks sudo perf record --call-graph=dwarf -g -o bench.data ./target/release/deps/pdu_loop-597b19205907e408 --bench --profile-time 5 'bench filter here' # Ctrl + C when you're done # Must use sudo to get kernel symbols sudo perf report -i perf.data # This won't show kernel symbols (possibly only when over SSH?) sudo chown $USER perf.data samply import perf.data # Forward the port on a remote machine with ssh -L 3000:localhost:3000 ethercrab # Otherwise symbols aren't loaded ``` -------------------------------- ### Run EK1100 Example on Linux Source: https://github.com/ethercrab-rs/ethercrab/blob/main/README.md Command to run the EK1100 example on Linux. Ensure RUST_LOG is set for debug output. ```bash RUST_LOG=debug cargo run --example ek1100 --release -- eth0 ``` -------------------------------- ### Run EK1100 Example on Windows Source: https://github.com/ethercrab-rs/ethercrab/blob/main/README.md Command to run the EK1100 example on Windows. Sets RUST_LOG environment variable and specifies the network interface. ```powershell $env:RUST_LOG="debug" ; cargo run --example ek1100 --release -- '\Device\NPF_{FF0ACEE6-E8CD-48D5-A399-619CD2340465}' ``` -------------------------------- ### macOS Profiling Setup for Rust Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Instructions for profiling Rust applications on macOS using `cargo instruments`. It details the necessary Xcode setup and provides a command to run benchmarks with CPU profiling. ```bash - Install XCode - [`sudo xcode-select -switch /Library/Developer/CommandLineTools`](https://apple.stackexchange.com/a/446563) - `cargo instruments --template 'CPU Profiler' --profile profiling --bench pdu_loop -- --bench --profile-time 5` Other templates can be listed with `xctrace list templates` ``` -------------------------------- ### Configuring Slave Groups with a Closure Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md This example demonstrates initializing slave groups using a closure that maps detected slaves to specific groups. This approach allows for flexible configuration and handling of different slave types. Ensure the closure correctly returns a reference to the appropriate `SlaveGroup` or `None` to ignore a slave. ```rust struct Groups { io: SlaveGroup<16>, servos: SlaveGroup<3> } // Closure must return a reference to a SlaveGroup to insert the slave into let groups = client.init::(|&mut groups, slave, idx| { if slave.id == 0xwhatever { Some(&mut groups.io) } else if slave.manu == 0xwhatever { Some(&mut groups.servos) } else { // Might want to ignore a detected slave - maybe it's not a recognised device. Add config option // to make this an error? None } }).await.unwrap(); ``` -------------------------------- ### Rust Slave Group Configuration Example Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Demonstrates configuring slave groups in EtherCrab using typestates for different operational states (PreOp, Op). This approach avoids dynamic boxing and allows for fine-grained control over slave group transitions. ```rust let groups = client.assign_slaves(Groups::default(), |g, s| { ... }).await?; // Groups are in PreOp at this point let Groups { fast, slow } = client.init(...); // These could move into their respective tasks/threads if they don't use outside data, or make sure // outside data is `Sync` or whatever. let fast: SlaveGroup = fast.start_op(|slave| { ... }).await?; let slow: SlaveGroup = slow.start_op(|slave| { ... }).await?; ``` ```rust impl SlaveGroup { // Individual phases can be opened up in the future, like `into_safe_op`, `into_op`, etc /// Put the group all the way to PreOp -> SafeOp -> Op pub async fn into_op(self, hook: F) -> Result>, ()> { // Do group stuff in PreOp // Call hook // Do more group stuff or whatever // Put group into SafeOp // Internal methods which we could make pub in the future let g = Self { ..., _state: GroupState::SafeOp } // Put group into Op, wait for state // All good Ok(Self { ..., _state: GroupState::Op }) } } impl SlaveGroup { async fn tx_rx(&self) -> Result<(), Error> { // ... } // Future stuff. Neat! // Maybe INIT instead of PREOP? pub async fn shutdown(self) -> Result>, Error> } ``` -------------------------------- ### EtherCrab Distributed Clocks Debugging Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Configuration and logging setup for investigating distributed clocks (DC) in EtherCrab. Shows how to enable debug logging for the DC module and run an example with specific network interface. ```bash `RUST_LOG=info,ethercrab::dc=debug just linux-example-release dc enp2s0` ``` -------------------------------- ### tshark capture output examples Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Examples of `tshark` output showing packet frame numbers, relative time, time delta, source MAC, length, and EtherCAT specific fields. These examples illustrate varying delays between packets. ```text 212171 229.185843925 0.001081760 10:10:10:10:10:10 0x06 0x0e 0x1000 0x0910 212172 229.185851399 0.000007474 10:10:10:10:10:10 0x07 0x0c 212173 229.185881114 0.000029715 12:10:10:10:10:10 0x06 0x0e 0x1000 0x0910 212174 283.980272341 54.794391227 12:10:10:10:10:10 0x07 0x0c ``` ```text 286913 792.417020063 0.001086499 10:10:10:10:10:10 0x19 0x0e 0x1000 0x0910 286914 792.417028279 0.000008216 10:10:10:10:10:10 0x1a 0x0c 286915 792.417051592 0.000023313 12:10:10:10:10:10 0x19 0x0e 0x1000 0x0910 286916 795.080750017 2.663698425 12:10:10:10:10:10 0x1a 0x0c ``` ```text 50367 15.443982363 0.002090013 10:10:10:10:10:10 0x00 0x0c 50368 15.443990028 0.000007665 10:10:10:10:10:10 0x1f 0x0e 0x1000 0x0910 50369 15.444012700 0.000022672 12:10:10:10:10:10 0x00 0x0c 50370 15.444020715 0.000008015 12:10:10:10:10:10 0x1f 0x0e 0x1000 0x0910 50371 15.446105098 0.002084383 10:10:10:10:10:10 0x01 0x0e 0x1000 0x0910 50372 15.446112552 0.000007454 10:10:10:10:10:10 0x02 0x0c 50373 15.446140353 0.000027801 12:10:10:10:10:10 0x01 0x0e 0x1000 0x0910 50374 27.842931439 12.396791086 12:10:10:10:10:10 0x02 0x0c ``` ```text 1705 0.852359273 0.002079843 10:10:10:10:10:10 36 0x09 0x0e 0x1000 0x0910 1706 0.852366577 0.000007304 10:10:10:10:10:10 28 0x0a 0x0c 1707 0.852395721 0.000029144 12:10:10:10:10:10 60 0x09 0x0e 0x1000 0x0910 1708 58.197952942 57.345557221 12:10:10:10:10:10 60 0x0a 0x0c ``` ```text 39151 9.952622248 0.001104738 10:10:10:10:10:10 36 0x17 0x0e 0x1000 0x0910 39152 9.952626089 0.000003841 10:10:10:10:10:10 28 0x18 0x0c 39153 9.952637403 0.000011314 12:10:10:10:10:10 60 0x17 0x0e 0x1000 0x0910 39154 130.431369768 120.478732365 12:10:10:10:10:10 60 0x18 0x0c ``` -------------------------------- ### Aligning Middle Fields with pre_skip and post_skip Source: https://github.com/ethercrab-rs/ethercrab/blob/main/ethercrab-wire-derive/README.md This example demonstrates how to align a field that is in the middle of a byte, maintaining 8-bit alignment by using both `pre_skip` and `post_skip` attributes. ```rust #[derive(ethercrab_wire::EtherCrabWireReadWrite)] #[wire(bytes = 1)] struct Middle { #[wire(pre_skip = 2, bits = 3, post_skip = 3)] foo: u8, } ``` -------------------------------- ### Fixing Struct Field Alignment with post_skip Source: https://github.com/ethercrab-rs/ethercrab/blob/main/ethercrab-wire-derive/README.md This example shows how to fix alignment issues by using the `post_skip` attribute to realign the next field to 8 bits. ```rust #[derive(ethercrab_wire::EtherCrabWireReadWrite)] #[wire(bytes = 2)] struct Fixed { #[wire(bits = 3, post_skip = 5)] foo: u8, #[wire(bytes = 1)] bar: u8, } ``` -------------------------------- ### Pin Tasks to Separate Cores Source: https://github.com/ethercrab-rs/ethercrab/blob/main/doc/windows-tuning.md Pin the main thread and the TX/RX thread to separate CPU cores for reduced latency. Ensure these cores are isolated for real-time performance. This example uses the `core-affinity` crate. ```rust let core_ids = core_affinity::get_core_ids().expect("Get core IDs"); // Pick the non-HT cores on my Intel i5-8500T test system. YMMV! let main_thread_core = core_ids[0]; let tx_rx_core = core_ids[2]; // Pinning this and the TX/RX thread reduce packet RTT spikes significantly core_affinity::set_for_current(main_thread_core); thread_priority::ThreadBuilder::default() .name("tx-rx-thread") // For best performance, this MUST be set if pinning NIC IRQs to the same core .priority(ThreadPriority::Os(ThreadPriorityOsValue::from( WinAPIThreadPriority::TimeCritical, ))) .spawn(move |_| { // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV core_affinity::set_for_current(tx_rx_core) .then_some(()) .expect("Set TX/RX thread core"); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tx_rx_task_blocking(&interface, tx, rx, TxRxTaskConfig { spinloop: false }) .expect("TX/RX task"); }) .unwrap(); ``` -------------------------------- ### System Configuration Summary Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Lists the hardware and software configuration used for performance testing, including CPU, memory, NIC, EtherCAT hardware, runtime, and thread settings. Useful for reproducing results. ```text 1800 s: mean 0.998 ms, std dev 0.010 ms (0.99 % / 2.18 % max) ``` ```text - i3-7100T / 8GB DDR4 (Dell Optiplex 3050 micro) - Realtek somethingsomething gigabit NIC - EK1100 + EL2004 slave device - `smol` - 4 total threads - ``` ❯ ps -m -l -c $(pidof jitter) F S UID PID PPID CLS PRI ADDR SZ WCHAN TTY TIME CMD 4 - 1000 23969 23904 - - - 35340 - pts/5 0:01 ./target/release/examples/jitter enp2s0 4 S 1000 - - FF 139 - - - - 0:01 - 1 S 1000 - - FF 139 - - - - 0:00 - 1 S 1000 - - FF 139 - - - - 0:00 - ``` - `tuned-adm profile latency-performance` - Hyperthreading disabled with `echo off > /sys/devices/system/cpu/smt/control` (2 cores on i3-7100T) - 1ms cycle time - Setting thread priority to 99 and `FIFO` policy. ``` -------------------------------- ### List Network Interfaces on Windows (getmac) Source: https://github.com/ethercrab-rs/ethercrab/blob/main/FAQ.md The 'getmac' command can be used to list network adapters on Windows. Consider using '/fo csv /v' for CSV formatted output and verbose details. ```batch getmac???? TODO ``` -------------------------------- ### List Network Interfaces on Linux Source: https://github.com/ethercrab-rs/ethercrab/blob/main/FAQ.md Use the 'ip a' command to list available network interfaces on Linux systems. ```bash ip a ``` -------------------------------- ### Struct Field Alignment Error Source: https://github.com/ethercrab-rs/ethercrab/blob/main/ethercrab-wire-derive/README.md This example demonstrates a struct that will fail compilation due to incorrect field alignment. Ensure fields of 1 byte or more are byte-aligned. ```rust #[derive(ethercrab_wire::EtherCrabWireReadWrite)] #[wire(bytes = 2)] struct Broken { #[wire(bits = 3)] foo: u8, // There are 5 bits here unaccounted for #[wire(bytes = 1)] bar: u8, } ``` -------------------------------- ### Run Linux Test with Replay Source: https://github.com/ethercrab-rs/ethercrab/blob/main/tests/README.md Execute the integration tests on a Linux machine using a specified network interface and the replay functionality. ```bash INTERFACE=enp2s0 just linux-test replay ``` -------------------------------- ### Build SOEM with NMake on Windows Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md These commands outline the process of configuring and building SOEM on Windows using NMake Makefiles within the Developer PowerShell for VS 2022. ```bash mkdir build cd build cmake .. -G "NMake Makefiles" nmake ``` -------------------------------- ### Increase Watchdog Timeouts Source: https://github.com/ethercrab-rs/ethercrab/blob/main/doc/windows-tuning.md Increase watchdog timeouts to prevent issues caused by large hangs in timers or network traffic. This example shows how to write to the `WatchdogDivider` register. ```rust let watchdog_timeout_millis = 200u16; subdevice .register_write( RegisterAddress::WatchdogDivider, // Multiplier for default 100us watchdog divider watchdog_timeout_millis * 10, ) .await?; ``` -------------------------------- ### Capture Replay with `just` Source: https://github.com/ethercrab-rs/ethercrab/blob/main/tests/README.md Use this command to capture network traffic for a specific replay. Ensure the replay name matches a file in `tests/`. ```bash just capture-replay replay-ek1100-el2828-el2889 enx00e04c680066 ``` -------------------------------- ### List Network Interfaces on macOS Source: https://github.com/ethercrab-rs/ethercrab/blob/main/FAQ.md Use the 'networksetup -listallhardwareports' command to list network hardware ports on macOS. The output includes the device name (e.g., en3) which can be used for command-line arguments. ```bash networksetup -listallhardwareports ``` -------------------------------- ### List Threads and Priority with ps Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Use this command to list threads, their priorities, and scheduling policies for a running process. Useful for diagnosing performance issues. ```bash ps -m -l -c $(pidof jitter) ``` -------------------------------- ### Allocate Frame and Submit PDUs Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Demonstrates allocating a frame and submitting multiple PDUs with different commands and parameters. The frame is then marked as sent, and individual PDU results are extracted. ```rust let mut frame = pdu_loop.allocate_frame(); let sub1 = frame.submit(Command::FRMW, 0u64, None, true); let sub2 = frame.submit(Command::LRW, &pdi, None, false); let result = frame.mark_sent().await; let res1 = result.take(sub1); let res2 = result.take(sub2); ``` -------------------------------- ### Configuring EtherCAT Sync0 Activation via SDO Write Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md This C code snippet demonstrates how to activate Sync0 on an EtherCAT slave by writing to a specific SDO address. This is often required by certain drivers regardless of the ec_dSync0 instruction. ```c uint16 activate = 1; ec_SDOwrite(1, 0x0300, 0x00, true, sizeof(activate), &activate, EC_TIMEOUTRXM); ``` -------------------------------- ### Initialize and Control EtherCAT Devices Source: https://github.com/ethercrab-rs/ethercrab/blob/main/README.md Main function to initialize EtherCAT devices, configure specific modules like EL3004, and enter operational state. It then enters a loop to continuously exchange data and increment output bytes. ```rust use env_logger::Env; use ethercrab::{ error::Error, std::{ethercat_now, tx_rx_task}, MainDevice, MainDeviceConfig, PduStorage, Timeouts }; use std::{sync::Arc, time::Duration}; use tokio::time::MissedTickBehavior; /// Maximum number of SubDevices that can be stored. This must be a power of 2 greater than 1. const MAX_SUBDEVICES: usize = 16; /// Maximum PDU data payload size - set this to the max PDI size or higher. const MAX_PDU_DATA: usize = 1100; /// Maximum number of EtherCAT frames that can be in flight at any one time. const MAX_FRAMES: usize = 16; /// Maximum total PDI length. const PDI_LEN: usize = 64; static PDU_STORAGE: PduStorage = PduStorage::new(); #[tokio::main] async fn main() -> Result<(), Error> { env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); let interface = std::env::args() .nth(1) .expect("Provide network interface as first argument."); log::info!("Starting EK1100 demo..."); log::info!("Ensure an EK1100 is the first SubDevice, with any number of modules connected after"); log::info!("Run with RUST_LOG=ethercrab=debug or =trace for debug information"); let (tx, rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once"); let maindevice = Arc::new(MainDevice::new( pdu_loop, Timeouts { wait_loop_delay: Duration::from_millis(2), mailbox_response: Duration::from_millis(1000), ..Default::default() }, MainDeviceConfig::default(), )); tokio::spawn(tx_rx_task(&interface, tx, rx).expect("spawn TX/RX task")); let mut group = maindevice .init_single_group::(ethercat_now) .await .expect("Init"); log::info!("Discovered {} SubDevices", group.len()); for subdevice in group.iter(&maindevice) { // Special case: if an EL3004 module is discovered, it needs some specific config during // init to function properly if subdevice.name() == "EL3004" { log::info!("Found EL3004. Configuring..."); subdevice.sdo_write(0x1c12, 0, 0u8).await?; subdevice.sdo_write(0x1c13, 0, 0u8).await?; subdevice.sdo_write(0x1c13, 1, 0x1a00u16).await?; subdevice.sdo_write(0x1c13, 2, 0x1a02u16).await?; subdevice.sdo_write(0x1c13, 3, 0x1a04u16).await?; subdevice.sdo_write(0x1c13, 4, 0x1a06u16).await?; subdevice.sdo_write(0x1c13, 0, 4u8).await?; } } let mut group = group.into_op(&maindevice).await.expect("PRE-OP -> OP"); for subdevice in group.iter(&maindevice) { let io = subdevice.io_raw(); log::info!( "-> SubDevice {:#06x} {} inputs: {} bytes, outputs: {} bytes", subdevice.configured_address(), subdevice.name(), io.inputs().len(), io.outputs().len() ); } let mut tick_interval = tokio::time::interval(Duration::from_millis(5)); tick_interval.set_missed_tick_behavior(MissedTickBehavior::Skip); loop { group.tx_rx(&maindevice).await.expect("TX/RX"); // Increment every output byte for every SubDevice by one for mut subdevice in group.iter(&maindevice) { let mut io = subdevice.io_raw_mut(); for byte in io.outputs().iter_mut() { *byte = byte.wrapping_add(1); } } tick_interval.tick().await; } } ``` -------------------------------- ### Jitter Measurement Command Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Executes a jitter measurement test on Linux using the 'just' build tool. Ensure the 'just' command is available and the 'linux-example-release' target is configured. ```bash just linux-example-release jitter enp2s0 ``` -------------------------------- ### Configure TX/RX Thread with Realtime Priority and Affinity Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Configure the TX/RX thread with realtime priority and affinity using `thread_priority` and `core_affinity`. This requires a realtime kernel and may need adjustments in `/etc/security/limits.conf`. It also utilizes a thread-local async executor. ```rust thread_priority::ThreadBuilder::default() .name("tx-rx-task") // Might need to set ` hard rtprio 99` and ` soft rtprio 99` in `/etc/security/limits.conf` // Check limits with `ulimit -Hr` or `ulimit -Sr` .priority(ThreadPriority::Crossplatform( ThreadPriorityValue::try_from(99u8).unwrap(), )) // NOTE: Requires a realtime kernel .policy(ThreadSchedulePolicy::Realtime( RealtimeThreadSchedulePolicy::Fifo, )) .spawn(move |_| { core_affinity::set_for_current(core_affinity::CoreId { id: 0 }) .then_some(()) .expect("Set TX/RX thread core"); let ex = LocalExecutor::new(); futures_lite::future::block_on( ex.run(tx_rx_task(&interface, tx, rx).expect("spawn TX/RX task")), ) .expect("TX/RX task exited"); }) .unwrap(); ``` -------------------------------- ### Setting Realtime Priority Limits in Linux Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Configure /etc/security/limits.conf to allow higher priority threads for a specified user. This is crucial for realtime performance tuning on Linux systems. ```bash soft rtprio 99 hard rtprio 99 ``` -------------------------------- ### Configuring Network Interface with Ethtool in Debian Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md This configuration snippet for Debian systems ensures that ethtool settings for realtime network performance are applied on boot. It includes specific commands for Pi 4 and other NICs. ```bash allow-hotplug eth0 iface eth0 inet manual # Pi 4 post-up /sbin/ethtool -C eth0 rx-usecs 0 tx-frames 1 rx-frames 1 # Pi 5, most other NICs post-up /sbin/ethtool -C eth0 tx-usecs 0 rx-usecs 0 ``` -------------------------------- ### List Network Interfaces on Windows (PowerShell) Source: https://github.com/ethercrab-rs/ethercrab/blob/main/FAQ.md Use PowerShell commands to retrieve network adapter information on Windows. 'Get-NetAdapter' lists adapters, and 'Format-List -Property *' displays all properties. 'Select-Object Name, InterfaceGuid' can filter for specific properties. ```powershell Get-NetAdapter -Name * -IncludeHidden | Format-List -Property * Select-Object Name, InterfaceGuid ``` -------------------------------- ### Display Process Threads and Priorities (Before Priority Change) Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Shows the process threads and their scheduling class (TS) and priority (19) before applying custom thread priority settings. Useful for comparison. ```text ❯ ps -m -l -c $(pidof jitter) F S UID PID PPID CLS PRI ADDR SZ WCHAN TTY TIME CMD 4 - 1000 25164 25158 - - - 35337 - pts/5 0:01 ./target/release/examples/jitter enp2s0 4 S 1000 - - TS 19 - - - - 0:00 - 1 S 1000 - - TS 19 - - - - 0:00 - 1 S 1000 - - TS 19 - - - - 0:00 - ``` -------------------------------- ### Use High-Resolution Timers for Process Data Cycles Source: https://github.com/ethercrab-rs/ethercrab/blob/main/doc/windows-tuning.md Utilize `spin-sleep` and `quanta` for high-resolution timers in the main process data cycle, bypassing Windows' default coarse timers. Initialize `quanta::Clock` before EtherCAT operations and use `SpinSleeper` for accurate cycle timing. ```rust // Both `smol` and `tokio` use Windows' coarse timer, which has a resolution of at least // 15ms. This isn't useful for decent cycle times, so we use a more accurate clock from // `quanta` and a spin sleeper to get better timing accuracy. let sleeper = SpinSleeper::default().with_spin_strategy(SpinStrategy::SpinLoopHint); // NOTE: This takes ~200ms to return, so it must be called before any proper EtherCAT stuff // happens. let clock = quanta::Clock::new(); // // Do your init, into OP, etc here // let cycle_time = Duration::from_millis(5); loop { let now = clock.now(); group.tx_rx(&maindevice).await.expect("TX/RX"); // // Application logic here // let wait = cycle_time.saturating_sub(now.elapsed()); sleeper.sleep(wait); } ``` -------------------------------- ### Display Process Threads and Priorities (After Priority Change) Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Displays the process threads after applying custom priority settings, showing the updated scheduling class (FF) and priority (139). This confirms the thread priority changes. ```text ❯ ps -m -l -c $(pidof jitter) F S UID PID PPID CLS PRI ADDR SZ WCHAN TTY TIME CMD 4 - 1000 23969 23904 - - - 35340 - pts/5 0:01 ./target/release/examples/jitter enp2s0 4 S 1000 - - FF 139 - - - - 0:01 - 1 S 1000 - - FF 139 - - - - 0:00 - 1 S 1000 - - FF 139 - - - - 0:00 - ``` -------------------------------- ### Network driver information before kernel upgrade Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Shows the `r8169` driver version `5.15.0-1032-realtime` and its firmware version. ```text driver: r8169 version: 5.15.0-1032-realtime firmware-version: rtl8168h-2_0.0.2 02/26/15 expansion-rom-version: bus-info: 0000:02:00.0 supports-statistics: yes supports-test: no supports-eeprom-access: no supports-register-dump: yes supports-priv-flags: no ``` -------------------------------- ### Configuring Ethtool for Realtime Network Performance Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md These commands configure ethtool settings for network interfaces to improve realtime performance. The specific settings may vary depending on the hardware, such as for Raspberry Pi 4. ```bash sudo ethtool -C eth0 tx-usecs 0 rx-usecs 0 ``` ```bash sudo ethtool -C eth0 rx-usecs 0 tx-frames 1 rx-frames 1 ``` -------------------------------- ### Cross-Platform Logging with defmt and log Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Integrates `defmt` and `log` logging frameworks using feature flags. This allows direct use of `log` macros without an adapter, supporting no_std and std environments. ```rust use log::info; #[cfg(feature = "defmt")] use defmt::println; #[cfg(not(feature = "defmt"))] macro_rules! log_message { ($($arg:tt)*) => { log::info!($($arg)*) }; } #[cfg(feature = "defmt")] macro_rules! log_message { ($($arg:tt)*) => { defmt::println!($($arg)*) }; } fn main() { log_message!("Hello, world!"); } ``` -------------------------------- ### ethtool interface driver information Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Display detailed information about the network interface driver, including its version and firmware version. This is useful for checking if kernel upgrades or driver updates have occurred. ```bash sudo ethtool -i enp2s0 ``` -------------------------------- ### Pin Main Task to CPU Core Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Pin the main application task to a specific CPU core using the `core_affinity` crate. Ensure the core is isolated via kernel boot parameters. ```rust core_affinity::set_for_current(CoreId { id: 2 }) .then_some(()) .expect("Set main task core"); ``` -------------------------------- ### SlaveGroup Structure and Initialization Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Defines the `SlaveGroup` struct, which holds a collection of slaves. The `new` function provides a basic way to create an empty group, and `init_from_eeprom_and_push` is intended for initializing slaves within the group, managing PDI offsets. ```rust struct SlaveGroup { slaves: heapless::Vec } impl SlaveGroup { fn new() -> Self { // This could probably impl default Self { slaves: heapless::Vec::new() } } async fn init_from_eeprom_and_push(&mut self, &client, slave) -> Result<(), Error> { // What the client already does, but scoped to a group. // Needs to also return the PDI offset ready for the next group to use. } pub async fn tx_rx(&self, &client) -> Result<(), Error> { // } } ``` -------------------------------- ### Modify CMakeLists.txt for Windows SOEM Build Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md This diff shows how to modify the CMakeLists.txt file to include Windows-specific subdirectories for building SOEM tests on Windows. ```diff if(BUILD_TESTS) add_subdirectory(test/simple_ng) + # add_subdirectory(test/linux/slaveinfo) - add_subdirectory(test/linux/slaveinfo) add_subdirectory(test/linux/eepromtool) + # add_subdirectory(test/linux/simple_test) - add_subdirectory(test/linux/simple_test) + add_subdirectory(test/win32/slaveinfo) + add_subdirectory(test/win32/simple_test) endif() ``` -------------------------------- ### Async Client Task with RefCell Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Demonstrates an asynchronous task for managing client requests using `RefCell` for mutable access to shared state. Ensure `RefCell` borrows do not cross `await` points. ```rust struct Client { state: RefCell, } struct ClientState { waker: WakerRegistration, requests: [Option; N], } struct RequestState { waker: WakerRegistration, state: RequestStateEnum, } enum RequestStateEnum { Created{payload: [u8;N]}, Waiting, Done{response: [u8;N]}, } #[embassy::task] async fn client_task(device: Device, client: &'static Client) { poll_fn(|cx| { let client = &mut *self.client.borrow_mut(); client.waker.register(cx.waker); device.register_waker(self.waker); // process tx for each request in client.requests{ match request.state { Created(payload) => { // if we can't send it now, try again later. if !device.transmit_ready() { break; } device.transmit(payload); request.state = Waiting } _ => {} } } // process rx while let Some(pkt) = device.receive() { // parse pkt let req = // find waiting request matching the packet req.state = Done{payload}; req.waker.wake(); } }) } ``` ```rust impl Client { async fn do_request(&self, payload: [u8;N]) -> [u8;N] { // braces to ensure we don't hold the refcell across awaits!! let slot = { let client = &mut *self.client.borrow_mut(); let slot: usize = // find empty slot in client.requests client.requests[slot] = Some(RequestState{ state: Created(payload), ... }); client.waker.wake(); slot }; poll_fn(|cx| { let client = &mut *self.client.borrow_mut(); client.requests[slot].waker.register(cx.waker); match client.requests[slot].state { Done(payload) => Poll::Ready(payload), _ => Poll::Pending } }).await } } ``` -------------------------------- ### Iterating and Transmitting PDI with Slave Groups Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md This snippet shows the typical loop for processing PDI updates for a slave group. Ensure slaves are grouped correctly for thread safety. The `tx_rx` method handles the communication for the entire group. ```rust let interval = Interval::new(Duration::from_millis(2)); while let Some(_) = interval.next().await { group.tx_rx(&client).await; // Or whatever group.slave_by_index(0).outputs()[0] = 0xff; for slave in group.slaves_mut() { // Whatever } } ``` -------------------------------- ### Add EtherCrab for no_std targets Source: https://github.com/ethercrab-rs/ethercrab/blob/main/README.md Use this command to add the EtherCrab crate to your project with default features disabled and the `defmt` feature enabled, suitable for no_std environments. ```bash cargo add --no-default-features --features defmt ``` -------------------------------- ### Embassy Async Task for Packet Reception Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md This Rust code demonstrates an Embassy task that continuously receives packets from a device and processes them. It highlights the use of `async fn` within an `#[embassy::task]` macro. ```rust #[embassy::task] async fn my_task(..) { loop { let pkt = device.receive().await; // process pkt } } ``` -------------------------------- ### Network driver information after kernel upgrade Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Shows the `r8169` driver version `6.1.0-18-rt-amd64` and its firmware version after a kernel upgrade. ```text driver: r8169 version: 6.1.0-18-rt-amd64 firmware-version: rtl8168h-2_0.0.2 02/26/15 expansion-rom-version: bus-info: 0000:02:00.0 supports-statistics: yes supports-test: no supports-eeprom-access: no supports-register-dump: yes supports-priv-flags: no ``` -------------------------------- ### Configure ethtool to capture all packets Source: https://github.com/ethercrab-rs/ethercrab/blob/main/NOTES.md Use `ethtool` to configure the network interface to accept all incoming packets, including those with bad FCS, and to enable CRC stripping. This allows Wireshark to capture all packets, though it may increase packet size by 4 bytes. ```bash sudo ethtool -K eth0 rx-fcs on rx-all on ``` -------------------------------- ### Manual Capture with `tshark` Source: https://github.com/ethercrab-rs/ethercrab/blob/main/tests/README.md Manually capture EtherCAT traffic on a local machine using tshark. This command filters for EtherCAT packets using a specific frame type. ```bash tshark -w tests/replay-ek1100-el2828-el2889.pcapng --interface enp2s0 -f 'ether proto 0x88a4' ```