| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| use anyhow::Result; |
| use std::time::Duration; |
| |
| use crate::io::gpio::GpioPin; |
| use crate::io::uart::Uart; |
| use crate::util::rom_detect::{RomDetect, RomKind}; |
| |
| /// Command for Transport::dispatch(). |
| pub struct FpgaProgram<'a> { |
| /// The bitstream content to load into the FPGA. |
| pub bitstream: Vec<u8>, |
| /// What type of ROM to expect. |
| pub rom_kind: Option<RomKind>, |
| /// How long of a reset pulse to send to the device. |
| pub rom_reset_pulse: Duration, |
| /// How long to wait for the ROM to print its type and version. |
| pub rom_timeout: Duration, |
| /// A progress function to provide user feedback. |
| /// Will be called with the address and length of each chunk sent to the target device. |
| pub progress: Option<Box<dyn Fn(u32, u32) + 'a>>, |
| } |
| |
| impl FpgaProgram<'_> { |
| pub fn check_correct_version(&self, uart: &dyn Uart, reset_pin: &dyn GpioPin) -> Result<bool> { |
| if let Some(rom_kind) = &self.rom_kind { |
| let mut rd = RomDetect::new(*rom_kind, &self.bitstream, Some(self.rom_timeout))?; |
| |
| // Send a reset pulse so the ROM will print the FPGA version. |
| // Reset is active low, sleep, then drive high. |
| reset_pin.write(false)?; |
| std::thread::sleep(self.rom_reset_pulse); |
| // Also clear the UART RX buffer for improved robustness. |
| uart.clear_rx_buffer()?; |
| reset_pin.write(true)?; |
| |
| // Now read the uart until the ROM prints it's version. |
| if rd.detect(&*uart)? { |
| log::info!("Already running the correct bitstream. Skip loading bitstream."); |
| // If we're already running the right ROM+bitstream, |
| // then we can skip bootstrap. |
| return Ok(true); |
| } |
| } |
| Ok(false) |
| } |
| |
| pub fn skip(&self) -> bool { |
| self.bitstream.starts_with(b"__skip__") |
| } |
| } |
| |
| /// Command for Transport::dispatch(). |
| pub struct ClearBitstream; |