forked from M-Labs/humpback-dds
92 lines
2.7 KiB
Rust
92 lines
2.7 KiB
Rust
|
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!("../build/top.bin");
|
||
|
|
||
|
// A public method to flash iCE40 FPGA on Humpback
|
||
|
pub fn flash_ice40_fpga<SPI: Transfer<u8>,
|
||
|
SS: OutputPin,
|
||
|
RST: OutputPin,
|
||
|
DELAY: DelayUs<u32>,
|
||
|
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(())
|
||
|
|
||
|
}
|