From ba90bafbf9924a5e464744d259166f7401800c37 Mon Sep 17 00:00:00 2001 From: Philippe Leduc Date: Fri, 19 Jun 2026 10:09:08 +0200 Subject: [PATCH] Rework README and doc in general Add a uncrustify configuration to enforce part of the code style --- README.md | 889 +++---------------- docs/BUILDING.md | 168 ++++ docs/FEATURES.md | 120 +++ docs/HARDWARE.md | 237 +++++ docs/PERFORMANCE.md | 94 ++ docs/SIMULATION.md | 152 ++++ docs/TOOLS.md | 107 +++ {doc => docs}/architecture.md | 0 {doc => docs}/freedom-demo.gif | Bin {doc => docs}/kickCAT_logo_dark_mode.png | Bin {doc => docs}/kickCAT_logo_light_mode.png | Bin {doc => docs}/multi-slave-easycat-output.gif | Bin {doc => docs}/multi-slave-example.png | Bin simulation/README.md | 87 +- tools/eeprom.cc | 1 - uncrustify.cfg | 129 +++ 16 files changed, 1121 insertions(+), 863 deletions(-) create mode 100644 docs/BUILDING.md create mode 100644 docs/FEATURES.md create mode 100644 docs/HARDWARE.md create mode 100644 docs/PERFORMANCE.md create mode 100644 docs/SIMULATION.md create mode 100644 docs/TOOLS.md rename {doc => docs}/architecture.md (100%) rename {doc => docs}/freedom-demo.gif (100%) rename {doc => docs}/kickCAT_logo_dark_mode.png (100%) rename {doc => docs}/kickCAT_logo_light_mode.png (100%) rename {doc => docs}/multi-slave-easycat-output.gif (100%) rename {doc => docs}/multi-slave-example.png (100%) create mode 100644 uncrustify.cfg diff --git a/README.md b/README.md index f584457f..a45eb06a 100644 --- a/README.md +++ b/README.md @@ -1,847 +1,176 @@

- - + + KickCAT

- A lightweight, efficient EtherCAT master/slave stack for embedded systems -

- -

- Kick-start your slaves! ⚡ + A lightweight, efficient EtherCAT master/slave stack for embedded and real-time systems

