✨ Current status – Full HDL Implementation with USB Host Mode!
The project now provides two implementations:
- New HDL Implementation (
HDL/) - A complete Verilog implementation with:
- ✅ USB passthrough and monitoring
- ✅ UART-controlled HID injection
- ✅ NEW: USB Host Mode - Complete device enumeration and HID keyboard support
- Legacy Python/Luna (
legacy/) - Original amaranth/LUNA implementationThe HDL implementation can now act as both a passive USB sniffer and an active USB keyboard host!
HurricaneFPGA explores low‑level USB manipulation on the Cynthion FPGA. It ships:
- Hurricane HDL – Complete Verilog implementation for advanced USB manipulation
- Legacy Amaranth/LUNA gateware – FS passthrough + UART HID injection
- Rust CLI – network‑to‑UART bridge for easy scripting
- Features
- Requirements
- Installation
3.1 Environment setup | 3.2 Build gateware | 3.3 Flash gateware | 3.4 Build Rust CLI - Hardware setup
- Usage
5.1 Run gateway | 5.2 Send commands - Architecture
- Development
- Troubleshooting
- License
- Acknowledgements
- USB passthrough – Full‑/Low‑Speed packets flow between TARGET (J2) ⇄ CONTROL (J3).
- HID injection – FPGA can splice one‑byte‑per‑axis mouse reports (
buttons, dx, dy). - UART control – Send exactly three bytes over PMOD A @ 115200 baud to inject.
- Rust gateway – Accepts UDP strings (
buttons,dx,dy) → forwards raw UART bytes. - Command acknowledgment – FPGA sends ACK/NAK responses with error codes.
- Complete device enumeration – Automatically enumerate USB devices with speed detection (LS/FS/HS)
- HID keyboard support – Poll USB keyboards and extract key reports
- HID mouse support – Poll USB mice and extract movement/button data
- Automatic operation – Self-configuring after enumeration completes
- Status monitoring – LED indicators and comprehensive status reporting
- Python control interface – Easy-to-use script for host mode control
- Real-time events – Monitor key presses and mouse movements with decoded reports
See HDL/USB_HOST_QUICKSTART.md for a quick start guide!
| Category | Items |
|---|---|
| Hardware | Cynthion board (r0.5+) |
| FPGA toolchain | OSS CAD Suite (Yosys + nextpnr‑ecp5 + Trellis) |
| Python | Python 3 · amaranth · luna · pyserial |
| Rust | Stable toolchain (rustup, cargo) |
| Debug Interface | Built-in UART0→USB (no external adapter needed!) |
You only need Docker (with BuildKit support) for the initial build. We provide an optimized Makefile with layer caching for fast rebuilds:
# Quick start - build everything with caching
make build
# Fast rebuild using existing cache (much faster for incremental changes)
make build-fast
# Or manually with Docker
docker build -t amaranth-cynthion .Performance Tips:
- First build will take ~10-15 minutes as it downloads and compiles all dependencies
- Subsequent builds with
make build-fasttypically complete in 1-2 minutes - BuildKit cache mounts speed up Cargo and pip operations significantly
- The
.dockerignorefile ensures only necessary files are included in build context - In CI environments (GitHub Actions), inline cache is automatically used for compatibility
See make help for all available commands.
Python should be able to run this with python legacy/src/flash_fpga.py
The HDL implementation provides dedicated flashing tools:
cd HDL/tools
./flash_cynthion.sh # Flash the precompiled bitstreamFor advanced builds and debugging:
# Compile a custom bitstream
./compile_bitstream.sh
# Validate HDL before building
./validate_hdl.sh
# Debug connected Cynthion
python cynthion_debugger.pySee HDL/architecture.md and HDL/transparent_proxy_implementation.md for detailed documentation on the HDL implementation.
The Rust CLI is now built automatically within Docker:
# Build everything (Docker image + extract binaries)
make build
# Or manually
docker build -t amaranth-cynthion .
./deploy.sh
# Run the CLI
make run-rust
# Or directly:
./build/binaries/hurricanefpga --helpThe binary will be available at build/binaries/hurricanefpga.
Note: Docker now builds both the PC CLI tool and the SAMD51 embedded firmware using ARM cross-compilation. Both binaries are extracted to the
build/directory. Seedocs/RUST_PROJECTS.mdfor details on the two Rust projects.
The build system uses Docker BuildKit with aggressive caching for optimal rebuild times:
# Normal build (updates cache)
make build # Full build with cache update (~2 min after first build)
# Fast incremental builds
make build-fast # Use cache but skip cache export (~1 min)
make rebuild-fast # Clean artifacts, keep cache, rebuild
# Complete rebuilds
make rebuild # Clean everything including cache (~10-15 min)
make clean-cache # Remove only the Docker build cacheCache Storage:
- Local development:
/tmp/docker-cache-amaranth-cynthion/(local filesystem cache) - GitHub Actions: Inline cache in Docker image layers (compatible with docker driver)
- Cargo registry cache: Mounted during build (ephemeral)
- Pip cache: Mounted during build (ephemeral)
When to use each:
make build-fast: Day-to-day development (fastest)make build: After major dependency changesmake rebuild: When troubleshooting build issues
CI/CD Notes: The build system automatically detects CI environments and uses inline caching instead of local filesystem cache for compatibility with GitHub Actions' docker driver.
| Connection | Details |
|---|---|
| Host PC USB | → TARGET (J2) |
| USB device (keyboard, mouse, etc.) | → CONTROL (J3) or TARGET B |
| Debug/Control | → CONTROL port (USB, built-in UART0→USB CDC-ACM) |
Debug Interface: Connect Cynthion's CONTROL port to your PC. It appears as
/dev/ttyACM0(Linux) or a COM port (Windows) for real-time status and HID report monitoring. No external UART adapter needed!See
HDL/UART0_USB_INTEGRATION.mdfor complete details.
The FPGA now outputs real-time status via UART0→USB:
# Linux/macOS
picocom -b 115200 /dev/ttyACM0
# Or simply:
cat /dev/ttyACM0
# Windows (PowerShell)
# Check Device Manager for COM port, then:
mode COM3 BAUD=115200 PARITY=n DATA=8
type COM3Example Output:
[STATUS] Proxy: ON, Host: ON, Enum: DONE
[HID-KBD] Mod: 0x00, Keys: [0x04, 0x00, 0x00]
[HID-MOUSE] Btn: 0x01, dX: 0x05, dY: 0xFD
[STATUS] Proxy: ON, Host: ON, Enum: DONE
See HDL/UART0_USB_INTEGRATION.md for detailed message formats.
# list serial ports
target/release/packetry_injector --list
# start UDP→UART bridge
target/release/packetry_injector \
--udp 127.0.0.1:9001 \
--control-serial /dev/ttyUSB0 # or COM3 on WindowsThe FPGA now provides acknowledgments for each command sent:
- ACK (0x06) - Command was successfully received and processed
- NAK (0x15) + Error Code - Command failed with specific error code:
0x01- Value out of range0x02- Syntax error0x03- System busy0x04- Buffer overflow
The Rust CLI automatically handles these acknowledgments.
The HDL implementation now supports USB host functionality for enumerating and communicating with USB keyboards.
# Install Python dependencies
pip install pyusb
# Navigate to tools directory
cd HDL/tools
# Enable USB host mode
./usb_host_control.py --enable
# Connect USB keyboard to PHY2 port (J2 on Cynthion)
# OR connect USB mouse to PHY2 port
# Start enumeration
./usb_host_control.py --enumerate
# Monitor keyboard events in real-time
./usb_host_control.py --monitor # Auto-detects keyboard or mouseKeyboard:
Detected keyboard, starting keyboard monitor...
Monitoring keyboard events... (Ctrl+C to stop)
[10:45:23] H
[10:45:24] E
[10:45:25] L
[10:45:26] L
[10:45:27] O
[10:45:28] LEFT_SHIFT + W
[10:45:29] O
[10:45:30] R
[10:45:31] L
[10:45:32] D
Mouse:
Detected mouse, starting mouse monitor...
Monitoring mouse events... (Ctrl+C to stop)
[10:50:15] Move(+10, + 5)
[10:50:16] LEFT
[10:50:17] Move( +3, + 2) LEFT
[10:50:18] Wheel( +1)
| LED | Meaning | State |
|---|---|---|
| LED7 | Host Mode Enabled | ON when host mode active |
| LED6 | Enumeration Complete | ON after successful enumeration |
| LED5 | Device Polling | ON when actively polling keyboard/mouse |
| LED4 | New Report | BLINKS when keys pressed or mouse moved |
For complete USB host mode documentation, see:
HDL/USB_HOST_QUICKSTART.md- Quick start and troubleshootingHDL/USB_HOST_INTEGRATION.md- Complete architecture and integration detailsHDL/USB_HOST_COMPLETE.md- Full feature summary
┌──────────────────┐ Serial ┌────────────┐
│ Rust Gateway CLI │ ───────► │ UART Dongle│
│ packetry_injector│ │ (FT232) │
└──────────────────┘ └─────┬──────┘
│
▼
┌────────────────────────────────────────┐
─────────────> │ Cynthion FPGA (ECP5) │
|DEVICE| │ • ULPI AUX ↔ Host PC │
│ • ULPI HOST ↔ Target Device │
│ • UART Rx/Tx ↔ PMOD A │
│ • Amaranth/LUNA passthrough/injector │
└────────────────────────────────────────┘
|
|
▼
HOST PC
┌─────────────────────────────────────────────────────────────────────┐
│ Cynthion FPGA (ECP5) │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ USB Host Mode │ │
│ │ ┌──────────────┐ ┌───────────────┐ ┌──────────────┐ │ │
│ │ │ Reset Ctrl │ │ Enumerator │ │Transaction │ │ │
│ │ │ │ │ │ │Engine │ │ │
│ │ └──────┬───────┘ └───────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │ │
│ │ └──────────┬───────┴──────────────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ USB Host Arbiter │ Priority-based TX │ │
│ │ │ (PHY2 TX Multiplexer) │ signal multiplexing │ │
│ │ └─────────┬───────────────┘ │ │
│ │ │ │ │
│ │ ┌─────────▼───────────────┐ │ │
│ │ │ Token Request Arbiter │ Request arbitration │ │
│ │ │ (Token Generator Mux) │ for shared resources │ │
│ │ └─────────┬───────────────┘ │ │
│ │ ▼ │ │
│ │ ┌──────────────────┐ │ │
│ │ │ Token Generator │ │ │
│ │ └──────────────────┘ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ HID Keyboard │ │ HID Mouse │ │ │
│ │ │ Engine │ │ Engine │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ USB Proxy/Monitor │ │
│ │ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ │
│ │ │USB Monitor │ │Packet Proxy│ │Buffer Manager│ │ │
│ │ │ (PHY0/1) │ │ │ │ (32KB) │ │ │
│ │ └────────────┘ └────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ UART Interface │ │
│ │ UART0 (Built-in USB CDC-ACM) → Status & Debug Output │ │
│ │ PMOD A → Command Input (Legacy) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
PHY0 (J1) PHY1 (J3) PHY2 (J2)
Device Side Control/Monitor Host Side
The HDL implementation uses a dual-arbiter architecture to manage multiple USB host controllers:
Purpose: Multiplexes UTMI TX signals from multiple host controllers to PHY2
Priority Order (Highest to Lowest):
- Reset Controller - Handles bus reset and speed detection
- Enumerator - Manages device enumeration sequence
- Transaction Engine - Executes USB transactions (SETUP/IN/OUT)
- Protocol Handler - Handles protocol-level responses
- Token Generator - Sends USB token packets
- SOF Generator - Generates Start-of-Frame packets
Key Features:
- Combinatorial priority-based selection
- Active signal gating for priority control
- Separate TX data/valid wires per module
- Single unified output to PHY
Purpose: Arbitrates token generator requests from multiple controllers
Priority Order (Highest to Lowest):
- Enumerator - Enumeration must complete without interruption
- Transaction Engine - Normal data transfers
- Keyboard Engine - Periodic polling (lowest priority)
Key Features:
- Request-level arbitration (not data path)
- Shared resource management for token generator
- Prevents conflicting token requests
- Maintains proper USB timing
-
USB Host Operation:
- Controller generates TX data on dedicated wires
- Controller asserts active signal
- USB Host Arbiter selects highest priority active controller
- Selected TX data forwarded to PHY2
-
Token Generation:
- Controller asserts token request signals
- Token Request Arbiter selects highest priority request
- Token Generator processes unified request
- Token completion signaled to all controllers
-
Data Reception:
- PHY2 RX data broadcast to all controllers
- Each controller processes relevant packets
- Protocol handler manages handshakes
USB Host Components:
usb_reset_controller.v- Bus reset, speed detection, connection monitoringusb_enumerator.v- Complete enumeration sequence (GetDescriptor, SetAddress, SetConfig)usb_transaction_engine.v- SETUP/IN/OUT transaction managementusb_token_generator.v- USB token packet generation (IN/OUT/SETUP/SOF)usb_sof_generator.v- Start-of-Frame timing and frame number trackingusb_protocol_handler.v- USB protocol state tracking and validationusb_host_arbiter.v- NEW: PHY TX signal multiplexerusb_token_arbiter.v- NEW: Token request arbitration
HID Engines:
usb_hid_keyboard_engine.v- Keyboard interrupt endpoint pollingusb_hid_mouse_engine.v- Mouse interrupt endpoint polling
Proxy/Monitor:
usb_monitor.v- Packet capture and analysispacket_proxy.v- Packet forwarding and modificationbuffer_manager.v- 32KB ring buffer with dual-port BRAM
Supporting:
uart_interface.v- UART0 CDC-ACM interface for debug outputdebug_interface.v- Status register accesstimestamp_generator.v- Microsecond-precision timestamping
The HDL uses Yosys for synthesis with three optimization levels:
make synth-fast # ~30s - Quick iteration
make synth # ~1-2min - Balanced (recommended)
make synth-max # ~3-5min - Maximum optimizationSee HDL/architecture.md for detailed module documentation.
cd HDL
# Validate Verilog syntax
make validate
# Synthesize (fast iteration)
make synth-fast
# Full build with place & route
make all
# Flash to device
cd tools && ./flash_cynthion.sh# Rust CLI (Host PC)
cargo build --release
# SAMD51 Firmware (Cross-compile for ARM)
cd firmware/samd51_hid_injector
cargo build --releaseComprehensive test coverage across all layers:
# Run all tests
make test-local
# or
./run_tests.sh
# Run individual test suites
make test-rust # Firmware unit tests (66 tests)
make test-python # Python unit tests (59 tests)
# Test results
# ✅ Rust firmware: 66/66 passed
# ✅ Python tools: 59/59 passed
# ✅ Total: 125 testsTest Coverage:
- Firmware: HID reports, recoil patterns, protocol parsing, state management
- Python Tools: Command generation, validation, descriptor parsing
- HDL: USB token generation, transaction engine, HID injector
See TESTING.md for complete testing documentation and TEST_COVERAGE_SUMMARY.md for detailed coverage metrics.
USB device on J3 not detected by host
- Re‑flash correct bitstream & reset.
- Check cabling: Host ↔ J2, Device ↔ J3.
- Only FS/LS devices work.
- Ensure VBUS on J3 (jumper) or self‑powered device.
# Cynthion DFU
SUBSYSTEM=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="615b", MODE="0666"
# CP210x example
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="0666", GROUP="dialout"
# FT232 example
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", GROUP="dialout"
sudo udevadm control --reload-rules && sudo udevadm triggerDistributed under the terms of the MIT License – see LICENSE.
- Cynthion by Great Scott Gadgets.
- Built with Amaranth HDL, LUNA USB framework, and a stack of fantastic Rust crates.