use crate::config;
use crate::error::FpgadError;
use crate::platforms::platform::Fpga;
use crate::system_io::{fs_read, fs_write};
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!("{}'s state is 'operating'", self.device_handle);
Ok(())
}
_ => Err(FpgadError::FPGAState(format!(
"After loading bitstream, {}'s state 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<(), 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(()),
Ok(returned_flags) => Err(FpgadError::Flag(format!(
"Setting {}'s flags to '{}' failed. Resulting flag was '{}'",
self.device_handle, flags, returned_flags
))),
Err(e) => Err(FpgadError::Flag(format!(
"Failed to read {}'s flags after setting to '{}': {}",
self.device_handle, flags, e
))),
}
}
fn load_firmware(&self, bitstream_path_rel: &Path) -> Result<(), FpgadError> {
let control_path = Path::new(config::FPGA_MANAGERS_DIR)
.join(self.device_handle())
.join("firmware");
fs_write(&control_path, false, bitstream_path_rel.to_string_lossy())?;
self.assert_state()
}
}