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
|
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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
26
zynq-boot-image/tester/Cargo.lock
generated
26
zynq-boot-image/tester/Cargo.lock
generated
@@ -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",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user