Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ cargo build --release [opts]
### run

Using default build settings, the resulting binaries will be in `./target/debug/`
such as `./target/debug/fpgad` for the daemon and `./target/debug/cli` for the
such as `./target/debug/fpgad` for the daemon and `./target/debug/fpgad_cli` for the
command line application. If using release, then the directory is `./target/release/`.

If running as a standalone binary (not snap) then dbus needs to be configured to know about
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ clap = { version = "4.5.41", default-features = false, features = ["std", "deriv
env_logger = { version = "0.11.8", default-features = false }
log = "0.4.27"
tokio = { version = "1.47.0", default-features = false, features = ["rt-multi-thread", "macros", "sync"] }
zbus = { version = "5.9.0", default-features = false, features = ["tokio"] }
zbus = { version = "5.9.0", default-features = false, features = ["tokio"] }
131 changes: 99 additions & 32 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,90 +5,157 @@
Usage: [snap run] fpgad [OPTIONS] <COMMAND>

OPTIONs:
-h, --help Print help
--handle <DEVICE_HANDLE> fpga device `HANDLE` to be used for the operations.
Default value for this option is calculated in runtime
and the application picks the first available fpga device
in the system (under `/sys/class/fpga_manager/`)
-h, --help Print help
-d, --device <DEVICE_HANDLE> FPGA device handle to be used for operations.
Default value is calculated at runtime and picks
the first available FPGA device in the system
(under `/sys/class/fpga_manager/`)
-p, --platform <PLATFORM> Override the platform detection. Format:
"universal" or "vendor,device"
Examples: --platform=universal
--platform=xlnx,zynqmp-pcap-fpga

COMMANDs:
├── load Load a bitstream or overlay
│ ├── overlay <FILE> [--handle <OVERLAY_HANDLE>]
│ │ Load overlay (.dtbo) into the system using the default OVERLAY_HANDLE
│ ├── overlay <FILE> [--name <OVERLAY_NAME>]
│ │ Load overlay (.dtbo) into the system using the default OVERLAY_NAME
│ │ (either the provided DEVICE_HANDLE or "overlay0") or provide
│ │ --handle: to name the overlay directory
│ │ --name (-n): to name the overlay directory
│ └── bitstream <FILE>
│ Load bitstream (e.g. `.bit.bin` file) into the FPGA
├── set <ATTRIBUTE> <VALUE>
│ Set an attribute/flag under `/sys/class/fpga_manager/<DEVICE_HANDLE>/<ATTRIBUTE>`
├── status [--handle <DEVICE_HANDLE>]
│ Show FPGA status (all devices and overlays) or provide
│ --handle: for a specific device status
├── status
│ Show FPGA status (all devices and overlays)
└── remove Remove an overlay or bitstream
├── overlay [--handle <OVERLAY_HANDLE>]
│ Removes the first overlay found (call repeatedly to remove all) or provide
│ --handle: to remove overlay previously loaded with given OVERLAY_HANDLE
└── bitstream
Remove active bitstream from FPGA (bitstream removal is vendor specific)
├── remove Remove an overlay or bitstream
│ ├── overlay [--name <OVERLAY_NAME>]
│ │ Removes the first overlay found (call repeatedly to remove all) or provide
│ │ --name (-n): to remove overlay previously loaded with given OVERLAY_NAME
│ └── bitstream [--handle <HANDLE>]
│ Remove active bitstream from FPGA (bitstream removal is vendor specific)
│ Optionally specify --handle to name the removal handle
├── universal Low-level read/write access to FPGA manager properties
│ ├── read <SUB_CMD> <PATH>
│ │ Read from sysfs path where SUB_CMD is:
│ │ read_property: read property from sysfs path
│ │ read_flags: read flags from device handle
│ └── write <SUB_CMD> <PATH> <VALUE>
│ Write to sysfs path where SUB_CMD is:
│ write_flags: write flags to device handle
│ write_property: write string property to sysfs path
│ write_property_bytes: write bytes to sysfs path
└── dfx-mgr <CMD> Pass commands to dfx-mgr-client (Xilinx DFX manager)
Examples: listPackage, listSlot, load, remove
```

### Loading

```shell
fpgad [--handle=<device_handle>] load ( (overlay <file> [--handle=<handle>]) | (bitstream <file>) )
fpgad [--device=<device_handle>] load ( (overlay <file> [--name=<name>]) | (bitstream <file>) )
```

### Removing

```shell
fpgad [--handle=<device_handle>] remove ( ( overlay <HANDLE> ) | ( bitstream ) )
fpgad [--device=<device_handle>] remove ( ( overlay [--name=<NAME>] ) | ( bitstream [--handle=<handle>] ) )
```

### Set

```shell
fpgad [--handle=<device_handle>] set ATTRIBUTE VALUE
fpgad [--device=<device_handle>] set ATTRIBUTE VALUE
```

### Status

```shell
fpgad [--handle=<device_handle>] status
fpgad [--device=<device_handle>] status
```

### Universal

```shell
fpgad [--device=<device_handle>] universal ( (read <sub_cmd> <path>) | (write <sub_cmd> <path> <value>) )
```

### DFX Manager

```shell
fpgad dfx-mgr <CMD>
```

## examples (for testing)

### Load

```shell
sudo ./target/debug/cli load bitstream /lib/firmware/k26-starter-kits.bit.bin
sudo ./target/debug/cli --handle=fpga0 load bitstream /lib/firmware/k26-starter-kits.bit.bin
sudo ./target/debug/fpgad_cli load bitstream /lib/firmware/k26-starter-kits.bit.bin
sudo ./target/debug/fpgad_cli --device=fpga0 load bitstream /lib/firmware/k26-starter-kits.bit.bin

sudo ./target/debug/cli load overlay /lib/firmware/k26-starter-kits.dtbo
sudo ./target/debug/cli load overlay /lib/firmware/k26-starter-kits.dtbo --handle=overlay_handle
sudo ./target/debug/cli --handle=fpga0 load overlay /lib/firmware/k26-starter-kits.dtbo --handle=overlay_handle
sudo ./target/debug/fpgad_cli load overlay /lib/firmware/k26-starter-kits.dtbo
sudo ./target/debug/fpgad_cli load overlay /lib/firmware/k26-starter-kits.dtbo --name=overlay_handle
sudo ./target/debug/fpgad_cli --device=fpga0 load overlay /lib/firmware/k26-starter-kits.dtbo --name=overlay_handle
```

### Remove

```shell
sudo ./target/debug/cli --handle=fpga0 remove overlay
sudo ./target/debug/cli --handle=fpga0 remove overlay --handle=overlay_handle
sudo ./target/debug/fpgad_cli --device=fpga0 remove overlay
sudo ./target/debug/fpgad_cli --device=fpga0 remove overlay --name=overlay_handle
sudo ./target/debug/fpgad_cli remove bitstream
sudo ./target/debug/fpgad_cli remove bitstream --handle=my_handle
```

### Set

```shell
sudo ./target/debug/cli set flags 0
sudo ./target/debug/cli --handle=fpga0 set flags 0
sudo ./target/debug/fpgad_cli set flags 0
sudo ./target/debug/fpgad_cli --device=fpga0 set flags 0
```

### Status

```shell
./target/debug/cli status
./target/debug/cli --handle=fpga0 status
./target/debug/fpgad_cli status
./target/debug/fpgad_cli --device=fpga0 status
```

### Universal

```shell
# Read operations
sudo ./target/debug/fpgad_cli universal read read_property /sys/class/fpga_manager/fpga0/name
sudo ./target/debug/fpgad_cli universal read read_flags fpga0

# Write operations
sudo ./target/debug/fpgad_cli universal write write_flags fpga0 0
sudo ./target/debug/fpgad_cli universal write write_property /sys/class/fpga_manager/fpga0/flags 0
sudo ./target/debug/fpgad_cli universal write write_property_bytes /sys/class/fpga_manager/fpga0/firmware BYTES
```

### DFX Manager

```shell
# List packages and slots
sudo ./target/debug/fpgad_cli dfx-mgr listPackage
sudo ./target/debug/fpgad_cli dfx-mgr listSlot

# Load and remove with dfx-mgr
sudo ./target/debug/fpgad_cli dfx-mgr "load slot:0 package:my-package"
sudo ./target/debug/fpgad_cli dfx-mgr "remove slot:0"
```

### Platform Override

```shell
# Force universal platform
sudo ./target/debug/fpgad_cli --platform=universal load bitstream /lib/firmware/bitstream.bin

# Force specific Xilinx platform
sudo ./target/debug/fpgad_cli --platform=xlnx,zynqmp-pcap-fpga load bitstream /lib/firmware/bitstream.bin
```
65 changes: 65 additions & 0 deletions cli/src/dfx_mgr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// This file is part of fpgad, an application to manage FPGA subsystem together with device-tree and kernel modules.
//
// Copyright 2025 Canonical Ltd.
//
// SPDX-License-Identifier: GPL-3.0-only
//
// fpgad is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published by the Free Software Foundation.
//
// fpgad is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

//! DFX Manager command implementation for the FPGA CLI.
//!
//! This module passes commands directly to `dfx-mgr-client` via the fpgad daemon's
//! DBus interface.
//!
//! If the connected daemon does not expose `dfx_mgr` (for example because it was built
//! without `xilinx-dfx-mgr`), this handler returns a clear compatibility error.
//!
//!
//! # Usage
//!
//! ```shell
//! fpgad dfx-mgr "-listPackage"
//! fpgad dfx-mgr "-load 0 my_design"
//! fpgad dfx-mgr "-remove 0"
//! ```
//!
//! The entire command string is passed as a single argument and split by the daemon
//! before forwarding to `dfx-mgr-client`.

use crate::proxies::control_proxy;
use zbus::Connection;

/// Main handler for the dfx-mgr command.
///
/// Forwards the provided command string to the daemon's `dfx_mgr` DBus method,
/// which in turn passes it to `dfx-mgr-client`.
///
/// # Arguments
///
/// * `cmd` - Space-separated arguments to pass to `dfx-mgr-client`
/// (e.g. `"-listPackage"` or `"-load 0 my_design"`)
///
/// # Returns: `Result<String, zbus::Error>`
/// * `Ok(String)` - Exit status, stdout, and stderr from `dfx-mgr-client`
/// * `Err(zbus::Error)` - DBus communication error, missing component, or FpgadError.
/// See [Error Handling](../index.html#error-handling) for details.
pub async fn dfx_mgr_handler(cmd: &str) -> Result<String, zbus::Error> {
let connection = Connection::system().await?;
let proxy = control_proxy::ControlProxy::new(&connection).await?;
match proxy.dfx_mgr(cmd).await {
Ok(out) => Ok(out),
Err(zbus::Error::MethodError(name, _, _))
if name.as_str() == "org.freedesktop.DBus.Error.UnknownMethod" =>
{
Err(zbus::Error::Failure(
"feature not enabled in daemon, or CLI/daemon out of date (missing dfx_mgr method)"
.to_string(),
))
}
Err(e) => Err(e),
}
}
62 changes: 62 additions & 0 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@
//! ./target/debug/cli --device=fpga0 status
//! ```

// TODO(Artie): add universal and dfx-mgr subcommand documentation help strings
// dfx-mgr can link to their repo
// universal should properly contain all valid types
mod proxies;

pub mod load;
Expand All @@ -182,6 +185,10 @@ pub mod status;

pub mod set;

pub mod universal;

pub mod dfx_mgr;

use clap::{Parser, Subcommand};

/// Command-line interface structure for FPGA management operations.
Expand Down Expand Up @@ -324,13 +331,49 @@ pub enum RemoveSubcommand {
},
}

/// Subcommands for the universal platform interface.
///
/// Provides direct access to the daemon's `universal` read/write DBus methods,
/// allowing low-level control of FPGA manager sysfs properties and flags.
///
/// # Examples
///
/// ```shell
/// fpgad universal read read_flags fpga0
/// fpgad universal read read_property /sys/class/fpga_manager/fpga0/name
/// fpgad universal write write_flags fpga0 0x20
/// fpgad universal write write_property /sys/class/fpga_manager/fpga0/key VALUE
/// fpgad universal write write_property_bytes /sys/class/fpga_manager/fpga0/key BYTES
/// ```
#[derive(Subcommand, Debug)]
pub enum UniversalSubcommand {
/// Read an FPGA property using the universal interface
Read {
/// Subcommand: `read_property` (sysfs path) or `read_flags` (device handle)
sub_cmd: String,
/// Sysfs property path for `read_property`, or device handle for `read_flags`
path: String,
},
/// Write an FPGA property using the universal interface
Write {
/// Subcommand: `write_flags` (device handle), `write_property`, or `write_property_bytes`
sub_cmd: String,
/// Device handle for `write_flags`, or sysfs property path for property writes
path: String,
/// Value to write
value: String,
},
}

/// Top-level commands supported by the CLI.
///
/// This enum represents all the primary operations available through the fpgad CLI:
/// - **Load**: Load bitstreams or device tree overlays onto the FPGA
/// - **Set**: Configure FPGA attributes and flags (e.g., programming flags)
/// - **Status**: Query the current state of FPGA devices and loaded overlays
/// - **Remove**: Unload bitstreams or device tree overlays from the FPGA
/// - **Universal**: Low-level read/write access to FPGA manager properties via the universal interface
/// - **DfxMgr**: Pass commands directly to `dfx-mgr-client` (requires dfx-mgr component)
///
/// Each command communicates with the fpgad daemon via DBus to perform privileged
/// operations on FPGA devices managed through the Linux kernel's FPGA subsystem.
Expand All @@ -352,6 +395,15 @@ pub enum RemoveSubcommand {
///
/// # Remove an overlay by name
/// fpgad remove overlay --name=my_overlay
///
/// # Read FPGA flags via universal interface
/// fpgad universal read read_flags fpga0
///
/// # Write flags via universal interface
/// fpgad universal write write_flags fpga0 0x20
///
/// # Invoke dfx-mgr-client
/// fpgad dfx-mgr "-listPackage"
/// ```
#[derive(Subcommand, Debug)]
pub enum Commands {
Expand All @@ -369,4 +421,14 @@ pub enum Commands {
#[command(subcommand)]
command: RemoveSubcommand,
},
/// Low-level read/write access to FPGA manager properties (universal platform interface)
Universal {
#[command(subcommand)]
command: UniversalSubcommand,
},
/// Pass a command directly to dfx-mgr-client (requires dfx-mgr snap component)
DfxMgr {
/// Space-separated arguments to pass to dfx-mgr-client (e.g. "-listPackage")
cmd: String,
},
}
Loading
Loading