133 lines
3.2 KiB
Rust
133 lines
3.2 KiB
Rust
|
#![no_main]
|
||
|
#![no_std]
|
||
|
|
||
|
use panic_semihosting as _;
|
||
|
|
||
|
use stm32h7xx_hal::hal::digital::v2::{
|
||
|
InputPin,
|
||
|
OutputPin,
|
||
|
};
|
||
|
use stm32h7xx_hal::{pac, prelude::*, spi};
|
||
|
|
||
|
use cortex_m;
|
||
|
use cortex_m::asm::nop;
|
||
|
use cortex_m_rt::entry;
|
||
|
use cortex_m_semihosting::hprintln;
|
||
|
|
||
|
use core::ptr;
|
||
|
use nb::block;
|
||
|
|
||
|
|
||
|
#[entry]
|
||
|
fn main() -> ! {
|
||
|
|
||
|
hprintln!("Flashing configuration bitstream to iCE40 HX8K on Humpback.").unwrap();
|
||
|
|
||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||
|
let dp = pac::Peripherals::take().unwrap();
|
||
|
|
||
|
let pwr = dp.PWR.constrain();
|
||
|
let vos = pwr.freeze();
|
||
|
|
||
|
let rcc = dp.RCC.constrain();
|
||
|
let ccdr = rcc
|
||
|
.sys_ck(400.mhz())
|
||
|
.pll1_q_ck(48.mhz())
|
||
|
.freeze(vos, &dp.SYSCFG);
|
||
|
|
||
|
let mut delay = cp.SYST.delay(ccdr.clocks);
|
||
|
|
||
|
let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);
|
||
|
let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB);
|
||
|
let gpiod = dp.GPIOD.split(ccdr.peripheral.GPIOD);
|
||
|
let gpiof = dp.GPIOF.split(ccdr.peripheral.GPIOF);
|
||
|
|
||
|
// Using SPI_1 alternate functions (af5)
|
||
|
let fpga_sck = gpiob.pb3.into_alternate_af5();
|
||
|
let fpga_sdo = gpiob.pb4.into_alternate_af5();
|
||
|
let fpga_sdi = gpiob.pb5.into_alternate_af5();
|
||
|
|
||
|
// Setup SPI_SS_B and CRESET_B
|
||
|
let mut fpga_ss = gpioa.pa4.into_push_pull_output();
|
||
|
let mut fpga_creset = gpiof.pf3.into_open_drain_output();
|
||
|
|
||
|
// Setup CDONE
|
||
|
let fpga_cdone = gpiod.pd15.into_pull_up_input();
|
||
|
|
||
|
// Setup SPI interface
|
||
|
let mut fpga_cfg_spi = dp.SPI1.spi(
|
||
|
(fpga_sck, fpga_sdo, fpga_sdi),
|
||
|
spi::MODE_3,
|
||
|
12.mhz(),
|
||
|
ccdr.peripheral.SPI1,
|
||
|
&ccdr.clocks,
|
||
|
);
|
||
|
|
||
|
// Data buffer setup
|
||
|
let mut dummy_byte :[u8; 1] = [0x00];
|
||
|
let mut dummy_13_bytes :[u8; 13] = [0x00; 13];
|
||
|
|
||
|
// Drive CRESET_B low
|
||
|
fpga_creset.set_low().unwrap();
|
||
|
|
||
|
// Drive SPI_SS_B low
|
||
|
fpga_ss.set_low().unwrap();
|
||
|
|
||
|
// Wait at least 200ns
|
||
|
delay.delay_us(1_u16);
|
||
|
|
||
|
// Drive CRESET_B high
|
||
|
fpga_creset.set_high().unwrap();
|
||
|
|
||
|
// Wait at least another 1200us to clear internal config memory
|
||
|
delay.delay_us(1200_u16);
|
||
|
|
||
|
// Before data transmission starts, check if C_DONE is truly dine
|
||
|
match fpga_cdone.is_high() {
|
||
|
Ok(false) => hprintln!("Reset successful!"),
|
||
|
Ok(_) => hprintln!("Reset unsuccessful!"),
|
||
|
Err(_) => hprintln!("Reset error!"),
|
||
|
}.unwrap();
|
||
|
|
||
|
// Set SPI_SS_B high
|
||
|
fpga_ss.set_high().unwrap();
|
||
|
|
||
|
// Send 8 dummy clock, effectively 1 byte of 0x00
|
||
|
fpga_cfg_spi.transfer(&mut dummy_byte).unwrap();
|
||
|
|
||
|
// Drive SPI_SS_B low
|
||
|
fpga_ss.set_low().unwrap();
|
||
|
|
||
|
// Send the whole image without interruption
|
||
|
let base_address = 0x08100000;
|
||
|
let size = 135100;
|
||
|
for index in 0..size {
|
||
|
unsafe {
|
||
|
let data :u8 = ptr::read_volatile((base_address + index) as *const u8);
|
||
|
block!(fpga_cfg_spi.send(data)).unwrap();
|
||
|
block!(fpga_cfg_spi.read()).unwrap();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Drive SPI_SS_B high
|
||
|
fpga_ss.set_high().unwrap();
|
||
|
|
||
|
// Send at another 100 dummy clocks (choosing 13 bytes)
|
||
|
fpga_cfg_spi.transfer(&mut dummy_13_bytes).unwrap();
|
||
|
|
||
|
// Check the CDONE output from FPGA
|
||
|
if !(fpga_cdone.is_high().unwrap()) {
|
||
|
hprintln!("ERROR!").unwrap();
|
||
|
}
|
||
|
else {
|
||
|
hprintln!("Configuration successful!").unwrap();
|
||
|
// Send at least another 49 clock cycles to activate IO pins (choosing same 13 bytes)
|
||
|
fpga_cfg_spi.transfer(&mut dummy_13_bytes).unwrap();
|
||
|
hprintln!("User I/O pins activated.").unwrap();
|
||
|
}
|
||
|
|
||
|
loop {
|
||
|
nop();
|
||
|
}
|
||
|
}
|