use embedded_hal::{ digital::v2::{OutputPin, InputPin}, blocking::spi::Transfer, blocking::delay::DelayUs, }; #[derive(Debug)] pub enum FPGAFlashError { SPICommunicationError, NegotiationError, ResetStatusError, } const DATA: &'static [u8] = include_bytes!("../FPGA/build/blinky.bin"); // const DATA: &'static [u8] = include_bytes!("../build/top.bin"); // A public method to flash iCE40 FPGA on Humpback pub fn flash_ice40_fpga, SS: OutputPin, RST: OutputPin, DELAY: DelayUs, DONE: InputPin> (mut spi: SPI, mut ss: SS, mut creset: RST, cdone: DONE, mut delay: DELAY) -> Result<(), FPGAFlashError> { // Data buffer setup let mut dummy_byte :[u8; 1] = [0x00]; let mut dummy_13_bytes :[u8; 13] = [0x00; 13]; // Drive CRESET_B low creset.set_low() .map_err(|_| FPGAFlashError::NegotiationError)?; // Drive SPI_SS_B low ss.set_low() .map_err(|_| FPGAFlashError::NegotiationError)?; // Wait at least 200ns delay.delay_us(1_u32); // Drive CRESET_B high creset.set_high() .map_err(|_| FPGAFlashError::NegotiationError)?; // Wait at least another 1200us to clear internal config memory delay.delay_us(1200_u32); // Before data transmission starts, check if C_DONE is truly low // If C_DONE is high, the FPGA reset procedure is unsuccessful match cdone.is_low() { Ok(true) => {}, _ => return Err(FPGAFlashError::ResetStatusError), }; // Set SPI_SS_B high ss.set_high() .map_err(|_| FPGAFlashError::NegotiationError)?; // Send 8 dummy clock, effectively 1 byte of 0x00 spi.transfer(&mut dummy_byte) .map_err(|_| FPGAFlashError::SPICommunicationError)?; // Drive SPI_SS_B low ss.set_low() .map_err(|_| FPGAFlashError::NegotiationError)?; // Send the whole image without interruption for byte in DATA.into_iter() { let mut single_byte_slice = [*byte]; spi.transfer(&mut single_byte_slice) .map_err(|_| FPGAFlashError::SPICommunicationError)?; } // Drive SPI_SS_B high ss.set_high() .map_err(|_| FPGAFlashError::NegotiationError)?; // Send at another 100 dummy clocks (choosing 13 bytes) spi.transfer(&mut dummy_13_bytes) .map_err(|_| FPGAFlashError::SPICommunicationError)?; // Check the CDONE output from FPGA // CDONE needs to be high match cdone.is_high() { Ok(true) => {}, _ => return Err(FPGAFlashError::ResetStatusError), }; // Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes) spi.transfer(&mut dummy_13_bytes).map_err(|_| FPGAFlashError::SPICommunicationError)?; Ok(()) }