use crate::error::FpgadError;
use crate::platforms::platform::Fpga;
use crate::platforms::universal_components::universal_helpers;
use crate::system_io::{fs_read, fs_write};
use crate::{config, system_io};
use log::{error, info, trace, warn};
use std::path::Path;
#[derive(Debug)]
pub struct UniversalFPGA {
pub(crate) device_handle: String,
}
impl UniversalFPGA {
pub(crate) fn new(device_handle: &str) -> UniversalFPGA {
UniversalFPGA {
device_handle: device_handle.to_owned(),
}
}
pub(crate) fn assert_state(&self) -> Result<(), FpgadError> {
match self.state() {
Ok(state) => match state.to_string().as_str() {
"operating" => {
info!("The state of '{}' is 'operating'", self.device_handle);
Ok(())
}
_ => Err(FpgadError::FPGAState(format!(
"After loading bitstream, the state of '{}' should be should be 'operating' but it is '{}'",
self.device_handle, state
))),
},
Err(e) => Err(e),
}
}
}
impl Fpga for UniversalFPGA {
fn device_handle(&self) -> &str {
&self.device_handle
}
fn state(&self) -> Result<String, FpgadError> {
let state_path = Path::new(config::FPGA_MANAGERS_DIR)
.join(self.device_handle.clone())
.join("state");
trace!("reading '{state_path:?}'");
fs_read(&state_path).map(|s| s.trim_end_matches('\n').to_string())
}
fn flags(&self) -> Result<u32, FpgadError> {
let flag_path = Path::new(config::FPGA_MANAGERS_DIR)
.join(self.device_handle.clone())
.join("flags");
let contents = fs_read(&flag_path)?;
let trimmed = contents.trim().trim_start_matches("0x");
u32::from_str_radix(trimmed, 16)
.map_err(|_| FpgadError::Flag("Parsing flags failed".into()))
}
fn set_flags(&self, flags: u32) -> Result<String, FpgadError> {
let flag_path = Path::new(config::FPGA_MANAGERS_DIR)
.join(self.device_handle.clone())
.join("flags");
trace!("Writing '0x{flags:X}' to '{flag_path:?}'");
if let Err(e) = fs_write(&flag_path, false, format!("0x{flags:X}")) {
error!("Failed to read state.");
return Err(e);
}
match self.state() {
Ok(state) => match state.as_str() {
"operating" => {
info!(
"{}'s state is 'operating' after writing flags.",
self.device_handle
)
}
_ => {
warn!(
"{}'s state is '{}' after writing flags.",
self.device_handle, state
);
}
},
Err(e) => return Err(e),
};
match self.flags() {
Ok(returned_flags) if returned_flags == flags => Ok(format!(
"Flags set to '0x{:X}' for '{}'",
flags, self.device_handle
)),
Ok(returned_flags) => Err(FpgadError::Flag(format!(
"Setting flags of '{}' to '{}' failed. Resulting flag was '{}'",
self.device_handle, flags, returned_flags
))),
Err(e) => Err(FpgadError::Flag(format!(
"Failed to read device '{}' flags after setting to '{}': {}",
self.device_handle, flags, e
))),
}
}
fn load_firmware(
&self,
bitstream_path: &Path,
firmware_lookup_path: &Path,
) -> Result<String, FpgadError> {
let (prefix, suffix) = system_io::make_firmware_pair(bitstream_path, firmware_lookup_path)?;
universal_helpers::write_firmware_source_dir(&prefix.to_string_lossy())?;
let control_path = Path::new(config::FPGA_MANAGERS_DIR)
.join(self.device_handle())
.join("firmware");
fs_write(&control_path, false, suffix.to_string_lossy())?;
self.assert_state()?;
Ok(format!(
"'{:#?}' loaded to '{}' using firmware lookup path '{:#?}'",
bitstream_path, self.device_handle, prefix
))
}
fn remove_firmware(&self, _handle: Option<&str>) -> Result<String, FpgadError> {
Err(FpgadError::Internal(
"UniversalPlatform does not support removing bitstreams".to_string(),
))
}
}