finished QSPI flasher
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:
@@ -11,7 +11,7 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||
categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2", features = ["critical-section-single-core"] }
|
||||
cortex-ar = "0.3"
|
||||
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||
zynq7000 = { path = "../../zynq7000" }
|
||||
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||
@@ -20,7 +20,7 @@ dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev =
|
||||
static_cell = "2"
|
||||
critical-section = "1"
|
||||
heapless = "0.9"
|
||||
embedded-io = "0.6"
|
||||
embedded-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
|
||||
@@ -8,7 +8,7 @@ use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{clocks, gic, gpio, gtc, time::Hertz, uart, BootMode, InteruptConfig};
|
||||
use zynq7000_hal::{BootMode, InteruptConfig, clocks, gic, gpio, gtc, time::Hertz, uart};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2", features = ["critical-section-single-core"] }
|
||||
cortex-ar = "0.3"
|
||||
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||
zynq7000 = { path = "../../zynq7000" }
|
||||
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||
embedded-io = "0.6"
|
||||
embedded-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
|
||||
@@ -11,7 +11,7 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||
categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2", features = ["critical-section-single-core"] }
|
||||
cortex-ar = "0.3"
|
||||
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||
zynq7000 = { path = "../../zynq7000" }
|
||||
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||
@@ -19,7 +19,7 @@ zynq7000-embassy = { path = "../../zynq7000-embassy" }
|
||||
zedboard-bsp = { path = "../../zedboard-bsp" }
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" }
|
||||
embedded-io = "0.6"
|
||||
embedded-io = "0.7"
|
||||
bitbybit = "1.4"
|
||||
arbitrary-int = "2"
|
||||
embedded-io-async = "0.6"
|
||||
|
||||
@@ -10,7 +10,7 @@ use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zedboard_bsp::qspi_spansion;
|
||||
use zynq7000_hal::{clocks, gic, gpio, gtc, prelude::*, qspi, uart, BootMode};
|
||||
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, prelude::*, qspi, uart};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{clocks, gic, gpio, gtc, uart, BootMode};
|
||||
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, uart};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ pub enum SectorArchictecture {
|
||||
Hybrid = 0x01,
|
||||
}
|
||||
|
||||
pub const PAGE_SIZE: usize = 256;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BaseDeviceId {
|
||||
manufacturer_id: u8,
|
||||
@@ -221,6 +223,8 @@ pub enum ProgramPageError {
|
||||
ProgrammingErrorBitSet,
|
||||
#[error("address error: {0}")]
|
||||
Addr(#[from] AddrError),
|
||||
#[error("data is larger than page size {PAGE_SIZE}")]
|
||||
DataLargerThanPage,
|
||||
}
|
||||
|
||||
pub struct QspiSpansionS25Fl256SIoMode(RefCell<QspiIoMode>);
|
||||
@@ -428,14 +432,17 @@ impl QspiSpansionS25Fl256SIoMode {
|
||||
/// This function also takes care of enabling writes before programming the page.
|
||||
/// This function will block until the operation has completed.
|
||||
///
|
||||
/// TODO: Allow smaller write size
|
||||
pub fn program_page(&mut self, addr: u32, data: &[u8; 256]) -> Result<(), ProgramPageError> {
|
||||
/// The data length max not exceed the page size [PAGE_SIZE].
|
||||
pub fn program_page(&mut self, addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
|
||||
if addr + data.len() as u32 > u24::MAX.as_u32() {
|
||||
return Err(AddrError::OutOfRange.into());
|
||||
}
|
||||
if !addr.is_multiple_of(0x100) {
|
||||
return Err(AddrError::Alignment.into());
|
||||
}
|
||||
if data.len() > PAGE_SIZE {
|
||||
return Err(ProgramPageError::DataLargerThanPage);
|
||||
}
|
||||
self.write_enable();
|
||||
let qspi = self.0.get_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
@@ -448,8 +455,9 @@ impl QspiSpansionS25Fl256SIoMode {
|
||||
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
|
||||
let mut read_index: u32 = 0;
|
||||
let mut current_byte_index = 0;
|
||||
let fifo_writes = data.len().div_ceil(4);
|
||||
// Fill the FIFO until it is full.
|
||||
for _ in 0..FIFO_DEPTH - 1 {
|
||||
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
|
||||
transfer.write_word_txd_00(u32::from_ne_bytes(
|
||||
data[current_byte_index..current_byte_index + 4]
|
||||
.try_into()
|
||||
@@ -470,25 +478,38 @@ impl QspiSpansionS25Fl256SIoMode {
|
||||
}
|
||||
};
|
||||
|
||||
// Immediately fill the FIFO again with the remaining 8 bytes.
|
||||
wait_for_tx_slot(&mut transfer);
|
||||
while current_byte_index < data.len() {
|
||||
// Immediately fill the FIFO again with the remaining 8 bytes.
|
||||
wait_for_tx_slot(&mut transfer);
|
||||
|
||||
transfer.write_word_txd_00(u32::from_ne_bytes(
|
||||
data[current_byte_index..current_byte_index + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
));
|
||||
current_byte_index += 4;
|
||||
let word = match core::cmp::min(4, data.len() - current_byte_index) {
|
||||
1 => {
|
||||
let mut bytes = [0; 4];
|
||||
bytes[0] = data[current_byte_index];
|
||||
u32::from_ne_bytes(bytes)
|
||||
}
|
||||
2 => {
|
||||
let mut bytes = [0; 4];
|
||||
bytes[0..2].copy_from_slice(&data[current_byte_index..current_byte_index + 2]);
|
||||
u32::from_ne_bytes(bytes)
|
||||
}
|
||||
3 => {
|
||||
let mut bytes = [0; 4];
|
||||
bytes[0..3].copy_from_slice(&data[current_byte_index..current_byte_index + 3]);
|
||||
u32::from_ne_bytes(bytes)
|
||||
}
|
||||
4 => u32::from_ne_bytes(
|
||||
data[current_byte_index..current_byte_index + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
transfer.write_word_txd_00(word);
|
||||
current_byte_index += 4;
|
||||
}
|
||||
|
||||
wait_for_tx_slot(&mut transfer);
|
||||
|
||||
transfer.write_word_txd_00(u32::from_ne_bytes(
|
||||
data[current_byte_index..current_byte_index + 4]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
while read_index < 256 {
|
||||
while read_index < data.len() as u32 {
|
||||
if transfer.read_status().rx_above_threshold() {
|
||||
transfer.read_rx_data();
|
||||
read_index = read_index.wrapping_add(4);
|
||||
@@ -528,11 +549,7 @@ impl QspiSpansionS25Fl256SIoMode {
|
||||
if dummy_byte {
|
||||
bytes_to_write += 1;
|
||||
}
|
||||
let fifo_writes = if bytes_to_write.is_multiple_of(4) {
|
||||
bytes_to_write / 4
|
||||
} else {
|
||||
(bytes_to_write / 4) + 1
|
||||
};
|
||||
let fifo_writes = bytes_to_write.div_ceil(4);
|
||||
// Fill the FIFO until it is full or all 0 bytes have been written.
|
||||
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
|
||||
transfer.write_word_txd_00(0);
|
||||
|
||||
@@ -38,3 +38,25 @@ vivado zedboard-rust.xpr
|
||||
|
||||
You can perform all the steps specified in the Vivado GUI as well using `Execute TCL script` and
|
||||
`Load Project`.
|
||||
|
||||
# Generating the SDT folder from a hardware description
|
||||
|
||||
You can generate a hardware description by building the block design by using `Generate Bitstream`
|
||||
inside the Vivado GUI and then exporting the hardware description via
|
||||
`File -> Export -> Export Hardware`. This allows to generate a `*.xsa` file which describes the
|
||||
hardware.
|
||||
|
||||
After that, you can generate the SDT output folder which contains various useful files like
|
||||
the `ps7_init.tcl` script. The provided ` sdtgen.tcl` and `stdgen.py` script simplify this process.
|
||||
|
||||
For example, the following command generates the SDT output folder inside a folder
|
||||
named `sdt_out` for a hardware description files `zedboard-rust/zedboard-rust.xsa`,
|
||||
assuming that the Vitis tool suite is installed at `/tools/Xilinx/Vitis/2024.1`:
|
||||
|
||||
```sh
|
||||
export AMD_TOOLS="/tools/Xilinx/Vitis/2024.1"
|
||||
./sdtgen.py -x ./zedboard-rust/zedboard-rust.xsa
|
||||
```
|
||||
|
||||
Run `stdgen.py -h` for more information and configuration options. The `stdgen.py` is a helper
|
||||
script which will invoke `sdtgen.tcl` to generate the SDT.
|
||||
|
||||
@@ -9,13 +9,13 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2", features = ["critical-section-single-core"] }
|
||||
cortex-ar = "0.3"
|
||||
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-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
|
||||
@@ -16,9 +16,9 @@ use zynq7000_hal::{
|
||||
pll::{PllConfig, configure_arm_pll, configure_io_pll},
|
||||
},
|
||||
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest},
|
||||
gic, gpio, l2_cache,
|
||||
devcfg, gic, gpio, l2_cache,
|
||||
prelude::*,
|
||||
qspi,
|
||||
qspi::{self, QSPI_START_ADDRESS},
|
||||
time::Hertz,
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
@@ -225,7 +225,19 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode) -> ! {
|
||||
match dest_dev {
|
||||
DestinationDevice::Pl => {
|
||||
info!("Loading image '{name}' to PL (FPGA)..");
|
||||
// TODO: Load the bitstream.
|
||||
// Load the bitstream directly from linear mapped QSPI memory.
|
||||
let boot_bin_slice = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
(QSPI_START_ADDRESS
|
||||
+ partition
|
||||
.data_offset()
|
||||
.expect("invalid PL partition data offset"))
|
||||
as *const _,
|
||||
partition.total_partition_length().unwrap(),
|
||||
)
|
||||
};
|
||||
devcfg::configure_bitstream_non_secure(true, boot_bin_slice)
|
||||
.expect("unexpected unaligned address");
|
||||
}
|
||||
DestinationDevice::Ps => {
|
||||
// TODO: Load the binary into DDR. Jump at lowest load address after all
|
||||
|
||||
@@ -4,11 +4,12 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", branch = "main" }
|
||||
cortex-ar = { version = "0.3" }
|
||||
zynq7000-rt = { path = "../zynq7000-rt" }
|
||||
zynq7000 = { path = "../zynq7000" }
|
||||
zynq7000-hal = { path = "../zynq7000-hal" }
|
||||
zynq-boot-image = { path = "../zynq-boot-image" }
|
||||
zedboard-bsp = { path = "../zedboard-bsp" }
|
||||
embedded-io = "0.6"
|
||||
embedded-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
log = "0.4"
|
||||
|
||||
31
zedboard-qspi-flasher/build.rs
Normal file
31
zedboard-qspi-flasher/build.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
//! This build script copies the `memory.x` file from the crate root into
|
||||
//! a directory where the linker can always find it at build time.
|
||||
//! For many projects this is optional, as the linker always searches the
|
||||
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||
//! are using a workspace or have a more complicated build setup, this
|
||||
//! build script becomes required. Additionally, by requesting that
|
||||
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||
//! updating `memory.x` ensures a rebuild of the application with the
|
||||
//! new memory settings.
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put `memory.x` in our output directory and ensure it's
|
||||
// on the linker search path.
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// By default, Cargo will re-run a build script whenever
|
||||
// any file in the project changes. By specifying `memory.x`
|
||||
// here, we ensure the build script is only re-run when
|
||||
// `memory.x` is changed.
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
}
|
||||
22
zedboard-qspi-flasher/memory.x
Normal file
22
zedboard-qspi-flasher/memory.x
Normal file
@@ -0,0 +1,22 @@
|
||||
MEMORY
|
||||
{
|
||||
/* Zedboard: 512 MB DDR3. Only use 63 MB for now, should be plenty for a bare-metal app.
|
||||
Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This is
|
||||
recommended for something like DMA descriptors. */
|
||||
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 63M
|
||||
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
|
||||
}
|
||||
|
||||
REGION_ALIAS("DATA", CODE);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Uncached memory */
|
||||
.uncached (NOLOAD) : ALIGN(4) {
|
||||
. = ALIGN(4);
|
||||
_sbss_uncached = .;
|
||||
*(.uncached .uncached.*);
|
||||
. = ALIGN(4);
|
||||
_ebss_uncached = .;
|
||||
} > UNCACHED
|
||||
}
|
||||
151
zedboard-qspi-flasher/qspi-flasher.tcl
Normal file
151
zedboard-qspi-flasher/qspi-flasher.tcl
Normal file
@@ -0,0 +1,151 @@
|
||||
if {[info exists env(ip_address_hw_server)]} {
|
||||
set ip $env(ip_address_hw_server)
|
||||
} else {
|
||||
set ip "localhost"
|
||||
}
|
||||
|
||||
# absolute directory that contains *this* script
|
||||
set script_dir [file dirname [info script]]
|
||||
|
||||
# Defaults
|
||||
set boot_bin_addr 0x10000000
|
||||
set boot_bin_size_addr 0x900000
|
||||
set init_tcl ""
|
||||
set bin ""
|
||||
set bitstream ""
|
||||
|
||||
# Usage helper
|
||||
proc usage {} {
|
||||
puts "Usage: xsct qspi-flasher.tcl <init.tcl> \[-b|--bin <boot.bin>\]"
|
||||
puts "Options:"
|
||||
puts " -b, --bin Path to boot binary to flash"
|
||||
puts " -h, --help Show this help"
|
||||
}
|
||||
|
||||
# Compact, robust parser
|
||||
set expecting ""
|
||||
set endopts 0
|
||||
foreach arg $argv {
|
||||
# If previous option expects a value, take this arg
|
||||
if {$expecting ne ""} {
|
||||
set $expecting $arg
|
||||
set expecting ""
|
||||
continue
|
||||
}
|
||||
|
||||
# Option handling (until we see --)
|
||||
if {!$endopts && [string match "-*" $arg]} {
|
||||
if {$arg eq "--"} { set endopts 1; continue }
|
||||
if {$arg eq "-h" || $arg eq "--help"} { usage; exit 0 }
|
||||
if {$arg eq "-b" || $arg eq "--bin"} { set expecting app; continue }
|
||||
puts "error: unknown option: $arg"; usage; exit 1
|
||||
}
|
||||
|
||||
# Positional: expect only <init.tcl>
|
||||
if {$init_tcl eq ""} {
|
||||
set init_tcl $arg
|
||||
} else {
|
||||
puts "error: unexpected positional argument: $arg"
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Check that QSPI flasher app exists.
|
||||
set flasher_app [file join $script_dir .. target armv7a-none-eabihf release zedboard-qspi-flasher]
|
||||
if {![file exists $flasher_app]} {
|
||||
error "QSPI flasher application not found at path: $flasher_app"
|
||||
}
|
||||
set flasher_app [file normalize $flasher_app]
|
||||
|
||||
# Validate required init script
|
||||
if {$init_tcl eq ""} {
|
||||
puts "error: missing required first argument pointing to initialization TCL script"
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
if {![file exists $init_tcl]} {
|
||||
puts "error: the PS init tcl script '$init_tcl' does not exist"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Resolve app: CLI takes precedence over env(APP)
|
||||
if {$bin ne ""} {
|
||||
if {![file exists $bin]} {
|
||||
puts "error: the boot binary file '$bin' does not exist"
|
||||
exit 1
|
||||
}
|
||||
} elseif {[info exists env(BOOTBIN)]} {
|
||||
if {[file exists $env(BOOTBIN)]} {
|
||||
set bin $env(BOOTBIN)
|
||||
} else {
|
||||
puts "warning: BOOTBIN environment variable is set but file does not exist: $env(BOOTBIN)"
|
||||
}
|
||||
}
|
||||
|
||||
if {$bin eq ""} {
|
||||
puts "error: boot.bin binary required as BOOTBIN environment"
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Validate bitstream if provided
|
||||
if {$bitstream ne "" && ![file exists $bitstream]} {
|
||||
puts "error: the bitstream file '$bitstream' does not exist"
|
||||
exit 1
|
||||
}
|
||||
|
||||
puts "Hardware server IP address: $ip"
|
||||
connect -url tcp:$ip:3121
|
||||
|
||||
set devices [targets]
|
||||
|
||||
set apu_line [string trim [targets -nocase -filter {name =~ "APU"}]]
|
||||
set arm_core_0_line [string trim [targets -nocase -filter {name =~ "ARM Cortex-A9 MPCore #0"}]]
|
||||
set fpga_line [string trim [targets -nocase -filter {name =~ "xc7z020"}]]
|
||||
|
||||
set apu_device_num [string index $apu_line 0]
|
||||
set arm_core_0_num [string index $arm_core_0_line 0]
|
||||
set fpga_device_num [string index $fpga_line 0]
|
||||
|
||||
puts "Select ps target with number: $apu_device_num"
|
||||
|
||||
# Select the target
|
||||
target $apu_device_num
|
||||
|
||||
# Resetting the target involved problems when an image is stored on the flash.
|
||||
# It has turned out that it is not essential to reset the system before loading
|
||||
# the software components into the device.
|
||||
puts "Reset target"
|
||||
# TODO: Make the reset optional/configurable via input argument.
|
||||
# Reset the target
|
||||
rst
|
||||
|
||||
puts "Set ps target with device number: $arm_core_0_num"
|
||||
targets $arm_core_0_num
|
||||
|
||||
puts "Initialize processing system"
|
||||
# Init processing system
|
||||
source $init_tcl
|
||||
|
||||
ps7_init
|
||||
ps7_post_config
|
||||
|
||||
puts "Set arm core 0 target with number: $arm_core_0_num"
|
||||
target $arm_core_0_num
|
||||
|
||||
puts "Download boot.bin $bin to target DDR at address $boot_bin_addr"
|
||||
dow -data $bin $boot_bin_addr
|
||||
|
||||
# Write boot.bin binary size to specific address.
|
||||
set boot_bin_size [file size $bin]
|
||||
puts "Writing boot.bin size $boot_bin_size to target DDR at address $boot_bin_size_addr"
|
||||
mwr ${boot_bin_size_addr} ${boot_bin_size}
|
||||
|
||||
puts "Flashing QSPI flasher app"
|
||||
dow $flasher_app
|
||||
|
||||
puts "Starting QSPI flasher app"
|
||||
con
|
||||
|
||||
puts "Success"
|
||||
@@ -7,10 +7,11 @@ use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embedded_hal::{delay::DelayNs as _, digital::StatefulOutputPin as _};
|
||||
use embedded_io::Write as _;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use zedboard_bsp::qspi_spansion;
|
||||
use zynq_boot_image::BootHeader;
|
||||
use zynq7000_hal::{
|
||||
clocks, gpio, prelude::*, priv_tim, qspi, time::Hertz, uart, BootMode, LevelShifterConfig,
|
||||
BootMode, LevelShifterConfig, clocks, gpio, prelude::*, priv_tim, qspi, time::Hertz, uart,
|
||||
};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
@@ -19,6 +20,15 @@ use zynq7000_rt as _;
|
||||
// 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,
|
||||
@@ -75,6 +85,7 @@ pub fn main() -> ! {
|
||||
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");
|
||||
@@ -96,7 +107,78 @@ pub fn main() -> ! {
|
||||
|
||||
let qspi_io_mode = qspi.into_io_mode(false);
|
||||
|
||||
let _spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
|
||||
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];
|
||||
while current_addr < boot_bin_size {
|
||||
if current_addr % 0x10000 == 0 {
|
||||
info!("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];
|
||||
info!("Programming address {:#x}", current_addr);
|
||||
match spansion_qspi.program_page(current_addr as u32, write_slice) {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
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;
|
||||
}
|
||||
info!("flashing done");
|
||||
*/
|
||||
|
||||
let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
|
||||
loop {
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { version = "2", default-features = false }
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", branch = "bump-arbitrary-int" }
|
||||
cortex-ar = { version = "0.3" }
|
||||
|
||||
[features]
|
||||
tools = []
|
||||
|
||||
@@ -11,7 +11,7 @@ keywords = ["no-std", "hal", "amd", "zynq7000", "xilinx", "bare-metal"]
|
||||
categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", branch = "bump-arbitrary-int" }
|
||||
cortex-ar = { version = "0.3" }
|
||||
zynq7000 = { path = "../zynq7000" }
|
||||
zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" }
|
||||
|
||||
@@ -22,7 +22,7 @@ thiserror = { version = "2", default-features = false }
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
ringbuf = { version = "0.4.8", default-features = false }
|
||||
embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
embedded-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
embedded-hal-async = "1"
|
||||
heapless = "0.9"
|
||||
@@ -39,7 +39,7 @@ embassy-net-driver = "0.2"
|
||||
smoltcp = { version = "0.12", default-features = false, features = ["proto-ipv4", "medium-ethernet", "socket-raw"] }
|
||||
vcell = "0.1"
|
||||
raw-slicee = "0.1"
|
||||
embedded-io-async = "0.6"
|
||||
embedded-io-async = "0.7"
|
||||
|
||||
[features]
|
||||
std = ["thiserror/std", "alloc"]
|
||||
|
||||
@@ -14,8 +14,8 @@ extern crate alloc;
|
||||
|
||||
use slcr::Slcr;
|
||||
use zynq7000::{
|
||||
slcr::{BootModeRegister, BootPllConfig, LevelShifterRegister},
|
||||
SpiClockPhase, SpiClockPolarity,
|
||||
slcr::{BootModeRegister, BootPllConfig, LevelShifterRegister},
|
||||
};
|
||||
|
||||
pub mod cache;
|
||||
|
||||
@@ -202,4 +202,9 @@ impl embedded_io_async::Write for TxAsync {
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
Ok(self.write(buf).await)
|
||||
}
|
||||
|
||||
/// This implementation does not do anything.
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
cortex-a-rt = { version = "0.1", optional = true, features = ["vfp-dp"] }
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", branch = "bump-arbitrary-int" }
|
||||
cortex-ar = { version = "0.3" }
|
||||
arbitrary-int = "2"
|
||||
zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user