use crate::bladerf1::board::{ConfigSession, FlashSession, RfLinkSession};
use crate::bladerf1::hardware::spi_flash::{
BLADERF_FLASH_ADDR_FPGA, BLADERF_FLASH_ERASE_BLOCK_SIZE, BLADERF_FLASH_PAGE_SIZE,
};
use crate::error::{Error, Result};
use crate::flash::{binkv_encode_field, pad_to_page};
use crate::usb::{
BladeRf1UsbInterfaceCommands, CONTROL_ENDPOINT_OUT, UsbInterfaceCommands, VendorRequest,
};
use std::fmt;
use std::thread;
use std::time::Duration;
pub const BLADERF_FLASH_FPGA_SIZE_40KLE: usize = 1_191_788;
pub const BLADERF_FLASH_FPGA_SIZE_115KLE: usize = 3_571_462;
pub fn is_valid_fpga_size(len: usize) -> bool {
len == BLADERF_FLASH_FPGA_SIZE_40KLE || len == BLADERF_FLASH_FPGA_SIZE_115KLE
}
const LOG_EOF: u32 = 0x00000000;
const LOG_ERR: u32 = 0xFFFFFFFF;
const FPGA_LOAD_TIMEOUT: Duration = Duration::from_secs(3);
const FPGA_STATUS_POLL_INTERVAL: Duration = Duration::from_millis(200);
const FPGA_STATUS_POLL_ATTEMPTS: u32 = 10;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FwLogFile {
None,
BladeRf,
Flash,
Fpga,
Gpif,
Logger,
Rf,
SpiFlash,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FwLogEntry {
data: u16,
line: u16,
file: FwLogFile,
}
impl FwLogEntry {
pub fn data(&self) -> u16 {
self.data
}
pub fn line(&self) -> u16 {
self.line
}
pub fn file(&self) -> FwLogFile {
self.file
}
}
impl FwLogFile {
fn from_id(id: u8) -> Self {
match id {
0 => Self::None,
1 => Self::BladeRf,
2 => Self::Flash,
3 => Self::Fpga,
4 => Self::Gpif,
5 => Self::Logger,
6 => Self::Rf,
7 => Self::SpiFlash,
_ => Self::None,
}
}
fn as_str(&self) -> &'static str {
match self {
Self::None => "<none>",
Self::BladeRf => "bladeRF.c",
Self::Flash => "flash.c",
Self::Fpga => "fpga.c",
Self::Gpif => "gpif.c",
Self::Logger => "logger.c",
Self::Rf => "rf.c",
Self::SpiFlash => "spi_flash_lib.c",
}
}
}
impl FwLogEntry {
fn from_u32(entry: u32) -> Self {
Self {
data: (entry & 0xFFFF) as u16,
line: ((entry >> 16) & 0x7FF) as u16,
file: FwLogFile::from_id(((entry >> 27) & 0x1F) as u8),
}
}
}
impl fmt::Display for FwLogEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}, {}, 0x{:04X}",
self.file.as_str(),
self.line,
self.data
)
}
}
impl RfLinkSession<'_> {
pub fn is_fpga_configured(&mut self) -> Result<bool> {
self.nios.usb_is_fpga_configured()
}
pub fn read_fw_log(&mut self) -> Result<Vec<FwLogEntry>> {
let mut entries = Vec::new();
loop {
let raw = self.nios.usb_vendor_cmd_int(VendorRequest::ReadLogEntry)?;
if raw == LOG_EOF {
break;
}
if raw == LOG_ERR {
log::warn!("firmware log read error");
break;
}
entries.push(FwLogEntry::from_u32(raw));
}
Ok(entries)
}
}
impl ConfigSession<'_> {
pub fn load_fpga(&mut self, bitstream: &[u8]) -> Result<()> {
if !is_valid_fpga_size(bitstream.len()) {
return Err(Error::Argument(format!(
"invalid FPGA bitstream size: {} bytes (expected {} or {})",
bitstream.len(),
BLADERF_FLASH_FPGA_SIZE_40KLE,
BLADERF_FLASH_FPGA_SIZE_115KLE,
)));
}
self.nios.usb_begin_fpga_prog()?;
self.nios
.usb_bulk_out(CONTROL_ENDPOINT_OUT, bitstream, FPGA_LOAD_TIMEOUT)?;
let configured = {
let mut result = false;
for _ in 0..FPGA_STATUS_POLL_ATTEMPTS {
if self.nios.usb_is_fpga_configured()? {
result = true;
break;
}
thread::sleep(FPGA_STATUS_POLL_INTERVAL);
}
result
};
if !configured {
return Err(Error::Timeout);
}
Ok(())
}
}
impl FlashSession<'_> {
pub fn flash_fpga(&mut self, bitstream: &[u8]) -> Result<()> {
if !is_valid_fpga_size(bitstream.len()) {
return Err(Error::Argument(format!(
"invalid FPGA bitstream size: {} bytes (expected {} or {})",
bitstream.len(),
BLADERF_FLASH_FPGA_SIZE_40KLE,
BLADERF_FLASH_FPGA_SIZE_115KLE,
)));
}
let fpga_page = BLADERF_FLASH_ADDR_FPGA / BLADERF_FLASH_PAGE_SIZE as u32;
let padded = pad_to_page(bitstream);
let mut meta = [0xFFu8; BLADERF_FLASH_PAGE_SIZE];
let len_str = bitstream.len().to_string();
binkv_encode_field(&mut meta, 0, "LEN", &len_str)?;
let mut all_data = Vec::with_capacity(BLADERF_FLASH_PAGE_SIZE + padded.len());
all_data.extend_from_slice(&meta);
all_data.extend_from_slice(&padded);
self.erase_write_verify(fpga_page, &all_data)?;
Ok(())
}
pub fn erase_stored_fpga(&mut self) -> Result<()> {
let fpga_sector = BLADERF_FLASH_ADDR_FPGA / BLADERF_FLASH_ERASE_BLOCK_SIZE as u32;
let count = self.total_sectors() - fpga_sector;
self.erase_sectors(fpga_sector, count)
}
}