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
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:
@@ -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
|
||||
|
||||
```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
|
||||
|
||||
@@ -6,6 +6,13 @@ use zynq7000_hal::qspi::{
|
||||
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)]
|
||||
pub enum RegisterId {
|
||||
/// WRR
|
||||
@@ -593,6 +600,8 @@ impl QspiSpansionS25Fl256SIoMode {
|
||||
pub struct QspiSpansionS25Fl256SLinearMode(QspiLinearAddressing);
|
||||
|
||||
impl QspiSpansionS25Fl256SLinearMode {
|
||||
pub const BASE_ADDR: usize = QspiLinearAddressing::BASE_ADDRESS;
|
||||
|
||||
pub fn into_io_mode(self, dual_flash: bool) -> QspiSpansionS25Fl256SIoMode {
|
||||
let qspi = self.0.into_io_mode(dual_flash);
|
||||
QspiSpansionS25Fl256SIoMode::new(qspi, false)
|
||||
|
||||
@@ -13,6 +13,8 @@ cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar
|
||||
zynq7000-rt = { path = "../zynq7000-rt" }
|
||||
zynq7000 = { path = "../zynq7000" }
|
||||
zynq7000-hal = { path = "../zynq7000-hal" }
|
||||
zedboard-bsp = { path = "../zedboard-bsp" }
|
||||
zynq-boot-image = { path = "../zynq-boot-image" }
|
||||
embedded-io = "0.6"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
|
||||
@@ -7,6 +7,8 @@ use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embedded_io::Write as _;
|
||||
use log::{error, info};
|
||||
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
|
||||
use zynq7000::{PsPeripherals, qspi::MmioQspi};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::{
|
||||
@@ -15,8 +17,10 @@ use zynq7000_hal::{
|
||||
},
|
||||
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest},
|
||||
gic::GicConfigurator,
|
||||
gpio::GpioPins,
|
||||
gpio::{GpioPins, mio},
|
||||
l2_cache,
|
||||
prelude::*,
|
||||
qspi::{self, QSPI_START_ADDRESS},
|
||||
time::Hertz,
|
||||
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;
|
||||
|
||||
#[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)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
@@ -130,6 +147,79 @@ pub fn main() -> ! {
|
||||
}
|
||||
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 {
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
|
||||
@@ -5,3 +5,5 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { version = "2", default-features = false }
|
||||
arbitrary-int = "2"
|
||||
bitbybit = "1.4"
|
||||
|
||||
@@ -8,6 +8,16 @@ pub const IMAGE_ID_U32: u32 = 0x584C4E58;
|
||||
/// This is the fixed size of the boot header.
|
||||
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> {
|
||||
//base_addr: usize,
|
||||
// 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].
|
||||
/// This constructor calls [Self::check_image_id_validity] and [Self::verify_header_checksum]
|
||||
/// 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 {
|
||||
return None;
|
||||
return Err(InvalidBootHeader::DataTooSmall);
|
||||
}
|
||||
let header = BootHeader { data };
|
||||
if !header.check_image_id_validity() || !header.verify_header_checksum() {
|
||||
return None;
|
||||
if !header.check_image_id_validity() {
|
||||
return Err(InvalidBootHeader::ImageIdInvalid);
|
||||
}
|
||||
Some(header)
|
||||
if !header.verify_header_checksum() {
|
||||
return Err(InvalidBootHeader::ChecksumInvalid);
|
||||
}
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -55,21 +68,13 @@ impl<'a> BootHeader<'a> {
|
||||
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]
|
||||
pub fn source_offset(&self) -> 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]
|
||||
pub fn header_checksum(&self) -> u32 {
|
||||
self.read_u32_le(0x48)
|
||||
@@ -329,6 +334,44 @@ pub struct PartitionHeader<'a> {
|
||||
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> {
|
||||
pub const SIZE: usize = 0x40;
|
||||
|
||||
@@ -372,6 +415,16 @@ impl<'a> PartitionHeader<'a> {
|
||||
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]
|
||||
pub fn section_count(&self) -> usize {
|
||||
self.read_u32_le(0x1C) as usize
|
||||
|
||||
26
zynq-boot-image/tester/Cargo.lock
generated
26
zynq-boot-image/tester/Cargo.lock
generated
@@ -52,6 +52,30 @@ dependencies = [
|
||||
"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]]
|
||||
name = "clap"
|
||||
version = "4.5.46"
|
||||
@@ -275,5 +299,7 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
name = "zynq-boot-image"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.0.0",
|
||||
"bitbybit",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -51,6 +51,7 @@ fn main() {
|
||||
.image_header_iterator()
|
||||
.expect("failed extracting boot header iterator");
|
||||
for (idx, image_header) in image_header_iter.enumerate() {
|
||||
println!("--------------------------------------");
|
||||
println!(
|
||||
"Image header {} with partition count {}",
|
||||
idx,
|
||||
@@ -64,6 +65,9 @@ fn main() {
|
||||
let partition_iter = image_header
|
||||
.partition_header_iterator(header_buf.as_slice())
|
||||
.unwrap();
|
||||
if image_header.partition_count() > 0 {
|
||||
println!("--------------------------------------");
|
||||
}
|
||||
for partition in partition_iter {
|
||||
println!(
|
||||
"partition with size {} and load address {:#08x}, section count {}",
|
||||
@@ -71,6 +75,8 @@ fn main() {
|
||||
partition.destination_load_address(),
|
||||
partition.section_count()
|
||||
);
|
||||
println!("section attributes: {:?}", partition.section_attributes());
|
||||
}
|
||||
println!("--------------------------------------\n\r");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,6 +623,9 @@ pub struct 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 {
|
||||
self.ll.enable_io_mode(dual_flash);
|
||||
QspiIoMode { ll: self.ll }
|
||||
@@ -636,6 +639,9 @@ impl QspiLinearAddressing {
|
||||
pub struct QspiLinearReadGuard<'a>(&'a mut QspiLinearAddressing);
|
||||
|
||||
impl QspiLinearReadGuard<'_> {
|
||||
/// Memory-mapped QSPI base address.
|
||||
pub const BASE_ADDRESS: usize = QSPI_START_ADDRESS;
|
||||
|
||||
pub fn new(qspi: &mut QspiLinearAddressing) -> QspiLinearReadGuard<'_> {
|
||||
qspi.ll
|
||||
.0
|
||||
|
||||
Reference in New Issue
Block a user