use std::env;
use std::path::Path;
use std::sync::OnceLock;
use crate::error::FpgadError;
use crate::platforms::platform::{Fpga, OverlayHandler, Platform};
use crate::softeners::error::FpgadSoftenerError;
use fpgad_macros::platform;
use log::trace;
use xilinx_dfx_mgr_fpga::XilinxDfxMgrFPGA;
use xilinx_dfx_mgr_overlay_handler::XilinxDfxMgrOverlayHandler;
pub mod xilinx_dfx_mgr_fpga;
pub mod xilinx_dfx_mgr_helpers;
pub mod xilinx_dfx_mgr_overlay_handler;
#[platform(compat_string = "xlnx,zynqmp-pcap-fpga,versal-fpga,zynq-devcfg-1.0")]
pub struct XilinxDfxMgrPlatform {
fpga: OnceLock<XilinxDfxMgrFPGA>,
overlay_handler: OnceLock<XilinxDfxMgrOverlayHandler>,
}
impl Default for XilinxDfxMgrPlatform {
fn default() -> Self {
Self::new()
}
}
impl XilinxDfxMgrPlatform {
pub fn new() -> Self {
trace!("creating new XilinxDfxMgrPlatform");
XilinxDfxMgrPlatform {
fpga: OnceLock::new(),
overlay_handler: OnceLock::new(),
}
}
}
impl Platform for XilinxDfxMgrPlatform {
fn fpga(&self, device_handle: &str) -> Result<&dyn Fpga, FpgadError> {
Ok(self
.fpga
.get_or_init(|| XilinxDfxMgrFPGA::new(device_handle)))
}
fn overlay_handler(&self, overlay_handle: &str) -> Result<&dyn OverlayHandler, FpgadError> {
Ok(self
.overlay_handler
.get_or_init(|| XilinxDfxMgrOverlayHandler::new(overlay_handle)))
}
fn status_message(&self) -> Result<String, FpgadError> {
Ok(list_package()?)
}
fn platform_compat_string(&self) -> String {
"xlnx,zynqmp-pcap-fpga,versal-fpga,zynq-devcfg-1.0".into()
}
}
pub fn list_package() -> Result<String, FpgadSoftenerError> {
run_dfx_mgr(&["-listPackage"])
}
#[allow(dead_code)]
pub fn load(accel_name: &str) -> Result<String, FpgadSoftenerError> {
run_dfx_mgr(&["-load", accel_name])
}
#[allow(dead_code)]
pub fn remove(slot_handle: Option<&str>) -> Result<String, FpgadSoftenerError> {
match slot_handle {
Some(slot_handle) => Ok(run_dfx_mgr(&["-remove", slot_handle])?),
None => Ok(run_dfx_mgr(&["-remove"])?),
}
}
pub fn load_bitstream(bitstream_path: &Path) -> Result<String, FpgadSoftenerError> {
let path_str = bitstream_path.to_str().ok_or_else(|| {
FpgadSoftenerError::DfxMgr(format!(
"Bitstream path contains invalid UTF-8: {}",
bitstream_path.display()
))
})?;
run_dfx_mgr(&["-b", path_str])
}
pub fn load_overlay(bitstream_path: &Path, dtbo_path: &Path) -> Result<String, FpgadSoftenerError> {
let bitstream_str = bitstream_path.to_str().ok_or_else(|| {
FpgadSoftenerError::DfxMgr(format!(
"Bitstream path contains invalid UTF-8: {}",
bitstream_path.display()
))
})?;
let dtbo_str = dtbo_path.to_str().ok_or_else(|| {
FpgadSoftenerError::DfxMgr(format!(
"DTBO path contains invalid UTF-8: {}",
dtbo_path.display()
))
})?;
run_dfx_mgr(&["-o", dtbo_str, "-b", bitstream_str])
}
fn run_dfx_mgr(args: &[&str]) -> Result<String, FpgadSoftenerError> {
let prefix = if let Ok(snap_env) = env::var("SNAP_COMPONENTS") {
snap_env + "/dfx-mgr"
} else {
"".to_string()
};
let dfx_mgr_client_path = format!("{}/usr/bin/dfx-mgr-client", prefix);
if !Path::new(&dfx_mgr_client_path).exists() {
return Err(FpgadSoftenerError::DfxMgr(format!(
"dfx-mgr-client not found at '{}'. Install the dfx-mgr component with: snap install fpgad+dfx-mgr.comp",
dfx_mgr_client_path
)));
}
trace!("Calling dfx-mgr with args {:#?}", args);
let output = std::process::Command::new(&dfx_mgr_client_path)
.args(args)
.output()
.map_err(|e| {
FpgadSoftenerError::DfxMgr(format!("dfx-mgr-client failed to produce output:\n{e}"))
})?;
if output.status.success() {
Ok(String::from_utf8_lossy(&output.stdout).to_string())
} else {
Err(FpgadSoftenerError::DfxMgr(format!(
"dfx-mgr-client failed.\n{}\nStdout:\n{:?}\nStderr:\n{:?}",
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)))
}
}