Files
zynq7000-rs/zynq/zedboard-qspi-flasher/src/main.rs
T
Robin Mueller 893d2e870e
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
UART and docs update
2025-11-28 13:06:39 +01:00

226 lines
7.2 KiB
Rust

//! QSPI flasher for the Zedboard. Assumes that external scripting took care of transferring
//! a boot binary to RAM.
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embedded_hal::{delay::DelayNs as _, digital::StatefulOutputPin as _};
use embedded_io::Write as _;
use log::{error, info};
use zedboard_bsp::qspi_spansion;
use zynq7000_boot_image::BootHeader;
use zynq7000_hal::{
BootMode, LevelShifterConfig, clocks, gpio, prelude::*, priv_tim, qspi, time::Hertz, uart,
};
use zynq7000_rt as _;
// Define the clock frequency as a constant.
//
// Not required for the PAC mode, is required for clean delays in HAL mode.
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333);
// TODO: Make this configurable somehow?
const BOOT_BIN_BASE_ADDR: usize = 0x1000_0000;
const BOOT_BIN_SIZE_ADDR: usize = 0x900_000;
// Maximum of 16 MB is allowed for now.
const MAX_BOOT_BIN_SIZE: usize = 16 * 1024 * 1024;
const VERIFY_PROGRAMMING: bool = true;
#[allow(dead_code)]
const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombination {
vendor: qspi::QspiVendor::WinbondAndSpansion,
operating_mode: qspi::OperatingMode::FastReadQuadOutput,
two_devices: false,
};
/// Entry point (not called like a normal main function)
#[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
if cpu_id != 0 {
panic!("unexpected CPU ID {}", cpu_id);
}
main();
}
const INIT_STRING: &str = "-- Zynq 7000 Zedboard QSPI flasher --\n\r";
#[unsafe(export_name = "main")]
pub fn main() -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Unwrap okay, we only call this once on core 0 here.
let mut timer = priv_tim::CpuPrivateTimer::take(clocks.arm_clocks()).unwrap();
let gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Info,
false,
)
};
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let qspi_clock_config =
qspi::ClockConfig::calculate_with_loopback(qspi::SrcSelIo::IoPll, &clocks, 100.MHz())
.expect("QSPI clock calculation failed");
let qspi = qspi::Qspi::new_single_qspi_with_feedback(
periphs.qspi,
qspi_clock_config,
qspi::MODE_0,
qspi::IoType::LvCmos33,
gpio_pins.mio.mio1,
(
gpio_pins.mio.mio2,
gpio_pins.mio.mio3,
gpio_pins.mio.mio4,
gpio_pins.mio.mio5,
),
gpio_pins.mio.mio6,
gpio_pins.mio.mio8,
);
let qspi_io_mode = qspi.into_io_mode(false);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let mut boot_bin_slice = unsafe {
core::slice::from_raw_parts(BOOT_BIN_BASE_ADDR as *const _, BootHeader::FIXED_SIZED_PART)
};
// This perform some basic validity checks.
let _boot_header = BootHeader::new(&boot_bin_slice[0..BootHeader::FIXED_SIZED_PART])
.expect("failed to parse boot header");
let boot_bin_size =
unsafe { core::ptr::read_volatile(BOOT_BIN_SIZE_ADDR as *const u32) as usize };
if boot_bin_size == 0 || boot_bin_size > MAX_BOOT_BIN_SIZE {
panic!(
"boot binary size read at address {:#x} is invalid: found {}, must be in range [0, {}]",
BOOT_BIN_SIZE_ADDR, boot_bin_size, MAX_BOOT_BIN_SIZE
);
}
boot_bin_slice =
unsafe { core::slice::from_raw_parts(BOOT_BIN_BASE_ADDR as *const _, boot_bin_size) };
info!(
"flashing boot binary with {} bytes to QSPI address 0x0",
boot_bin_size
);
let mut current_addr = 0;
let mut read_buf = [0u8; 256];
let mut next_checkpoint = 0.05;
while current_addr < boot_bin_size {
if current_addr % 0x10000 == 0 {
log::debug!("Erasing sector at address {:#x}", current_addr);
match spansion_qspi.erase_sector(current_addr as u32) {
Ok(()) => {}
Err(e) => {
error!(
"failed to erase sector at address {:#x}: {:?}",
current_addr, e
);
panic!("QSPI erase failed");
}
}
}
let write_size = core::cmp::min(256, boot_bin_size - current_addr);
let write_slice = &boot_bin_slice[current_addr..current_addr + write_size];
log::debug!("Programming address {:#x}", current_addr);
match spansion_qspi.program_page(current_addr as u32, write_slice) {
Ok(()) => {}
Err(e) => {
log::error!(
"failed to write data to QSPI at address {:#x}: {:?}",
current_addr,
e
);
panic!("QSPI write failed");
}
}
if VERIFY_PROGRAMMING {
spansion_qspi.read_page_fast_read(
current_addr as u32,
&mut read_buf[0..write_size],
true,
);
if &read_buf[0..write_size] != write_slice {
error!(
"data verification failed at address {:#x}: wrote {:x?}, read {:x?}",
current_addr,
&write_slice[0..core::cmp::min(16, write_size)],
&read_buf[0..core::cmp::min(16, write_size)]
);
panic!("QSPI data verification failed");
}
}
current_addr += write_size;
if current_addr as f32 / boot_bin_size as f32 >= next_checkpoint {
log::info!("Write progress {} %", libm::roundf(next_checkpoint * 100.0));
next_checkpoint += 0.05;
}
}
info!("flashing done");
let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
loop {
mio_led.toggle().unwrap();
timer.delay_ms(500);
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {
nop();
}
}