--- ## Overview -KickCAT is a thin EtherCAT stack designed to be embedded in complex software with efficiency in mind. It provides both **master** and **slave** implementations, supporting multiple operating systems and hardware platforms. - -**Key Features:** -- Works with Linux (including RT-PREEMPT), Windows, and PikeOS -- Full state machine support (INIT → PRE-OP → SAFE-OP → OP) -- CoE (CANopen over EtherCAT) support with SDO read/write -- CoE: SDO Information -- Interface redundancy -- Bus diagnostics and error handling -- Master side python bindings -- Built-in ESC emulator for testing without hardware - ---- - -## Quick Start - -### Prerequisites - -- **Linux**: gcc, cmake, conan (for dependencies) -- **Python bindings**: uv or pip -- **Hardware**: Network interface with raw socket access capabilities - -### Build and Install - -#### Linux (Recommended) - -```bash -# 1. Configure build options (optional — defaults are sensible) -./scripts/configure.sh build --with=unit_tests - -# 2. Setup build environment (installs Conan deps + runs CMake) -./scripts/setup_build.sh build - -# 3. Build -cd build && make - -# 4. Grant network capabilities (required for raw socket access) -sudo setcap 'cap_net_raw,cap_net_admin=+ep' ./tools/your_binary -``` - -#### Python Bindings - -```bash -# Install with uv (recommended) -uv pip install . - -# Or for development (faster rebuilds) -uv pip install --no-build-isolation -Cbuild-dir=/tmp/build -v . -``` - -The wheel build enables the EEPROM-editor GUI (`BUILD_EEPROM_EDITOR=ON` in -`pyproject.toml`), which needs `imgui` and `glfw`. The cibuildwheel images provide -them; on a bare environment that lacks them, either install them (via conan) or -disable the GUI for the build: - -```bash -uv pip install --config-setting=cmake.define.BUILD_EEPROM_EDITOR=OFF . -``` - -#### Multi wheel (CI) -This project use cibuildwheel to generate multiples wheel to support all configurations. To use it locally, call: -```bash -uvx cibuildwheel -``` - -
-Manual Build Setup - -```bash -# 1. Create build directory -mkdir -p build - -# 2. Install dependencies with conan -python3 -m venv kickcat_venv -source kickcat_venv/bin/activate -pip install conan - -conan install conan/conanfile.py -of=build/ \ - -pr:h conan/your_profile_host.txt \ - -pr:b conan/your_profile_target.txt \ - --build=missing -s build_type=Release - -# Or create the conan package directly -conan create conan/all --build=missing --version 2.5 -pr build/profile.txt - -# 3. Configure and build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -make -``` - -
- -
-Windows Build - -**Note:** Windows is NOT suitable for real-time use but useful for tools and testing. - -Requirements: -- Conan for Windows (tested with 2.9.1) -- gcc for Windows (tested with w64devkit 2.0.0) -- npcap (driver 1.80 + SDK 1.70) - -Follow the manual setup instructions above, using appropriate Windows paths. - -
- -
-PikeOS Build - -Tested on PikeOS 5.1 for native personality (p4ext). - -Provide a CMake cross-toolchain file that defines the `PIKEOS` variable. Example process/thread configurations are in `examples/PikeOS/p4ext_config.c`. - -
- ---- - -## Getting Started: Complete Walkthrough - -This section provides a complete end-to-end example using the **Freedom K64F board** with LAN9252 EtherCAT slave controller. - -### Hardware Requirements - -- **NXP Freedom K64F** development board -- **LAN9252** EtherCAT slave controller (SPI connection) -- **Ethernet cable** (connects slave to your PC) -- **USB cable** (for programming the board) -- **Linux PC** with EtherCAT master - -See the [NuttX Prerequisite section](#nuttx-prerequisite) before starting. - -### Step 1: Build the Slave Firmware - -```bash -# Build firmware for Freedom K64F -./scripts/build_slave_bin.sh freedom-k64f ~/nuttxspace/nuttx - -# Output will be in: build_freedom-k64f/easycat_frdm_k64f.bin -# We have deployment scripts that are available here is an example: -./examples/slave/nuttx/lan9252/freedom-k64f/deploy.sh build_freedom-k64f/easycat_frdm_k64f.bin -``` - -### Step 2: Flash the Firmware - -```bash -# Deploy to the board (connects via USB) -./examples/slave/nuttx/lan9252/freedom-k64f/board/deploy.sh \ - build_freedom-k64f/easycat_frdm_k64f.bin -``` - -### Step 3: Program the EEPROM - -The EtherCAT slave requires EEPROM configuration with device information: - -```bash -# Connect slave to your PC via Ethernet -# Write EEPROM (interface ? will be auto-detected) -sudo ./tools/eeprom -s 0 -c write -f examples/slave/nuttx/lan9252/freedom-k64f/eeprom.bin -i "?" -``` +KickCAT is a thin EtherCAT stack designed to be embedded in larger software with +efficiency in mind. It provides **both master and slave** implementations in one +codebase and runs on Linux (including RT-PREEMPT), Windows, PikeOS, and NuttX. -**Note:** The `?` tells the tool to auto-detect the interface where the slave is connected. - -### Step 4: Run the Master - -Now you can control your slave using either C++ or Python: - -**Option A - C++ Master:** -```bash -# Run master example and follow terminal instructions -sudo ./build/examples/master/freedom-k64f/freedom_k64f_example -i "?" -``` - -**Option B - Python Master:** -```bash -# Install KickCAT Python bindings -pip install kickcat - -# Grant raw access to Python interpreter -./py_bindings/enable_raw_access.sh - -# Run Python example -python py_bindings/examples/freedom-k64f.py -i enp8s0 -``` - -### Expected Output - -![Freedom K64F Master-Slave Communication](doc/freedom-demo.gif) - -The master will: -1. Discover the slave on the network -2. Transition through states: INIT → PRE-OP → SAFE-OP → OP -3. Begin exchanging process data (PDOs) -4. Display diagnostic information - -
-Troubleshooting - -**Slave not detected:** -- Check Ethernet cable connection -- Verify EEPROM was written successfully -- Check that slave firmware is running (LED indicators) - -**Permission denied:** -- Ensure you're running master with `sudo` or proper capabilities -- For Python: run `./py_bindings/enable_raw_access.sh` - -**Interface not found:** -- List available interfaces: `ip link show` -- Use the correct interface name (e.g., `eth0`, `enp8s0`, `eno1`) - -
- ---- - -## Getting Started with Examples - -KickCAT includes working master and slave examples that work together out of the box. - -### Master Examples - -Located in `examples/master/`: - -- **easycat**: Basic example for EasyCAT shield -- **elmo**: Motor control example (Elmo drives) -- **ingenia**: Motor control example (Ingenia drives) -- **freedom-k64f**: Example for Kinetis Freedom board -- **gateway**: EtherCAT mailbox gateway implementation -- **load_esi**: ESI file loading utility - -#### Running a Master Example - -**C++ Examples:** -```bash -cd build -./examples/master/easycat/easycat_example -i eth0 -``` - -**Python Examples:** - -KickCAT is available on PyPI for easy installation: - -```bash -# Install from PyPI -pip install kickcat - -# Run Python examples -python py_bindings/examples/freedom-k64f.py --interface eth0 - -# With redundancy -python py_bindings/examples/easycat.py -i eth0 -r eth1 -``` - -
-Python Examples Help - -```bash -$ python py_bindings/examples/freedom-k64f.py --help -usage: freedom-k64f.py [-h] -i INTERFACE [-r REDUNDANCY] - -EtherCAT master for Freedom K64F using EasyCAT - -options: - -h, --help show this help message and exit - -i INTERFACE, --interface INTERFACE - Primary network interface (e.g., eth0) - -r REDUNDANCY, --redundancy REDUNDANCY - Redundancy network interface (e.g., eth1) -``` - -**Important:** Python interpreter needs raw socket capabilities: -```bash -# Use the helper script to grant permissions -./py_bindings/enable_raw_access.sh -``` - -
- -Replace `eth0` with your network interface name. - -### Slave Examples - -Located in `examples/slave/nuttx/`: - -**Supported Boards:** -- **XMC4800** (Infineon XMC4800 Relax Kit) -- **Arduino Due** (with EasyCAT shield + LAN9252) -- **Freedom K64F** (NXP Kinetis with LAN9252) - -#### NuttX Prerequisite: -
-NuttX Setup Instructions - -1. Install NuttX dependencies: https://nuttx.apache.org/docs/latest/quickstart/install.html -2. Download ARM GCC toolchain (>= 12.0): https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads - -#### Board-Specific Setup - -**Arduino Due:** -- Requires `bossac-1.6.1-arduino` for flashing (available from Arduino IDE installation) -- Connect USB to the port closest to power jack -- If flashing fails with "No device found on ttyACM0": - - Press ERASE button for a few seconds, release - - Press RESET button - - Try flashing again - -**Freedom K64F:** -- Standard OpenOCD flashing supported - -**XMC4800:** -- Use provided flashing scripts in board directory - -
- - -#### Building Slave Examples - -All slave examples use NuttX RTOS. Use the automated build script: - -```bash -./scripts/build_slave_bin.sh [build-name] -``` - -**Example:** -```bash -# Build for XMC4800 -./scripts/build_slave_bin.sh xmc4800-relax ~/nuttxspace/nuttx +CoE (CANopen over EtherCAT) is fully supported on both sides. Beyond the stack +itself, KickCAT ships a complete **software ESC and network emulator**, GUIs, and +CLI tooling, so a bus can be developed, configured, and tested without physical +hardware. -# Deploy for XMC4800 -./examples/slave/nuttx/xmc4800/deploy.sh build_xmc4800-relax/xmc4800_relax.bin +For exactly what is and isn't supported, see the +[feature support matrix](docs/FEATURES.md). -# Build for Arduino Due -./scripts/build_slave_bin.sh arduino-due ~/nuttxspace/nuttx +## Capabilities -# Deploy for Arduino Due -./examples/slave/nuttx/lan9252/arduino-due/deploy.sh build_arduino-due/easycat_arduino_due.bin +### Master stack -# Build for Freedom K64F -./scripts/build_slave_bin.sh freedom-k64f ~/nuttxspace/nuttx - -# Deploy for Freedom K64F -/examples/slave/nuttx/lan9252/freedom-k64f/deploy.sh build_freedom-k64f/easycat_frdm_k64f.bin -``` - -
-Testing Master-Slave Communication - -1. **Start the simulator or flash a slave:** - ```bash - # Option A: Use simulator with TAP socket (preferred, same machine) - ./build/simulation/network_simulator -i "?" -s simulation/slave_configs/freedom-k64f.json - # Then select "tap:server" - - # Option B: Flash real hardware (e.g., Arduino Due) - ./scripts/build_slave_bin.sh arduino-due ~/nuttxspace/nuttx - # Flash the resulting binary to your board - ``` - -2. **Run a master example:** - ```bash - # With TAP socket (select "tap:client"): - sudo ./build/examples/master/easycat/easycat_example -i "?" - - # Or with the interface connected to your slave: - sudo ./build/examples/master/easycat/easycat_example -i eth0 - ``` - -3. **Expected behavior:** - - Master transitions slave through states: INIT → PRE-OP → SAFE-OP → OP - - PDO data exchange begins in OP state - - Check console output for diagnostics - -
- ---- - -## Multi-Slave Example - -This example demonstrates how to run **multiple EtherCAT slaves** (Freedom and XMC4800) on the same bus and read their data using the EasyCAT Python master. - -### 1. Build and deploy the slave firmware - -Follow the [Building Slave Examples section](#building-slave-examples) section to build and flash the firmware on both boards: -- **Freedom board** -- **XMC4800 board** - -Make sure both slaves are running before continuing. - -### 2. Connect the hardware - -Wire the boards according to the setup below: - -![Multi Slave Example Setup](doc/multi-slave-example.png) - -Ensure that: -- All slaves are connected in the correct EtherCAT order -- The master interface is connected to the first slave - -### 3. Run the Python EasyCAT master - -Start the EasyCAT Python master to read and display data from each detected slave: - -```bash -python ./py_bindings/examples/easycat.py -i eth0 -``` -Replace eth0 with the network interface connected to your EtherCAT bus if needed - -### 3. Expected output -The master will enumerate all slaves on the bus and continuously print their input/output data. - -Expected output: -![Multi Slave EasyCAT Example Output](doc/multi-slave-easycat-output.gif) ---- - -## Simulator - -Test your EtherCAT applications without physical hardware using the built-in network simulator. -See [`simulation/README.md`](simulation/README.md) for the full guide (architecture, config schema, named TAP segments). - -The quickest way to drive an emulated slave is the **in-process** example — master and slave in one process, no network setup: - -```bash -./build/examples/master/simulated_bus/simulated_bus -f "Beckhoff EL1xxx.xml" -t EL1008 -``` - -The two-process `network_simulator` (below) is closer to a real setup and lets an unmodified master example connect over a TAP socket. - -### Transport Options - -When the master and the simulator run on the **same machine**, prefer using **TAP sockets** (shared-memory IPC) instead of a virtual Ethernet pair. TAP sockets are faster and simpler to set up — no kernel `veth` configuration is needed. - -When using the `?` interactive selector for the network interface, two TAP entries are available: -- `tap:server` — to be used by the simulator -- `tap:client` — to be used by the master - -If you need to run the master and simulator on **different machines**, use real network interfaces or a virtual Ethernet pair: - -```bash -# Create a virtual ethernet pair (Linux, same machine, alternative to TAP) -sudo ./simulation/create_virtual_ethernet.sh create veth -``` - -### Running the Simulator - -```bash -# Start the simulator (must be started before the master) -# Using TAP socket (preferred, same machine): -./build/simulation/network_simulator -i "?" -s simulation/slave_configs/freedom-k64f.json -# Then select "tap:server" - -# Using a specific interface: -./build/simulation/network_simulator -i eth1 -s simulation/slave_configs/freedom-k64f.json - -# Multiple slaves: -./build/simulation/network_simulator -i "?" -s simulation/slave_configs/freedom-k64f.json simulation/slave_configs/xmc4800.json -``` - -Then start the master on the matching transport: -```bash -# Using TAP socket (select "tap:client"): -sudo ./build/examples/master/easycat/easycat_example -i "?" - -# Or with a specific interface: -sudo ./build/examples/master/easycat/easycat_example -i eth0 -``` - -**Current Capabilities:** -- Load EEPROM configurations -- Emulate basic sync manager behavior -- Emulate basic FMMU (Fieldbus Memory Management Unit) behavior -- CoE mailbox with SDO support (via ESI XML) -- DL status / network topology emulation - -**Limitations:** -- No interrupt emulation -- No redundancy support yet - ---- - -## Tools - -KickCAT includes several utility tools in the `tools/` directory: - -- **eeprom**: Read/write/dump EEPROM from ESC -- **scan_topology**: Scan the bus and display the network topology as an ASCII tree -- **check_network_stability**: Monitor packet loss and corruption over time (Linux only) -- **od_generator**: Generate Object Dictionary code from ESI files -- **EtherCAT GUI**: PySide6-based graphical interface for bus monitoring - -Build tools with the main project: -```bash -cd build -make -ls tools/ # Your built tools will be here -``` - -
-EtherCAT GUI Tool Usage - -The KickCAT EtherCAT GUI is a PySide6-based application for monitoring and controlling the EtherCAT bus. - -#### Requirements -- Python 3 -- PySide6 -- `kickcat` package (installed via `pip install -e .` in project root) - -#### Usage -Run the application as a module from the project root: -```bash -python -m tools.ethercat_gui -i enp8s0 -``` -
- -### EEPROM Tool Usage - -Read, write, or dump EEPROM content from your EtherCAT Slave Controller: - -```bash -# Write EEPROM to slave at position 0 -sudo ./tools/eeprom -s 0 -c write -f path/to/eeprom.bin -i - -# Auto-detect interface -sudo ./tools/eeprom -s 0 -c write -f path/to/eeprom.bin -i "?" - -# Read EEPROM from slave -sudo ./tools/eeprom -s 0 -c read -f output.bin -i -``` - -### Object Dictionary Generator - -If your application uses CoE mailbox with SDO, you can generate Object Dictionary code from ESI files. - -#### Generate OD from ESI File - -```bash -# Generate od_populator.cc from your ESI file -./tools/od_generator -f your_device.esi - -# This creates: od_populator.cc -``` - -#### Using Generated OD Code - -Include the generated file in your slave application: - -```cpp -#include "od_populator.h" - -int main() { - // Initialize your slave - // ... - - // Populate Object Dictionary - auto dictionary = CoE::createOD(); - - // Continue with slave operation - // ... -} -``` - -#### Custom OD Population - -You can also manually create `od_populator.cc` by implementing the `CoE::createOD()` function. - -**Examples:** See `examples/slave/nuttx/xmc4800/od_populator.cc` and `examples/slave/nuttx/lan9252/freedom-k64f/od_populator.cc` for reference implementations. - ---- - -## Architecture - -### Master Stack Status - -✅ **Implemented:** -- Full EtherCAT state machine (INIT, PRE-OP, SAFE-OP, OP) -- Process data (PI) read/write -- CoE: SDO read/write (blocking and async) -- CoE: Emergency messages -- CoE: SDO Information service +- Full EtherCAT State Machine (INIT, PRE-OP, SAFE-OP, OP) +- Process data (PDO) read/write +- CoE: SDO read/write (expedited, normal, segmented), SDO Information, Emergency - Bus diagnostics with error counters - Cable redundancy -- Hook system for non-compliant slaves - Consecutive writes (up to 255 datagrams in flight) -- EtherCAT mailbox gateway (ETG.8200) -- Distributed Clock (DC) support - experimental -- AF_XDP Linux socket for improved performance *(available, opt-in)* - -📋 **Planned:** -- CoE: Segmented transfer (partial) -- CoE: Diagnosis message (0x10F3) -- FoE, EoE, AoE, SoE profiles -- Auto-discovery of broken wires -- Addressing groups (multi-PDO) +- Mailbox gateway (ETG.8200) +- Distributed Clock (experimental) +- AF_XDP socket backend on Linux (opt-in, lower latency) +- Python bindings -### Slave Stack Status +### Slave stack -✅ **Implemented:** -- State machine: INIT → PRE-OP → SAFE-OP → OP +- Full EtherCAT State Machine - Process data read/write +- CoE: Object Dictionary, SDO (including segmented and SDO Information) - ESC support: LAN9252 (SPI), XMC4800 -- CoE: Object dictionary -- CoE: SDO support -- EEPROM flash/dump tools -- CTT (Conformance Test Tool) validated (WDC_FOOT) +- EEPROM provisioning tooling +- Conformance Test Tool (CTT) validated (WDC_FOOT) -📋 **Planned:** -- Extended mailbox protocols (SDO Information, FoE, EoE) -- Multi-PDO support (>2 sync managers) -- Distributed clock -- Enhanced error reporting via AL_STATUS +### Mailbox protocols ---- +CoE is fully supported on master and slave. FoE and EoE are planned. SoE, AoE, +and VoE are not currently on the roadmap (no maintainer hardware to test +against) -- contributions are welcome. See the +[feature support matrix](docs/FEATURES.md) for the full breakdown. -## Performance Optimization (Linux) - -For real-time performance on Linux: - -1. **Use RT-PREEMPT kernel** - ```bash - # Check if RT patches are applied - uname -a | grep PREEMPT - ``` - -2. **Set real-time scheduler** - ```bash - sudo chrt -f 80 ./your_ethercat_app - ``` - -3. **Disable NIC interrupt coalescing** - ```bash - sudo ethtool -C eth0 rx-usecs 0 tx-usecs 0 - ``` - -4. **Disable RT throttling** - ```bash - echo -1 | sudo tee /proc/sys/kernel/sched_rt_runtime_us - ``` - -5. **Isolate CPU cores** - ```bash - # Add to kernel boot parameters - isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3 - ``` - -6. **Adjust network IRQ priority** - ```bash - # Find IRQ number - cat /proc/interrupts | grep eth0 - # Set priority - sudo chrt -f 90 -p - ``` - -7. **Use AF_XDP socket (optional, Linux only)** - - AF_XDP bypasses most of the kernel networking stack by using shared memory - ring buffers between user space and the NIC driver. This can reduce latency - significantly compared to the default `AF_PACKET` raw socket. - - **Build requirements:** `libxdp-dev`, `libbpf-dev`, `clang` (for BPF compilation). - - ```bash - # Install dependencies (Debian/Ubuntu) - sudo apt install libxdp-dev libbpf-dev clang - - # Build with AF_XDP support - cmake .. -DENABLE_AF_XDP=ON - make - ``` - - **Usage:** prefix your interface name with `xdp:` to select the AF_XDP backend: - - ```bash - # AF_XDP socket (requires CAP_NET_ADMIN + CAP_BPF, or root) - sudo ./examples/master/easycat/easycat_example -i xdp:eth0 - - # Regular AF_PACKET socket (default, unchanged) - sudo ./examples/master/easycat/easycat_example -i eth0 - ``` - - **Requirements:** - - Linux kernel >= 5.4 with `CONFIG_XDP_SOCKETS=y` (see check below) - - `CAP_NET_ADMIN` + `CAP_BPF` capabilities (or root) - - NIC driver with XDP support (most modern drivers: `i40e`, `ixgbe`, `mlx5`, `igc`, `e1000e`, etc.) - - **Verify kernel support:** - ```bash - grep CONFIG_XDP_SOCKETS /boot/config-$(uname -r) - # Expected: CONFIG_XDP_SOCKETS=y - ``` - - If `CONFIG_XDP_SOCKETS` is not set or missing, add the following to your kernel - configuration and rebuild: - ``` - CONFIG_BPF_SYSCALL=y - CONFIG_XDP_SOCKETS=y - CONFIG_XDP_SOCKETS_DIAG=y - ``` +### Tooling and GUIs ---- +- **KickUI** -- ImGui bus dashboard: topology view, SDO/PDO panels, DS402 motor bench +- **EEPROM editor** -- ImGui structured SII/EEPROM editor +- **eeprom** -- CLI to read/write slave EEPROM +- **scan_topology** -- enumerate slaves and per-port link status +- **check_network_stability** -- monitor packet loss/corruption over time (Linux) +- **od_generator** -- generate CoE Object Dictionary code from ESI files +- **ethercat_gui** -- PySide6 bus monitoring application -### EtherCAT Specifications -- [ETG Official Documentation](https://www.ethercat.org/en/downloads.html) -- [Beckhoff EtherCAT Documentation](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_io_intro/1257993099.html) -- [ESC Datasheets](https://download.beckhoff.com/download/document/io/ethercat-development-products/) +See [docs/TOOLS.md](docs/TOOLS.md) for usage. -### Learning Resources -- [EtherCAT Device Protocol Poster](https://www.ethercat.org/download/documents/EtherCAT_Device_Protocol_Poster.pdf) -- [EtherCAT Diagnostics Guide](https://www.automation.com/en-us/articles/2014-2/diagnostics-with-ethercat-part-4) -- [ESC Comparison](https://download.beckhoff.com/download/document/io/ethercat-development-products/an_esc_comparison_v2i7.pdf) +### Simulation and emulation ---- +KickCAT includes a software EtherCAT Slave Controller (`EmulatedESC`: registers, +SyncManagers, FMMUs, DC clock, EEPROM) and a network fabric (`EmulatedNetwork`: +topology routing, redundancy, runtime wire break/heal). Run a master against it +either in-process (the `simulated_bus` example) or as a separate process +(`network_simulator` over a shared-memory TAP socket). -## Testing +See [docs/SIMULATION.md](docs/SIMULATION.md). -### Unit Tests +## Getting started + +Build the stack and tools (Linux): ```bash -# Enable unit tests and set up ./scripts/configure.sh build --with=unit_tests ./scripts/setup_build.sh build - -# Build and run tests -cd build && make -make test +cd build && make -j ``` -### Code Coverage +Then run a master against an emulated slave -- no hardware, single process: -Install gcovr and build with coverage: ```bash -# Install gcovr -uv pip install gcovr - -# Enable tests + coverage -./scripts/configure.sh build --with=unit_tests --with=code_coverage -./scripts/setup_build.sh build - -# Build and generate coverage -cd build && make -make coverage +./build/examples/master/simulated_bus/simulated_bus \ + -f examples/slave/nuttx/lan9252/freedom-k64f/freedom-k64f.xml -t Board ``` ---- - -## Release Process - -KickCAT follows [Semantic Versioning](https://semver.org/). +Point `-f` at any vendor ESI XML and `-t` at the device `` to emulate a +different slave. Beckhoff publish ESI files for their devices at +. -### Release Requirements +Where to go next: -Before a version leaves release candidate status: -- ✅ 5+ continuous days of testing without bugs (no realtime loss, crashes, or memory leaks) -- ✅ 80% line coverage and 50% branch coverage for master/slave stack +- Build, install, Python bindings, Windows/PikeOS -- [docs/BUILDING.md](docs/BUILDING.md) +- Real hardware: slave firmware, flashing, end-to-end walkthrough -- [docs/HARDWARE.md](docs/HARDWARE.md) +- Simulator and emulator -- [docs/SIMULATION.md](docs/SIMULATION.md) +- Tools and GUIs -- [docs/TOOLS.md](docs/TOOLS.md) +- Real-time performance tuning -- [docs/PERFORMANCE.md](docs/PERFORMANCE.md) -Note: integration test is done with the master running on Linux (x86-64) and the slave is the Freedom-K64F - ---- - -## Contributing +## Platform support -Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. +The master runs as a host OS process; the slave runs the stack on an RTOS +against an EtherCAT Slave Controller (ESC). -### Development Setup +**Master (host OS):** -```bash -# Clone repository -git clone https://github.com/leducp/KickCAT.git -cd KickCAT +| OS | Architecture | Status | +|------------|--------------|----------------------------------------------| +| Linux | x86-64 | Production; RT-PREEMPT recommended for real-time | +| PikeOS 5.1 | ARMv8 | Production | +| Windows | x86-64 | Tools and testing only (not real-time) | -# Configure and setup build environment -./scripts/configure.sh build --with=unit_tests -./scripts/setup_build.sh build +**Slave (NuttX RTOS + ESC):** -# Build -cd build && make -``` +| ESC | Interface | Status | +|----------|------------|--------------------------| +| LAN9252 | SPI | Production | +| XMC4800 | Integrated | Production; CTT validated | ---- +Reference boards: NXP Freedom K64F (LAN9252), Arduino Due with EasyCAT shield +(LAN9252), Infineon XMC4800 Relax Kit (XMC4800). See the +[hardware guide](docs/HARDWARE.md). -## Platform Support +## Documentation -| Platform | Type | Status | -|----------|--------|-------| -| Linux (x86_64) | Master | Production - RT_PREEMPT Recommended for real-time | -| Windows | Master | ⚠️ Testing/tools only | -| PikeOS 5.1 (ARMv8) | Master | Production | -| NuttX RTOS | Slave | Production | -| Arduino Due | Slave | via NuttX | -| Infineon XMC4800 | Slave | via NuttX, CTT validated | -| NXP Freedom K64F | Slave | via NuttX | +- [Feature support matrix](docs/FEATURES.md) +- [Building and installing](docs/BUILDING.md) +- [Hardware guide](docs/HARDWARE.md) +- [Simulation and emulation](docs/SIMULATION.md) +- [Tools and GUIs](docs/TOOLS.md) +- [Real-time performance tuning](docs/PERFORMANCE.md) +- [Architecture overview](docs/architecture.md) +- [Release procedure](release/RELEASE_PROCEDURE.md) ---- +### EtherCAT references -## Known Limitations +- [ETG official documentation](https://www.ethercat.org/en/downloads.html) +- [Beckhoff EtherCAT documentation](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_io_intro/1257993099.html) +- [Beckhoff ESI (XML device description) files](https://download.beckhoff.com/download/configuration-files/io/ethercat/xml-device-description) +- [ESC datasheets](https://download.beckhoff.com/download/document/io/ethercat-development-products/) +- [EtherCAT Device Protocol Poster](https://www.ethercat.org/download/documents/EtherCAT_Device_Protocol_Poster.pdf) +- [ESC comparison](https://download.beckhoff.com/download/document/io/ethercat-development-products/an_esc_comparison_v2i7.pdf) -### Master Stack -- **Little-endian only**: Current implementation supports little-endian hosts only -- **Windows**: Not suitable for real-time applications -- **Mailbox protocols**: Limited to CoE SDO (FoE, EoE planned) +## Contributing -### Slave Stack -- **PDO limitation**: Currently supports up to 2 sync managers (working on multi-PDO) -- **Distributed Clock**: Not yet implemented -- **Mailbox protocols**: Limited to CoE SDO (FoE, EoE planned) +Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for code-style +and PR conventions, and [docs/BUILDING.md](docs/BUILDING.md) for the development +build (unit tests and coverage). ---- +KickCAT follows [Semantic Versioning](https://semver.org/). The release workflow +is documented in [release/RELEASE_PROCEDURE.md](release/RELEASE_PROCEDURE.md). ## License [CeCILL-C](LICENSE) ---- - -## Support & Community - -- **Issues**: [GitHub Issues](https://github.com/leducp/KickCAT/issues) -- **Discussions**: [GitHub Discussions](https://github.com/leducp/KickCAT/discussions) -- **Conan Package**: [conan-center-index](https://github.com/conan-io/conan-center-index/tree/master/recipes/kickcat) - ---- - -## Project Status - -🟢 **Active Development** - KickCAT is actively maintained and used in production systems. +## Support -**Current Version**: Check [Releases](https://github.com/leducp/KickCAT/releases) for the latest stable version. +- Issues: [GitHub Issues](https://github.com/leducp/KickCAT/issues) +- Discussions: [GitHub Discussions](https://github.com/leducp/KickCAT/discussions) +- Conan package: [conan-center-index](https://github.com/conan-io/conan-center-index/tree/master/recipes/kickcat) -**Release Cycle**: Following semantic versioning with thorough testing before each major release. +Status: actively maintained and used in production systems. See +[Releases](https://github.com/leducp/KickCAT/releases) for the latest stable +version. diff --git a/docs/BUILDING.md b/docs/BUILDING.md new file mode 100644 index 00000000..fb429c0a --- /dev/null +++ b/docs/BUILDING.md @@ -0,0 +1,168 @@ +# Building KickCAT + +KickCAT uses Conan (for dependencies) and CMake, wrapped by the scripts in +`scripts/`. Prefer the wrappers over invoking `cmake` / `conan` directly. + +## Prerequisites + +- **Linux**: gcc, cmake, conan +- **Python bindings**: uv or pip +- **Hardware access**: a network interface with raw-socket capability + +## Linux (recommended) + +```bash +# 1. Configure build options (optional -- defaults are sensible) +./scripts/configure.sh build --with=unit_tests + +# 2. Set up the build environment (installs Conan deps + runs CMake) +./scripts/setup_build.sh build + +# 3. Build +cd build && make -j + +# 4. Grant network capabilities (required for raw-socket access) +sudo setcap 'cap_net_raw,cap_net_admin=+ep' ./tools/your_binary +``` + +`configure.sh` stores the selected options in `.buildconfig`, which +`setup_build.sh` consumes. Available options include `unit_tests`, +`code_coverage`, `esi_parser`, `simulation`, `tools`, `master_examples`, +`slave_examples`, `eeprom_editor`, and `kickui`. + +### Rebuild after source changes + +From an existing `build` directory, no need to re-run `configure.sh` / +`setup_build.sh` unless CMakeLists or Conan deps changed: + +```bash +cd build && make -j +``` + +### GUI tools (KickUI, EEPROM editor) + +These are off by default and need ImGui + GLFW: + +```bash +./scripts/configure.sh build --with=kickui --with=eeprom_editor +./scripts/setup_build.sh build +cd build && make -j +``` + +See [TOOLS.md](TOOLS.md) for what each tool does. + +## Python bindings + +```bash +# Install with uv (recommended) +uv pip install . + +# Or for development (faster rebuilds) +uv pip install --no-build-isolation -Cbuild-dir=/tmp/build -v . +``` + +The wheel build enables the EEPROM-editor GUI (`BUILD_EEPROM_EDITOR=ON` in +`pyproject.toml`), which needs `imgui` and `glfw`. The cibuildwheel images +provide them; on a bare environment that lacks them, either install them (via +Conan) or disable the GUI for the build: + +```bash +uv pip install --config-setting=cmake.define.BUILD_EEPROM_EDITOR=OFF . +``` + +The published package is available on PyPI: + +```bash +pip install kickcat +``` + +The Python interpreter needs raw-socket capabilities to drive a bus: + +```bash +./py_bindings/enable_raw_access.sh +``` + +### Multi-wheel (CI) + +This project uses cibuildwheel to generate wheels for all supported +configurations. To run it locally: + +```bash +uvx cibuildwheel +``` + +## Manual build (without the wrapper scripts) + +```bash +# 1. Create the build directory +mkdir -p build + +# 2. Install dependencies with Conan +python3 -m venv kickcat_venv +source kickcat_venv/bin/activate +pip install conan + +conan install conan/conanfile.py -of=build/ \ + -pr:h conan/your_profile_host.txt \ + -pr:b conan/your_profile_target.txt \ + --build=missing -s build_type=Release + +# Or create the Conan package directly +conan create conan/all --build=missing --version 2.5 -pr build/profile.txt + +# 3. Configure and build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Release +make -j +``` + +## Windows + +Windows is **not** suitable for real-time use, but it is useful for tools and +testing. + +Requirements: + +- Conan for Windows (tested with 2.9.1) +- gcc for Windows (tested with w64devkit 2.0.0) +- npcap (driver 1.80 + SDK 1.70) + +Follow the manual build instructions above, using the appropriate Windows paths +and the `conan/profile_windows_x86_64.txt` profile. + +## PikeOS + +Tested on PikeOS 5.1 for the native personality (p4ext). + +Provide a CMake cross-toolchain file that defines the `PIKEOS` variable. Example +process/thread configurations are in `lib/src/OS/PikeOS/p4ext_config.c`. + +## Slave firmware + +Building and flashing NuttX slave firmware (XMC4800, Arduino Due, Freedom K64F) +is covered in [HARDWARE.md](HARDWARE.md). + +## Testing + +### Unit tests + +```bash +./scripts/configure.sh build --with=unit_tests +./scripts/setup_build.sh build +cd build && make -j +make test +``` + +The test binary is `build/kickcat_unit`. Run a subset with +`./kickcat_unit --gtest_filter='*'`. + +### Code coverage + +```bash +uv pip install gcovr + +./scripts/configure.sh build --with=unit_tests --with=code_coverage +./scripts/setup_build.sh build +cd build && make -j +make coverage +``` diff --git a/docs/FEATURES.md b/docs/FEATURES.md new file mode 100644 index 00000000..f56a4d98 --- /dev/null +++ b/docs/FEATURES.md @@ -0,0 +1,120 @@ +# Feature support matrix + +This document maps the EtherCAT feature set to what the KickCAT stack currently +provides, separately for the **master** and **slave** sides. It is the +authoritative source for "what works today"; the README only summarizes it. + +## Legend + +| Status | Meaning | +|----------------|--------------------------------------------------------------------------------| +| Supported | Implemented and exercised by examples and/or unit tests. | +| Partial | Usable but incomplete, or limited to a subset of the feature. | +| Experimental | Implemented but not yet validated for production use. | +| Planned | On the roadmap; protocol definitions or scaffolding may already exist. | +| Not planned | No maintainer hardware to develop or test against. Contributions are welcome. | +| Not applicable | The feature does not apply to that side. | + +## State machine and process data + +| Feature | Master | Slave | +|------------------------------------------|-----------|-----------| +| EtherCAT State Machine (INIT/PRE-OP/SAFE-OP/OP) | Supported | Supported | +| Process data exchange (PDO read/write) | Supported | Supported | +| FMMU configuration | Supported | Supported | +| SyncManager configuration | Supported | Supported | +| One input + one output PDO mapping per slave | Supported | Supported | +| Multiple PDO SyncManagers (>1 input or >1 output per slave) | Not supported | Not supported | +| Mailbox status polling via dedicated FMMUs (LRD/LRW) | Supported | Not applicable | + +## Mailbox protocols + +| Protocol | Master | Slave | Notes | +|----------|------------|------------|-------| +| CoE (CANopen over EtherCAT) | Supported | Supported | See CoE breakdown below. | +| FoE (File over EtherCAT) | Planned | Planned | Protocol header only; no mailbox handlers yet. | +| EoE (Ethernet over EtherCAT)| Planned | Planned | Protocol header only; no mailbox handlers yet. | +| SoE (Servo over EtherCAT) | Not planned | Not planned | ESI parsing only. No maintainer hardware; contributions welcome. | +| AoE (ADS over EtherCAT) | Not planned | Not planned | ESI parsing only. No maintainer hardware; contributions welcome. | +| VoE (Vendor over EtherCAT) | Not planned | Not planned | ESI parsing only. No maintainer hardware; contributions welcome. | + +### CoE breakdown + +| CoE feature | Master | Slave | +|-------------------------------------|-----------|-----------| +| SDO expedited upload/download | Supported | Supported | +| SDO normal (sized) transfer | Supported | Supported | +| SDO segmented transfer | Supported | Supported | +| SDO Information service | Supported | Supported | +| Emergency messages | Supported | Not applicable | +| Object Dictionary | Not applicable | Supported | +| PDO mapping / assignment | Supported | Supported | + +## Distributed Clocks (DC) + +| Feature | Master | Slave | +|---------------------------------|--------------|-------------| +| DC synchronization | Experimental | Planned | +| DC modeling in the emulator | Supported (local clock, drift-ppm injection, SYNC0) | -- | + +The ESC emulator models a DC clock with configurable drift; see +[SIMULATION.md](SIMULATION.md). Slave-side DC is not implemented on real ESCs yet. + +## Networking and reliability + +| Feature | Master | Slave | +|-------------------------------------------|-----------|-----------| +| Cable redundancy | Supported | Not applicable | +| Consecutive writes (frames in flight) | Supported | Not applicable | +| Mailbox gateway (ETG.8200) | Supported | Not applicable | +| Bus diagnostics and error counters | Supported | Supported | +| AF_XDP socket backend (Linux, opt-in) | Supported | Not applicable | +| Auto-discovery of broken wires | Planned | Not applicable | + +## EEPROM / SII + +| Feature | Master | Slave | +|----------------------------------|-----------|-----------| +| SII (EEPROM) parsing | Supported | Supported | +| EEPROM read/dump tooling | Supported | Not applicable | +| EEPROM write (slave provisioning)| Supported | Not applicable | + +See [TOOLS.md](TOOLS.md) for the `eeprom` CLI and the EEPROM editor GUI. + +## Platforms + +The master and slave run on different layers: the master is a host OS process, +while the slave is the stack running on an RTOS against an EtherCAT Slave +Controller (ESC). + +### Master (host operating system) + +| OS | Architecture | Status | +|------------|--------------|----------------------------------------------| +| Linux | x86-64 | Supported; RT-PREEMPT recommended for real-time | +| PikeOS 5.1 | ARMv8 | Supported | +| Windows | x86-64 | Tools and testing only (not real-time) | + +### Slave (RTOS + ESC hardware) + +The slave stack runs on the NuttX RTOS. Supported EtherCAT Slave Controllers: + +| ESC | Interface | Status | +|----------|------------|--------------------------| +| LAN9252 | SPI | Supported | +| XMC4800 | Integrated | Supported; CTT-validated | + +Reference boards: NXP Freedom K64F (LAN9252), Arduino Due with EasyCAT shield +(LAN9252), and the Infineon XMC4800 Relax Kit (XMC4800). Build and flashing +details are in [HARDWARE.md](HARDWARE.md). + +## Known limitations + +- Little-endian hosts only. +- Windows is not suitable for real-time operation; use it for tooling and tests. +- Process data is limited to one input and one output PDO SyncManager per slave + on both master and slave; multiple PDO SyncManagers per direction are not + supported. +- Slave-side Distributed Clocks are not implemented. +- The simulator/emulator does not emulate interrupts; see + [SIMULATION.md](SIMULATION.md). diff --git a/docs/HARDWARE.md b/docs/HARDWARE.md new file mode 100644 index 00000000..17bad819 --- /dev/null +++ b/docs/HARDWARE.md @@ -0,0 +1,237 @@ +# Hardware guide: slaves, firmware, and deployment + +This guide covers running KickCAT against real hardware: building and flashing +NuttX slave firmware, provisioning EEPROM, and running a master against the +slaves. For a no-hardware path, use the simulator instead -- see +[SIMULATION.md](SIMULATION.md). + +Build the host-side binaries first; see [BUILDING.md](BUILDING.md). + +--- + +## Complete walkthrough: Freedom K64F + LAN9252 + +End-to-end example using the **NXP Freedom K64F** board with a **LAN9252** +EtherCAT slave controller. + +### Hardware requirements + +- NXP Freedom K64F development board +- LAN9252 EtherCAT slave controller (SPI connection) +- Ethernet cable (slave to PC) +- USB cable (for programming the board) +- Linux PC running the EtherCAT master + +See [NuttX prerequisites](#nuttx-prerequisites) before starting. + +### Step 1: Build the slave firmware + +```bash +./scripts/build_slave_bin.sh freedom-k64f ~/nuttxspace/nuttx +# Output: build_freedom-k64f/easycat_frdm_k64f.bin +``` + +### Step 2: Flash the firmware + +```bash +./examples/slave/nuttx/lan9252/freedom-k64f/deploy.sh \ + build_freedom-k64f/easycat_frdm_k64f.bin +``` + +### Step 3: Program the EEPROM + +The slave requires EEPROM configuration with device information: + +```bash +# Connect the slave to your PC via Ethernet, then write the EEPROM. +# "?" auto-detects the interface where the slave is connected. +sudo ./tools/eeprom -s 0 -c write -f examples/slave/nuttx/lan9252/freedom-k64f/eeprom.bin -i "?" +``` + +### Step 4: Run the master + +C++ master: + +```bash +sudo ./build/examples/master/freedom-k64f/freedom_k64f_static_map_example -i "?" +``` + +Python master: + +```bash +pip install kickcat +./py_bindings/enable_raw_access.sh +python py_bindings/examples/freedom-k64f.py -i enp8s0 +``` + +### Expected output + +![Freedom K64F master-slave communication](freedom-demo.gif) + +The master will: + +1. Discover the slave on the network. +2. Transition through INIT -> PRE-OP -> SAFE-OP -> OP. +3. Begin exchanging process data (PDOs). +4. Display diagnostic information. + +### Troubleshooting + +**Slave not detected** + +- Check the Ethernet cable connection. +- Verify the EEPROM was written successfully. +- Check that the slave firmware is running (LED indicators). + +**Permission denied** + +- Run the master with `sudo` or grant capabilities (see [BUILDING.md](BUILDING.md)). +- For Python: run `./py_bindings/enable_raw_access.sh`. + +**Interface not found** + +- List interfaces: `ip link show`. +- Use the correct interface name (e.g. `eth0`, `enp8s0`, `eno1`). + +--- + +## Examples + +### Master examples + +Located in `examples/master/`: + +- **easycat**: basic example for the EasyCAT shield +- **elmo**: motor control (Elmo drives) +- **ingenia**: motor control (Ingenia drives) +- **freedom-k64f**: example for the Kinetis Freedom board +- **gateway**: EtherCAT mailbox gateway (ETG.8200) +- **load_esi**: ESI file loading utility +- **wdc_foot**: CTT conformance example +- **simulated_bus**: in-process master + emulated slave (no hardware) -- see + [SIMULATION.md](SIMULATION.md) + +Run a C++ example: + +```bash +cd build +./examples/master/easycat/easycat_example -i eth0 +``` + +Run a Python example: + +```bash +pip install kickcat +python py_bindings/examples/freedom-k64f.py --interface eth0 + +# With redundancy +python py_bindings/examples/easycat.py -i eth0 -r eth1 +``` + +The Python interpreter needs raw-socket capabilities: + +```bash +./py_bindings/enable_raw_access.sh +``` + +### Slave examples + +Located in `examples/slave/nuttx/`. Supported boards: + +- **XMC4800** (Infineon XMC4800 Relax Kit) +- **Arduino Due** (with EasyCAT shield + LAN9252) +- **Freedom K64F** (NXP Kinetis with LAN9252) + +--- + +## NuttX prerequisites + +1. Install NuttX dependencies: + +2. Download the ARM GCC toolchain (>= 12.0): + + +### Board-specific setup + +**Arduino Due** + +- Requires `bossac-1.6.1-arduino` for flashing (from the Arduino IDE install). +- Connect USB to the port closest to the power jack. +- If flashing fails with "No device found on ttyACM0": press ERASE for a few + seconds, release, press RESET, and try again. + +**Freedom K64F** + +- Standard OpenOCD flashing supported. + +**XMC4800** + +- Use the provided flashing scripts in the board directory (J-Link). + +--- + +## Building and deploying slave firmware + +All slave examples use NuttX. Use the automated build script: + +```bash +./scripts/build_slave_bin.sh [build-name] +``` + +Examples: + +```bash +# XMC4800 +./scripts/build_slave_bin.sh xmc4800-relax ~/nuttxspace/nuttx +./examples/slave/nuttx/xmc4800/deploy.sh build_xmc4800-relax/xmc4800_relax.bin + +# Arduino Due +./scripts/build_slave_bin.sh arduino-due ~/nuttxspace/nuttx +./examples/slave/nuttx/lan9252/arduino-due/deploy.sh build_arduino-due/easycat_arduino_due.bin + +# Freedom K64F +./scripts/build_slave_bin.sh freedom-k64f ~/nuttxspace/nuttx +./examples/slave/nuttx/lan9252/freedom-k64f/deploy.sh build_freedom-k64f/easycat_frdm_k64f.bin +``` + +CI artifacts can be extracted and deployed with: + +```bash +./scripts/deploy_artifacts.sh +``` + +--- + +## Multi-slave example + +Run **multiple slaves** (Freedom and XMC4800) on the same bus and read their +data with the EasyCAT Python master. + +### 1. Build and deploy the firmware + +Follow the section above to build and flash both boards (Freedom and XMC4800). +Make sure both slaves are running before continuing. + +### 2. Connect the hardware + +![Multi-slave example setup](multi-slave-example.png) + +Ensure that: + +- All slaves are connected in the correct EtherCAT order. +- The master interface is connected to the first slave. + +### 3. Run the Python EasyCAT master + +```bash +python ./py_bindings/examples/easycat.py -i eth0 +``` + +Replace `eth0` with the interface connected to your bus. + +### Expected output + +The master enumerates all slaves and continuously prints their input/output +data. + +![Multi-slave EasyCAT example output](multi-slave-easycat-output.gif) diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md new file mode 100644 index 00000000..839e0423 --- /dev/null +++ b/docs/PERFORMANCE.md @@ -0,0 +1,94 @@ +# Real-time performance tuning (Linux) + +Guidance for low-latency, deterministic operation of a KickCAT master on Linux. + +## 1. Use an RT-PREEMPT kernel + +```bash +uname -a | grep PREEMPT +``` + +## 2. Set a real-time scheduler + +```bash +sudo chrt -f 80 ./your_ethercat_app +``` + +## 3. Disable NIC interrupt coalescing + +```bash +sudo ethtool -C eth0 rx-usecs 0 tx-usecs 0 +``` + +## 4. Disable RT throttling + +```bash +echo -1 | sudo tee /proc/sys/kernel/sched_rt_runtime_us +``` + +## 5. Isolate CPU cores + +Add to the kernel boot parameters: + +``` +isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3 +``` + +## 6. Adjust network IRQ priority + +```bash +# Find the IRQ number +cat /proc/interrupts | grep eth0 +# Set the priority of the IRQ thread +sudo chrt -f 90 -p +``` + +## 7. AF_XDP socket (optional) + +AF_XDP bypasses most of the kernel networking stack using shared-memory ring +buffers between user space and the NIC driver. This can reduce latency +significantly compared to the default `AF_PACKET` raw socket. + +**Build requirements:** `libxdp-dev`, `libbpf-dev`, `clang` (for BPF compilation). + +```bash +# Install dependencies (Debian/Ubuntu) +sudo apt install libxdp-dev libbpf-dev clang + +# Build with AF_XDP support +cmake .. -DENABLE_AF_XDP=ON +make -j +``` + +**Usage:** prefix the interface name with `xdp:` to select the AF_XDP backend: + +```bash +# AF_XDP socket (requires CAP_NET_ADMIN + CAP_BPF, or root) +sudo ./examples/master/easycat/easycat_example -i xdp:eth0 + +# Regular AF_PACKET socket (default, unchanged) +sudo ./examples/master/easycat/easycat_example -i eth0 +``` + +**Requirements:** + +- Linux kernel >= 5.4 with `CONFIG_XDP_SOCKETS=y` (see the check below) +- `CAP_NET_ADMIN` + `CAP_BPF` capabilities (or root) +- A NIC driver with XDP support (most modern drivers: `i40e`, `ixgbe`, `mlx5`, + `igc`, `e1000e`, and others) + +**Verify kernel support:** + +```bash +grep CONFIG_XDP_SOCKETS /boot/config-$(uname -r) +# Expected: CONFIG_XDP_SOCKETS=y +``` + +If `CONFIG_XDP_SOCKETS` is not set, add the following to your kernel +configuration and rebuild: + +``` +CONFIG_BPF_SYSCALL=y +CONFIG_XDP_SOCKETS=y +CONFIG_XDP_SOCKETS_DIAG=y +``` diff --git a/docs/SIMULATION.md b/docs/SIMULATION.md new file mode 100644 index 00000000..a5d9d608 --- /dev/null +++ b/docs/SIMULATION.md @@ -0,0 +1,152 @@ +# Simulation and emulation + +KickCAT ships with a software EtherCAT Slave Controller and a network fabric, so +a master application can be developed and tested with no physical hardware. + +There are two layers: + +- The **emulator** is the engine: `EmulatedESC` is a software ESC (registers, + SyncManagers, FMMUs, EEPROM, DC clock) and `EmulatedNetwork` wires emulated + ESCs into a topology. Both live in `lib/slave/include/kickcat/`. +- The **simulator** is how you run a master against the emulator: either + in-process (the `simulated_bus` example) or as a separate process + (`network_simulator`, under `simulation/`). + +--- + +## Emulator engine + +### EmulatedESC + +`EmulatedESC` (`lib/slave/include/kickcat/ESC/EmulatedESC.h`) models a complete +EtherCAT Slave Controller and is driven by the real slave stack +(`Slave` + `PDO` + optional CoE mailbox). It implements: + +- 16 SyncManagers, including mailbox SyncManagers (RxSM/TxSM). +- 16 FMMUs (logical-to-physical process-data mapping). +- AL (Application Layer) state machine registers and event masks. +- DL (Data Link) port descriptors, per-port link status, and error counters. +- EEPROM loaded from a raw `.bin` or compiled from an ESI XML device. +- A DC local clock with configurable drift (ppm), receive-time latching, + system-time offset, and SYNC0. +- PDI and process-data watchdogs. +- Per-port loopback behaviour (open/closed ports, circulating frames). + +A slave image is built either from a pre-made EEPROM `.bin` or, preferably, +compiled from an **ESI XML device** (the parser produces the SII image and the +CoE object dictionary). + +### EmulatedNetwork + +`EmulatedNetwork` (`lib/slave/include/kickcat/EmulatedNetwork.h`) routes frames +between emulated ESCs as a physical network would: + +- Explicit port-to-port wiring (default is a daisy-chained line topology). +- Frame walk in physical EtherCAT port order, branching depth-first. +- Head and tail master injection points for cable redundancy, with ring-closure + detection. +- Runtime wire break/heal (fault injection) via link-state changes. +- DC forwarding delays computed from the topology. + +### Current limitations + +- No interrupt emulation. +- See the matrix in [FEATURES.md](FEATURES.md) for DC and redundancy status. + +--- + +## Run modes + +### 1. In-process loopback (simplest) + +The master `Bus` and the emulated slave live in the **same process**, connected +by `kickcat::LoopbackSocket` (`lib/include/kickcat/LoopbackSocket.h`). No network +interface, no `/dev/shm`, no second terminal. Each master frame is run through +the slave and the slave is ticked once. This is the recommended starting point +and the easiest to debug (single process, single thread). + +Canonical example -- `examples/master/simulated_bus`: + +```bash +./build/examples/master/simulated_bus/simulated_bus \ + -f examples/slave/nuttx/lan9252/freedom-k64f/freedom-k64f.xml -t Board +``` + +`-f` takes any vendor ESI XML and `-t` selects the device by its `` +(omit `-t` to use the first device in the file). It builds the selected device, +drives INIT -> PRE_OP -> SAFE_OP -> OPERATIONAL, and exchanges process data -- +all in a short example you can read top to bottom. + +Beckhoff publish ESI files for their devices at +; +most vendors ship the matching ESI XML with their hardware. + +`test/integration/bench/esi_boot` uses the same loopback to boot an entire ESI +catalog in parallel (a good template for batch/regression use). + +### 2. Two-process over a TAP socket (network_simulator) + +`network_simulator` runs the slaves in their own process and exposes them on a +shared-memory **TAP socket**; a separate master process connects to the other +end. This is closer to a real two-node setup and lets an unmodified master +example run against the simulator. + +```bash +# Terminal 1 -- the simulator is the TAP server (start it first): +./build/simulation/network_simulator -i tap:server -s simulation/slave_configs/freedom-k64f.json + +# Terminal 2 -- the master is the TAP client: +sudo ./build/examples/master/easycat/easycat_example -i tap:client +``` + +To boot from an ESI device instead, point a config's `esi` key at your vendor's +ESI XML (see the schema below). + +Chain several slaves by passing multiple configs: + +```bash +./build/simulation/network_simulator -i tap:server \ + -s simulation/slave_configs/freedom-k64f.json simulation/slave_configs/xmc4800.json +``` + +#### Named TAP segments + +`tap:server` / `tap:client` use the default shared-memory name. Append `:` +to run independent server/client pairs side by side: + +```bash +./build/simulation/network_simulator -i tap:server:busA -s ... # one bus +./build/examples/master/.../example -i tap:client:busA # its master +``` + +#### Different machines + +If the master and the simulator run on **different machines**, use real network +interfaces or a virtual Ethernet pair instead of a TAP socket: + +```bash +# Create a virtual Ethernet pair (Linux, same machine, alternative to TAP) +sudo ./simulation/create_virtual_ethernet.sh create veth +``` + +--- + +## Slave config schema (JSON) + +One JSON object per slave file. Provide **exactly one** source of the slave +image -- `esi` or `eeprom`: + +| Key | Type | Notes | +|----------------|--------|--------------------------------------------------------------------| +| `esi` | string | Path to an ESI XML (relative to the config dir). Compiles the SII + CoE dictionary. | +| `device_type` | string | With `esi`: pick the device by `` (e.g. `"EL1008"`). | +| `product_code` | uint32 | With `esi`: pick the device by product code. | +| `revision_no` | uint32 | With `esi`: pick the device by revision. | +| `eeprom` | string | Path to a raw EEPROM `.bin` (alternative to `esi`). | +| `coe_xml` | string | With `eeprom`: optional CoE dictionary XML for the mailbox. | + +When several `esi` device filters match, the first match is used. With `esi`, +the CoE mailbox (if the device declares CoE) is built from the same device, so +`coe_xml` is not needed. + +Examples live in `simulation/slave_configs/`. diff --git a/docs/TOOLS.md b/docs/TOOLS.md new file mode 100644 index 00000000..05a4c11a --- /dev/null +++ b/docs/TOOLS.md @@ -0,0 +1,107 @@ +# Tools and GUIs + +KickCAT ships CLI utilities (in `tools/`), two ImGui-based GUIs, and a +PySide6 application. CLI tools build with the main project; the GUIs are opt-in. + +```bash +cd build && make -j +ls tools/ # built CLI tools land here +``` + +## KickUI (GUI) + +ImGui + GLFW dashboard for inspecting, configuring, and operating an EtherCAT +bus from the master side (`tools/kickui/`). It provides: + +- A network **topology view** with per-slave ports and links. +- **SDO** read/write across data types (integers, reals, strings, raw hex). +- **PDO** mapping inspection/editing and a real-time process-data view. +- A **DS402 motor bench** (setpoints, units configuration). +- An event/error log and an embedded simulator launcher. + +Off by default; build it with: + +```bash +./scripts/configure.sh build --with=kickui +./scripts/setup_build.sh build +cd build && make -j +./tools/kickui/kickui +``` + +## EEPROM editor (GUI) + +ImGui + GLFW structured editor for slave SII/EEPROM images +(`tools/eeprom_editor/`), with tabs for device info, strings, SyncManagers, +FMMUs, and PDO mappings. Off by default: + +```bash +./scripts/configure.sh build --with=eeprom_editor +./scripts/setup_build.sh build +cd build && make -j +./tools/eeprom_editor/kickcat_eeprom_editor +``` + +## eeprom (CLI) + +Read or write EEPROM content from an EtherCAT Slave Controller +(`tools/eeprom.cc`): + +```bash +# Write EEPROM to the slave at position 0 +sudo ./tools/eeprom -s 0 -c write -f path/to/eeprom.bin -i + +# Auto-detect the interface +sudo ./tools/eeprom -s 0 -c write -f path/to/eeprom.bin -i "?" + +# Read EEPROM from the slave (serializes the SII image to the output file) +sudo ./tools/eeprom -s 0 -c read -f output.bin -i +``` + +## scan_topology (CLI) + +Enumerate the slaves on the bus and display the topology, including each slave's +state and per-port DL status. + +## check_network_stability (CLI, Linux) + +Long-running monitor for packet loss and corruption: broadcast-reads in a loop +and reports tx/rx counts and error deltas over time. + +## od_generator (CLI) + +Generate Object Dictionary code from an ESI file (requires +`ENABLE_ESI_PARSER=ON`): + +```bash +# Generate od_populator.cc from an ESI file +./tools/od_generator -f your_device.esi +``` + +The generated `od_populator.cc` defines `CoE::createOD()` (declared in +`kickcat/CoE/OD.h`). Add it to your slave application's build and call it: + +```cpp +#include "kickcat/CoE/OD.h" + +int main() +{ + // ... + auto dictionary = CoE::createOD(); + // ... +} +``` + +You can also write `od_populator.cc` by hand by implementing `CoE::createOD()`. +See `examples/slave/nuttx/xmc4800/od_populator.cc` and +`examples/slave/nuttx/lan9252/freedom-k64f/od_populator.cc` for references. + +## ethercat_gui (Python, PySide6) + +A pure-Python GUI for master bus control, shipped with the Python package. + +Requirements: Python 3, PySide6, and the `kickcat` package +(`pip install -e .` from the project root). + +```bash +python -m tools.ethercat_gui -i enp8s0 +``` diff --git a/doc/architecture.md b/docs/architecture.md similarity index 100% rename from doc/architecture.md rename to docs/architecture.md diff --git a/doc/freedom-demo.gif b/docs/freedom-demo.gif similarity index 100% rename from doc/freedom-demo.gif rename to docs/freedom-demo.gif diff --git a/doc/kickCAT_logo_dark_mode.png b/docs/kickCAT_logo_dark_mode.png similarity index 100% rename from doc/kickCAT_logo_dark_mode.png rename to docs/kickCAT_logo_dark_mode.png diff --git a/doc/kickCAT_logo_light_mode.png b/docs/kickCAT_logo_light_mode.png similarity index 100% rename from doc/kickCAT_logo_light_mode.png rename to docs/kickCAT_logo_light_mode.png diff --git a/doc/multi-slave-easycat-output.gif b/docs/multi-slave-easycat-output.gif similarity index 100% rename from doc/multi-slave-easycat-output.gif rename to docs/multi-slave-easycat-output.gif diff --git a/doc/multi-slave-example.png b/docs/multi-slave-example.png similarity index 100% rename from doc/multi-slave-example.png rename to docs/multi-slave-example.png diff --git a/simulation/README.md b/simulation/README.md index 117dbdb3..76a9b886 100644 --- a/simulation/README.md +++ b/simulation/README.md @@ -1,86 +1,9 @@ # KickCAT simulator -Run a master against emulated EtherCAT slaves with no physical hardware. Each -emulated slave is an `EmulatedESC` (the ESC registers, SyncManagers, FMMUs and -EEPROM) driven by the slave stack (`Slave` + `PDO` + optional CoE mailbox). A -slave is built either from a pre-made EEPROM `.bin` or, preferably, **compiled -from an ESI XML device** (the parser produces the SII image and the CoE object -dictionary). +Run a master against emulated EtherCAT slaves with no physical hardware. -There are two ways to wire a master to emulated slaves. +The full guide -- emulator engine (`EmulatedESC` / `EmulatedNetwork`), the +in-process and two-process run modes, and the JSON slave-config schema -- lives +in [../docs/SIMULATION.md](../docs/SIMULATION.md). -## 1. In-process loopback (simplest) - -Master `Bus` and the emulated slave live in the **same process**, connected by -`kickcat::LoopbackSocket` (`lib/include/kickcat/LoopbackSocket.h`). No network -interface, no `/dev/shm`, no second terminal. Each master frame is run through -the slave and the slave is ticked once. This is the recommended starting point -and the easiest to debug (single process, single thread). - -Canonical example — `examples/master/simulated_bus`: - -```bash -./build/examples/master/simulated_bus/simulated_bus -f "Beckhoff EL1xxx.xml" -t EL1008 -``` - -It builds the selected device, drives INIT → PRE_OP → SAFE_OP → OPERATIONAL, and -exchanges process data — all in ~150 lines you can read top to bottom. - -`test/integration/bench/esi_boot` uses the same loopback to boot an entire ESI -catalog in parallel (a good template for batch/regression use). - -## 2. Two-process over a TAP socket (`network_simulator`) - -`network_simulator` runs the slaves in their own process and exposes them on a -shared-memory **TAP socket**; a separate master process connects to the other -end. Closer to a real two-node setup; useful for CI and for running an unmodified -master example against the simulator. - -```bash -# Terminal 1 — the simulator is the TAP server (start it first): -./build/simulation/network_simulator -i tap:server -s simulation/slave_configs/freedom-k64f.json - -# Terminal 2 — the master is the TAP client: -sudo ./build/examples/master/easycat/easycat_example -i tap:client -``` - -To boot from an ESI device instead, point a config's `esi` key at your vendor's -ESI XML (see the schema below); the in-process example above is the quickest way -to try one. - -Chain several slaves by passing multiple configs, or repeat one with `-n N`: - -```bash -./build/simulation/network_simulator -i tap:server \ - -s simulation/slave_configs/freedom-k64f.json simulation/slave_configs/xmc4800.json -``` - -### Named TAP segments - -`tap:server` / `tap:client` use the default shared-memory name. Append `:` -to run independent server/client pairs side by side: - -```bash -./build/simulation/network_simulator -i tap:server:busA -s ... # one bus -./build/examples/master/.../example -i tap:client:busA # its master -``` - -## Slave config schema (JSON) - -One JSON object per slave file. Provide **exactly one** source of the slave -image — `esi` or `eeprom`: - -| Key | Type | Notes | -|----------------|--------|--------------------------------------------------------------------| -| `esi` | string | Path to an ESI XML (relative to the config dir). Compiles the SII + CoE dictionary. | -| `device_type` | string | With `esi`: pick the device by `` (e.g. `"EL1008"`). | -| `product_code` | uint32 | With `esi`: pick the device by product code. | -| `revision_no` | uint32 | With `esi`: pick the device by revision. | -| `eeprom` | string | Path to a raw EEPROM `.bin` (alternative to `esi`). | -| `coe_xml` | string | With `eeprom`: optional CoE dictionary XML for the mailbox. | - -When several `esi` device filters match, the first match is used. With `esi`, -the CoE mailbox (if the device declares CoE) is built from the same device, so -`coe_xml` is not needed. - -Examples live in `simulation/slave_configs/`. +Ready-made slave configurations are in `slave_configs/`. diff --git a/tools/eeprom.cc b/tools/eeprom.cc index 6c0bf342..4c4c85eb 100644 --- a/tools/eeprom.cc +++ b/tools/eeprom.cc @@ -1,5 +1,4 @@ // Tool to dump and flash the eeprom of a slave on the bus. -// Current state: POC, can only dump eeprom file. #include "kickcat/Bus.h" #include "kickcat/Link.h" diff --git a/uncrustify.cfg b/uncrustify.cfg new file mode 100644 index 00000000..af5c05ec --- /dev/null +++ b/uncrustify.cfg @@ -0,0 +1,129 @@ +# KickCAT coding style — uncrustify config +# Tested with Uncrustify 0.83.x +# +# Philosophy: enforce the mechanical, unambiguous parts of the house style +# (4-space indent, indented namespaces, Allman braces, spacing around keywords +# and operators, pointer placement) and deliberately leave judgement calls to +# the author. In particular, hand-aligned "tabular" blocks (struct/enum value +# columns, grouped assignments, trailing comments) are PRESERVED rather than +# imposed or destroyed: operator spacing uses "add" (ensure a space exists, do +# not collapse extra ones) and the align_* passes are disabled. + +# +# Indentation +# +indent_columns = 4 +indent_with_tabs = 0 # spaces only +indent_namespace = true # body of a namespace is indented +indent_class = true # body of a class is indented +indent_access_spec = 5 # public:/private: at the class-brace column + # (absolute col = value-1; assumes the + # common "one namespace deep" nesting) +indent_switch_case = 4 # case labels indented inside switch +indent_label = 1 +indent_continue = 0 # continuations align to the open paren + +# Lambdas assigned to a variable (auto x = [...]{ ... };) keep their brace and +# body at the enclosing statement indent rather than aligned under the capture. +indent_align_assign = false +indent_off_after_assign = true + +# +# Braces: Allman (brace on its own line) everywhere +# +nl_fdef_brace = add # function definition +nl_if_brace = add +nl_brace_else = add # else on its own line +nl_else_brace = add +nl_elseif_brace = add +nl_for_brace = add +nl_while_brace = add +nl_do_brace = add +nl_brace_while = remove +nl_switch_brace = add +nl_namespace_brace = add +nl_class_brace = add +nl_struct_brace = add +nl_union_brace = add +nl_enum_brace = add + +# +# Newline / blank-line cleanup +# +nl_max = 3 # allow at most 2 consecutive blank lines +nl_end_of_file = force # trailing newline +nl_end_of_file_min = 1 +eat_blanks_before_close_brace = true +eat_blanks_after_open_brace = true + +# +# Spacing around keywords and parentheses (unambiguous) +# +sp_before_sparen = force # if (, switch (, for (, while ( +sp_inside_sparen = remove +sp_inside_paren = remove +sp_inside_fparen = remove +sp_paren_paren = remove +sp_before_semi = remove +sp_after_semi_for = force +sp_after_type = ignore # preserve hand-aligned declaration columns +sp_after_cast = remove # static_cast(x) +sp_inside_angle = remove +sp_before_angle = remove +sp_inside_square = remove +sp_angle_shift = remove # keep nested closers together: vector> +sp_permit_cpp11_shift = true # required for sp_angle_shift to collapse '> >' + +# +# Pointer placement: star hugs the type (void* p, char const* s). +# Reference (&) spacing is deliberately NOT set: the codebase already writes +# "T& name" consistently, and forcing "remove before byref" makes uncrustify +# misparse bitwise '&' (mode & FLAG) — so references are simply left as written. +# +sp_before_ptr_star = remove +sp_after_ptr_star = add # "add" (not "force") preserves aligned pointer columns +sp_before_ptr_star_func = remove +sp_after_ptr_star_func = add +sp_between_ptr_star = remove + +# +# Operator spacing: "add" ensures a space without collapsing hand-alignment, +# so tabular blocks the author aligned by hand survive a reformat untouched. +# +sp_assign = add +sp_after_comma = add +sp_before_comma = remove +sp_arith = add +sp_bool = add +sp_compare = add + +# +# Alignment passes left OFF: alignment is an author judgement call (blur). +# Disabling them means uncrustify neither imposes new alignment nor destroys +# existing aligned blocks (paired with the "add" operator spacing above). +# +align_assign_span = 0 +align_var_def_span = 0 +align_struct_init_span = 0 +align_right_cmt_span = 0 + +# +# One-line bodies are a deliberate readability device here (inline accessors, +# tabular "case X: { return ...; }" rows). Leave every existing one-liner as the +# author wrote it instead of exploding it to Allman form. +# +nl_func_leave_one_liners = true +nl_getset_leave_one_liners = true +nl_class_leave_one_liners = true +nl_enum_leave_one_liners = true +nl_assign_leave_one_liners = true +nl_if_leave_one_liners = true +nl_for_leave_one_liners = true +nl_while_leave_one_liners = true +nl_do_leave_one_liners = true +nl_cpp_lambda_leave_one_liners = true + +mod_full_brace_if = ignore +mod_full_brace_for = ignore +mod_full_brace_while = ignore +mod_remove_extra_semicolon = true