continue QSPI boot
Some checks failed
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

This commit is contained in:
2025-09-04 17:42:36 +02:00
parent 031345148d
commit 3a8b286675
10 changed files with 211 additions and 17 deletions

View File

@@ -35,7 +35,7 @@ The folder contains a README with all the steps required to load this project fr
Use the following command to have a starting `config.toml` file Use the following command to have a starting `config.toml` file
```sh ```sh
cp .cargo/def-config.toml .cargo/config.toml cp .cargo/config.toml.template .cargo/config.toml
``` ```
You then can adapt the `config.toml` to your needs. For example, you can configure runners You then can adapt the `config.toml` to your needs. For example, you can configure runners

View File

@@ -6,6 +6,13 @@ use zynq7000_hal::qspi::{
QspiLinearReadGuard, QspiLinearReadGuard,
}; };
pub const QSPI_DEV_COMBINATION_REV_F: zynq7000_hal::qspi::QspiDeviceCombination =
zynq7000_hal::qspi::QspiDeviceCombination {
vendor: zynq7000_hal::qspi::QspiVendor::WinbondAndSpansion,
operating_mode: zynq7000_hal::qspi::OperatingMode::FastReadQuadOutput,
two_devices: false,
};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum RegisterId { pub enum RegisterId {
/// WRR /// WRR
@@ -593,6 +600,8 @@ impl QspiSpansionS25Fl256SIoMode {
pub struct QspiSpansionS25Fl256SLinearMode(QspiLinearAddressing); pub struct QspiSpansionS25Fl256SLinearMode(QspiLinearAddressing);
impl QspiSpansionS25Fl256SLinearMode { impl QspiSpansionS25Fl256SLinearMode {
pub const BASE_ADDR: usize = QspiLinearAddressing::BASE_ADDRESS;
pub fn into_io_mode(self, dual_flash: bool) -> QspiSpansionS25Fl256SIoMode { pub fn into_io_mode(self, dual_flash: bool) -> QspiSpansionS25Fl256SIoMode {
let qspi = self.0.into_io_mode(dual_flash); let qspi = self.0.into_io_mode(dual_flash);
QspiSpansionS25Fl256SIoMode::new(qspi, false) QspiSpansionS25Fl256SIoMode::new(qspi, false)

View File

@@ -13,6 +13,8 @@ cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar
zynq7000-rt = { path = "../zynq7000-rt" } zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" } zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" } zynq7000-hal = { path = "../zynq7000-hal" }
zedboard-bsp = { path = "../zedboard-bsp" }
zynq-boot-image = { path = "../zynq-boot-image" }
embedded-io = "0.6" embedded-io = "0.6"
embedded-hal = "1" embedded-hal = "1"
fugit = "0.3" fugit = "0.3"

View File

@@ -7,6 +7,8 @@ use core::panic::PanicInfo;
use cortex_ar::asm::nop; use cortex_ar::asm::nop;
use embedded_io::Write as _; use embedded_io::Write as _;
use log::{error, info}; use log::{error, info};
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
use zynq7000::{PsPeripherals, qspi::MmioQspi};
use zynq7000_hal::{ use zynq7000_hal::{
BootMode, BootMode,
clocks::{ clocks::{
@@ -15,8 +17,10 @@ use zynq7000_hal::{
}, },
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest}, ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest},
gic::GicConfigurator, gic::GicConfigurator,
gpio::GpioPins, gpio::{GpioPins, mio},
l2_cache, l2_cache,
prelude::*,
qspi::{self, QSPI_START_ADDRESS},
time::Hertz, time::Hertz,
uart::{ClockConfigRaw, Uart, UartConfig}, uart::{ClockConfigRaw, Uart, UartConfig},
}; };
@@ -42,6 +46,19 @@ const DDR_CLK: Hertz = Hertz::from_raw(2 * DDR_FREQUENCY.raw());
const PERFORM_DDR_MEMTEST: bool = false; const PERFORM_DDR_MEMTEST: bool = false;
#[derive(Debug, PartialEq, Eq)]
pub enum BootMethod {
Qspi,
SdCard,
}
pub const BOOT_METHOD: BootMethod = BootMethod::Qspi;
pub const ELF_BASE_ADDR: usize = 0x100000;
/// 8 MB reserved for application ELF.
pub const BOOT_BIN_STAGING_OFFSET: usize = 8 * 1024 * 1024;
/// Entry point (not called like a normal main function) /// Entry point (not called like a normal main function)
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn boot_core(cpu_id: u32) -> ! { pub extern "C" fn boot_core(cpu_id: u32) -> ! {
@@ -130,6 +147,79 @@ pub fn main() -> ! {
} }
info!("DDR memory test success."); info!("DDR memory test success.");
} }
if BOOT_METHOD == BootMethod::Qspi {
let qspi_clock_config = qspi::ClockConfig::calculate_with_loopback(
zynq7000::slcr::clocks::SrcSelIo::IoPll,
&clocks,
100.MHz(),
)
.expect("QSPI clock calculation failed");
let qspi = qspi::Qspi::new_single_qspi_with_feedback(
dp.qspi,
qspi_clock_config,
embedded_hal::spi::MODE_0,
qspi::IoType::LvCmos33,
mio_pins.mio1,
(mio_pins.mio2, mio_pins.mio3, mio_pins.mio4, mio_pins.mio5),
mio_pins.mio6,
mio_pins.mio8,
);
let qspi_io_mode = qspi.into_io_mode(false);
let spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let spansion_lqspi =
spansion_qspi.into_linear_addressed(qspi_spansion::QSPI_DEV_COMBINATION_REV_F.into());
qspi_boot(spansion_lqspi);
}
loop {
cortex_ar::asm::nop();
}
}
fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode) -> ! {
let boot_bin_base_addr = ELF_BASE_ADDR + BOOT_BIN_STAGING_OFFSET;
let mut boot_header_slice = unsafe {
core::slice::from_raw_parts_mut(
boot_bin_base_addr as *mut u8,
zynq_boot_image::FIXED_BOOT_HEADER_SIZE,
)
};
let read_guard = qspi.read_guard();
// Currently, only boot.bin at address 0x0 of the QSPI is supported.
unsafe {
core::ptr::copy_nonoverlapping(
QspiSpansionS25Fl256SLinearMode::BASE_ADDR as *mut u8,
boot_header_slice.as_mut_ptr(),
zynq_boot_image::FIXED_BOOT_HEADER_SIZE,
);
}
drop(read_guard);
let boot_header = zynq_boot_image::BootHeader::new(boot_header_slice).unwrap();
let fsbl_offset = boot_header.source_offset();
boot_header_slice =
unsafe { core::slice::from_raw_parts_mut(boot_bin_base_addr as *mut u8, fsbl_offset) };
// Read the rest of the boot header metadata.
let read_guard = qspi.read_guard();
unsafe {
core::ptr::copy_nonoverlapping(
(QspiSpansionS25Fl256SLinearMode::BASE_ADDR + zynq_boot_image::FIXED_BOOT_HEADER_SIZE)
as *mut u8,
boot_header_slice[zynq_boot_image::FIXED_BOOT_HEADER_SIZE..].as_mut_ptr(),
fsbl_offset - zynq_boot_image::FIXED_BOOT_HEADER_SIZE,
);
}
drop(read_guard);
let boot_header = zynq_boot_image::BootHeader::new_unchecked(boot_header_slice);
let mut name_buf: [u8; 256] = [0; 256];
for image_header in boot_header.image_header_iterator().unwrap() {
let name = image_header.image_name(&mut name_buf).unwrap();
}
loop { loop {
cortex_ar::asm::nop(); cortex_ar::asm::nop();
} }

View File

@@ -5,3 +5,5 @@ edition = "2024"
[dependencies] [dependencies]
thiserror = { version = "2", default-features = false } thiserror = { version = "2", default-features = false }
arbitrary-int = "2"
bitbybit = "1.4"

View File

@@ -8,6 +8,16 @@ pub const IMAGE_ID_U32: u32 = 0x584C4E58;
/// This is the fixed size of the boot header. /// This is the fixed size of the boot header.
pub const FIXED_BOOT_HEADER_SIZE: usize = 0xA0; pub const FIXED_BOOT_HEADER_SIZE: usize = 0xA0;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum InvalidBootHeader {
#[error("image ID invalid")]
ImageIdInvalid,
#[error("checksum is invalid")]
ChecksumInvalid,
#[error("provided data slice too small")]
DataTooSmall,
}
pub struct BootHeader<'a> { pub struct BootHeader<'a> {
//base_addr: usize, //base_addr: usize,
// Snapshot of the boot header and following data (at least fixed header size) // Snapshot of the boot header and following data (at least fixed header size)
@@ -27,15 +37,18 @@ impl<'a> BootHeader<'a> {
/// The passed buffer must have a minimal size of [Self::FIXED_SIZED_PART]. /// The passed buffer must have a minimal size of [Self::FIXED_SIZED_PART].
/// This constructor calls [Self::check_image_id_validity] and [Self::verify_header_checksum] /// This constructor calls [Self::check_image_id_validity] and [Self::verify_header_checksum]
/// to verify whether the boot header structure is actually valid. /// to verify whether the boot header structure is actually valid.
pub fn new(data: &'a [u8]) -> Option<Self> { pub fn new(data: &'a [u8]) -> Result<Self, InvalidBootHeader> {
if data.len() < Self::FIXED_SIZED_PART { if data.len() < Self::FIXED_SIZED_PART {
return None; return Err(InvalidBootHeader::DataTooSmall);
} }
let header = BootHeader { data }; let header = BootHeader { data };
if !header.check_image_id_validity() || !header.verify_header_checksum() { if !header.check_image_id_validity() {
return None; return Err(InvalidBootHeader::ImageIdInvalid);
} }
Some(header) if !header.verify_header_checksum() {
return Err(InvalidBootHeader::ChecksumInvalid);
}
Ok(header)
} }
#[inline] #[inline]
@@ -55,21 +68,13 @@ impl<'a> BootHeader<'a> {
self.image_id() == IMAGE_ID_U32 self.image_id() == IMAGE_ID_U32
} }
/// Offset to the FSBL image in bytes. This information can be used to only extract the boot
/// binary metadata (everything except actual partition data).
#[inline] #[inline]
pub fn source_offset(&self) -> usize { pub fn source_offset(&self) -> usize {
self.read_u32_le(0x30) as usize self.read_u32_le(0x30) as usize
} }
// The boot image metadata is all the data up to the first partition of the FSBL.
//
// This information can be used to copy all relevant information for image and partition
// parsing into RAM. This includes the image header table, the image headers and the
// partition headers.
//#[inline]
//pub fn boot_image_metadata_len(&self) -> usize {
//self.source_offset() - self.base_addr
//}
#[inline] #[inline]
pub fn header_checksum(&self) -> u32 { pub fn header_checksum(&self) -> u32 {
self.read_u32_le(0x48) self.read_u32_le(0x48)
@@ -329,6 +334,44 @@ pub struct PartitionHeader<'a> {
header_data: &'a [u8], header_data: &'a [u8],
} }
#[bitbybit::bitenum(u2, exhaustive = false)]
#[derive(Debug)]
#[non_exhaustive]
pub enum PartitionOwner {
Fsbl = 0,
Uboot = 1,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug)]
#[non_exhaustive]
pub enum ChecksumType {
None = 0,
Md5 = 1,
}
#[bitbybit::bitenum(u4, exhaustive = false)]
#[derive(Debug)]
#[non_exhaustive]
pub enum DestinationDevice {
None = 0,
Ps = 1,
Pl = 2,
Int = 3,
}
#[bitbybit::bitfield(u32, debug)]
pub struct SectionAttributes {
#[bits(16..=17, rw)]
partition_owner: Option<PartitionOwner>,
#[bit(15, rw)]
rsa_signature_present: bool,
#[bits(12..=14, rw)]
checksum_type: Option<ChecksumType>,
#[bits(4..=7, rw)]
destination_device: Option<DestinationDevice>,
}
impl<'a> PartitionHeader<'a> { impl<'a> PartitionHeader<'a> {
pub const SIZE: usize = 0x40; pub const SIZE: usize = 0x40;
@@ -372,6 +415,16 @@ impl<'a> PartitionHeader<'a> {
self.read_u32_le(0x10) self.read_u32_le(0x10)
} }
#[inline]
pub fn section_attributes(&self) -> SectionAttributes {
SectionAttributes::new_with_raw_value(self.section_attributes_raw())
}
#[inline]
pub fn section_attributes_raw(&self) -> u32 {
self.read_u32_le(0x18)
}
#[inline] #[inline]
pub fn section_count(&self) -> usize { pub fn section_count(&self) -> usize {
self.read_u32_le(0x1C) as usize self.read_u32_le(0x1C) as usize

View File

@@ -52,6 +52,30 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "arbitrary-int"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5"
[[package]]
name = "arbitrary-int"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c858caffa49edfc4ecc45a4bec37abd3e88041a2903816f10f990b7b41abc281"
[[package]]
name = "bitbybit"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec187a89ab07e209270175faf9e07ceb2755d984954e58a2296e325ddece2762"
dependencies = [
"arbitrary-int 1.3.0",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.46" version = "4.5.46"
@@ -275,5 +299,7 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
name = "zynq-boot-image" name = "zynq-boot-image"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"arbitrary-int 2.0.0",
"bitbybit",
"thiserror", "thiserror",
] ]

View File

@@ -51,6 +51,7 @@ fn main() {
.image_header_iterator() .image_header_iterator()
.expect("failed extracting boot header iterator"); .expect("failed extracting boot header iterator");
for (idx, image_header) in image_header_iter.enumerate() { for (idx, image_header) in image_header_iter.enumerate() {
println!("--------------------------------------");
println!( println!(
"Image header {} with partition count {}", "Image header {} with partition count {}",
idx, idx,
@@ -64,6 +65,9 @@ fn main() {
let partition_iter = image_header let partition_iter = image_header
.partition_header_iterator(header_buf.as_slice()) .partition_header_iterator(header_buf.as_slice())
.unwrap(); .unwrap();
if image_header.partition_count() > 0 {
println!("--------------------------------------");
}
for partition in partition_iter { for partition in partition_iter {
println!( println!(
"partition with size {} and load address {:#08x}, section count {}", "partition with size {} and load address {:#08x}, section count {}",
@@ -71,6 +75,8 @@ fn main() {
partition.destination_load_address(), partition.destination_load_address(),
partition.section_count() partition.section_count()
); );
} println!("section attributes: {:?}", partition.section_attributes());
}
println!("--------------------------------------\n\r");
} }
} }

View File

@@ -623,6 +623,9 @@ pub struct QspiLinearAddressing {
} }
impl QspiLinearAddressing { impl QspiLinearAddressing {
/// Memory-mapped QSPI base address.
pub const BASE_ADDRESS: usize = QSPI_START_ADDRESS;
pub fn into_io_mode(mut self, dual_flash: bool) -> QspiIoMode { pub fn into_io_mode(mut self, dual_flash: bool) -> QspiIoMode {
self.ll.enable_io_mode(dual_flash); self.ll.enable_io_mode(dual_flash);
QspiIoMode { ll: self.ll } QspiIoMode { ll: self.ll }
@@ -636,6 +639,9 @@ impl QspiLinearAddressing {
pub struct QspiLinearReadGuard<'a>(&'a mut QspiLinearAddressing); pub struct QspiLinearReadGuard<'a>(&'a mut QspiLinearAddressing);
impl QspiLinearReadGuard<'_> { impl QspiLinearReadGuard<'_> {
/// Memory-mapped QSPI base address.
pub const BASE_ADDRESS: usize = QSPI_START_ADDRESS;
pub fn new(qspi: &mut QspiLinearAddressing) -> QspiLinearReadGuard<'_> { pub fn new(qspi: &mut QspiLinearAddressing) -> QspiLinearReadGuard<'_> {
qspi.ll qspi.ll
.0 .0