#![allow(dead_code)]
use crate::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FPGAVendor {
Intel,
Xilinx,
Microsemi,
Lattice,
Altera, Unknown(String),
}
impl std::fmt::Display for FPGAVendor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FPGAVendor::Intel => write!(f, "Intel"),
FPGAVendor::Xilinx => write!(f, "Xilinx"),
FPGAVendor::Microsemi => write!(f, "Microsemi"),
FPGAVendor::Lattice => write!(f, "Lattice"),
FPGAVendor::Altera => write!(f, "Altera"),
FPGAVendor::Unknown(name) => write!(f, "{name}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FPGAFamily {
IntelArria10,
IntelStratix10,
IntelCyclone5,
IntelAgilex,
XilinxKintex7,
XilinxVirtex7,
XilinxZynq7000,
XilinxZynqUltraScale,
XilinxKintexUltraScale,
XilinxVirtexUltraScale,
XilinxVersal,
MicrosemiPolarFire,
LatticeECP5,
Unknown(String),
}
impl std::fmt::Display for FPGAFamily {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FPGAFamily::IntelArria10 => write!(f, "Intel Arria 10"),
FPGAFamily::IntelStratix10 => write!(f, "Intel Stratix 10"),
FPGAFamily::IntelCyclone5 => write!(f, "Intel Cyclone V"),
FPGAFamily::IntelAgilex => write!(f, "Intel Agilex"),
FPGAFamily::XilinxKintex7 => write!(f, "Xilinx Kintex-7"),
FPGAFamily::XilinxVirtex7 => write!(f, "Xilinx Virtex-7"),
FPGAFamily::XilinxZynq7000 => write!(f, "Xilinx Zynq-7000"),
FPGAFamily::XilinxZynqUltraScale => write!(f, "Xilinx Zynq UltraScale+"),
FPGAFamily::XilinxKintexUltraScale => write!(f, "Xilinx Kintex UltraScale+"),
FPGAFamily::XilinxVirtexUltraScale => write!(f, "Xilinx Virtex UltraScale+"),
FPGAFamily::XilinxVersal => write!(f, "Xilinx Versal"),
FPGAFamily::MicrosemiPolarFire => write!(f, "Microsemi PolarFire"),
FPGAFamily::LatticeECP5 => write!(f, "Lattice ECP5"),
FPGAFamily::Unknown(name) => write!(f, "{name}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FPGAInterface {
PCIe,
USB,
Ethernet,
SPI,
I2C,
JTAG,
Embedded,
Unknown(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FPGAInfo {
pub vendor: FPGAVendor,
pub family: FPGAFamily,
pub model: String,
pub device_id: Option<String>,
pub vendor_id: Option<String>,
pub interface: FPGAInterface,
pub logic_elements: Option<u64>,
pub block_ram_bits: Option<u64>,
pub dsp_blocks: Option<u32>,
pub max_frequency_mhz: Option<u32>,
pub power_consumption: Option<f32>,
pub ml_capabilities: HashMap<String, String>,
pub development_tools: Vec<String>,
pub current_config: Option<String>,
pub driver_version: Option<String>,
pub temperature: Option<f32>,
}
impl FPGAInfo {
pub fn detect_fpgas() -> Result<Vec<FPGAInfo>> {
let mut fpgas = Vec::new();
fpgas.extend(Self::detect_pcie_fpgas()?);
fpgas.extend(Self::detect_usb_fpgas()?);
fpgas.extend(Self::detect_embedded_fpgas()?);
Ok(fpgas)
}
fn detect_pcie_fpgas() -> Result<Vec<FPGAInfo>> {
let mut fpgas = Vec::new();
#[cfg(target_os = "linux")]
{
if let Ok(entries) = fs::read_dir("/sys/bus/pci/devices") {
for entry in entries.flatten() {
if let Some(fpga) = Self::check_pci_device_for_fpga(&entry.path())? {
fpgas.push(fpga);
}
}
}
}
#[cfg(target_os = "windows")]
{
fpgas.extend(Self::detect_fpgas_windows_wmi()?);
}
#[cfg(target_os = "macos")]
{
fpgas.extend(Self::detect_fpgas_macos()?);
}
Ok(fpgas)
}
#[cfg(target_os = "linux")]
fn check_pci_device_for_fpga(device_path: &Path) -> Result<Option<FPGAInfo>> {
let vendor_id = Self::read_hex_file(&device_path.join("vendor"))?;
let device_id = Self::read_hex_file(&device_path.join("device"))?;
if let (Some(vendor), Some(device)) = (vendor_id, device_id) {
if let Some(fpga_info) = Self::identify_fpga_by_ids(vendor, device) {
return Ok(Some(fpga_info));
}
if let Some(class) = Self::read_hex_file(&device_path.join("class"))? {
if class == 0x120000 || class == 0x058000 {
return Ok(Some(Self::create_generic_fpga_info(vendor, device)));
}
}
}
Ok(None)
}
fn read_hex_file(path: &Path) -> Result<Option<u32>> {
if let Ok(content) = fs::read_to_string(path) {
if let Ok(value) = u32::from_str_radix(content.trim().trim_start_matches("0x"), 16) {
return Ok(Some(value));
}
}
Ok(None)
}
fn identify_fpga_by_ids(vendor_id: u32, device_id: u32) -> Option<FPGAInfo> {
match vendor_id {
0x1172 => { Some(Self::create_intel_fpga_info(device_id))
},
0x10EE => { Some(Self::create_xilinx_fpga_info(device_id))
},
0x11F8 => { Some(Self::create_microsemi_fpga_info(device_id))
},
0x1204 => { Some(Self::create_lattice_fpga_info(device_id))
},
_ => None,
}
}
fn create_intel_fpga_info(device_id: u32) -> FPGAInfo {
let (family, model, specs) = match device_id {
0x09C4 => (FPGAFamily::IntelArria10, "Arria 10 GX", (1150000, 53248000, 1518, 800)),
0x09C5 => (FPGAFamily::IntelArria10, "Arria 10 GT", (1150000, 65536000, 1518, 800)),
0x1D1C => (FPGAFamily::IntelStratix10, "Stratix 10 GX", (2753000, 229376000, 5760, 1000)),
0x1D1D => (FPGAFamily::IntelStratix10, "Stratix 10 TX", (2753000, 229376000, 5760, 1000)),
0x4350 => (FPGAFamily::IntelAgilex, "Agilex F-Series", (2700000, 270000000, 5760, 1100)),
_ => {
let family = FPGAFamily::Unknown(format!("Intel Device 0x{device_id:04X}"));
let model = format!("Intel FPGA Device 0x{device_id:04X}");
return FPGAInfo {
vendor: FPGAVendor::Intel,
family,
model,
device_id: Some(format!("0x{device_id:04X}")),
vendor_id: Some("0x1172".to_string()),
interface: FPGAInterface::PCIe,
logic_elements: None,
block_ram_bits: None,
dsp_blocks: None,
max_frequency_mhz: None,
power_consumption: None,
ml_capabilities: HashMap::from([
("openCL_support".to_string(), "true".to_string()),
("oneAPI_support".to_string(), "true".to_string()),
("dsp_optimization".to_string(), "true".to_string()),
]),
development_tools: vec![
"Intel Quartus Prime".to_string(),
"Intel OpenCL SDK".to_string(),
"Intel oneAPI".to_string(),
],
current_config: None,
driver_version: None,
temperature: None,
};
}
};
FPGAInfo {
vendor: FPGAVendor::Intel,
family,
model: model.to_string(),
device_id: Some(format!("0x{device_id:04X}")),
vendor_id: Some("0x1172".to_string()),
interface: FPGAInterface::PCIe,
logic_elements: if specs.0 > 0 { Some(specs.0) } else { None },
block_ram_bits: if specs.1 > 0 { Some(specs.1) } else { None },
dsp_blocks: if specs.2 > 0 { Some(specs.2) } else { None },
max_frequency_mhz: if specs.3 > 0 { Some(specs.3) } else { None },
power_consumption: None,
ml_capabilities: HashMap::from([
("openCL_support".to_string(), "true".to_string()),
("oneAPI_support".to_string(), "true".to_string()),
("dsp_optimization".to_string(), "true".to_string()),
]),
development_tools: vec![
"Intel Quartus Prime".to_string(),
"Intel OpenCL SDK".to_string(),
"Intel oneAPI".to_string(),
],
current_config: None,
driver_version: None,
temperature: None,
}
}
fn create_xilinx_fpga_info(device_id: u32) -> FPGAInfo {
let (family, model, specs) = match device_id {
0x7028 => (FPGAFamily::XilinxKintex7, "Kintex-7 K325T", (326080, 16020000, 840, 464)),
0x7034 => (FPGAFamily::XilinxVirtex7, "Virtex-7 V485T", (485760, 37080000, 2800, 600)),
0x7020 => (FPGAFamily::XilinxZynq7000, "Zynq-7000 Z020", (85000, 4900000, 220, 766)),
0x9038 => (FPGAFamily::XilinxZynqUltraScale, "Zynq UltraScale+ ZU19EG", (1143000, 75900000, 1968, 850)),
0x906C => (FPGAFamily::XilinxKintexUltraScale, "Kintex UltraScale+ KU15P", (1451000, 75900000, 1968, 925)),
0x9058 => (FPGAFamily::XilinxVirtexUltraScale, "Virtex UltraScale+ VU19P", (8938000, 270000000, 12288, 750)),
0x5008 => (FPGAFamily::XilinxVersal, "Versal Prime VP1202", (899000, 57600000, 1968, 1300)),
_ => {
let family = FPGAFamily::Unknown(format!("Xilinx Device 0x{device_id:04X}"));
let model = format!("Xilinx FPGA Device 0x{device_id:04X}");
return FPGAInfo {
vendor: FPGAVendor::Xilinx,
family,
model,
device_id: Some(format!("0x{device_id:04X}")),
vendor_id: Some("0x10EE".to_string()),
interface: FPGAInterface::PCIe,
logic_elements: None,
block_ram_bits: None,
dsp_blocks: None,
max_frequency_mhz: None,
power_consumption: None,
ml_capabilities: HashMap::from([
("vitis_ai_support".to_string(), "true".to_string()),
("dpu_acceleration".to_string(), "true".to_string()),
("hls_support".to_string(), "true".to_string()),
]),
development_tools: vec![
"Xilinx Vivado".to_string(),
"Xilinx Vitis".to_string(),
"Xilinx Vitis AI".to_string(),
],
current_config: None,
driver_version: None,
temperature: None,
};
}
};
FPGAInfo {
vendor: FPGAVendor::Xilinx,
family,
model: model.to_string(),
device_id: Some(format!("0x{device_id:04X}")),
vendor_id: Some("0x10EE".to_string()),
interface: FPGAInterface::PCIe,
logic_elements: if specs.0 > 0 { Some(specs.0) } else { None },
block_ram_bits: if specs.1 > 0 { Some(specs.1) } else { None },
dsp_blocks: if specs.2 > 0 { Some(specs.2) } else { None },
max_frequency_mhz: if specs.3 > 0 { Some(specs.3) } else { None },
power_consumption: None,
ml_capabilities: HashMap::from([
("vitis_ai_support".to_string(), "true".to_string()),
("dpu_acceleration".to_string(), "true".to_string()),
("hls_support".to_string(), "true".to_string()),
]),
development_tools: vec![
"Xilinx Vivado".to_string(),
"Xilinx Vitis".to_string(),
"Xilinx Vitis AI".to_string(),
],
current_config: None,
driver_version: None,
temperature: None,
}
}
fn create_microsemi_fpga_info(device_id: u32) -> FPGAInfo {
FPGAInfo {
vendor: FPGAVendor::Microsemi,
family: FPGAFamily::MicrosemiPolarFire,
model: format!("Microsemi Device 0x{device_id:04X}"),
device_id: Some(format!("0x{device_id:04X}")),
vendor_id: Some("0x11F8".to_string()),
interface: FPGAInterface::PCIe,
logic_elements: None,
block_ram_bits: None,
dsp_blocks: None,
max_frequency_mhz: None,
power_consumption: None,
ml_capabilities: HashMap::from([
("low_power_inference".to_string(), "true".to_string()),
]),
development_tools: vec!["Libero SoC".to_string()],
current_config: None,
driver_version: None,
temperature: None,
}
}
fn create_lattice_fpga_info(device_id: u32) -> FPGAInfo {
FPGAInfo {
vendor: FPGAVendor::Lattice,
family: FPGAFamily::LatticeECP5,
model: format!("Lattice Device 0x{device_id:04X}"),
device_id: Some(format!("0x{device_id:04X}")),
vendor_id: Some("0x1204".to_string()),
interface: FPGAInterface::PCIe,
logic_elements: None,
block_ram_bits: None,
dsp_blocks: None,
max_frequency_mhz: None,
power_consumption: None,
ml_capabilities: HashMap::from([
("edge_ai_inference".to_string(), "true".to_string()),
("low_power".to_string(), "true".to_string()),
]),
development_tools: vec!["Lattice Diamond".to_string(), "Lattice Radiant".to_string()],
current_config: None,
driver_version: None,
temperature: None,
}
}
fn create_generic_fpga_info(vendor_id: u32, device_id: u32) -> FPGAInfo {
FPGAInfo {
vendor: FPGAVendor::Unknown(format!("0x{vendor_id:04X}")),
family: FPGAFamily::Unknown("Unknown".to_string()),
model: format!("FPGA Device 0x{vendor_id:04X}:0x{device_id:04X}"),
device_id: Some(format!("0x{device_id:04X}")),
vendor_id: Some(format!("0x{vendor_id:04X}")),
interface: FPGAInterface::PCIe,
logic_elements: None,
block_ram_bits: None,
dsp_blocks: None,
max_frequency_mhz: None,
power_consumption: None,
ml_capabilities: HashMap::new(),
development_tools: Vec::new(),
current_config: None,
driver_version: None,
temperature: None,
}
}
fn detect_usb_fpgas() -> Result<Vec<FPGAInfo>> {
Ok(Vec::new())
}
fn detect_embedded_fpgas() -> Result<Vec<FPGAInfo>> {
Ok(Vec::new())
}
#[cfg(target_os = "windows")]
fn detect_fpgas_windows_wmi() -> Result<Vec<FPGAInfo>> {
Ok(Vec::new())
}
#[cfg(target_os = "macos")]
fn detect_fpgas_macos() -> Result<Vec<FPGAInfo>> {
Ok(Vec::new())
}
pub fn calculate_ai_performance(&self) -> HashMap<String, f64> {
let mut metrics = HashMap::new();
if let (Some(dsp_blocks), Some(freq_mhz)) = (self.dsp_blocks, self.max_frequency_mhz) {
let ops_per_second = (dsp_blocks as f64) * (freq_mhz as f64) * 1_000_000.0;
metrics.insert("theoretical_ops_per_second".to_string(), ops_per_second);
metrics.insert("int8_ops_per_second".to_string(), ops_per_second * 4.0);
metrics.insert("int16_ops_per_second".to_string(), ops_per_second * 2.0);
metrics.insert("fp32_ops_per_second".to_string(), ops_per_second);
}
if let Some(logic_elements) = self.logic_elements {
let efficiency_score = (logic_elements as f64) / 1_000_000.0; metrics.insert("logic_efficiency_score".to_string(), efficiency_score);
}
metrics
}
pub fn get_ai_framework_support(&self) -> Vec<String> {
let mut frameworks = Vec::new();
match self.vendor {
FPGAVendor::Intel => {
frameworks.extend(vec![
"Intel OpenVINO".to_string(),
"Intel oneAPI".to_string(),
"OpenCL".to_string(),
]);
},
FPGAVendor::Xilinx => {
frameworks.extend(vec![
"Xilinx Vitis AI".to_string(),
"TensorFlow".to_string(),
"PyTorch".to_string(),
"ONNX".to_string(),
]);
},
_ => {
frameworks.push("Custom FPGA frameworks".to_string());
}
}
frameworks
}
}