1#![allow(dead_code)] use crate::Result;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::fs;
7use std::path::Path;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub enum FPGAVendor {
12 Intel,
13 Xilinx,
14 Microsemi,
15 Lattice,
16 Altera, Unknown(String),
18}
19
20impl std::fmt::Display for FPGAVendor {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 FPGAVendor::Intel => write!(f, "Intel"),
24 FPGAVendor::Xilinx => write!(f, "Xilinx"),
25 FPGAVendor::Microsemi => write!(f, "Microsemi"),
26 FPGAVendor::Lattice => write!(f, "Lattice"),
27 FPGAVendor::Altera => write!(f, "Altera"),
28 FPGAVendor::Unknown(name) => write!(f, "{name}"),
29 }
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub enum FPGAFamily {
36 IntelArria10,
38 IntelStratix10,
39 IntelCyclone5,
40 IntelAgilex,
41
42 XilinxKintex7,
44 XilinxVirtex7,
45 XilinxZynq7000,
46 XilinxZynqUltraScale,
47 XilinxKintexUltraScale,
48 XilinxVirtexUltraScale,
49 XilinxVersal,
50
51 MicrosemiPolarFire,
53 LatticeECP5,
54
55 Unknown(String),
56}
57
58impl std::fmt::Display for FPGAFamily {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 FPGAFamily::IntelArria10 => write!(f, "Intel Arria 10"),
62 FPGAFamily::IntelStratix10 => write!(f, "Intel Stratix 10"),
63 FPGAFamily::IntelCyclone5 => write!(f, "Intel Cyclone V"),
64 FPGAFamily::IntelAgilex => write!(f, "Intel Agilex"),
65 FPGAFamily::XilinxKintex7 => write!(f, "Xilinx Kintex-7"),
66 FPGAFamily::XilinxVirtex7 => write!(f, "Xilinx Virtex-7"),
67 FPGAFamily::XilinxZynq7000 => write!(f, "Xilinx Zynq-7000"),
68 FPGAFamily::XilinxZynqUltraScale => write!(f, "Xilinx Zynq UltraScale+"),
69 FPGAFamily::XilinxKintexUltraScale => write!(f, "Xilinx Kintex UltraScale+"),
70 FPGAFamily::XilinxVirtexUltraScale => write!(f, "Xilinx Virtex UltraScale+"),
71 FPGAFamily::XilinxVersal => write!(f, "Xilinx Versal"),
72 FPGAFamily::MicrosemiPolarFire => write!(f, "Microsemi PolarFire"),
73 FPGAFamily::LatticeECP5 => write!(f, "Lattice ECP5"),
74 FPGAFamily::Unknown(name) => write!(f, "{name}"),
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
81pub enum FPGAInterface {
82 PCIe,
83 USB,
84 Ethernet,
85 SPI,
86 I2C,
87 JTAG,
88 Embedded,
89 Unknown(String),
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct FPGAInfo {
95 pub vendor: FPGAVendor,
97
98 pub family: FPGAFamily,
100
101 pub model: String,
103
104 pub device_id: Option<String>,
106
107 pub vendor_id: Option<String>,
109
110 pub interface: FPGAInterface,
112
113 pub logic_elements: Option<u64>,
115
116 pub block_ram_bits: Option<u64>,
118
119 pub dsp_blocks: Option<u32>,
121
122 pub max_frequency_mhz: Option<u32>,
124
125 pub power_consumption: Option<f32>,
127
128 pub ml_capabilities: HashMap<String, String>,
130
131 pub development_tools: Vec<String>,
133
134 pub current_config: Option<String>,
136
137 pub driver_version: Option<String>,
139
140 pub temperature: Option<f32>,
142}
143
144impl FPGAInfo {
145 pub fn detect_fpgas() -> Result<Vec<FPGAInfo>> {
147 let mut fpgas = Vec::new();
148
149 fpgas.extend(Self::detect_pcie_fpgas()?);
151
152 fpgas.extend(Self::detect_usb_fpgas()?);
154
155 fpgas.extend(Self::detect_embedded_fpgas()?);
157
158 Ok(fpgas)
159 }
160
161 fn detect_pcie_fpgas() -> Result<Vec<FPGAInfo>> {
162 let mut fpgas = Vec::new();
163
164 #[cfg(target_os = "linux")]
165 {
166 if let Ok(entries) = fs::read_dir("/sys/bus/pci/devices") {
168 for entry in entries.flatten() {
169 if let Some(fpga) = Self::check_pci_device_for_fpga(&entry.path())? {
170 fpgas.push(fpga);
171 }
172 }
173 }
174 }
175
176 #[cfg(target_os = "windows")]
177 {
178 fpgas.extend(Self::detect_fpgas_windows_wmi()?);
180 }
181
182 #[cfg(target_os = "macos")]
183 {
184 fpgas.extend(Self::detect_fpgas_macos()?);
186 }
187
188 Ok(fpgas)
189 }
190
191 #[cfg(target_os = "linux")]
192 fn check_pci_device_for_fpga(device_path: &Path) -> Result<Option<FPGAInfo>> {
193 let vendor_id = Self::read_hex_file(&device_path.join("vendor"))?;
194 let device_id = Self::read_hex_file(&device_path.join("device"))?;
195
196 if let (Some(vendor), Some(device)) = (vendor_id, device_id) {
197 if let Some(fpga_info) = Self::identify_fpga_by_ids(vendor, device) {
199 return Ok(Some(fpga_info));
200 }
201
202 if let Some(class) = Self::read_hex_file(&device_path.join("class"))? {
204 if class == 0x120000 || class == 0x058000 {
206 return Ok(Some(Self::create_generic_fpga_info(vendor, device)));
207 }
208 }
209 }
210
211 Ok(None)
212 }
213
214 fn read_hex_file(path: &Path) -> Result<Option<u32>> {
215 if let Ok(content) = fs::read_to_string(path) {
216 if let Ok(value) = u32::from_str_radix(content.trim().trim_start_matches("0x"), 16) {
217 return Ok(Some(value));
218 }
219 }
220 Ok(None)
221 }
222
223 fn identify_fpga_by_ids(vendor_id: u32, device_id: u32) -> Option<FPGAInfo> {
224 match vendor_id {
225 0x1172 => { Some(Self::create_intel_fpga_info(device_id))
227 },
228 0x10EE => { Some(Self::create_xilinx_fpga_info(device_id))
230 },
231 0x11F8 => { Some(Self::create_microsemi_fpga_info(device_id))
233 },
234 0x1204 => { Some(Self::create_lattice_fpga_info(device_id))
236 },
237 _ => None,
238 }
239 }
240
241 fn create_intel_fpga_info(device_id: u32) -> FPGAInfo {
242 let (family, model, specs) = match device_id {
243 0x09C4 => (FPGAFamily::IntelArria10, "Arria 10 GX", (1150000, 53248000, 1518, 800)),
244 0x09C5 => (FPGAFamily::IntelArria10, "Arria 10 GT", (1150000, 65536000, 1518, 800)),
245 0x1D1C => (FPGAFamily::IntelStratix10, "Stratix 10 GX", (2753000, 229376000, 5760, 1000)),
246 0x1D1D => (FPGAFamily::IntelStratix10, "Stratix 10 TX", (2753000, 229376000, 5760, 1000)),
247 0x4350 => (FPGAFamily::IntelAgilex, "Agilex F-Series", (2700000, 270000000, 5760, 1100)),
248 _ => {
249 let family = FPGAFamily::Unknown(format!("Intel Device 0x{device_id:04X}"));
250 let model = format!("Intel FPGA Device 0x{device_id:04X}");
251 return FPGAInfo {
252 vendor: FPGAVendor::Intel,
253 family,
254 model,
255 device_id: Some(format!("0x{device_id:04X}")),
256 vendor_id: Some("0x1172".to_string()),
257 interface: FPGAInterface::PCIe,
258 logic_elements: None,
259 block_ram_bits: None,
260 dsp_blocks: None,
261 max_frequency_mhz: None,
262 power_consumption: None,
263 ml_capabilities: HashMap::from([
264 ("openCL_support".to_string(), "true".to_string()),
265 ("oneAPI_support".to_string(), "true".to_string()),
266 ("dsp_optimization".to_string(), "true".to_string()),
267 ]),
268 development_tools: vec![
269 "Intel Quartus Prime".to_string(),
270 "Intel OpenCL SDK".to_string(),
271 "Intel oneAPI".to_string(),
272 ],
273 current_config: None,
274 driver_version: None,
275 temperature: None,
276 };
277 }
278 };
279
280 FPGAInfo {
281 vendor: FPGAVendor::Intel,
282 family,
283 model: model.to_string(),
284 device_id: Some(format!("0x{device_id:04X}")),
285 vendor_id: Some("0x1172".to_string()),
286 interface: FPGAInterface::PCIe,
287 logic_elements: if specs.0 > 0 { Some(specs.0) } else { None },
288 block_ram_bits: if specs.1 > 0 { Some(specs.1) } else { None },
289 dsp_blocks: if specs.2 > 0 { Some(specs.2) } else { None },
290 max_frequency_mhz: if specs.3 > 0 { Some(specs.3) } else { None },
291 power_consumption: None,
292 ml_capabilities: HashMap::from([
293 ("openCL_support".to_string(), "true".to_string()),
294 ("oneAPI_support".to_string(), "true".to_string()),
295 ("dsp_optimization".to_string(), "true".to_string()),
296 ]),
297 development_tools: vec![
298 "Intel Quartus Prime".to_string(),
299 "Intel OpenCL SDK".to_string(),
300 "Intel oneAPI".to_string(),
301 ],
302 current_config: None,
303 driver_version: None,
304 temperature: None,
305 }
306 }
307
308 fn create_xilinx_fpga_info(device_id: u32) -> FPGAInfo {
309 let (family, model, specs) = match device_id {
310 0x7028 => (FPGAFamily::XilinxKintex7, "Kintex-7 K325T", (326080, 16020000, 840, 464)),
311 0x7034 => (FPGAFamily::XilinxVirtex7, "Virtex-7 V485T", (485760, 37080000, 2800, 600)),
312 0x7020 => (FPGAFamily::XilinxZynq7000, "Zynq-7000 Z020", (85000, 4900000, 220, 766)),
313 0x9038 => (FPGAFamily::XilinxZynqUltraScale, "Zynq UltraScale+ ZU19EG", (1143000, 75900000, 1968, 850)),
314 0x906C => (FPGAFamily::XilinxKintexUltraScale, "Kintex UltraScale+ KU15P", (1451000, 75900000, 1968, 925)),
315 0x9058 => (FPGAFamily::XilinxVirtexUltraScale, "Virtex UltraScale+ VU19P", (8938000, 270000000, 12288, 750)),
316 0x5008 => (FPGAFamily::XilinxVersal, "Versal Prime VP1202", (899000, 57600000, 1968, 1300)),
317 _ => {
318 let family = FPGAFamily::Unknown(format!("Xilinx Device 0x{device_id:04X}"));
319 let model = format!("Xilinx FPGA Device 0x{device_id:04X}");
320 return FPGAInfo {
321 vendor: FPGAVendor::Xilinx,
322 family,
323 model,
324 device_id: Some(format!("0x{device_id:04X}")),
325 vendor_id: Some("0x10EE".to_string()),
326 interface: FPGAInterface::PCIe,
327 logic_elements: None,
328 block_ram_bits: None,
329 dsp_blocks: None,
330 max_frequency_mhz: None,
331 power_consumption: None,
332 ml_capabilities: HashMap::from([
333 ("vitis_ai_support".to_string(), "true".to_string()),
334 ("dpu_acceleration".to_string(), "true".to_string()),
335 ("hls_support".to_string(), "true".to_string()),
336 ]),
337 development_tools: vec![
338 "Xilinx Vivado".to_string(),
339 "Xilinx Vitis".to_string(),
340 "Xilinx Vitis AI".to_string(),
341 ],
342 current_config: None,
343 driver_version: None,
344 temperature: None,
345 };
346 }
347 };
348
349 FPGAInfo {
350 vendor: FPGAVendor::Xilinx,
351 family,
352 model: model.to_string(),
353 device_id: Some(format!("0x{device_id:04X}")),
354 vendor_id: Some("0x10EE".to_string()),
355 interface: FPGAInterface::PCIe,
356 logic_elements: if specs.0 > 0 { Some(specs.0) } else { None },
357 block_ram_bits: if specs.1 > 0 { Some(specs.1) } else { None },
358 dsp_blocks: if specs.2 > 0 { Some(specs.2) } else { None },
359 max_frequency_mhz: if specs.3 > 0 { Some(specs.3) } else { None },
360 power_consumption: None,
361 ml_capabilities: HashMap::from([
362 ("vitis_ai_support".to_string(), "true".to_string()),
363 ("dpu_acceleration".to_string(), "true".to_string()),
364 ("hls_support".to_string(), "true".to_string()),
365 ]),
366 development_tools: vec![
367 "Xilinx Vivado".to_string(),
368 "Xilinx Vitis".to_string(),
369 "Xilinx Vitis AI".to_string(),
370 ],
371 current_config: None,
372 driver_version: None,
373 temperature: None,
374 }
375 }
376
377 fn create_microsemi_fpga_info(device_id: u32) -> FPGAInfo {
378 FPGAInfo {
379 vendor: FPGAVendor::Microsemi,
380 family: FPGAFamily::MicrosemiPolarFire,
381 model: format!("Microsemi Device 0x{device_id:04X}"),
382 device_id: Some(format!("0x{device_id:04X}")),
383 vendor_id: Some("0x11F8".to_string()),
384 interface: FPGAInterface::PCIe,
385 logic_elements: None,
386 block_ram_bits: None,
387 dsp_blocks: None,
388 max_frequency_mhz: None,
389 power_consumption: None,
390 ml_capabilities: HashMap::from([
391 ("low_power_inference".to_string(), "true".to_string()),
392 ]),
393 development_tools: vec!["Libero SoC".to_string()],
394 current_config: None,
395 driver_version: None,
396 temperature: None,
397 }
398 }
399
400 fn create_lattice_fpga_info(device_id: u32) -> FPGAInfo {
401 FPGAInfo {
402 vendor: FPGAVendor::Lattice,
403 family: FPGAFamily::LatticeECP5,
404 model: format!("Lattice Device 0x{device_id:04X}"),
405 device_id: Some(format!("0x{device_id:04X}")),
406 vendor_id: Some("0x1204".to_string()),
407 interface: FPGAInterface::PCIe,
408 logic_elements: None,
409 block_ram_bits: None,
410 dsp_blocks: None,
411 max_frequency_mhz: None,
412 power_consumption: None,
413 ml_capabilities: HashMap::from([
414 ("edge_ai_inference".to_string(), "true".to_string()),
415 ("low_power".to_string(), "true".to_string()),
416 ]),
417 development_tools: vec!["Lattice Diamond".to_string(), "Lattice Radiant".to_string()],
418 current_config: None,
419 driver_version: None,
420 temperature: None,
421 }
422 }
423
424 fn create_generic_fpga_info(vendor_id: u32, device_id: u32) -> FPGAInfo {
425 FPGAInfo {
426 vendor: FPGAVendor::Unknown(format!("0x{vendor_id:04X}")),
427 family: FPGAFamily::Unknown("Unknown".to_string()),
428 model: format!("FPGA Device 0x{vendor_id:04X}:0x{device_id:04X}"),
429 device_id: Some(format!("0x{device_id:04X}")),
430 vendor_id: Some(format!("0x{vendor_id:04X}")),
431 interface: FPGAInterface::PCIe,
432 logic_elements: None,
433 block_ram_bits: None,
434 dsp_blocks: None,
435 max_frequency_mhz: None,
436 power_consumption: None,
437 ml_capabilities: HashMap::new(),
438 development_tools: Vec::new(),
439 current_config: None,
440 driver_version: None,
441 temperature: None,
442 }
443 }
444
445 fn detect_usb_fpgas() -> Result<Vec<FPGAInfo>> {
446 Ok(Vec::new())
448 }
449
450 fn detect_embedded_fpgas() -> Result<Vec<FPGAInfo>> {
451 Ok(Vec::new())
453 }
454
455 #[cfg(target_os = "windows")]
456 fn detect_fpgas_windows_wmi() -> Result<Vec<FPGAInfo>> {
457 Ok(Vec::new())
459 }
460
461 #[cfg(target_os = "macos")]
462 fn detect_fpgas_macos() -> Result<Vec<FPGAInfo>> {
463 Ok(Vec::new())
465 }
466
467 pub fn calculate_ai_performance(&self) -> HashMap<String, f64> {
469 let mut metrics = HashMap::new();
470
471 if let (Some(dsp_blocks), Some(freq_mhz)) = (self.dsp_blocks, self.max_frequency_mhz) {
472 let ops_per_second = (dsp_blocks as f64) * (freq_mhz as f64) * 1_000_000.0;
474 metrics.insert("theoretical_ops_per_second".to_string(), ops_per_second);
475
476 metrics.insert("int8_ops_per_second".to_string(), ops_per_second * 4.0);
478 metrics.insert("int16_ops_per_second".to_string(), ops_per_second * 2.0);
479 metrics.insert("fp32_ops_per_second".to_string(), ops_per_second);
480 }
481
482 if let Some(logic_elements) = self.logic_elements {
483 let efficiency_score = (logic_elements as f64) / 1_000_000.0; metrics.insert("logic_efficiency_score".to_string(), efficiency_score);
486 }
487
488 metrics
489 }
490
491 pub fn get_ai_framework_support(&self) -> Vec<String> {
493 let mut frameworks = Vec::new();
494
495 match self.vendor {
496 FPGAVendor::Intel => {
497 frameworks.extend(vec![
498 "Intel OpenVINO".to_string(),
499 "Intel oneAPI".to_string(),
500 "OpenCL".to_string(),
501 ]);
502 },
503 FPGAVendor::Xilinx => {
504 frameworks.extend(vec![
505 "Xilinx Vitis AI".to_string(),
506 "TensorFlow".to_string(),
507 "PyTorch".to_string(),
508 "ONNX".to_string(),
509 ]);
510 },
511 _ => {
512 frameworks.push("Custom FPGA frameworks".to_string());
513 }
514 }
515
516 frameworks
517 }
518}