FSBL-rs #9
@@ -1,24 +1,3 @@
|
||||
[target.armv7a-none-eabihf]
|
||||
runner = "./scripts/runner.sh"
|
||||
|
||||
rustflags = [
|
||||
"-Ctarget-cpu=cortex-a9",
|
||||
"-Ctarget-feature=+vfp3",
|
||||
"-Ctarget-feature=+neon",
|
||||
"-Clink-arg=-Tlink.x",
|
||||
# If this is not enabled, debugging / stepping can become problematic.
|
||||
"-Cforce-frame-pointers=yes",
|
||||
# Can be useful for debugging.
|
||||
# "-Clink-args=-Map=app.map"
|
||||
]
|
||||
|
||||
# Tier 3 target, so no pre-compiled artifacts included.
|
||||
[unstable]
|
||||
build-std = ["core", "alloc"]
|
||||
|
||||
[build]
|
||||
target = "armv7a-none-eabihf"
|
||||
|
||||
[env]
|
||||
# The following two env variables need to be set for the supplied runner.sh script to work.
|
||||
|
||||
50
.github/workflows/ci.yml
vendored
50
.github/workflows/ci.yml
vendored
@@ -7,41 +7,69 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: extractions/setup-just@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rust-src
|
||||
# Copy config file for rustflags and to build core/alloc.
|
||||
- run: cp .cargo/def-config.toml .cargo/config.toml
|
||||
- run: cargo check --target armv7a-none-eabihf -p zynq7000
|
||||
- run: cargo check --target armv7a-none-eabihf -p zynq7000-rt
|
||||
- run: just check zynq
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: just check tools
|
||||
- run: just check zynq7000-boot-image
|
||||
|
||||
build:
|
||||
name: Check build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: extractions/setup-just@v3
|
||||
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rust-src
|
||||
- run: just build zynq
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: just build tools
|
||||
- run: just build zynq7000-boot-image
|
||||
|
||||
fmt:
|
||||
name: Check formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: extractions/setup-just@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- run: cargo fmt --all -- --check
|
||||
- run: just fmt zynq
|
||||
- run: just fmt tools
|
||||
- run: just fmt zynq7000-boot-image
|
||||
|
||||
docs:
|
||||
name: Check Documentation Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: extractions/setup-just@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p zynq7000-rt --all-features
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p zynq7000 --all-features
|
||||
with:
|
||||
components: rust-src
|
||||
- run: just docs-zynq
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: extractions/setup-just@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: clippy, rust-src
|
||||
# Copy config file for rustflags and to build core/alloc.
|
||||
- run: cp .cargo/def-config.toml .cargo/config.toml
|
||||
- run: cargo clippy --target armv7a-none-eabihf -- -D warnings
|
||||
- run: just clippy zynq
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
- run: just clippy tools
|
||||
- run: just clippy zynq7000-boot-image
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/target
|
||||
target
|
||||
|
||||
/app.map
|
||||
/xsct-output.log
|
||||
/.vscode
|
||||
|
||||
23
Cargo.toml
23
Cargo.toml
@@ -1,23 +0,0 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"zynq7000-rt",
|
||||
"zynq7000",
|
||||
"zynq7000-hal",
|
||||
"zynq7000-embassy",
|
||||
"examples/simple",
|
||||
"examples/embassy",
|
||||
"examples/zedboard",
|
||||
"zynq-mmu",
|
||||
]
|
||||
exclude = ["experiments"]
|
||||
|
||||
# cargo build/run --release
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = false # <-
|
||||
incremental = false
|
||||
lto = true
|
||||
opt-level = 3 # <-
|
||||
overflow-checks = false # <-
|
||||
50
README.md
50
README.md
@@ -6,36 +6,60 @@ family of SoCs.
|
||||
|
||||
# List of crates
|
||||
|
||||
This workspace contains the following crates:
|
||||
This project contains the following crates:
|
||||
|
||||
- The [`zynq7000-rt`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-rt)
|
||||
## [Zynq Workspace](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq)
|
||||
|
||||
This workspace contains libraries and application which can only be run on the target system.
|
||||
|
||||
- The [`zynq7000-rt`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-rt)
|
||||
run-time crate containing basic low-level startup code necessary to boot a Rust app on the
|
||||
Zynq7000.
|
||||
- The [`zynq7000`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000) PAC
|
||||
crate containing basic low-level register definition.
|
||||
- The [`zynq7000-hal`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-hal)
|
||||
- The [`zynq7000`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000) PAC
|
||||
crate containing basic low-level register definitions.
|
||||
- The [`zynq7000-mmu`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-hal)
|
||||
crate containing common MMU abstractions used by both the HAL and the run-time crate.
|
||||
- The [`zynq7000-hal`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-hal)
|
||||
HAL crate containing higher-level abstractions on top of the PAC register crate.
|
||||
- The [`zynq7000-embassy`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-embassy)
|
||||
crate containing support for running the embassy-rs RTOS.
|
||||
- The [`zynq7000-embassy`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zynq7000-embassy)
|
||||
crate containing support for running the embassy-rs asynchronous run-time.
|
||||
|
||||
This project was developed using a Zedboard, so there are several crates available targeted towards
|
||||
this board:
|
||||
|
||||
- The [`zedboard-bsp`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zedboard-bsp)
|
||||
crate containing board specific components for the Zedboard.
|
||||
- The [`zedboard-fsbl`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zedboard-fsbl)
|
||||
contains a simple first-stage bootloader application for the Zedboard.
|
||||
- The [`zedboard-qspi-flasher`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/zedboard-qspi-flasher)
|
||||
contains an application which is able to flash a boot binary from DDR to the QSPI.
|
||||
|
||||
It also contains the following helper crates:
|
||||
|
||||
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples)
|
||||
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples)
|
||||
folder contains various example applications crates using the HAL and the PAC.
|
||||
This folder also contains dedicated example applications using the
|
||||
[`embassy`](https://github.com/embassy-rs/embassy) native Rust RTOS.
|
||||
|
||||
The [`zedboard-fpga-design`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zedboard-fpga-design)
|
||||
folder contains a sample FPGA design and block design which was used in
|
||||
some of the provided software examples. The project was created with Vivado version 2024.1.
|
||||
The folder contains a README with all the steps required to load this project from a TCL script.
|
||||
## Other libraries and tools
|
||||
|
||||
- The [`zedboard-fpga-design`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zedboard-fpga-design)
|
||||
folder contains a sample FPGA design and block design which was used in some of the provided software examples. The project was created with Vivado version 2024.1.
|
||||
The folder contains a README with all the steps required to load this project from a TCL script.
|
||||
- The [`zynq7000-boot-image`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-boot-image)
|
||||
library contains generic helpers to interface with the AMD
|
||||
[boot binary](https://docs.amd.com/r/en-US/ug1283-bootgen-user-guide).
|
||||
- The [`tools/zynq7000-ps7init-extract`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract)
|
||||
tool allows extracting configuration from the AMD generated `ps7init.tcl` file which contains
|
||||
static configuration parameters for DDR initialization.
|
||||
|
||||
# Using the `.cargo/config.toml` file
|
||||
|
||||
This is mostly relevant for development directly inside this repostiory.
|
||||
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
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
configure_level_shifter,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{GpioPins, Output, PinState},
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r";
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Enable PS-PL level shifters.
|
||||
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = GlobalTimerCounter::new(dp.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::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::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||
|
||||
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||
let mut emio_leds: [Output; 8] = [
|
||||
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
|
||||
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
|
||||
];
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
// Create a wave pattern for emio_leds
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.toggle().unwrap();
|
||||
ticker.next().await; // Wait for the next ticker for each toggle
|
||||
}
|
||||
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
loop {}
|
||||
}
|
||||
45
justfile
Normal file
45
justfile
Normal file
@@ -0,0 +1,45 @@
|
||||
all: check-all build-all clean-all fmt-all clippy-all docs-zynq
|
||||
|
||||
check-all: (check "zynq") (check "tools") (check "zynq7000-boot-image")
|
||||
clean-all: (clean "zynq") (clean "tools") (clean "zynq7000-boot-image")
|
||||
build-all: build-zynq (build "tools") (build "zynq7000-boot-image")
|
||||
fmt-all: (fmt "zynq") (fmt "tools") (fmt "zynq7000-boot-image")
|
||||
clippy-all: (clippy "zynq") (clippy "tools") (clippy "zynq7000-boot-image")
|
||||
|
||||
check target:
|
||||
cd {{target}} && cargo check
|
||||
|
||||
build target:
|
||||
cd {{target}} && cargo build
|
||||
|
||||
build-zynq: (build "zynq")
|
||||
cd "zynq/zedboard-fsbl" && cargo build --release
|
||||
|
||||
clean target:
|
||||
cd {{target}} && cargo clean
|
||||
|
||||
fmt target:
|
||||
cd {{target}} && cargo +stable fmt --all -- --check
|
||||
|
||||
clippy target:
|
||||
cd {{target}} && cargo clippy -- -D warnings
|
||||
|
||||
[working-directory: 'zynq']
|
||||
docs-zynq:
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p zynq7000
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p zynq7000-hal --features alloc
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p zynq7000-mmu
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p zynq7000-rt
|
||||
|
||||
[working-directory: 'zynq-boot-image/staging']
|
||||
bootgen:
|
||||
bootgen -arch zynq -image boot.bif -o boot.bin -w on
|
||||
echo "Generated boot.bin at zynq-boot-image/staging"
|
||||
|
||||
[no-cd]
|
||||
run binary:
|
||||
# Run the initialization script. It needs to be run inside the justfile directory.
|
||||
python3 {{justfile_directory()}}/scripts/zynq7000-init.py
|
||||
|
||||
# Run the GDB debugger in GUI mode.
|
||||
gdb-multiarch -q -x {{justfile_directory()}}/zynq/gdb.gdb {{binary}} -tui
|
||||
22
scripts/memory_ddr.x
Normal file
22
scripts/memory_ddr.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
|
||||
}
|
||||
24
scripts/memory_ocm.x
Normal file
24
scripts/memory_ocm.x
Normal file
@@ -0,0 +1,24 @@
|
||||
MEMORY
|
||||
{
|
||||
/* The Zynq7000 has 192 kB of OCM memory which can be used for the FSBL */
|
||||
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
|
||||
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
|
||||
/* Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This can
|
||||
be used for something like DMA descriptors, but the DDR needs to be set up first in addition
|
||||
to configuring the page at address 0x400_0000 accordingly */
|
||||
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
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit if no arguments are provided
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "Error: No arguments provided."
|
||||
echo "Usage: run.sh <binary>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the absolute path to the `scripts/` directory
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)"
|
||||
|
||||
# Get the absolute path to the project root
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Run the initialization script
|
||||
"$SCRIPT_DIR/zynq7000-init.py"
|
||||
|
||||
# Run the GDB debugger in GUI mode.
|
||||
gdb-multiarch -q -x gdb.gdb "$@" -tui
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
cargo +stable test --target $(rustc -vV | grep host | cut -d ' ' -f2) -p zynq7000-hal
|
||||
cargo +stable test --target $(rustc -vV | grep host | cut -d ' ' -f2) -p zynq7000
|
||||
144
scripts/xsct-flasher.tcl
Normal file
144
scripts/xsct-flasher.tcl
Normal file
@@ -0,0 +1,144 @@
|
||||
if {[info exists env(ip_address_hw_server)]} {
|
||||
set ip $env(ip_address_hw_server)
|
||||
} else {
|
||||
set ip "localhost"
|
||||
}
|
||||
|
||||
# Defaults
|
||||
set init_tcl ""
|
||||
set app ""
|
||||
set bitstream ""
|
||||
|
||||
# Usage helper
|
||||
proc usage {} {
|
||||
puts "Usage: xsct xsct-helper.tcl <init.tcl> \[-a|--app app.elf\] \[-b|--bit design.bit]"
|
||||
puts "Options:"
|
||||
puts " -a, --app Path to application ELF to download"
|
||||
puts " -b, --bit Path to FPGA bitstream (.bit) to program"
|
||||
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 "-a" || $arg eq "--app"} { set expecting app; continue }
|
||||
if {$arg eq "-b" || $arg eq "--bit"} { set expecting bitstream; 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
|
||||
}
|
||||
}
|
||||
|
||||
# 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 {$app ne ""} {
|
||||
if {![file exists $app]} {
|
||||
puts "error: the app file '$app' does not exist"
|
||||
exit 1
|
||||
}
|
||||
} elseif {[info exists env(APP)]} {
|
||||
if {[file exists $env(APP)]} {
|
||||
set app $env(APP)
|
||||
} else {
|
||||
puts "warning: APP environment variable is set but file does not exist: $env(APP)"
|
||||
}
|
||||
}
|
||||
|
||||
# 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
|
||||
|
||||
# Check if bitstream is set and the file exists before programming FPGA
|
||||
if {$bitstream eq ""} {
|
||||
puts "Skipping bitstream programming (bitstream argument not set)"
|
||||
} elseif {![file exists $bitstream]} {
|
||||
puts "Error: The bitstream file '$bitstream' does not exist"
|
||||
} else {
|
||||
puts "Set FPGA target with number: $fpga_device_num"
|
||||
target $fpga_device_num
|
||||
|
||||
# Without this delay, the FPGA programming may fail
|
||||
after 1500
|
||||
|
||||
puts "Programming FPGA with bitstream: $bitstream"
|
||||
fpga -f $bitstream
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if {$app ne ""} {
|
||||
puts "Download app $app to target"
|
||||
dow $app
|
||||
puts "Starting app"
|
||||
con
|
||||
}
|
||||
|
||||
puts "Success"
|
||||
@@ -1,86 +0,0 @@
|
||||
if {[info exists env(ip_address_hw_server)]} {
|
||||
set ip $env(ip_address_hw_server)
|
||||
} else {
|
||||
set ip "localhost"
|
||||
}
|
||||
|
||||
set init_tcl ""
|
||||
if {[llength $argv] >= 1} {
|
||||
set init_tcl [lindex $argv 0]
|
||||
} else {
|
||||
puts "error: missing required first argument pointing to initialization TCL script"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if {![file exists $init_tcl]} {
|
||||
puts "the ps init tcl script '$init_tcl' does not exist"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# parse command-line arguments
|
||||
set bitstream ""
|
||||
if {[llength $argv] >= 2} {
|
||||
set bitstream [lindex $argv 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
|
||||
|
||||
# Check if bitstream is set and the file exists before programming FPGA
|
||||
if {$bitstream eq ""} {
|
||||
puts "Skipping bitstream programming (bitstream argument not set)"
|
||||
} elseif {![file exists $bitstream]} {
|
||||
puts "Error: The bitstream file '$bitstream' does not exist"
|
||||
} else {
|
||||
puts "Set FPGA target with number: $fpga_device_num"
|
||||
target $fpga_device_num
|
||||
|
||||
# Without this delay, the FPGA programming may fail
|
||||
after 1500
|
||||
|
||||
puts "Programming FPGA with bitstream: $bitstream"
|
||||
fpga -f $bitstream
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if {[info exists env(APP)] && [file exists $env(APP)]} {
|
||||
puts "Download app $env(APP) to target"
|
||||
dow $env(APP)
|
||||
}
|
||||
|
||||
puts "Successful"
|
||||
@@ -7,7 +7,7 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Define the default values
|
||||
TCL_SCRIPT_NAME = "xsct-init.tcl"
|
||||
TCL_SCRIPT_NAME = "xsct-flasher.tcl"
|
||||
SCRIPTS_DIR = "scripts"
|
||||
DEFAULT_IP_ADDRESS_HW_SERVER = "localhost"
|
||||
|
||||
@@ -55,7 +55,7 @@ def main():
|
||||
default=os.getenv("TCL_INIT_SCRIPT"),
|
||||
help="Path to the ps7 initialization TCL file to prepare the processing system.\n"
|
||||
"You can also set the TCL_INIT_SCRIPT env variable to set this.\n"
|
||||
"It is also set implicitely when specifying the SDT folder with --sdt"
|
||||
"It is also set implicitely when specifying the SDT folder with --sdt",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
@@ -63,7 +63,7 @@ def main():
|
||||
dest="bit",
|
||||
default=os.getenv("ZYNQ_BITSTREAM"),
|
||||
help="Optional path to the bitstream which will also be programed to the device. It is"
|
||||
" also searched automatically if the --sdt option is used.\n"
|
||||
" also searched automatically if the --sdt option is used.\n",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -91,9 +91,6 @@ def main():
|
||||
print(f"The app '{args.app}' does not exist")
|
||||
sys.exit(1)
|
||||
|
||||
# Export environment variables
|
||||
if args.app:
|
||||
os.environ["APP"] = args.app
|
||||
os.environ["IP_ADDRESS_HW_SERVER"] = args.ip
|
||||
init_tcl = None
|
||||
bitstream = None
|
||||
@@ -124,7 +121,9 @@ def main():
|
||||
bitstream = args.bit
|
||||
init_tcl = args.init_tcl
|
||||
|
||||
xsct_script = Path(TCL_SCRIPT_NAME)
|
||||
# Get the script's directory
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
xsct_script = script_dir / TCL_SCRIPT_NAME
|
||||
|
||||
if not xsct_script.exists():
|
||||
xsct_script = Path(os.path.join(SCRIPTS_DIR, TCL_SCRIPT_NAME))
|
||||
@@ -136,7 +135,11 @@ def main():
|
||||
# Prepare tcl_args as a list to avoid manual string concatenation issues
|
||||
cmd_list = ["xsct", str(xsct_script), init_tcl]
|
||||
if bitstream:
|
||||
cmd_list.append("--bit")
|
||||
cmd_list.append(bitstream)
|
||||
if args.app:
|
||||
cmd_list.append("--app")
|
||||
cmd_list.append(args.app)
|
||||
|
||||
# Join safely for shell execution
|
||||
xsct_cmd = shlex.join(cmd_list)
|
||||
|
||||
796
tools/Cargo.lock
generated
Normal file
796
tools/Cargo.lock
generated
Normal file
@@ -0,0 +1,796 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[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 = "arm-targets"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3371884971a96d71d8bd4e781188a7d327d7e5e455d07ef4c352922c66695e9e"
|
||||
|
||||
[[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 = "boot-image-test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"zynq7000-boot-image",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-ar"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ea2a354642e242870bc43b57a517359b0be6e96d302b2811cd0644c979c54e"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.0.0",
|
||||
"arm-targets",
|
||||
"bitbybit",
|
||||
"num_enum",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-mmio"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "005a6dabf68a87a460d3cb9b8e2fd5de3f474fc34e8d9451f5a1b6db518da143"
|
||||
dependencies = [
|
||||
"derive-mmio-macro",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-mmio-macro"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "584dc8e12e4aeb88000c2be8ef7db15657c817fba3e77999a24807d1efcdeefa"
|
||||
dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.176"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "mmu-table-gen"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"zynq7000-mmu",
|
||||
"zynq7000-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
|
||||
dependencies = [
|
||||
"num_enum_derive",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum_derive"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_logger"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"log",
|
||||
"time",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "zynq7000"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.0.0",
|
||||
"bitbybit",
|
||||
"derive-mmio",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"static_assertions",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zynq7000-boot-image"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.0.0",
|
||||
"bitbybit",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zynq7000-mmu"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cortex-ar",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zynq7000-ps7init-extract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"simple_logger",
|
||||
"syn",
|
||||
"zynq7000",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zynq7000-rt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.0.0",
|
||||
"cortex-ar",
|
||||
"zynq7000-mmu",
|
||||
]
|
||||
7
tools/Cargo.toml
Normal file
7
tools/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"boot-image-test",
|
||||
"mmu-table-gen",
|
||||
"zynq7000-ps7init-extract",
|
||||
]
|
||||
1
tools/boot-image-test/.gitignore
vendored
Normal file
1
tools/boot-image-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
305
tools/boot-image-test/Cargo.lock
generated
Normal file
305
tools/boot-image-test/Cargo.lock
generated
Normal file
@@ -0,0 +1,305 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tester"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"zynq-boot-image",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "zynq-boot-image"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.0.0",
|
||||
"bitbybit",
|
||||
"thiserror",
|
||||
]
|
||||
8
tools/boot-image-test/Cargo.toml
Normal file
8
tools/boot-image-test/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "boot-image-test"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
zynq7000-boot-image= { path = "../../zynq7000-boot-image" }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
82
tools/boot-image-test/src/main.rs
Normal file
82
tools/boot-image-test/src/main.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
//! Small tester app to verify some of the API offers by the zynq-boot-image crate.
|
||||
use std::{io::Read, path::Path};
|
||||
|
||||
use clap::Parser as _;
|
||||
use zynq7000_boot_image::{BootHeader, FIXED_BOOT_HEADER_SIZE};
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
#[command(version, about)]
|
||||
pub struct Cli {
|
||||
/// Path to boot.bin file to test.
|
||||
#[arg(short, long)]
|
||||
path: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
let boot_bin = Path::new(&cli.path);
|
||||
if !boot_bin.exists() {
|
||||
eprintln!("File not found: {}", boot_bin.display());
|
||||
std::process::exit(1);
|
||||
}
|
||||
let mut boot_bin_file = std::fs::File::open(boot_bin).expect("failed to open boot.bin file");
|
||||
let mut header_buf = Box::new([0u8; 8192]);
|
||||
boot_bin_file
|
||||
.read_exact(&mut header_buf[0..FIXED_BOOT_HEADER_SIZE])
|
||||
.expect("failed to read boot header");
|
||||
let mut boot_header = BootHeader::new(&header_buf[0..FIXED_BOOT_HEADER_SIZE])
|
||||
.expect("failed to parse boot header");
|
||||
let source_offset = boot_header.source_offset();
|
||||
boot_bin_file
|
||||
.read_exact(&mut header_buf[FIXED_BOOT_HEADER_SIZE..source_offset - FIXED_BOOT_HEADER_SIZE])
|
||||
.expect("failed to read full boot binary metadata");
|
||||
// Re-assign with newly read data.
|
||||
boot_header = BootHeader::new_unchecked(&header_buf[0..source_offset]);
|
||||
let image_header_table = boot_header
|
||||
.image_header_table()
|
||||
.expect("failed extracting image header table");
|
||||
let image_headers = image_header_table.count_of_headers();
|
||||
println!(
|
||||
"Image headers: {}, first image header offset {}, first partition header offset {}",
|
||||
image_headers,
|
||||
image_header_table
|
||||
.first_image_header_offset()
|
||||
.expect("failed reading first image header offset"),
|
||||
image_header_table
|
||||
.first_partition_header_offset()
|
||||
.expect("failed reading first partition header offset")
|
||||
);
|
||||
|
||||
let image_header_iter = boot_header
|
||||
.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,
|
||||
image_header.partition_count()
|
||||
);
|
||||
let mut test: [u8; 64] = [0; 64];
|
||||
let image_name = image_header
|
||||
.image_name(&mut test)
|
||||
.expect("image name error");
|
||||
println!("image name: {}", image_name);
|
||||
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 {}",
|
||||
partition.total_partition_length().unwrap(),
|
||||
partition.destination_load_address(),
|
||||
partition.section_count()
|
||||
);
|
||||
println!("section attributes: {:?}", partition.section_attributes());
|
||||
}
|
||||
println!("--------------------------------------\n\r");
|
||||
}
|
||||
}
|
||||
8
tools/mmu-table-gen/Cargo.toml
Normal file
8
tools/mmu-table-gen/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "mmu-table-gen"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
zynq7000-rt = { path = "../../zynq/zynq7000-rt", default-features = false }
|
||||
zynq7000-mmu = { path = "../../zynq/zynq7000-mmu", features = ["tools"] }
|
||||
@@ -16,7 +16,7 @@ macro_rules! write_l1_section {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let file_path = "src/mmu_table.rs";
|
||||
let file_path = "mmu_table.rs";
|
||||
let file = File::create(file_path).expect("Failed to create file");
|
||||
|
||||
let mut offset = 0;
|
||||
@@ -56,7 +56,7 @@ fn main() {
|
||||
|
||||
writeln!(buf_writer, "use crate::mmu::section_attrs;").unwrap();
|
||||
writeln!(buf_writer, "use cortex_ar::mmu::L1Section;").unwrap();
|
||||
writeln!(buf_writer, "use zynq_mmu::L1Table;").unwrap();
|
||||
writeln!(buf_writer, "use zynq7000_mmu::L1Table;").unwrap();
|
||||
writeln!(buf_writer, "").unwrap();
|
||||
|
||||
writeln!(buf_writer, "/// MMU Level 1 Page table.").unwrap();
|
||||
2
tools/zynq7000-ps7init-extract/.gitignore
vendored
Normal file
2
tools/zynq7000-ps7init-extract/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/ddrc_config_autogen.rs
|
||||
/ddriob_config_autogen.rs
|
||||
251
tools/zynq7000-ps7init-extract/Cargo.lock
generated
Normal file
251
tools/zynq7000-ps7init-extract/Cargo.lock
generated
Normal file
@@ -0,0 +1,251 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "zynq7000-ps7init-extract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
14
tools/zynq7000-ps7init-extract/Cargo.toml
Normal file
14
tools/zynq7000-ps7init-extract/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "zynq7000-ps7init-extract"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
zynq7000 = { path = "../../zynq/zynq7000" }
|
||||
log = "0.4"
|
||||
simple_logger = "5"
|
||||
regex = "1"
|
||||
quote = "1"
|
||||
syn = "2"
|
||||
proc-macro2 = "1"
|
||||
26
tools/zynq7000-ps7init-extract/README.md
Normal file
26
tools/zynq7000-ps7init-extract/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
Zynq7000 PS7 Init Extractor
|
||||
=========
|
||||
|
||||
AMD provides tooling to auto-generate some of the hardware initialization for the external DDR
|
||||
as native Rust code.
|
||||
|
||||
The AMD tooling generates these files as `ps7init.tcl`, `ps7init.c`, `ps7init.h` files but not as
|
||||
Rust files. The specific parameters required for different DDR chips are proprietary, so that
|
||||
portion is required for Rust programs as well. Do avoid the need of compiling the PS7 initialization
|
||||
scripts with a C compiler, this tool extracts all required configuration parameters for DDR and
|
||||
DDRIOB initialization and configuration and exports them as native Rust constants.
|
||||
|
||||
The generates files can be placed in individual projects or board support packages to initialize
|
||||
the DDR in conjunction with the [Zynq7000 HAL library](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/fsbl-rs/zynq/zynq7000-hal).
|
||||
|
||||
Right now, the script expects the `ps7init.tcl` file to be passed as a command line argument
|
||||
for `-p` or `--path`. It then generates the configuration as a `ddrc_config_autogen.rs` and
|
||||
`ddrc_config_autogen.rs` file.
|
||||
|
||||
For example, assuming that there is a `ps7init.tcl` script in the current directory, you can use
|
||||
|
||||
```sh
|
||||
cargo run -- --path ./ps7init.tcl
|
||||
```
|
||||
|
||||
to generate the configuration files.
|
||||
341
tools/zynq7000-ps7init-extract/src/main.rs
Normal file
341
tools/zynq7000-ps7init-extract/src/main.rs
Normal file
@@ -0,0 +1,341 @@
|
||||
use std::{collections::HashMap, ops::RangeInclusive, path::Path};
|
||||
|
||||
use clap::Parser as _;
|
||||
use simple_logger::SimpleLogger;
|
||||
|
||||
const DDRC_ADDR_RANGE: RangeInclusive<u32> = 0xf800_6000..=0xf800_62b4;
|
||||
const DDRIOB_ADDR_RANGE: RangeInclusive<u32> = 0xf800_0b40..=0xf800_0b68;
|
||||
|
||||
const DDRC_FILE_NAME: &str = "ddrc_config_autogen.rs";
|
||||
const DDRIOB_FILE_NAME: &str = "ddriob_config_autogen.rs";
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
#[command(version, about)]
|
||||
pub struct Cli {
|
||||
/// Path to ps7init.tcl file.
|
||||
#[arg(short, long)]
|
||||
path: String,
|
||||
}
|
||||
|
||||
fn extract_hex_values(line: &str) -> Option<(u32, u32, u32)> {
|
||||
let re = regex::Regex::new(r"0[xX]([0-9A-Fa-f]+)").unwrap();
|
||||
|
||||
let captures: Vec<u32> = re
|
||||
.captures_iter(line)
|
||||
.filter_map(|cap| u32::from_str_radix(&cap[1], 16).ok())
|
||||
.collect();
|
||||
|
||||
if captures.len() == 3 {
|
||||
Some((captures[0], captures[1], captures[2]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RegisterToValueMap(pub HashMap<u32, u32>);
|
||||
|
||||
impl RegisterToValueMap {
|
||||
fn val_as_token(&self, reg_name: &str, addr: u32) -> proc_macro2::TokenStream {
|
||||
let val = self.0.get(&addr).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"failed to retrieve register value for register {}",
|
||||
reg_name
|
||||
)
|
||||
});
|
||||
format!("{:#010x}", val)
|
||||
.parse::<proc_macro2::TokenStream>()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
enum ParsingMode {
|
||||
DdrRev3,
|
||||
MioRev3,
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let cli = Cli::parse();
|
||||
let ps7init_tcl = Path::new(&cli.path);
|
||||
if !ps7init_tcl.exists() {
|
||||
log::error!("File not found: {}", ps7init_tcl.display());
|
||||
std::process::exit(1);
|
||||
}
|
||||
let mut parsing_mode = None;
|
||||
|
||||
let mut reg_to_values = RegisterToValueMap::default();
|
||||
|
||||
for line in std::fs::read_to_string(ps7init_tcl)?.lines() {
|
||||
match parsing_mode {
|
||||
None => {
|
||||
if line.contains("ps7_ddr_init_data_3_0") {
|
||||
parsing_mode = Some(ParsingMode::DdrRev3);
|
||||
} else if line.contains("ps7_mio_init_data_3_0") {
|
||||
parsing_mode = Some(ParsingMode::MioRev3);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Some(ParsingMode::MioRev3) => {
|
||||
if line.contains("}") {
|
||||
parsing_mode = None;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Some(ParsingMode::DdrRev3) => {
|
||||
if line.contains("}") {
|
||||
parsing_mode = None;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((addr, _mask, value)) = extract_hex_values(line)
|
||||
&& (DDRC_ADDR_RANGE.contains(&addr) || DDRIOB_ADDR_RANGE.contains(&addr))
|
||||
&& addr % 4 == 0
|
||||
{
|
||||
// Only use first value.
|
||||
if reg_to_values.0.contains_key(&addr) {
|
||||
if addr != 0xF800_6000 {
|
||||
log::warn!("detected duplicate register value for address {}", addr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
reg_to_values.0.insert(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("generating DDRC config files: {}", DDRC_FILE_NAME);
|
||||
generate_ddrc_config(®_to_values, DDRC_FILE_NAME)?;
|
||||
|
||||
log::info!("generating DDRIOB config files: {}", DDRIOB_FILE_NAME);
|
||||
generate_ddriob_config(®_to_values, DDRIOB_FILE_NAME)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_ddrc_config(
|
||||
reg_to_values: &RegisterToValueMap,
|
||||
file_name: &str,
|
||||
) -> std::io::Result<()> {
|
||||
// Format as hex strings
|
||||
let ddrc = reg_to_values.val_as_token("DDRC Control", 0xF800_6000);
|
||||
let two_rank = reg_to_values.val_as_token("Two Rank", 0xF800_6004);
|
||||
let hpr = reg_to_values.val_as_token("HPR", 0xF800_6008);
|
||||
let lpr = reg_to_values.val_as_token("LPR", 0xF800_600C);
|
||||
let wr = reg_to_values.val_as_token("WR", 0xF800_6010);
|
||||
let dram_param_0 = reg_to_values.val_as_token("DRAM Reg0", 0xF800_6014);
|
||||
let dram_param_1 = reg_to_values.val_as_token("DRAM Reg1", 0xF800_6018);
|
||||
let dram_param_2 = reg_to_values.val_as_token("DRAM Reg2", 0xF800_601C);
|
||||
let dram_param_3 = reg_to_values.val_as_token("DRAM Reg3", 0xF800_6020);
|
||||
let dram_param_4 = reg_to_values.val_as_token("DRAM Reg4", 0xF800_6024);
|
||||
let dram_init_param = reg_to_values.val_as_token("DRAM Init Param", 0xF800_6028);
|
||||
let dram_emr = reg_to_values.val_as_token("DRAM EMR", 0xF800_602C);
|
||||
let dram_emr_mr = reg_to_values.val_as_token("DRAM EMR MR", 0xF800_6030);
|
||||
let dram_burst8_rdwr = reg_to_values.val_as_token("DRAM Burst8 RDWR", 0xF800_6034);
|
||||
let dram_disable_dq = reg_to_values.val_as_token("DRAM Disable DQ", 0xF800_6038);
|
||||
let dram_addr_map_bank = reg_to_values.val_as_token("DRAM Addr Map Bank", 0xF800_603C);
|
||||
let dram_addr_map_col = reg_to_values.val_as_token("DRAM Addr Map Col", 0xF800_6040);
|
||||
let dram_addr_map_row = reg_to_values.val_as_token("DRAM Addr Map Row", 0xF800_6044);
|
||||
let dram_odt = reg_to_values.val_as_token("DRAM ODT", 0xF800_6048);
|
||||
let phy_cmd_timeout_rddata_cpt = reg_to_values.val_as_token("PHY CMD Timeout", 0xF800_6050);
|
||||
let dll_calib = reg_to_values.val_as_token("DLL Calib", 0xF800_6058);
|
||||
let odt_delay_hold = reg_to_values.val_as_token("ODT Delay Hold", 0xF800_605C);
|
||||
let ctrl_reg1 = reg_to_values.val_as_token("CTRL Reg 1", 0xF800_6060);
|
||||
let ctrl_reg2 = reg_to_values.val_as_token("CTRL Reg 2", 0xF800_6064);
|
||||
let ctrl_reg3 = reg_to_values.val_as_token("CTRL Reg 3", 0xF800_6068);
|
||||
let ctrl_reg4 = reg_to_values.val_as_token("CTRL Reg 4", 0xF800_606C);
|
||||
let ctrl_reg5 = reg_to_values.val_as_token("CTRL Reg 5", 0xF800_6078);
|
||||
let ctrl_reg6 = reg_to_values.val_as_token("CTRL Reg 6", 0xF800_607C);
|
||||
let che_t_zq = reg_to_values.val_as_token("CHE T ZQ", 0xF800_60A4);
|
||||
let che_t_zq_short_interval_reg =
|
||||
reg_to_values.val_as_token("CHE T ZQ Short Interval", 0xF800_60A8);
|
||||
let deep_powerdown = reg_to_values.val_as_token("Deep Powerdown", 0xF800_60AC);
|
||||
let reg_2c = reg_to_values.val_as_token("Reg 2C", 0xF800_60B0);
|
||||
let reg_2d = reg_to_values.val_as_token("Reg 2D", 0xF800_60B4);
|
||||
let dfi_timing = reg_to_values.val_as_token("DFI Timing", 0xF800_60B8);
|
||||
let che_ecc_ctrl = reg_to_values.val_as_token("CHE ECC CTRL", 0xF800_60C4);
|
||||
let ecc_scrub = reg_to_values.val_as_token("ECC Scrub", 0xF800_60F4);
|
||||
let phy_receiver_enable = reg_to_values.val_as_token("PHY Receiver Enable", 0xF800_6114);
|
||||
let phy_config_0 = reg_to_values.val_as_token("PHY Config 0", 0xF800_6118);
|
||||
let phy_config_1 = reg_to_values.val_as_token("PHY Config 1", 0xF800_611C);
|
||||
let phy_config_2 = reg_to_values.val_as_token("PHY Config 2", 0xF800_6120);
|
||||
let phy_config_3 = reg_to_values.val_as_token("PHY Config 3", 0xF800_6124);
|
||||
let phy_init_ratio_0 = reg_to_values.val_as_token("PHY Init Ratio 0", 0xF800_612C);
|
||||
let phy_init_ratio_1 = reg_to_values.val_as_token("PHY Init Ratio 1", 0xF800_6130);
|
||||
let phy_init_ratio_2 = reg_to_values.val_as_token("PHY Init Ratio 2", 0xF800_6134);
|
||||
let phy_init_ratio_3 = reg_to_values.val_as_token("PHY Init Ratio 3", 0xF800_6138);
|
||||
let phy_rd_dqs_config_0 = reg_to_values.val_as_token("PHY RD DQS Config 0", 0xF800_6140);
|
||||
let phy_rd_dqs_config_1 = reg_to_values.val_as_token("PHY RD DQS Config 1", 0xF800_6144);
|
||||
let phy_rd_dqs_config_2 = reg_to_values.val_as_token("PHY RD DQS Config 2", 0xF800_6148);
|
||||
let phy_rd_dqs_config_3 = reg_to_values.val_as_token("PHY RD DQS Config 3", 0xF800_614C);
|
||||
let phy_wr_dqs_config_0 = reg_to_values.val_as_token("PHY WR DQS Config 0", 0xF800_6154);
|
||||
let phy_wr_dqs_config_1 = reg_to_values.val_as_token("PHY WR DQS Config 1", 0xF800_6158);
|
||||
let phy_wr_dqs_config_2 = reg_to_values.val_as_token("PHY WR DQS Config 2", 0xF800_615C);
|
||||
let phy_wr_dqs_config_3 = reg_to_values.val_as_token("PHY WR DQS Config 3", 0xF800_6160);
|
||||
let phy_we_cfg_0 = reg_to_values.val_as_token("PHY WE Config 0", 0xF800_6168);
|
||||
let phy_we_cfg_1 = reg_to_values.val_as_token("PHY WE Config 1", 0xF800_616C);
|
||||
let phy_we_cfg_2 = reg_to_values.val_as_token("PHY WE Config 2", 0xF800_6170);
|
||||
let phy_we_cfg_3 = reg_to_values.val_as_token("PHY WE Config 3", 0xF800_6174);
|
||||
let phy_wr_data_slv_0 = reg_to_values.val_as_token("PHY WR Data Slv 0", 0xF800_617C);
|
||||
let phy_wr_data_slv_1 = reg_to_values.val_as_token("PHY WR Data Slv 1", 0xF800_6180);
|
||||
let phy_wr_data_slv_2 = reg_to_values.val_as_token("PHY WR Data Slv 2", 0xF800_6184);
|
||||
let phy_wr_data_slv_3 = reg_to_values.val_as_token("PHY WR Data Slv 3", 0xF800_6188);
|
||||
let reg64 = reg_to_values.val_as_token("Reg64", 0xF800_6190);
|
||||
let reg65 = reg_to_values.val_as_token("Reg65", 0xF800_6194);
|
||||
let page_mask = reg_to_values.val_as_token("Page Mask", 0xF800_6204);
|
||||
let axi_priority_wr_port_0 = reg_to_values.val_as_token("AXI Priority WR Port 0", 0xF800_6208);
|
||||
let axi_priority_wr_port_1 = reg_to_values.val_as_token("AXI Priority WR Port 1", 0xF800_620C);
|
||||
let axi_priority_wr_port_2 = reg_to_values.val_as_token("AXI Priority WR Port 2", 0xF800_6210);
|
||||
let axi_priority_wr_port_3 = reg_to_values.val_as_token("AXI Priority WR Port 3", 0xF800_6214);
|
||||
let axi_priority_rd_port_0 = reg_to_values.val_as_token("AXI Priority RD Port 0", 0xF800_6218);
|
||||
let axi_priority_rd_port_1 = reg_to_values.val_as_token("AXI Priority RD Port 1", 0xF800_621C);
|
||||
let axi_priority_rd_port_2 = reg_to_values.val_as_token("AXI Priority RD Port 2", 0xF800_6220);
|
||||
let axi_priority_rd_port_3 = reg_to_values.val_as_token("AXI Priority RD Port 3", 0xF800_6224);
|
||||
let lpddr_ctrl_0 = reg_to_values.val_as_token("LPDDR CTRL 0", 0xF800_62A8);
|
||||
let lpddr_ctrl_1 = reg_to_values.val_as_token("LPDDR CTRL 1", 0xF800_62AC);
|
||||
let lpddr_ctrl_2 = reg_to_values.val_as_token("LPDDR CTRL 2", 0xF800_62B0);
|
||||
let lpddr_ctrl_3 = reg_to_values.val_as_token("LPDDR CTRL 3", 0xF800_62B4);
|
||||
|
||||
let generated = quote::quote! {
|
||||
//!This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program.
|
||||
//!
|
||||
//!This configuration file contains static DDR configuration parameters extracted from the
|
||||
//!AMD ps7init.tcl file
|
||||
use zynq7000::ddrc::regs;
|
||||
use zynq7000_hal::ddr::DdrcConfigSet;
|
||||
|
||||
pub const DDRC_CONFIG_ZEDBOARD: DdrcConfigSet = DdrcConfigSet {
|
||||
ctrl: regs::DdrcControl::new_with_raw_value(#ddrc),
|
||||
two_rank: regs::TwoRankConfig::new_with_raw_value(#two_rank),
|
||||
hpr: regs::LprHprQueueControl::new_with_raw_value(#hpr),
|
||||
lpr: regs::LprHprQueueControl::new_with_raw_value(#lpr),
|
||||
wr: regs::WriteQueueControl::new_with_raw_value(#wr),
|
||||
dram_param_0: regs::DramParamReg0::new_with_raw_value(#dram_param_0),
|
||||
dram_param_1: regs::DramParamReg1::new_with_raw_value(#dram_param_1),
|
||||
dram_param_2: regs::DramParamReg2::new_with_raw_value(#dram_param_2),
|
||||
dram_param_3: regs::DramParamReg3::new_with_raw_value(#dram_param_3),
|
||||
dram_param_4: regs::DramParamReg4::new_with_raw_value(#dram_param_4),
|
||||
dram_init_param: regs::DramInitParam::new_with_raw_value(#dram_init_param),
|
||||
dram_emr: regs::DramEmr::new_with_raw_value(#dram_emr),
|
||||
dram_emr_mr: regs::DramEmrMr::new_with_raw_value(#dram_emr_mr),
|
||||
dram_burst8_rdwr: regs::DramBurst8ReadWrite::new_with_raw_value(#dram_burst8_rdwr),
|
||||
disable_dq: regs::DisableDq::new_with_raw_value(#dram_disable_dq),
|
||||
dram_addr_map_bank: regs::DramAddrMapBank::new_with_raw_value(#dram_addr_map_bank),
|
||||
dram_addr_map_col: regs::DramAddrMapColumn::new_with_raw_value(#dram_addr_map_col),
|
||||
dram_addr_map_row: regs::DramAddrMapRow::new_with_raw_value(#dram_addr_map_row),
|
||||
dram_odt: regs::DramOdt::new_with_raw_value(#dram_odt),
|
||||
phy_cmd_timeout_rddata_cpt: regs::PhyCmdTimeoutRdDataCpt::new_with_raw_value(#phy_cmd_timeout_rddata_cpt),
|
||||
dll_calib: regs::DllCalib::new_with_raw_value(#dll_calib),
|
||||
odt_delay_hold: regs::OdtDelayHold::new_with_raw_value(#odt_delay_hold),
|
||||
ctrl_reg1: regs::CtrlReg1::new_with_raw_value(#ctrl_reg1),
|
||||
ctrl_reg2: regs::CtrlReg2::new_with_raw_value(#ctrl_reg2),
|
||||
ctrl_reg3: regs::CtrlReg3::new_with_raw_value(#ctrl_reg3),
|
||||
ctrl_reg4: regs::CtrlReg4::new_with_raw_value(#ctrl_reg4),
|
||||
ctrl_reg5: regs::CtrlReg5::new_with_raw_value(#ctrl_reg5),
|
||||
ctrl_reg6: regs::CtrlReg6::new_with_raw_value(#ctrl_reg6),
|
||||
che_t_zq: regs::CheTZq::new_with_raw_value(#che_t_zq),
|
||||
che_t_zq_short_interval_reg: regs::CheTZqShortInterval::new_with_raw_value(#che_t_zq_short_interval_reg),
|
||||
deep_powerdown: regs::DeepPowerdown::new_with_raw_value(#deep_powerdown),
|
||||
reg_2c: regs::Reg2c::new_with_raw_value(#reg_2c),
|
||||
reg_2d: regs::Reg2d::new_with_raw_value(#reg_2d),
|
||||
dfi_timing: regs::DfiTiming::new_with_raw_value(#dfi_timing),
|
||||
che_ecc_ctrl: regs::CheEccControl::new_with_raw_value(#che_ecc_ctrl),
|
||||
ecc_scrub: regs::EccScrub::new_with_raw_value(#ecc_scrub),
|
||||
phy_receiver_enable: regs::PhyReceiverEnable::new_with_raw_value(#phy_receiver_enable),
|
||||
phy_config: [
|
||||
regs::PhyConfig::new_with_raw_value(#phy_config_0),
|
||||
regs::PhyConfig::new_with_raw_value(#phy_config_1),
|
||||
regs::PhyConfig::new_with_raw_value(#phy_config_2),
|
||||
regs::PhyConfig::new_with_raw_value(#phy_config_3),
|
||||
],
|
||||
phy_init_ratio: [
|
||||
regs::PhyInitRatio::new_with_raw_value(#phy_init_ratio_0),
|
||||
regs::PhyInitRatio::new_with_raw_value(#phy_init_ratio_1),
|
||||
regs::PhyInitRatio::new_with_raw_value(#phy_init_ratio_2),
|
||||
regs::PhyInitRatio::new_with_raw_value(#phy_init_ratio_3),
|
||||
],
|
||||
phy_rd_dqs_config: [
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_rd_dqs_config_0),
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_rd_dqs_config_1),
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_rd_dqs_config_2),
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_rd_dqs_config_3),
|
||||
],
|
||||
phy_wr_dqs_config: [
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_wr_dqs_config_0),
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_wr_dqs_config_1),
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_wr_dqs_config_2),
|
||||
regs::PhyDqsConfig::new_with_raw_value(#phy_wr_dqs_config_3),
|
||||
],
|
||||
phy_we_cfg: [
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(#phy_we_cfg_0),
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(#phy_we_cfg_1),
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(#phy_we_cfg_2),
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(#phy_we_cfg_3),
|
||||
],
|
||||
phy_wr_data_slv: [
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(#phy_wr_data_slv_0),
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(#phy_wr_data_slv_1),
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(#phy_wr_data_slv_2),
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(#phy_wr_data_slv_3),
|
||||
],
|
||||
reg64: regs::Reg64::new_with_raw_value(#reg64),
|
||||
reg65: regs::Reg65::new_with_raw_value(#reg65),
|
||||
page_mask: #page_mask,
|
||||
axi_priority_wr_port: [
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(#axi_priority_wr_port_0),
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(#axi_priority_wr_port_1),
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(#axi_priority_wr_port_2),
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(#axi_priority_wr_port_3),
|
||||
],
|
||||
axi_priority_rd_port: [
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(#axi_priority_rd_port_0),
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(#axi_priority_rd_port_1),
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(#axi_priority_rd_port_2),
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(#axi_priority_rd_port_3),
|
||||
],
|
||||
lpddr_ctrl_0: regs::LpddrControl0::new_with_raw_value(#lpddr_ctrl_0),
|
||||
lpddr_ctrl_1: regs::LpddrControl1::new_with_raw_value(#lpddr_ctrl_1),
|
||||
lpddr_ctrl_2: regs::LpddrControl2::new_with_raw_value(#lpddr_ctrl_2),
|
||||
lpddr_ctrl_3: regs::LpddrControl3::new_with_raw_value(#lpddr_ctrl_3),
|
||||
};
|
||||
};
|
||||
|
||||
std::fs::write(file_name, generated.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_ddriob_config(
|
||||
reg_to_values: &RegisterToValueMap,
|
||||
file_name: &str,
|
||||
) -> std::io::Result<()> {
|
||||
// Format as hex strings
|
||||
let addr0 = reg_to_values.val_as_token("DDRIOB Addr 0", 0xF800_0B40);
|
||||
let addr1 = reg_to_values.val_as_token("DDRIOB Addr 1", 0xF800_0B44);
|
||||
let data0 = reg_to_values.val_as_token("DDRIOB Data 0", 0xF800_0B48);
|
||||
let data1 = reg_to_values.val_as_token("DDRIOB Data 1", 0xF800_0B4C);
|
||||
let diff0 = reg_to_values.val_as_token("DDRIOB Diff 0", 0xF800_0B50);
|
||||
let diff1 = reg_to_values.val_as_token("DDRIOB Diff 1", 0xF800_0B54);
|
||||
let clock = reg_to_values.val_as_token("DDRIOB Clock", 0xF800_0B58);
|
||||
let generated = quote::quote! {
|
||||
//!This file was auto-generated by the [zynq7000-ps7init-extract](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/tools/zynq7000-ps7init-extract) program.
|
||||
//!
|
||||
//!This configuration file contains static DDRIOB configuration parameters extracted from the
|
||||
//!AMD ps7init.tcl file
|
||||
use zynq7000::ddrc::regs;
|
||||
use zynq7000_hal::ddr::DdriobConfigSet;
|
||||
|
||||
pub const DDRIOB_CONFIG_SET_ZEDBOARD: DdriobConfigSet = DdriobConfigSet {
|
||||
addr0: regs::DdriobConfig::new_with_raw_value(#addr0),
|
||||
addr1: regs::DdriobConfig::new_with_raw_value(#addr1),
|
||||
data0: regs::DdriobConfig::new_with_raw_value(#data0),
|
||||
data1: regs::DdriobConfig::new_with_raw_value(#data1),
|
||||
diff0: regs::DdriobConfig::new_with_raw_value(#diff0),
|
||||
diff1: regs::DdriobConfig::new_with_raw_value(#diff1),
|
||||
clock: regs::DdriobConfig::new_with_raw_value(#clock),
|
||||
};
|
||||
};
|
||||
|
||||
std::fs::write(file_name, generated.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -675,6 +675,18 @@ proc create_root_design { parentCell } {
|
||||
connect_bd_net -net xlslice_0_Dout1 [get_bd_pins UART_MUX/Dout] [get_bd_pins uart_mux_0/sel]
|
||||
connect_bd_net -net xlslice_1_Dout [get_bd_pins EMIO_O_0/Dout] [get_bd_pins LEDS/Din] [get_bd_pins EMIO_I/In0] [get_bd_pins UART_MUX/Din]
|
||||
|
||||
# Set DDR properties specified in the datasheet.
|
||||
set_property -dict [list \
|
||||
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.410} \
|
||||
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.411} \
|
||||
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.341} \
|
||||
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.358} \
|
||||
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {0.025} \
|
||||
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {0.028} \
|
||||
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {-0.009} \
|
||||
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {-0.061} \
|
||||
] [get_bd_cells processing_system7_0]
|
||||
|
||||
# Create address segments
|
||||
assign_bd_address -offset 0x43C00000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_uart16550_0/S_AXI/Reg] -force
|
||||
assign_bd_address -offset 0x42C00000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_uartlite_0/S_AXI/Reg] -force
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "zynq-mmu"
|
||||
description = "Zynq MMU structures"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { version = "2", default-features = false }
|
||||
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2" }
|
||||
|
||||
[features]
|
||||
tools = []
|
||||
20
zynq/.cargo/config.toml
Normal file
20
zynq/.cargo/config.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[target.armv7a-none-eabihf]
|
||||
runner = "just run"
|
||||
|
||||
rustflags = [
|
||||
"-Ctarget-cpu=cortex-a9",
|
||||
"-Ctarget-feature=+vfp3",
|
||||
"-Ctarget-feature=+neon",
|
||||
"-Clink-arg=-Tlink.x",
|
||||
# If this is not enabled, debugging / stepping can become problematic.
|
||||
"-Cforce-frame-pointers=yes",
|
||||
# Can be useful for debugging.
|
||||
# "-Clink-args=-Map=app.map"
|
||||
]
|
||||
|
||||
# Tier 3 target, so no pre-compiled artifacts included.
|
||||
[unstable]
|
||||
build-std = ["core", "alloc"]
|
||||
|
||||
[build]
|
||||
target = "armv7a-none-eabihf"
|
||||
1
zynq/.gitignore
vendored
Normal file
1
zynq/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Cargo.lock
|
||||
21
zynq/Cargo.toml
Normal file
21
zynq/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"zynq7000-rt",
|
||||
"zynq7000-mmu",
|
||||
"zynq7000",
|
||||
"zynq7000-hal",
|
||||
"zynq7000-embassy",
|
||||
|
||||
"examples/simple",
|
||||
"examples/embassy",
|
||||
"examples/zedboard",
|
||||
|
||||
"zedboard-bsp",
|
||||
"zedboard-qspi-flasher",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
# Exclude, can not be built with debug optimization level, too large.
|
||||
"zedboard-fsbl",
|
||||
]
|
||||
@@ -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 = { version = "0.3", features = ["critical-section-single-core"] }
|
||||
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||
zynq7000 = { path = "../../zynq7000" }
|
||||
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||
@@ -19,20 +19,36 @@ zynq7000-embassy = { path = "../../zynq7000-embassy" }
|
||||
dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev = "10319bdeae9ace3bb0fc79a15da2869c5bf50f52", features = ["async"] }
|
||||
static_cell = "2"
|
||||
critical-section = "1"
|
||||
heapless = "0.8"
|
||||
embedded-io = "0.6"
|
||||
heapless = "0.9"
|
||||
embedded-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
|
||||
embassy-executor = { git = "https://github.com/us-irs/embassy.git", branch = "cortex-ar-update", features = [
|
||||
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = [
|
||||
"arch-cortex-ar",
|
||||
"executor-thread",
|
||||
]}
|
||||
# TODO: Remove generic-queue-16 feature as soon as upstream executor is used again.
|
||||
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] }
|
||||
|
||||
[profile.release]
|
||||
# cargo build/run
|
||||
[profile.dev]
|
||||
# default is opt-level = '0', but that makes very
|
||||
# verbose machine code
|
||||
opt-level = 's'
|
||||
# trade compile speed for slightly better optimisations
|
||||
codegen-units = 1
|
||||
debug = true
|
||||
lto = true
|
||||
|
||||
# cargo build/run --release
|
||||
[profile.release]
|
||||
# default is opt-level = '3', but that makes quite
|
||||
# verbose machine code
|
||||
opt-level = 's'
|
||||
# trade compile speed for slightly better optimisations
|
||||
codegen-units = 1
|
||||
# Use Link Time Optimisations to further inline things across
|
||||
# crates
|
||||
lto = 'fat'
|
||||
# Leave the debug symbols in (default is no debug info)
|
||||
debug = 2
|
||||
31
zynq/examples/embassy/build.rs
Normal file
31
zynq/examples/embassy/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");
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
|
||||
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 MPU. This is
|
||||
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
|
||||
@@ -17,10 +17,10 @@ use zynq7000_hal::{
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
time::Hertz,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
use zynq7000::PsPeripherals;
|
||||
use zynq7000::Peripherals;
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
@@ -44,7 +44,7 @@ const OPEN_DRAIN_PINS_MIO9_TO_MIO14: bool = false;
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
@@ -64,12 +64,12 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -137,7 +137,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
|
||||
}
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
@@ -161,8 +161,8 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
@@ -181,22 +181,22 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -8,18 +8,8 @@ use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{Output, PinState, mio},
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
time::Hertz,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
use zynq7000_hal::{BootMode, InteruptConfig, clocks, gic, gpio, gtc, time::Hertz, uart};
|
||||
|
||||
use zynq7000::PsPeripherals;
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
@@ -37,32 +27,27 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
let mio_pins = mio::Pins::new(dp.gpio);
|
||||
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
|
||||
init_l2_cache: true,
|
||||
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
|
||||
interrupt_config: Some(InteruptConfig::AllInterruptsToCpu0),
|
||||
})
|
||||
.unwrap();
|
||||
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = GlobalTimerCounter::new(dp.gtc, clocks.arm_clocks());
|
||||
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
let mio_pins = gpio::mio::Pins::new(periphs.gpio);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
let mut uart = uart::Uart::new_with_mio(
|
||||
periphs.uart_1,
|
||||
uart::Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -77,11 +62,11 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||
let mut led = gpio::Output::new_for_mio(mio_pins.mio7, gpio::PinState::Low);
|
||||
loop {
|
||||
info!("Hello, world!");
|
||||
led.toggle().unwrap();
|
||||
@@ -89,42 +74,42 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = gic::GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
gic::Interrupt::Sgi(_) => (),
|
||||
gic::Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
gic::Interrupt::Spi(_spi_interrupt) => (),
|
||||
gic::Interrupt::Invalid(_) => (),
|
||||
gic::Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000::PsPeripherals;
|
||||
use zynq7000::Peripherals;
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
@@ -18,7 +18,7 @@ use zynq7000_hal::{
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
time::Hertz,
|
||||
uart::{ClockConfigRaw, TxAsync, Uart, UartConfig, on_interrupt_tx},
|
||||
uart::{ClockConfig, Config, TxAsync, Uart, on_interrupt_tx},
|
||||
};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
@@ -38,7 +38,7 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
#[unsafe(export_name = "main")]
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
@@ -58,12 +58,12 @@ async fn main(spawner: Spawner) -> ! {
|
||||
let mio_pins = mio::Pins::new(dp.gpio);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -75,11 +75,11 @@ async fn main(spawner: Spawner) -> ! {
|
||||
|
||||
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||
spawner.spawn(led_task(led)).unwrap();
|
||||
spawner.spawn(led_task(led).unwrap());
|
||||
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
|
||||
loop {
|
||||
@@ -123,22 +123,22 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -23,10 +23,10 @@ use zynq7000_hal::{
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
time::Hertz,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
use zynq7000::PsPeripherals;
|
||||
use zynq7000::Peripherals;
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
@@ -44,7 +44,7 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
@@ -71,12 +71,12 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
pwm.set_duty_cycle_percent(50).unwrap();
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -91,7 +91,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
@@ -111,8 +111,8 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
@@ -131,22 +131,22 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
95
zynq/examples/embassy/src/main.rs
Normal file
95
zynq/examples/embassy/src/main.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use log::error;
|
||||
use zynq7000_hal::{InteruptConfig, clocks, gic, gpio, gtc, time::Hertz};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
|
||||
init_l2_cache: true,
|
||||
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
|
||||
interrupt_config: Some(InteruptConfig::AllInterruptsToCpu0),
|
||||
})
|
||||
.unwrap();
|
||||
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
let mio_pins = gpio::mio::Pins::new(periphs.gpio);
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let mut led = gpio::Output::new_for_mio(mio_pins.mio7, gpio::PinState::Low);
|
||||
loop {
|
||||
led.toggle().unwrap();
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
pub fn irq_handler() {
|
||||
let mut gic_helper = gic::GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
gic::Interrupt::Sgi(_) => (),
|
||||
gic::Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
gic::Interrupt::Spi(_spi_interrupt) => (),
|
||||
gic::Interrupt::Invalid(_) => (),
|
||||
gic::Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[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) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
loop {}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "blinky"
|
||||
name = "simple"
|
||||
version = "0.1.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2024"
|
||||
@@ -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"
|
||||
31
zynq/examples/simple/build.rs
Normal file
31
zynq/examples/simple/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
zynq/examples/simple/memory.x
Normal file
22
zynq/examples/simple/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
|
||||
}
|
||||
@@ -4,25 +4,33 @@
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use zynq7000::PsPeripherals;
|
||||
use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin};
|
||||
use zynq7000::Peripherals;
|
||||
use zynq7000_hal::{
|
||||
clocks::Clocks,
|
||||
gpio::{Output, PinState, mio},
|
||||
l2_cache,
|
||||
priv_tim::CpuPrivateTimer,
|
||||
time::Hertz,
|
||||
};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
pub const LIB: Lib = Lib::Hal;
|
||||
|
||||
/// One user LED is MIO7
|
||||
const ZEDBOARD_LED_MASK: u32 = 1 << 7;
|
||||
|
||||
// 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);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Lib {
|
||||
Pac,
|
||||
Hal,
|
||||
}
|
||||
|
||||
const LIB: Lib = Lib::Hal;
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
@@ -48,38 +56,39 @@ pub fn main() -> ! {
|
||||
}
|
||||
}
|
||||
Lib::Hal => {
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
let dp = Peripherals::take().unwrap();
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Unwrap okay, we only call this once on core 0 here.
|
||||
let mut cpu_tim = CpuPrivateTimer::take(clocks.arm_clocks()).unwrap();
|
||||
let mio_pins = mio::Pins::new(dp.gpio);
|
||||
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::High);
|
||||
loop {
|
||||
led.toggle().unwrap();
|
||||
for _ in 0..5_000_000 {
|
||||
nop();
|
||||
}
|
||||
cpu_tim.delay_ms(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
pub fn irq_handler() {}
|
||||
fn irq_handler() {}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -15,13 +15,13 @@ use zynq7000_hal::{
|
||||
l2_cache,
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333);
|
||||
|
||||
static MS_TICKS: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
@@ -36,7 +36,7 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
|
||||
#[unsafe(export_name = "main")]
|
||||
pub fn main() -> ! {
|
||||
let mut dp = zynq7000::PsPeripherals::take().unwrap();
|
||||
let mut dp = zynq7000::Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
@@ -49,7 +49,7 @@ pub fn main() -> ! {
|
||||
// Enable interrupt exception.
|
||||
unsafe { gic.enable_interrupts() };
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut gtc = GlobalTimerCounter::new(dp.gtc, clocks.arm_clocks());
|
||||
@@ -64,7 +64,7 @@ pub fn main() -> ! {
|
||||
let mio_pins = mio::Pins::new(dp.gpio);
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -92,8 +92,8 @@ pub fn main() -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
@@ -110,22 +110,22 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -16,7 +16,7 @@ use zynq7000_hal::{
|
||||
l2_cache,
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
@@ -37,7 +37,7 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
|
||||
#[unsafe(export_name = "main")]
|
||||
pub fn main() -> ! {
|
||||
let mut dp = zynq7000::PsPeripherals::take().unwrap();
|
||||
let mut dp = zynq7000::Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
@@ -50,7 +50,7 @@ pub fn main() -> ! {
|
||||
// Enable interrupt exception.
|
||||
unsafe { gic.enable_interrupts() };
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut gtc = GlobalTimerCounter::new(dp.gtc, clocks.arm_clocks());
|
||||
@@ -64,7 +64,7 @@ pub fn main() -> ! {
|
||||
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -79,7 +79,7 @@ pub fn main() -> ! {
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {boot_mode:?}");
|
||||
|
||||
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||
@@ -94,8 +94,8 @@ pub fn main() -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
@@ -113,22 +113,22 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
55
zynq/examples/simple/src/main.rs
Normal file
55
zynq/examples/simple/src/main.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
//! Simple blinky app, showing a PAC variant and a HAL variant.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use zynq7000_rt as _;
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "main")]
|
||||
pub fn main() -> ! {
|
||||
loop {
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,17 @@ 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" }
|
||||
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"
|
||||
bitbybit = "1.3"
|
||||
arbitrary-int = "1.3"
|
||||
embedded-io = "0.7"
|
||||
bitbybit = "1.4"
|
||||
arbitrary-int = "2"
|
||||
embedded-io-async = "0.6"
|
||||
critical-section = "1"
|
||||
static_cell = "2"
|
||||
@@ -38,6 +40,7 @@ embassy-executor = { git = "https://github.com/us-irs/embassy.git", branch = "co
|
||||
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] }
|
||||
embassy-net = { version = "0.7", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp"] }
|
||||
embassy-sync = { version = "0.7" }
|
||||
# TODO: Bump as soon as new compatible smoltcp/embassy-net version is released.
|
||||
heapless = "0.8"
|
||||
axi-uartlite = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uartlite.git" }
|
||||
axi-uart16550 = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uart16550.git" }
|
||||
31
zynq/examples/zedboard/build.rs
Normal file
31
zynq/examples/zedboard/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
zynq/examples/zedboard/memory.x
Normal file
22
zynq/examples/zedboard/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
|
||||
}
|
||||
@@ -33,10 +33,8 @@ use embedded_io::Write;
|
||||
use embedded_io_async::Write as _;
|
||||
use log::{LevelFilter, debug, error, info, warn};
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use zedboard::{
|
||||
PS_CLOCK_FREQUENCY,
|
||||
phy_marvell::{LatchingLinkStatus, MARVELL_88E1518_OUI},
|
||||
};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zedboard_bsp::phy_marvell;
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
@@ -48,10 +46,10 @@ use zynq7000_hal::{
|
||||
gpio::{GpioPins, Output, PinState},
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000::{Peripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt::{self as _, mmu::section_attrs::SHAREABLE_DEVICE, mmu_l1_table_mut};
|
||||
|
||||
const USE_DHCP: bool = true;
|
||||
@@ -72,8 +70,8 @@ const INIT_STRING: &str = "-- Zynq 7000 Zedboard Ethernet Example --\n\r";
|
||||
// Unicast address with OUI of the Marvell 88E1518 PHY.
|
||||
const MAC_ADDRESS: [u8; 6] = [
|
||||
0x00,
|
||||
((MARVELL_88E1518_OUI >> 8) & 0xff) as u8,
|
||||
(MARVELL_88E1518_OUI & 0xff) as u8,
|
||||
((phy_marvell::MARVELL_88E1518_OUI >> 8) & 0xff) as u8,
|
||||
(phy_marvell::MARVELL_88E1518_OUI & 0xff) as u8,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
@@ -208,7 +206,7 @@ async fn tcp_task(mut tcp: TcpSocket<'static>) -> ! {
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Enable PS-PL level shifters.
|
||||
@@ -236,12 +234,12 @@ async fn main(spawner: Spawner) -> ! {
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -249,7 +247,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::uart_blocking::init_unsafe_single_core(uart, LOG_LEVEL, false) };
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
static ETH_RX_BUFS: static_cell::ConstStaticCell<[AlignedBuffer; NUM_RX_SLOTS]> =
|
||||
@@ -317,9 +315,8 @@ async fn main(spawner: Spawner) -> ! {
|
||||
eth.set_rx_buf_descriptor_base_address(rx_descr_ref.base_addr());
|
||||
eth.set_tx_buf_descriptor_base_address(tx_descr_ref.base_addr());
|
||||
eth.start();
|
||||
let (mut phy, phy_rev) =
|
||||
zedboard::phy_marvell::Marvell88E1518Phy::new_autoprobe_addr(eth.mdio_mut())
|
||||
.expect("could not auto-detect phy");
|
||||
let (mut phy, phy_rev) = phy_marvell::Marvell88E1518Phy::new_autoprobe_addr(eth.mdio_mut())
|
||||
.expect("could not auto-detect phy");
|
||||
info!(
|
||||
"Detected Marvell 88E1518 PHY with revision number: {:?}",
|
||||
phy_rev
|
||||
@@ -456,7 +453,8 @@ async fn main(spawner: Spawner) -> ! {
|
||||
IpMode::StackReady => {
|
||||
let status = phy.read_copper_status();
|
||||
// Periodically check for link changes.
|
||||
if status.copper_link_status() == LatchingLinkStatus::DownSinceLastRead {
|
||||
if status.copper_link_status() == phy_marvell::LatchingLinkStatus::DownSinceLastRead
|
||||
{
|
||||
warn!("ethernet link is down.");
|
||||
ip_mode = IpMode::LinkDown;
|
||||
continue;
|
||||
@@ -467,8 +465,8 @@ async fn main(spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
@@ -499,22 +497,22 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -29,7 +29,7 @@ use zynq7000_hal::{
|
||||
uart,
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000::{Peripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
@@ -48,7 +48,7 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Enable PS-PL level shifters.
|
||||
@@ -73,12 +73,12 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = uart::ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = uart::Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
uart::UartConfig::new_with_clk_config(uart_clk_config),
|
||||
uart::Config::new_with_clk_config(uart_clk_config),
|
||||
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -93,7 +93,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let pin_sel = match I2C_ADDR_SEL {
|
||||
@@ -30,7 +30,7 @@ use zynq7000_hal::{
|
||||
uart::{self, TxAsync, on_interrupt_tx},
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig, spi::DelayControl};
|
||||
use zynq7000::{Peripherals, slcr::LevelShifterConfig, spi::DelayControl};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
@@ -51,7 +51,7 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Enable PS-PL level shifters.
|
||||
@@ -83,12 +83,12 @@ async fn main(spawner: Spawner) -> ! {
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = uart::ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut uart = uart::Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
uart::UartConfig::new_with_clk_config(uart_clk_config),
|
||||
uart::Config::new_with_clk_config(uart_clk_config),
|
||||
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -96,7 +96,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
.unwrap();
|
||||
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
if DEBUG_SPI_CLK_CONFIG {
|
||||
211
zynq/examples/zedboard/src/bin/qspi.rs
Normal file
211
zynq/examples/zedboard/src/bin/qspi.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zedboard_bsp::qspi_spansion;
|
||||
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, prelude::*, qspi, uart};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard QSPI example --\n\r";
|
||||
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 ERASE_PROGRAM_READ_TEST: bool = false;
|
||||
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
|
||||
init_l2_cache: true,
|
||||
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
|
||||
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
|
||||
})
|
||||
.unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
let gpio_pins = gpio::GpioPins::new(periphs.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// 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(
|
||||
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::Trace,
|
||||
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,
|
||||
embedded_hal::spi::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 rdid = spansion_qspi.read_rdid_extended();
|
||||
info!(
|
||||
"QSPI Info: manufacturer ID: {:?}, interface type: {:?}, density: {:?}, sector arch: {:?}, model number: {:?}",
|
||||
rdid.base_id().manufacturer_id(),
|
||||
rdid.base_id().memory_interface_type(),
|
||||
rdid.base_id().density(),
|
||||
rdid.sector_arch(),
|
||||
rdid.model_number()
|
||||
);
|
||||
let cr1 = spansion_qspi.read_configuration_register();
|
||||
info!("QSPI Configuration Register 1: {:?}", cr1);
|
||||
|
||||
let mut write_buf: [u8; u8::MAX as usize + 1] = [0x0; u8::MAX as usize + 1];
|
||||
for (idx, byte) in write_buf.iter_mut().enumerate() {
|
||||
*byte = idx as u8;
|
||||
}
|
||||
let mut read_buf = [0u8; 256];
|
||||
|
||||
if ERASE_PROGRAM_READ_TEST {
|
||||
info!("performing erase, program, read test");
|
||||
spansion_qspi
|
||||
.erase_sector(0x10000)
|
||||
.expect("erasing sector failed");
|
||||
spansion_qspi.read_page_fast_read(0x10000, &mut read_buf, true);
|
||||
for read in read_buf.iter() {
|
||||
assert_eq!(*read, 0xFF);
|
||||
}
|
||||
read_buf.fill(0);
|
||||
spansion_qspi.program_page(0x10000, &write_buf).unwrap();
|
||||
spansion_qspi.read_page_fast_read(0x10000, &mut read_buf, true);
|
||||
for (read, written) in read_buf.iter().zip(write_buf.iter()) {
|
||||
assert_eq!(read, written);
|
||||
}
|
||||
info!("test successful");
|
||||
}
|
||||
|
||||
let mut spansion_lqspi = spansion_qspi.into_linear_addressed(QSPI_DEV_COMBINATION.into());
|
||||
|
||||
let guard = spansion_lqspi.read_guard();
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
(qspi::QSPI_START_ADDRESS + 0x10000) as *const u8,
|
||||
read_buf.as_mut_ptr(),
|
||||
256,
|
||||
);
|
||||
}
|
||||
drop(guard);
|
||||
if ERASE_PROGRAM_READ_TEST {
|
||||
for (read, written) in read_buf.iter().zip(write_buf.iter()) {
|
||||
assert_eq!(
|
||||
read, written,
|
||||
"linear read failure, read and write missmatch"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||
|
||||
let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = gic::GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
gic::Interrupt::Sgi(_) => (),
|
||||
gic::Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
gic::Interrupt::Spi(_spi_interrupt) => (),
|
||||
gic::Interrupt::Invalid(_) => (),
|
||||
gic::Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[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) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
loop {}
|
||||
}
|
||||
@@ -20,10 +20,10 @@ use zynq7000_hal::{
|
||||
gpio::{GpioPins, Output, PinState},
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000::{Peripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard blocking UART example --\n\r";
|
||||
@@ -101,7 +101,7 @@ impl UartMultiplexer {
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Enable PS-PL level shifters.
|
||||
@@ -123,12 +123,12 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut log_uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -145,7 +145,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
|
||||
// UART0 routed through EMIO to PL pins.
|
||||
let mut uart_0 =
|
||||
Uart::new_with_emio(dp.uart_0, UartConfig::new_with_clk_config(uart_clk_config)).unwrap();
|
||||
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart_clk_config)).unwrap();
|
||||
// Safety: Valid address of AXI UARTLITE.
|
||||
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
|
||||
|
||||
@@ -160,7 +160,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
@@ -218,8 +218,8 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
@@ -238,22 +238,22 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -47,7 +47,7 @@ use zynq7000_hal::{
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
time::Hertz,
|
||||
uart::{ClockConfigRaw, Uart, UartConfig},
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
pub enum UartMode {
|
||||
@@ -62,7 +62,7 @@ const INIT_STRING: &str = "-- Zynq 7000 Zedboard non-blocking UART example --\n\
|
||||
#[global_allocator]
|
||||
static HEAP: Heap = Heap::empty();
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000::{Peripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
@@ -166,7 +166,7 @@ impl UartMultiplexer {
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
let mut dp = PsPeripherals::take().unwrap();
|
||||
let mut dp = Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// Enable PS-PL level shifters.
|
||||
@@ -194,12 +194,12 @@ async fn main(spawner: Spawner) -> ! {
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut log_uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -237,7 +237,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
|
||||
// UART0 routed through EMIO to PL pins.
|
||||
let uart_0 =
|
||||
Uart::new_with_emio(dp.uart_0, UartConfig::new_with_clk_config(uart_clk_config)).unwrap();
|
||||
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart_clk_config)).unwrap();
|
||||
// Safety: Valid address of AXI UARTLITE.
|
||||
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
|
||||
// We need to call this before splitting the structure, because the interrupt signal is
|
||||
@@ -254,7 +254,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||
@@ -432,8 +432,8 @@ async fn uart_16550_task(uart_tx: axi_uart16550::Tx) {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
|
||||
@@ -465,7 +465,7 @@ pub extern "C" fn _irq_handler() {
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
pub fn on_interrupt_axi_uartlite() {
|
||||
fn on_interrupt_axi_uartlite() {
|
||||
let mut buf = [0; axi_uartlite::FIFO_DEPTH];
|
||||
let mut rx = unsafe { axi_uartlite::Rx::steal(AXI_UARTLITE_BASE_ADDR as usize) };
|
||||
// Handle RX first: Empty the FIFO content into local buffer.
|
||||
@@ -485,7 +485,7 @@ pub fn on_interrupt_axi_uartlite() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_interrupt_axi_16550() {
|
||||
fn on_interrupt_axi_16550() {
|
||||
let mut buf = [0; axi_uart16550::FIFO_DEPTH];
|
||||
let mut read_bytes = 0;
|
||||
let mut rx = unsafe { axi_uart16550::Rx::steal(AXI_UAR16550_BASE_ADDR as usize) };
|
||||
@@ -545,22 +545,22 @@ fn on_interrupt_uart_0() {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
#[zynq7000_rt::exception(DataAbort)]
|
||||
fn data_abort_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
#[zynq7000_rt::exception(Undefined)]
|
||||
fn undefined_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
#[zynq7000_rt::exception(PrefetchAbort)]
|
||||
fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
use zynq7000_hal::time::Hertz;
|
||||
pub mod phy_marvell;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
pub const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333);
|
||||
140
zynq/examples/zedboard/src/main.rs
Normal file
140
zynq/examples/zedboard/src/main.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, uart};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r";
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
|
||||
init_l2_cache: true,
|
||||
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
|
||||
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
|
||||
})
|
||||
.unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
let mut gpio_pins = gpio::GpioPins::new(periphs.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// 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(
|
||||
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::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||
|
||||
let mut mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
|
||||
let mut emio_leds: [gpio::Output; 8] = [
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), gpio::PinState::Low),
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), gpio::PinState::Low),
|
||||
];
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
// Create a wave pattern for emio_leds
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.toggle().unwrap();
|
||||
ticker.next().await; // Wait for the next ticker for each toggle
|
||||
}
|
||||
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = gic::GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
gic::Interrupt::Sgi(_) => (),
|
||||
gic::Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
gic::Interrupt::Spi(_spi_interrupt) => (),
|
||||
gic::Interrupt::Invalid(_) => (),
|
||||
gic::Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[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) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
loop {}
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
[toolchain]
|
||||
# channel = "stable"
|
||||
channel = "nightly"
|
||||
12
zynq/zedboard-bsp/Cargo.toml
Normal file
12
zynq/zedboard-bsp/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "zedboard-bsp"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
zynq7000 = { path = "../zynq7000" }
|
||||
zynq7000-hal = { path = "../zynq7000-hal" }
|
||||
bitbybit = "1.4"
|
||||
arbitrary-int = "2"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
thiserror = { version = "2", default-features = false }
|
||||
100
zynq/zedboard-bsp/src/ddrc_config_autogen.rs
Normal file
100
zynq/zedboard-bsp/src/ddrc_config_autogen.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
# program."]
|
||||
#![doc = r""]
|
||||
#![doc = r"This configuration file contains static DDR configuration parameters extracted from the"]
|
||||
#![doc = r"AMD ps7init.tcl file"]
|
||||
use zynq7000::ddrc::regs;
|
||||
use zynq7000_hal::ddr::DdrcConfigSet;
|
||||
pub const DDRC_CONFIG_ZEDBOARD: DdrcConfigSet = DdrcConfigSet {
|
||||
ctrl: regs::DdrcControl::new_with_raw_value(0x00000080),
|
||||
two_rank: regs::TwoRankConfig::new_with_raw_value(0x00001082),
|
||||
hpr: regs::LprHprQueueControl::new_with_raw_value(0x03c0780f),
|
||||
lpr: regs::LprHprQueueControl::new_with_raw_value(0x02001001),
|
||||
wr: regs::WriteQueueControl::new_with_raw_value(0x00014001),
|
||||
dram_param_0: regs::DramParamReg0::new_with_raw_value(0x0004159b),
|
||||
dram_param_1: regs::DramParamReg1::new_with_raw_value(0x44e458d3),
|
||||
dram_param_2: regs::DramParamReg2::new_with_raw_value(0x7282bce5),
|
||||
dram_param_3: regs::DramParamReg3::new_with_raw_value(0x270872d0),
|
||||
dram_param_4: regs::DramParamReg4::new_with_raw_value(0x00000000),
|
||||
dram_init_param: regs::DramInitParam::new_with_raw_value(0x00002007),
|
||||
dram_emr: regs::DramEmr::new_with_raw_value(0x00000008),
|
||||
dram_emr_mr: regs::DramEmrMr::new_with_raw_value(0x00040b30),
|
||||
dram_burst8_rdwr: regs::DramBurst8ReadWrite::new_with_raw_value(0x000116d4),
|
||||
disable_dq: regs::DisableDq::new_with_raw_value(0x00000000),
|
||||
dram_addr_map_bank: regs::DramAddrMapBank::new_with_raw_value(0x00000777),
|
||||
dram_addr_map_col: regs::DramAddrMapColumn::new_with_raw_value(0xfff00000),
|
||||
dram_addr_map_row: regs::DramAddrMapRow::new_with_raw_value(0x0ff66666),
|
||||
dram_odt: regs::DramOdt::new_with_raw_value(0x0003c008),
|
||||
phy_cmd_timeout_rddata_cpt: regs::PhyCmdTimeoutRdDataCpt::new_with_raw_value(0x77010800),
|
||||
dll_calib: regs::DllCalib::new_with_raw_value(0x00000000),
|
||||
odt_delay_hold: regs::OdtDelayHold::new_with_raw_value(0x00005003),
|
||||
ctrl_reg1: regs::CtrlReg1::new_with_raw_value(0x0000003e),
|
||||
ctrl_reg2: regs::CtrlReg2::new_with_raw_value(0x00020000),
|
||||
ctrl_reg3: regs::CtrlReg3::new_with_raw_value(0x00284141),
|
||||
ctrl_reg4: regs::CtrlReg4::new_with_raw_value(0x00001610),
|
||||
ctrl_reg5: regs::CtrlReg5::new_with_raw_value(0x00466111),
|
||||
ctrl_reg6: regs::CtrlReg6::new_with_raw_value(0x00032222),
|
||||
che_t_zq: regs::CheTZq::new_with_raw_value(0x10200802),
|
||||
che_t_zq_short_interval_reg: regs::CheTZqShortInterval::new_with_raw_value(0x10200802),
|
||||
deep_powerdown: regs::DeepPowerdown::new_with_raw_value(0x000001fe),
|
||||
reg_2c: regs::Reg2c::new_with_raw_value(0x1cffffff),
|
||||
reg_2d: regs::Reg2d::new_with_raw_value(0x00000200),
|
||||
dfi_timing: regs::DfiTiming::new_with_raw_value(0x00200066),
|
||||
che_ecc_ctrl: regs::CheEccControl::new_with_raw_value(0x00000000),
|
||||
ecc_scrub: regs::EccScrub::new_with_raw_value(0x00000008),
|
||||
phy_receiver_enable: regs::PhyReceiverEnable::new_with_raw_value(0x00000000),
|
||||
phy_config: [
|
||||
regs::PhyConfig::new_with_raw_value(0x40000001),
|
||||
regs::PhyConfig::new_with_raw_value(0x40000001),
|
||||
regs::PhyConfig::new_with_raw_value(0x40000001),
|
||||
regs::PhyConfig::new_with_raw_value(0x40000001),
|
||||
],
|
||||
phy_init_ratio: [
|
||||
regs::PhyInitRatio::new_with_raw_value(0x00033c03),
|
||||
regs::PhyInitRatio::new_with_raw_value(0x00034003),
|
||||
regs::PhyInitRatio::new_with_raw_value(0x0002f400),
|
||||
regs::PhyInitRatio::new_with_raw_value(0x00030400),
|
||||
],
|
||||
phy_rd_dqs_config: [
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x00000035),
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x00000035),
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x00000035),
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x00000035),
|
||||
],
|
||||
phy_wr_dqs_config: [
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x00000083),
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x00000083),
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x0000007f),
|
||||
regs::PhyDqsConfig::new_with_raw_value(0x00000078),
|
||||
],
|
||||
phy_we_cfg: [
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(0x00000124),
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(0x00000125),
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(0x00000112),
|
||||
regs::PhyWriteEnableConfig::new_with_raw_value(0x00000116),
|
||||
],
|
||||
phy_wr_data_slv: [
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(0x000000c3),
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(0x000000c3),
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(0x000000bf),
|
||||
regs::PhyWriteDataSlaveConfig::new_with_raw_value(0x000000b8),
|
||||
],
|
||||
reg64: regs::Reg64::new_with_raw_value(0x00040080),
|
||||
reg65: regs::Reg65::new_with_raw_value(0x0001fc82),
|
||||
page_mask: 0x00000000,
|
||||
axi_priority_wr_port: [
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(0x000003ff),
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(0x000003ff),
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(0x000003ff),
|
||||
regs::AxiPriorityWritePort::new_with_raw_value(0x000003ff),
|
||||
],
|
||||
axi_priority_rd_port: [
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(0x000003ff),
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(0x000003ff),
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(0x000003ff),
|
||||
regs::AxiPriorityReadPort::new_with_raw_value(0x000003ff),
|
||||
],
|
||||
lpddr_ctrl_0: regs::LpddrControl0::new_with_raw_value(0x00000000),
|
||||
lpddr_ctrl_1: regs::LpddrControl1::new_with_raw_value(0x00000000),
|
||||
lpddr_ctrl_2: regs::LpddrControl2::new_with_raw_value(0x00005125),
|
||||
lpddr_ctrl_3: regs::LpddrControl3::new_with_raw_value(0x000012a8),
|
||||
};
|
||||
15
zynq/zedboard-bsp/src/ddriob_config_autogen.rs
Normal file
15
zynq/zedboard-bsp/src/ddriob_config_autogen.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
# program."]
|
||||
#![doc = r""]
|
||||
#![doc = r"This configuration file contains static DDRIOB configuration parameters extracted from the"]
|
||||
#![doc = r"AMD ps7init.tcl file"]
|
||||
use zynq7000::ddrc::regs;
|
||||
use zynq7000_hal::ddr::DdriobConfigSet;
|
||||
pub const DDRIOB_CONFIG_SET_ZEDBOARD: DdriobConfigSet = DdriobConfigSet {
|
||||
addr0: regs::DdriobConfig::new_with_raw_value(0x00000600),
|
||||
addr1: regs::DdriobConfig::new_with_raw_value(0x00000600),
|
||||
data0: regs::DdriobConfig::new_with_raw_value(0x00000672),
|
||||
data1: regs::DdriobConfig::new_with_raw_value(0x00000672),
|
||||
diff0: regs::DdriobConfig::new_with_raw_value(0x00000674),
|
||||
diff1: regs::DdriobConfig::new_with_raw_value(0x00000674),
|
||||
clock: regs::DdriobConfig::new_with_raw_value(0x00000600),
|
||||
};
|
||||
6
zynq/zedboard-bsp/src/lib.rs
Normal file
6
zynq/zedboard-bsp/src/lib.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod ddrc_config_autogen;
|
||||
pub mod ddriob_config_autogen;
|
||||
pub mod phy_marvell;
|
||||
pub mod qspi_spansion;
|
||||
630
zynq/zedboard-bsp/src/qspi_spansion.rs
Normal file
630
zynq/zedboard-bsp/src/qspi_spansion.rs
Normal file
@@ -0,0 +1,630 @@
|
||||
use core::cell::RefCell;
|
||||
|
||||
use arbitrary_int::{prelude::*, u24};
|
||||
use zynq7000_hal::qspi::{
|
||||
FIFO_DEPTH, LinearQspiConfig, QspiIoMode, QspiIoTransferGuard, QspiLinearAddressing,
|
||||
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
|
||||
WriteRegisters = 0x01,
|
||||
/// PP
|
||||
PageProgram = 0x02,
|
||||
/// READ
|
||||
Read = 0x03,
|
||||
/// WRDI
|
||||
WriteDisable = 0x04,
|
||||
/// RDSR1
|
||||
ReadStatus1 = 0x05,
|
||||
/// RDSR2
|
||||
ReadStatus2 = 0x07,
|
||||
/// WREN
|
||||
WriteEnable = 0x06,
|
||||
/// FAST_READ
|
||||
FastRead = 0x0B,
|
||||
/// SE
|
||||
SectorErase = 0xD8,
|
||||
/// CSLR
|
||||
ClearStatus = 0x30,
|
||||
/// RDCR
|
||||
ReadConfig = 0x35,
|
||||
/// RDID
|
||||
ReadId = 0x9F,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum MemoryInterfaceType {
|
||||
_128Mb = 0x20,
|
||||
_256Mb = 0x02,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Density {
|
||||
_128Mb = 0x18,
|
||||
_256Mb = 0x19,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, num_enum::TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum SectorArchictecture {
|
||||
/// Uniform 256 kB sectors.
|
||||
Uniform = 0x00,
|
||||
/// 32 4kB sectors and 64 kB sectors.
|
||||
Hybrid = 0x01,
|
||||
}
|
||||
|
||||
pub const PAGE_SIZE: usize = 256;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BaseDeviceId {
|
||||
manufacturer_id: u8,
|
||||
device_id: u16,
|
||||
}
|
||||
|
||||
impl BaseDeviceId {
|
||||
#[inline]
|
||||
pub const fn new(manufacturer_id: u8, device_id: u16) -> Self {
|
||||
BaseDeviceId {
|
||||
manufacturer_id,
|
||||
device_id,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn from_raw(raw: &[u8; 3]) -> Self {
|
||||
BaseDeviceId::new(raw[0], ((raw[1] as u16) << 8) | raw[2] as u16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn manufacturer_id(&self) -> u8 {
|
||||
self.manufacturer_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn device_id_raw(&self) -> u16 {
|
||||
self.device_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn memory_interface_type(&self) -> Result<MemoryInterfaceType, u8> {
|
||||
MemoryInterfaceType::try_from(((self.device_id >> 8) & 0xff) as u8).map_err(|e| e.number)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn density(&self) -> Result<Density, u8> {
|
||||
Density::try_from((self.device_id & 0xff) as u8).map_err(|e| e.number)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExtendedDeviceId {
|
||||
base: BaseDeviceId,
|
||||
id_cfi_len: u8,
|
||||
sector_arch: u8,
|
||||
family_id: u8,
|
||||
model_number: [u8; 2],
|
||||
}
|
||||
|
||||
impl ExtendedDeviceId {
|
||||
pub const fn from_raw(raw: &[u8; 8]) -> Self {
|
||||
ExtendedDeviceId {
|
||||
base: BaseDeviceId::from_raw(&[raw[0], raw[1], raw[2]]),
|
||||
id_cfi_len: raw[3],
|
||||
sector_arch: raw[4],
|
||||
family_id: raw[5],
|
||||
model_number: [raw[6], raw[7]],
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn id_cfi_len(&self) -> u8 {
|
||||
self.id_cfi_len
|
||||
}
|
||||
|
||||
pub const fn sector_arch_raw(&self) -> u8 {
|
||||
self.sector_arch
|
||||
}
|
||||
|
||||
pub fn sector_arch(&self) -> Result<SectorArchictecture, u8> {
|
||||
SectorArchictecture::try_from(self.sector_arch_raw()).map_err(|e| e.number)
|
||||
}
|
||||
|
||||
pub const fn family_id(&self) -> u8 {
|
||||
self.family_id
|
||||
}
|
||||
|
||||
pub const fn model_number_raw(&self) -> &[u8; 2] {
|
||||
&self.model_number
|
||||
}
|
||||
|
||||
pub const fn model_number(&self) -> [char; 2] {
|
||||
[self.model_number[0] as char, self.model_number[1] as char]
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedDeviceId {
|
||||
#[inline]
|
||||
pub fn base_id(&self) -> BaseDeviceId {
|
||||
self.base
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u8)]
|
||||
#[derive(Debug)]
|
||||
pub struct StatusRegister1 {
|
||||
#[bit(7, rw)]
|
||||
status_register_write_disable: bool,
|
||||
#[bit(6, r)]
|
||||
programming_error: bool,
|
||||
#[bit(5, r)]
|
||||
erase_error: bool,
|
||||
#[bit(4, r)]
|
||||
bp_2: bool,
|
||||
#[bit(3, r)]
|
||||
bp_1: bool,
|
||||
#[bit(2, r)]
|
||||
bp_0: bool,
|
||||
#[bit(1, r)]
|
||||
write_enable_latch: bool,
|
||||
#[bit(0, r)]
|
||||
write_in_progress: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u8)]
|
||||
#[derive(Debug)]
|
||||
pub struct ConfigRegister1 {
|
||||
#[bit(7, rw)]
|
||||
latency_code_1: bool,
|
||||
#[bit(6, rw)]
|
||||
latency_code_0: bool,
|
||||
/// This is an OTP bit. It can not be set back to 0 once it has been set to 1!
|
||||
#[bit(5, rw)]
|
||||
tbprot: bool,
|
||||
#[bit(3, rw)]
|
||||
bpnv: bool,
|
||||
/// This is an OTP bit. It can not be set back to 0 once it has been set to 1!
|
||||
#[bit(2, rw)]
|
||||
tbparm: bool,
|
||||
#[bit(1, rw)]
|
||||
quad: bool,
|
||||
#[bit(0, rw)]
|
||||
freeze: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
pub enum AddrError {
|
||||
#[error("address out of range")]
|
||||
OutOfRange,
|
||||
#[error("address not aligned")]
|
||||
Alignment,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
pub enum EraseError {
|
||||
#[error("erase error bit set in status register")]
|
||||
EraseErrorBitSet,
|
||||
#[error("address error: {0}")]
|
||||
Addr(#[from] AddrError),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||
pub enum ProgramPageError {
|
||||
#[error("programming error bit set in status register")]
|
||||
ProgrammingErrorBitSet,
|
||||
#[error("address error: {0}")]
|
||||
Addr(#[from] AddrError),
|
||||
#[error("data is larger than page size {PAGE_SIZE}")]
|
||||
DataLargerThanPage,
|
||||
}
|
||||
|
||||
pub struct QspiSpansionS25Fl256SIoMode(RefCell<QspiIoMode>);
|
||||
|
||||
impl QspiSpansionS25Fl256SIoMode {
|
||||
pub fn new(qspi: QspiIoMode, set_quad_bit_if_necessary: bool) -> Self {
|
||||
let mut spansion_qspi = QspiSpansionS25Fl256SIoMode(RefCell::new(qspi));
|
||||
if set_quad_bit_if_necessary {
|
||||
let mut cr1 = spansion_qspi.read_configuration_register();
|
||||
if cr1.quad() {
|
||||
// Quad bit is already set.
|
||||
return spansion_qspi;
|
||||
}
|
||||
cr1.set_quad(true);
|
||||
// Preserve the status register by reading it first.
|
||||
let sr1 = spansion_qspi.read_status_register_1();
|
||||
// Safety: Only the QUAD bit was set while all other bits are preserved.
|
||||
unsafe {
|
||||
spansion_qspi.write_status_and_config_register(sr1, cr1);
|
||||
}
|
||||
}
|
||||
spansion_qspi
|
||||
}
|
||||
|
||||
pub fn into_linear_addressed(
|
||||
self,
|
||||
config: LinearQspiConfig,
|
||||
) -> QspiSpansionS25Fl256SLinearMode {
|
||||
let qspi = self.0.into_inner().into_linear_addressed(config);
|
||||
QspiSpansionS25Fl256SLinearMode(qspi)
|
||||
}
|
||||
|
||||
pub fn write_enable(&mut self) {
|
||||
let qspi = self.0.get_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_01(RegisterId::WriteEnable as u32);
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
transfer.read_rx_data();
|
||||
}
|
||||
|
||||
pub fn write_disable(&mut self) {
|
||||
let qspi = self.0.get_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_01(RegisterId::WriteDisable as u32);
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
transfer.read_rx_data();
|
||||
}
|
||||
|
||||
/// Write a new value for the status register.
|
||||
///
|
||||
/// This API may not be used if the QUAD bit (CR1\[1\]) is set.
|
||||
pub fn write_status(&mut self, sr: StatusRegister1) {
|
||||
self.write_enable();
|
||||
let mut qspi = self.0.borrow_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_10(u32::from_ne_bytes([
|
||||
RegisterId::WriteRegisters as u8,
|
||||
sr.raw_value(),
|
||||
0x00,
|
||||
0x00,
|
||||
]));
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
transfer.read_rx_data();
|
||||
}
|
||||
|
||||
/// Write a new value for the status register. It is strongly recommended to read both
|
||||
/// the status and config register first and preserve all unchanged bits.
|
||||
///
|
||||
/// This API must be used if the QUAD bit (CR1\[1\]) is set.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Misuse of this API does not lead to undefined behavior. However, it writes the
|
||||
/// configuration register, which as OTP bits. Changing these bits from 0 to 1 is an
|
||||
/// irreversible operation.
|
||||
pub unsafe fn write_status_and_config_register(
|
||||
&mut self,
|
||||
sr: StatusRegister1,
|
||||
cr: ConfigRegister1,
|
||||
) {
|
||||
self.write_enable();
|
||||
let mut qspi = self.0.borrow_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_11(u32::from_ne_bytes([
|
||||
RegisterId::WriteRegisters as u8,
|
||||
sr.raw_value(),
|
||||
cr.raw_value(),
|
||||
0x00,
|
||||
]));
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
transfer.read_rx_data();
|
||||
}
|
||||
|
||||
pub fn read_status_register_1(&self) -> StatusRegister1 {
|
||||
let mut qspi = self.0.borrow_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_10(RegisterId::ReadStatus1 as u32);
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
let reply = transfer.read_rx_data();
|
||||
drop(transfer);
|
||||
// little-endian architecture, so the second byte received is the MSB.
|
||||
StatusRegister1::new_with_raw_value(((reply >> 24) & 0xff) as u8)
|
||||
}
|
||||
|
||||
pub fn read_configuration_register(&self) -> ConfigRegister1 {
|
||||
let mut qspi = self.0.borrow_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_10(RegisterId::ReadConfig as u32);
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
let reply = transfer.read_rx_data();
|
||||
drop(transfer);
|
||||
// little-endian architecture, so the second byte received is the MSB.
|
||||
ConfigRegister1::new_with_raw_value(((reply >> 24) & 0xff) as u8)
|
||||
}
|
||||
|
||||
pub fn clear_status(&mut self) {
|
||||
let qspi = self.0.get_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_01(RegisterId::ClearStatus as u32);
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
transfer.read_rx_data();
|
||||
}
|
||||
|
||||
pub fn read_rdid_base(&self) -> BaseDeviceId {
|
||||
let mut qspi = self.0.borrow_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_00(RegisterId::ReadId as u32);
|
||||
transfer.start();
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
let reply = transfer.read_rx_data();
|
||||
drop(transfer);
|
||||
BaseDeviceId::from_raw(reply.to_ne_bytes()[1..].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn read_rdid_extended(&self) -> ExtendedDeviceId {
|
||||
let mut reply: [u8; 12] = [0; 12];
|
||||
let mut qspi = self.0.borrow_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
transfer.write_word_txd_00(RegisterId::ReadId as u32);
|
||||
transfer.write_word_txd_00(0x00);
|
||||
transfer.write_word_txd_00(0x00);
|
||||
transfer.start();
|
||||
let mut read_index = 0;
|
||||
|
||||
while read_index < 3 {
|
||||
if transfer.read_status().rx_above_threshold() {
|
||||
reply[read_index * 4..(read_index + 1) * 4]
|
||||
.copy_from_slice(&transfer.read_rx_data().to_ne_bytes());
|
||||
read_index += 1;
|
||||
}
|
||||
}
|
||||
ExtendedDeviceId::from_raw(reply[1..9].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// This function will block until the operation has completed.
|
||||
pub fn erase_sector(&mut self, addr: u32) -> Result<(), EraseError> {
|
||||
if addr + 0x10000 > u24::MAX.as_u32() {
|
||||
return Err(AddrError::OutOfRange.into());
|
||||
}
|
||||
if !addr.is_multiple_of(0x10000) {
|
||||
return Err(AddrError::Alignment.into());
|
||||
}
|
||||
self.write_enable();
|
||||
let qspi = self.0.get_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
let raw_word: [u8; 4] = [
|
||||
RegisterId::SectorErase as u8,
|
||||
((addr >> 16) & 0xff) as u8,
|
||||
((addr >> 8) & 0xff) as u8,
|
||||
(addr & 0xff) as u8,
|
||||
];
|
||||
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
|
||||
transfer.start();
|
||||
|
||||
// Finish transfer
|
||||
while !transfer.read_status().rx_above_threshold() {}
|
||||
transfer.read_rx_data();
|
||||
|
||||
// Drive CS high to initiate the sector erase operation.
|
||||
drop(transfer);
|
||||
|
||||
// Now poll for completion.
|
||||
loop {
|
||||
let rdsr1 = self.read_status_register_1();
|
||||
if rdsr1.erase_error() {
|
||||
// The datasheet mentions that the status should be cleared and writes
|
||||
// should be disabled explicitely.
|
||||
self.clear_status();
|
||||
self.write_disable();
|
||||
return Err(EraseError::EraseErrorBitSet);
|
||||
}
|
||||
if !rdsr1.write_in_progress() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function also takes care of enabling writes before programming the page.
|
||||
/// This function will block until the operation has completed.
|
||||
///
|
||||
/// 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();
|
||||
let raw_word: [u8; 4] = [
|
||||
RegisterId::PageProgram as u8,
|
||||
((addr >> 16) & 0xff) as u8,
|
||||
((addr >> 8) & 0xff) as u8,
|
||||
(addr & 0xff) as u8,
|
||||
];
|
||||
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..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()
|
||||
.unwrap(),
|
||||
));
|
||||
current_byte_index += 4;
|
||||
}
|
||||
transfer.start();
|
||||
|
||||
let mut wait_for_tx_slot = |transfer: &mut QspiIoTransferGuard| loop {
|
||||
let status = transfer.read_status();
|
||||
if status.rx_above_threshold() {
|
||||
transfer.read_rx_data();
|
||||
read_index = read_index.wrapping_add(4);
|
||||
}
|
||||
if !status.tx_full() {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
while current_byte_index < data.len() {
|
||||
// Immediately fill the FIFO again with the remaining 8 bytes.
|
||||
wait_for_tx_slot(&mut transfer);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
drop(transfer);
|
||||
|
||||
// Now poll for completion.
|
||||
loop {
|
||||
let rdsr1 = self.read_status_register_1();
|
||||
if rdsr1.programming_error() {
|
||||
// The datasheet mentions that the status should be cleared and writes
|
||||
// should be disabled explicitely.
|
||||
self.clear_status();
|
||||
self.write_disable();
|
||||
return Err(ProgramPageError::ProgrammingErrorBitSet);
|
||||
}
|
||||
if !rdsr1.write_in_progress() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_page_fast_read(&self, addr: u32, buf: &mut [u8], dummy_byte: bool) {
|
||||
let mut qspi = self.0.borrow_mut();
|
||||
let mut transfer = qspi.transfer_guard();
|
||||
let raw_word: [u8; 4] = [
|
||||
RegisterId::FastRead as u8,
|
||||
((addr >> 16) & 0xff) as u8,
|
||||
((addr >> 8) & 0xff) as u8,
|
||||
(addr & 0xff) as u8,
|
||||
];
|
||||
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
|
||||
let mut read_index = 0;
|
||||
let mut written_words = 0;
|
||||
let mut bytes_to_write = buf.len();
|
||||
if dummy_byte {
|
||||
bytes_to_write += 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);
|
||||
written_words += 1;
|
||||
}
|
||||
|
||||
transfer.start();
|
||||
let mut reply_word_index = 0;
|
||||
|
||||
while read_index < buf.len() {
|
||||
if transfer.read_status().rx_above_threshold() {
|
||||
let reply = transfer.read_rx_data();
|
||||
if reply_word_index == 0 {
|
||||
reply_word_index += 1;
|
||||
continue;
|
||||
}
|
||||
let reply_as_bytes = reply.to_ne_bytes();
|
||||
let reply_size = core::cmp::min(buf.len() - read_index, 4);
|
||||
read_index += match (reply_size, reply_word_index == 1 && dummy_byte) {
|
||||
(1, false) => {
|
||||
buf[read_index] = reply_as_bytes[0];
|
||||
1
|
||||
}
|
||||
(1, true) => {
|
||||
buf[read_index] = reply_as_bytes[1];
|
||||
1
|
||||
}
|
||||
(2, false) => {
|
||||
buf[read_index..read_index + 2].copy_from_slice(&reply_as_bytes[0..2]);
|
||||
2
|
||||
}
|
||||
(2, true) => {
|
||||
buf[read_index..read_index + 2].copy_from_slice(&reply_as_bytes[1..3]);
|
||||
2
|
||||
}
|
||||
(3, false) => {
|
||||
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[0..3]);
|
||||
3
|
||||
}
|
||||
(3, true) => {
|
||||
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[1..4]);
|
||||
3
|
||||
}
|
||||
(4, false) => {
|
||||
buf[read_index..read_index + 4].copy_from_slice(&reply_as_bytes[0..4]);
|
||||
4
|
||||
}
|
||||
(4, true) => {
|
||||
buf[read_index..read_index + 3].copy_from_slice(&reply_as_bytes[1..4]);
|
||||
3
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
reply_word_index += 1;
|
||||
}
|
||||
if written_words < fifo_writes && !transfer.read_status().tx_full() {
|
||||
transfer.write_word_txd_00(0);
|
||||
written_words += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If the Spansion QSPI is used in linear addressed mode, no IO operations are allowed.
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn read_guard(&mut self) -> QspiLinearReadGuard<'_> {
|
||||
self.0.read_guard()
|
||||
}
|
||||
}
|
||||
22
zynq/zedboard-fsbl/Cargo.toml
Normal file
22
zynq/zedboard-fsbl/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "zedboard-fsbl"
|
||||
version = "0.1.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2024"
|
||||
description = "Rust First Stage Bootloader for the Zedboard"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.3", features = ["critical-section-single-core"] }
|
||||
zynq7000-rt = { path = "../zynq7000-rt" }
|
||||
zynq7000 = { path = "../zynq7000" }
|
||||
zynq7000-hal = { path = "../zynq7000-hal" }
|
||||
zynq7000-boot-image = { path = "../../zynq7000-boot-image" }
|
||||
zedboard-bsp = { path = "../zedboard-bsp" }
|
||||
embedded-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
arbitrary-int = "2"
|
||||
31
zynq/zedboard-fsbl/build.rs
Normal file
31
zynq/zedboard-fsbl/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");
|
||||
}
|
||||
24
zynq/zedboard-fsbl/memory.x
Normal file
24
zynq/zedboard-fsbl/memory.x
Normal file
@@ -0,0 +1,24 @@
|
||||
MEMORY
|
||||
{
|
||||
/* The Zynq7000 has 192 kB of OCM memory which can be used for the FSBL */
|
||||
CODE(rx) : ORIGIN = 0x00000000, LENGTH = 192K
|
||||
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
|
||||
/* Leave 1 MB of memory which will be configured as uncached device memory by the MMU. This can
|
||||
be used for something like DMA descriptors, but the DDR needs to be set up first in addition
|
||||
to configuring the page at address 0x400_0000 accordingly */
|
||||
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
|
||||
}
|
||||
362
zynq/zedboard-fsbl/src/main.rs
Normal file
362
zynq/zedboard-fsbl/src/main.rs
Normal file
@@ -0,0 +1,362 @@
|
||||
//! Simple FSBL for the Zedboard.
|
||||
//!
|
||||
//!This first variant is simplistic. It is currently only capable of QSPI boot. It searches for a
|
||||
//! bitstream and ELF file inside the boot binary, flashes them and jumps to them.
|
||||
//!
|
||||
//! It can be easily adapted to other boards by changing the static DDR/DDRIOB configuration
|
||||
//! and the used QSPI memory driver.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use arbitrary_int::u6;
|
||||
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_boot_image::DestinationDevice;
|
||||
use zynq7000_hal::priv_tim;
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::{
|
||||
Clocks,
|
||||
pll::{PllConfig, configure_arm_pll, configure_io_pll},
|
||||
},
|
||||
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest},
|
||||
devcfg, gic, gpio, l2_cache,
|
||||
prelude::*,
|
||||
qspi::{self, QSPI_START_ADDRESS},
|
||||
time::Hertz,
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// PS clock input frequency.
|
||||
const PS_CLK: Hertz = Hertz::from_raw(33_333_333);
|
||||
|
||||
/// 1600 MHz.
|
||||
const ARM_CLK: Hertz = Hertz::from_raw(1_600_000_000);
|
||||
/// 1000 MHz.
|
||||
const IO_CLK: Hertz = Hertz::from_raw(1_000_000_000);
|
||||
|
||||
/// DDR frequency for the MT41K128M16JT-125 device.
|
||||
const DDR_FREQUENCY: Hertz = Hertz::from_raw(533_333_333);
|
||||
|
||||
/// 1067 MHz.
|
||||
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) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "main")]
|
||||
pub fn main() -> ! {
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
// The unwraps are okay here, the provided clock frequencies are standard values also used
|
||||
// by other Xilinx tools.
|
||||
configure_arm_pll(
|
||||
boot_mode,
|
||||
PllConfig::new_from_target_clock(PS_CLK, ARM_CLK).unwrap(),
|
||||
);
|
||||
configure_io_pll(
|
||||
boot_mode,
|
||||
PllConfig::new_from_target_clock(PS_CLK, IO_CLK).unwrap(),
|
||||
);
|
||||
|
||||
let mut periphs = zynq7000::Peripherals::take().unwrap();
|
||||
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLK).unwrap();
|
||||
|
||||
let gpio_pins = gpio::GpioPins::new(periphs.gpio);
|
||||
let mio_pins = gpio_pins.mio;
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut logger_uart = Uart::new_with_mio(
|
||||
periphs.uart_1,
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
logger_uart
|
||||
.write_all(b"-- Zedboard Rust FSBL --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
logger_uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = gic::GicConfigurator::new_with_init(periphs.gicc, periphs.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
// Enable interrupt exception.
|
||||
unsafe { gic.enable_interrupts() };
|
||||
|
||||
info!("Configuring DDR..");
|
||||
configure_ddr_for_ddr3(
|
||||
periphs.ddrc,
|
||||
boot_mode,
|
||||
DdrClockSetupConfig {
|
||||
ps_clk: PS_CLK,
|
||||
ddr_clk: DDR_CLK,
|
||||
ddr_3x_div: u6::new(2),
|
||||
ddr_2x_div: u6::new(3),
|
||||
},
|
||||
&zedboard_bsp::ddriob_config_autogen::DDRIOB_CONFIG_SET_ZEDBOARD,
|
||||
&zedboard_bsp::ddrc_config_autogen::DDRC_CONFIG_ZEDBOARD,
|
||||
);
|
||||
info!("DDR init done.");
|
||||
|
||||
info!("L2 cache init..");
|
||||
// Set up the L2 cache now that the DDR is in normal operation mode.
|
||||
l2_cache::init_with_defaults(&mut periphs.l2c);
|
||||
info!("L2 cache init done.");
|
||||
|
||||
let priv_tim = priv_tim::CpuPrivateTimer::take(clocks.arm_clocks()).unwrap();
|
||||
|
||||
if PERFORM_DDR_MEMTEST {
|
||||
let ddr_base_addr = 0x100000;
|
||||
info!("performing DDR memory test..");
|
||||
unsafe {
|
||||
memtest::walking_zero_test(ddr_base_addr, 64).expect("walking one test failed");
|
||||
memtest::walking_one_test(ddr_base_addr, 64).expect("walking zero test failed");
|
||||
memtest::checkerboard_test(ddr_base_addr, 64).expect("checkerboard test failed");
|
||||
}
|
||||
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(
|
||||
periphs.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, priv_tim);
|
||||
}
|
||||
loop {
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::CpuPrivateTimer) -> ! {
|
||||
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,
|
||||
zynq7000_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(),
|
||||
zynq7000_boot_image::FIXED_BOOT_HEADER_SIZE,
|
||||
);
|
||||
}
|
||||
drop(read_guard);
|
||||
|
||||
let boot_header = zynq7000_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
|
||||
+ zynq7000_boot_image::FIXED_BOOT_HEADER_SIZE) as *mut u8,
|
||||
boot_header_slice[zynq7000_boot_image::FIXED_BOOT_HEADER_SIZE..].as_mut_ptr(),
|
||||
fsbl_offset - zynq7000_boot_image::FIXED_BOOT_HEADER_SIZE,
|
||||
);
|
||||
}
|
||||
drop(read_guard);
|
||||
|
||||
let boot_header = zynq7000_boot_image::BootHeader::new_unchecked(boot_header_slice);
|
||||
|
||||
let mut name_buf: [u8; 256] = [0; 256];
|
||||
let mut opt_jump_addr = None;
|
||||
for (index, image_header) in boot_header.image_header_iterator().unwrap().enumerate() {
|
||||
let name = image_header.image_name(&mut name_buf).unwrap();
|
||||
if index == 0 {
|
||||
if !name.contains("fsbl") {
|
||||
log::warn!("first image name did not contain FSBL string");
|
||||
}
|
||||
// Skip the FSBL. It is probably currently running, and we do not want to re-flash it,
|
||||
// which would also lead to a self-overwrite.
|
||||
log::info!("skipping FSBL image");
|
||||
continue;
|
||||
}
|
||||
|
||||
let _read_guard = qspi.read_guard();
|
||||
|
||||
for (partition_index, partition) in image_header
|
||||
.partition_header_iterator(boot_header_slice)
|
||||
.unwrap()
|
||||
.enumerate()
|
||||
{
|
||||
let section_attrs = partition.section_attributes();
|
||||
if section_attrs.destination_device().is_err() {
|
||||
log::error!(
|
||||
"invalid destination device ID {}",
|
||||
section_attrs.destination_device().unwrap_err()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let dest_dev = section_attrs.destination_device().unwrap();
|
||||
match dest_dev {
|
||||
DestinationDevice::Pl => {
|
||||
info!("Loading image '{name}' to PL (FPGA)..");
|
||||
// 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(),
|
||||
)
|
||||
};
|
||||
// The DMA will read from the linear mapped QSPI directly, so it
|
||||
// has to be configured for reads using the guard!
|
||||
devcfg::configure_bitstream_non_secure(true, boot_bin_slice)
|
||||
.expect("unexpected unaligned address");
|
||||
log::info!("loaded bitstream successfully");
|
||||
}
|
||||
DestinationDevice::Ps => {
|
||||
// Load the bitstream directly from linear mapped QSPI memory.
|
||||
let load_addr = partition.destination_load_address();
|
||||
if load_addr < 0x10_0000 {
|
||||
panic!("invalid load address which is not located in DDR memory");
|
||||
}
|
||||
log::info!(
|
||||
"Loading partition {partition_index} for '{name}' to PS with load address {load_addr}.."
|
||||
);
|
||||
|
||||
let source_slice = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
(QSPI_START_ADDRESS
|
||||
+ partition
|
||||
.data_offset()
|
||||
.expect("invalid PS partition data offset"))
|
||||
as *const _,
|
||||
partition.total_partition_length().unwrap(),
|
||||
)
|
||||
};
|
||||
let target_slice = unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
load_addr as *mut u8,
|
||||
partition.total_partition_length().unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
// Copy from the linear mapped QSPI to DDR,
|
||||
target_slice.copy_from_slice(source_slice);
|
||||
|
||||
match &mut opt_jump_addr {
|
||||
Some(current) => *current = core::cmp::min(*current, load_addr),
|
||||
None => opt_jump_addr = Some(load_addr),
|
||||
}
|
||||
log::info!("load success");
|
||||
}
|
||||
_ => {
|
||||
error!("Unsupported destination device {dest_dev:?}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match opt_jump_addr {
|
||||
Some(jump_addr) => {
|
||||
log::info!("jumping to address {}", jump_addr);
|
||||
zynq7000_hal::log::uart_blocking::flush();
|
||||
|
||||
// Some clean up and preparation for jumping to the user application.
|
||||
zynq7000_hal::cache::clean_and_invalidate_data_cache();
|
||||
cortex_ar::register::TlbIAll::write();
|
||||
cortex_ar::register::BpIAll::write();
|
||||
cortex_ar::asm::dsb();
|
||||
cortex_ar::asm::isb();
|
||||
|
||||
let jump_func: extern "C" fn() -> ! = unsafe { core::mem::transmute(jump_addr) };
|
||||
jump_func();
|
||||
}
|
||||
None => panic!("did not find application elf to boot inside boot binary!"),
|
||||
}
|
||||
}
|
||||
|
||||
#[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) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
loop {}
|
||||
}
|
||||
16
zynq/zedboard-qspi-flasher/Cargo.toml
Normal file
16
zynq/zedboard-qspi-flasher/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "zedboard-qspi-flasher"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { version = "0.3" }
|
||||
zynq7000-rt = { path = "../zynq7000-rt" }
|
||||
zynq7000 = { path = "../zynq7000" }
|
||||
zynq7000-hal = { path = "../zynq7000-hal" }
|
||||
zynq7000-boot-image = { path = "../../zynq7000-boot-image" }
|
||||
zedboard-bsp = { path = "../zedboard-bsp" }
|
||||
embedded-io = "0.7"
|
||||
embedded-hal = "1"
|
||||
log = "0.4"
|
||||
libm = "0.2"
|
||||
31
zynq/zedboard-qspi-flasher/build.rs
Normal file
31
zynq/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
zynq/zedboard-qspi-flasher/memory.x
Normal file
22
zynq/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
zynq/zedboard-qspi-flasher/qspi-flasher.tcl
Normal file
151
zynq/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 bin; 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"
|
||||
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"
|
||||
225
zynq/zedboard-qspi-flasher/src/main.rs
Normal file
225
zynq/zedboard-qspi-flasher/src/main.rs
Normal file
@@ -0,0 +1,225 @@
|
||||
//! QSPI flasher for the Zedboard. Assumes that external scripting took care of transferring
|
||||
//! a boot binary to RAM.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
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::{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(
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -11,20 +11,21 @@ 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", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2" }
|
||||
cortex-ar = { version = "0.3" }
|
||||
zynq7000 = { path = "../zynq7000" }
|
||||
zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" }
|
||||
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1.0" }
|
||||
|
||||
bitbybit = "1.3"
|
||||
arbitrary-int = "1.3"
|
||||
static_assertions = "1.1"
|
||||
bitbybit = "1.4"
|
||||
arbitrary-int = "2"
|
||||
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.8"
|
||||
heapless = "0.9"
|
||||
static_cell = "2"
|
||||
delegate = "0.13"
|
||||
paste = "1"
|
||||
@@ -38,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"]
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Clock module.
|
||||
use arbitrary_int::Number;
|
||||
use arbitrary_int::{prelude::*, u6};
|
||||
|
||||
pub mod pll;
|
||||
|
||||
use zynq7000::slcr::{
|
||||
ClockControl,
|
||||
@@ -45,21 +47,67 @@ impl ArmClocks {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DdrClocks {
|
||||
/// DDR reference clock generated by the DDR PLL.
|
||||
ref_clk: Hertz,
|
||||
ddr_3x_clk: Hertz,
|
||||
ddr_2x_clk: Hertz,
|
||||
}
|
||||
|
||||
impl DdrClocks {
|
||||
/// Update the DDR 3x and 2x clocks in the SLCR.
|
||||
///
|
||||
/// Usually, the DDR PLL output clock will be set to an even multiple of the DDR operating
|
||||
/// frequency. In that case, the multiplicator should be used as the DDR 3x clock divisor.
|
||||
/// The DDR 2x clock divisor should be set so that the resulting clock is 2/3 of the DDR
|
||||
/// operating frequency.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This should only be called once during start-up. It accesses the SLCR register.
|
||||
pub unsafe fn configure_2x_3x_clk(ddr_3x_div: u6, ddr_2x_div: u6) {
|
||||
// Safety: The DDR clock structure is a singleton.
|
||||
unsafe {
|
||||
crate::slcr::Slcr::with(|slcr| {
|
||||
slcr.clk_ctrl().modify_ddr_clk_ctrl(|mut val| {
|
||||
val.set_div_3x_clk(ddr_3x_div);
|
||||
val.set_div_2x_clk(ddr_2x_div);
|
||||
val
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the DDR 3x and 2x clocks in the SLCR and creates a DDR clock information structure.
|
||||
///
|
||||
/// Usually, the DDR PLL output clock will be set to an even multiple of the DDR operating
|
||||
/// frequency. In that case, the multiplicator should be used as the DDR 3x clock divisor.
|
||||
/// The DDR 2x clock divisor should be set so that the resulting clock is 2/3 of the DDR
|
||||
/// operating frequency.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This should only be called once during start-up. It accesses the SLCR register.
|
||||
pub unsafe fn new_with_2x_3x_init(ref_clk: Hertz, ddr_3x_div: u6, ddr_2x_div: u6) -> Self {
|
||||
unsafe { Self::configure_2x_3x_clk(ddr_3x_div, ddr_2x_div) };
|
||||
Self {
|
||||
ref_clk,
|
||||
ddr_3x_clk: ref_clk / ddr_3x_div.as_u32(),
|
||||
ddr_2x_clk: ref_clk / ddr_2x_div.as_u32(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference clock provided by DDR PLL which is used to calculate all other clock frequencies.
|
||||
pub const fn ref_clk(&self) -> Hertz {
|
||||
self.ref_clk
|
||||
}
|
||||
|
||||
/// DDR 3x clock which is used by the DRAM and must be set to the operating frequency.
|
||||
pub fn ddr_3x_clk(&self) -> Hertz {
|
||||
self.ddr_3x_clk
|
||||
}
|
||||
|
||||
/// DDR 2x clock is used by the interconnect and is typically set to 2/3 of the operating
|
||||
/// frequency.
|
||||
pub fn ddr_2x_clk(&self) -> Hertz {
|
||||
self.ddr_2x_clk
|
||||
}
|
||||
@@ -178,13 +226,16 @@ pub enum ClockModuleId {
|
||||
#[derive(Debug)]
|
||||
pub struct DivisorZero(pub ClockModuleId);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ClockReadError {
|
||||
/// The feedback value for the PLL clock output calculation is zero.
|
||||
#[error("PLL feedback divisor is zero")]
|
||||
PllFeedbackZero,
|
||||
/// Detected a divisor of zero.
|
||||
#[error("divisor is zero")]
|
||||
DivisorZero(DivisorZero),
|
||||
/// Detected a divisor that is not even.
|
||||
/// Detected a divisor that is not even and should be.
|
||||
#[error("divisor is not even")]
|
||||
DivisorNotEven,
|
||||
}
|
||||
|
||||
@@ -201,9 +252,9 @@ impl Clocks {
|
||||
pub fn new_from_regs(ps_clk_freq: Hertz) -> Result<Self, ClockReadError> {
|
||||
let mut clk_regs = unsafe { ClockControl::new_mmio_fixed() };
|
||||
|
||||
let arm_pll_cfg = clk_regs.read_arm_pll();
|
||||
let io_pll_cfg = clk_regs.read_io_pll();
|
||||
let ddr_pll_cfg = clk_regs.read_ddr_pll();
|
||||
let arm_pll_cfg = clk_regs.read_arm_pll_ctrl();
|
||||
let io_pll_cfg = clk_regs.read_io_pll_ctrl();
|
||||
let ddr_pll_cfg = clk_regs.read_ddr_pll_ctrl();
|
||||
|
||||
if arm_pll_cfg.fdiv().as_u32() == 0
|
||||
|| io_pll_cfg.fdiv().as_u32() == 0
|
||||
356
zynq/zynq7000-hal/src/clocks/pll.rs
Normal file
356
zynq/zynq7000-hal/src/clocks/pll.rs
Normal file
@@ -0,0 +1,356 @@
|
||||
use core::sync::atomic::AtomicBool;
|
||||
|
||||
use arbitrary_int::{u4, u7, u10};
|
||||
|
||||
use crate::{BootMode, time::Hertz};
|
||||
|
||||
/// Minimal value based on Zynq-7000 TRM Table 25-6, p.744
|
||||
pub const PLL_MUL_MIN: u32 = 13;
|
||||
/// Maximum value based on Zynq-7000 TRM Table 25-6, p.744
|
||||
pub const PLL_MUL_MAX: u32 = 66;
|
||||
|
||||
static ARM_PLL_INIT: AtomicBool = AtomicBool::new(false);
|
||||
static IO_PLL_INIT: AtomicBool = AtomicBool::new(false);
|
||||
static DDR_PLL_INIT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[error("pll muliplier value {0} is out of range ({PLL_MUL_MIN}..={PLL_MUL_MAX})")]
|
||||
pub struct MulOutOfRangeError(pub u32);
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PllConfigCtorError {
|
||||
#[error("invalid input")]
|
||||
InvalidInput,
|
||||
#[error("pll multiplier out of range: {0}")]
|
||||
MulOutOfRange(#[from] MulOutOfRangeError),
|
||||
}
|
||||
|
||||
pub struct PllConfig {
|
||||
fdiv: u7,
|
||||
charge_pump: u4,
|
||||
loop_resistor: u4,
|
||||
lock_count: u10,
|
||||
}
|
||||
|
||||
impl PllConfig {
|
||||
pub fn new_from_target_clock(
|
||||
ps_clk: Hertz,
|
||||
target_clk: Hertz,
|
||||
) -> Result<Self, PllConfigCtorError> {
|
||||
if ps_clk.raw() == 0 {
|
||||
return Err(PllConfigCtorError::InvalidInput);
|
||||
}
|
||||
let mul = target_clk / ps_clk;
|
||||
Self::new(mul).map_err(PllConfigCtorError::MulOutOfRange)
|
||||
}
|
||||
/// Create a new PLL configuration based on the multiplier value.
|
||||
///
|
||||
/// These configuration values are based on the Zynq-7000 TRM Table 25-6, p.744.
|
||||
pub fn new(pll_mul: u32) -> Result<Self, MulOutOfRangeError> {
|
||||
if !(PLL_MUL_MIN..=PLL_MUL_MAX).contains(&pll_mul) {
|
||||
return Err(MulOutOfRangeError(pll_mul));
|
||||
}
|
||||
|
||||
Ok(match pll_mul {
|
||||
13 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(6),
|
||||
u10::new(750),
|
||||
),
|
||||
14 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(6),
|
||||
u10::new(700),
|
||||
),
|
||||
15 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(6),
|
||||
u10::new(650),
|
||||
),
|
||||
16 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(10),
|
||||
u10::new(625),
|
||||
),
|
||||
17 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(10),
|
||||
u10::new(575),
|
||||
),
|
||||
18 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(10),
|
||||
u10::new(550),
|
||||
),
|
||||
19 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(10),
|
||||
u10::new(525),
|
||||
),
|
||||
20 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(500),
|
||||
),
|
||||
21 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(475),
|
||||
),
|
||||
22 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(450),
|
||||
),
|
||||
23 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(425),
|
||||
),
|
||||
24..=25 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(400),
|
||||
),
|
||||
26 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(375),
|
||||
),
|
||||
27..=28 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(350),
|
||||
),
|
||||
|
||||
29..=30 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(12),
|
||||
u10::new(325),
|
||||
),
|
||||
31..=33 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(2),
|
||||
u10::new(300),
|
||||
),
|
||||
34..=36 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(2),
|
||||
u10::new(275),
|
||||
),
|
||||
37..=40 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(2),
|
||||
u10::new(250),
|
||||
),
|
||||
41..=47 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(3),
|
||||
u4::new(12),
|
||||
u10::new(250),
|
||||
),
|
||||
48..=66 => Self::new_raw(
|
||||
u7::new(pll_mul as u8),
|
||||
u4::new(2),
|
||||
u4::new(4),
|
||||
u10::new(250),
|
||||
),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new PLL configuration with raw values.
|
||||
///
|
||||
/// It is recommended to use [Self::new] instead, which creates a configuration
|
||||
/// based on a look-up table provided in the Zynq-7000 TRM.
|
||||
pub fn new_raw(fdiv: u7, charge_pump: u4, loop_resistor: u4, lock_count: u10) -> Self {
|
||||
Self {
|
||||
fdiv,
|
||||
charge_pump,
|
||||
loop_resistor,
|
||||
lock_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function configures the ARM PLL based on the provided [PllConfig].
|
||||
pub fn configure_arm_pll(boot_mode: BootMode, pll_config: PllConfig) {
|
||||
if ARM_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
// Safety: This will only run at most once because of the atomic boolean check.
|
||||
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
|
||||
}
|
||||
|
||||
/// This function configures the IO PLL based on the provided [PllConfig].
|
||||
pub fn configure_io_pll(boot_mode: BootMode, pll_config: PllConfig) {
|
||||
if IO_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
// Safety: This will only run at most once because of the atomic boolean check.
|
||||
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
|
||||
}
|
||||
|
||||
/// This function configures the DDR PLL based on the provided [PllConfig].
|
||||
pub fn configure_ddr_pll(boot_mode: BootMode, pll_config: PllConfig) {
|
||||
if DDR_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
// Safety: This will only run at most once because of the atomic boolean check.
|
||||
unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
|
||||
}
|
||||
|
||||
/// This function configures the ARM PLL based on the provided [PllConfig].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should only be called once during system initialization, for example in the
|
||||
/// first-stage bootloader (FSBL).
|
||||
pub unsafe fn configure_arm_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
|
||||
unsafe {
|
||||
crate::slcr::Slcr::with(|slcr| {
|
||||
let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_arm_pll_ctrl();
|
||||
let pll_cfg_reg = slcr.clk_ctrl().pointer_to_arm_pll_cfg();
|
||||
configure_pll_unchecked(
|
||||
boot_mode,
|
||||
pll_config,
|
||||
PllType::Arm,
|
||||
slcr,
|
||||
pll_ctrl_reg,
|
||||
pll_cfg_reg,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// This function configures the IO PLL based on the provided [PllConfig].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should only be called once during system initialization, for example in the
|
||||
/// first-stage bootloader (FSBL).
|
||||
pub unsafe fn configure_io_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
|
||||
unsafe {
|
||||
crate::slcr::Slcr::with(|slcr| {
|
||||
let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_io_pll_ctrl();
|
||||
let pll_cfg_reg = slcr.clk_ctrl().pointer_to_io_pll_cfg();
|
||||
configure_pll_unchecked(
|
||||
boot_mode,
|
||||
pll_config,
|
||||
PllType::Io,
|
||||
slcr,
|
||||
pll_ctrl_reg,
|
||||
pll_cfg_reg,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// This function configures the DDR PLL based on the provided [PllConfig].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should only be called once during system initialization, for example in the
|
||||
/// first-stage bootloader (FSBL).
|
||||
pub unsafe fn configure_ddr_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
|
||||
unsafe {
|
||||
crate::slcr::Slcr::with(|slcr| {
|
||||
let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_ddr_pll_ctrl();
|
||||
let pll_cfg_reg = slcr.clk_ctrl().pointer_to_ddr_pll_cfg();
|
||||
configure_pll_unchecked(
|
||||
boot_mode,
|
||||
pll_config,
|
||||
PllType::Ddr,
|
||||
slcr,
|
||||
pll_ctrl_reg,
|
||||
pll_cfg_reg,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
enum PllType {
|
||||
Io,
|
||||
Ddr,
|
||||
Arm,
|
||||
}
|
||||
|
||||
impl PllType {
|
||||
pub const fn bit_offset_pll_locked(&self) -> usize {
|
||||
match self {
|
||||
PllType::Io => 2,
|
||||
PllType::Ddr => 1,
|
||||
PllType::Arm => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn configure_pll_unchecked(
|
||||
boot_mode: BootMode,
|
||||
cfg: PllConfig,
|
||||
pll_type: PllType,
|
||||
slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
|
||||
pll_ctrl_reg: *mut zynq7000::slcr::clocks::PllControl,
|
||||
pll_cfg_reg: *mut zynq7000::slcr::clocks::PllConfig,
|
||||
) {
|
||||
// Step 1: Program the multiplier and other PLL configuration parameters.
|
||||
// New values will only be consumed once the PLL is reset.
|
||||
let mut pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
|
||||
pll_ctrl.set_fdiv(cfg.fdiv);
|
||||
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
|
||||
|
||||
let mut pll_cfg = unsafe { core::ptr::read_volatile(pll_cfg_reg) };
|
||||
pll_cfg.set_charge_pump(cfg.charge_pump);
|
||||
pll_cfg.set_loop_resistor(cfg.loop_resistor);
|
||||
pll_cfg.set_lock_count(cfg.lock_count);
|
||||
unsafe { core::ptr::write_volatile(pll_cfg_reg, pll_cfg) };
|
||||
|
||||
// Step 2: Force the PLL into bypass mode. If the PLL bypass mode pin is tied high,
|
||||
// the PLLs need to be enabled. According to the TRM, this is done by setting the
|
||||
// PLL_BYPASS_QUAL bit to 0, which de-asserts the reset to the Arm PLL.
|
||||
pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
|
||||
if boot_mode.pll_config() == zynq7000::slcr::BootPllConfig::Bypassed {
|
||||
pll_ctrl.set_bypass_qual(false);
|
||||
}
|
||||
pll_ctrl.set_bypass_force(true);
|
||||
pll_ctrl.set_pwrdwn(false);
|
||||
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
|
||||
|
||||
// Step 3: Reset the PLL. This also loads the new configuration.
|
||||
pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
|
||||
pll_ctrl.set_reset(true);
|
||||
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
|
||||
pll_ctrl.set_reset(false);
|
||||
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
|
||||
|
||||
while ((slcr.clk_ctrl().read_pll_status().raw_value() >> pll_type.bit_offset_pll_locked())
|
||||
& 0b1)
|
||||
!= 1
|
||||
{
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
|
||||
pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
|
||||
pll_ctrl.set_bypass_force(false);
|
||||
unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
|
||||
}
|
||||
335
zynq/zynq7000-hal/src/ddr/ll.rs
Normal file
335
zynq/zynq7000-hal/src/ddr/ll.rs
Normal file
@@ -0,0 +1,335 @@
|
||||
//! Low-level DDR configuration module.
|
||||
use arbitrary_int::{prelude::*, u2, u3, u6};
|
||||
use zynq7000::ddrc::{MmioDdrController, regs::*};
|
||||
use zynq7000::slcr::{clocks::DciClockControl, ddriob::DdriobConfig};
|
||||
|
||||
use crate::{clocks::DdrClocks, time::Hertz};
|
||||
|
||||
const DCI_MAX_FREQ: Hertz = Hertz::from_raw(10_000_000);
|
||||
|
||||
// These values were extracted from the ps7_init files and are not documented in the TMR.
|
||||
// zynq-rs uses the same values.. I assume they are constant.
|
||||
|
||||
pub const DRIVE_SLEW_ADDR_CFG: u32 = 0x0018_c61c;
|
||||
pub const DRIVE_SLEW_DATA_CFG: u32 = 0x00f9_861c;
|
||||
pub const DRIVE_SLEW_DIFF_CFG: u32 = 0x00f9_861c;
|
||||
pub const DRIVE_SLEW_CLOCK_CFG: u32 = 0x00f9_861c;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DciClkConfig {
|
||||
div0: u6,
|
||||
div1: u6,
|
||||
}
|
||||
|
||||
/// Calculate the required DCI divisors for the given DDR clock.
|
||||
pub fn calculate_dci_divisors(ddr_clks: &DdrClocks) -> DciClkConfig {
|
||||
calculate_dci_divisors_with_ddr_clk(ddr_clks.ref_clk())
|
||||
}
|
||||
|
||||
/// Calculate the required DCI divisors for the given DDR clock frequency.
|
||||
pub fn calculate_dci_divisors_with_ddr_clk(ddr_clk: Hertz) -> DciClkConfig {
|
||||
let target_div = ddr_clk.raw().div_ceil(DCI_MAX_FREQ.raw());
|
||||
let mut config = DciClkConfig {
|
||||
div0: u6::new(u6::MAX.value()),
|
||||
div1: u6::new(u6::MAX.value()),
|
||||
};
|
||||
|
||||
let mut best_error = 0;
|
||||
for divisor0 in 1..63 {
|
||||
for divisor1 in 1..63 {
|
||||
let current_div = (divisor0 as u32) * (divisor1 as u32);
|
||||
let error = current_div.abs_diff(target_div);
|
||||
if error < best_error {
|
||||
config.div0 = u6::new(divisor0 as u8);
|
||||
config.div1 = u6::new(divisor1 as u8);
|
||||
best_error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
/// Configure the DCI module by configure its clock divisors and enabling it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function writes to DCI related registers. It should only be called once during
|
||||
/// DDR initialization.
|
||||
pub unsafe fn configure_dci(ddr_clk: &DdrClocks) {
|
||||
let cfg = calculate_dci_divisors(ddr_clk);
|
||||
// Safety: Only writes to DCI clock related registers.
|
||||
unsafe {
|
||||
crate::Slcr::with(|slcr| {
|
||||
slcr.clk_ctrl().write_dci_clk_ctrl(
|
||||
DciClockControl::builder()
|
||||
.with_divisor_1(cfg.div1)
|
||||
.with_divisor_0(cfg.div0)
|
||||
.with_clk_act(true)
|
||||
.build(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibrates the IOB impedance for DDR3 memory according to to TRM p.325, DDR IOB Impedance
|
||||
/// calibration.
|
||||
///
|
||||
/// This function will also enable the DCI clock with the provided clock configuration.
|
||||
/// You can use [calculate_dci_divisors] to calculate the divisor values for the given DDR clock,
|
||||
/// or you can hardcode the values if they are fixed.
|
||||
///
|
||||
/// Polling for completion can be disabled. The calibration takes 1-2 ms according to the TRM, so
|
||||
/// the user can set other configuration values which are not reliant on DDR operation before
|
||||
/// polling for completion.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function writes to the DDR IOB related registers. It should only be called once during
|
||||
/// DDR initialization.
|
||||
pub unsafe fn calibrate_iob_impedance_for_ddr3(dci_clk_cfg: DciClkConfig, poll_for_done: bool) {
|
||||
unsafe {
|
||||
calibrate_iob_impedance(
|
||||
dci_clk_cfg,
|
||||
u3::new(0),
|
||||
u2::new(0),
|
||||
u3::new(0b001),
|
||||
u3::new(0),
|
||||
u2::new(0),
|
||||
poll_for_done,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibrates the IOB impedance according to to TRM p.325, DDR IOB Impedance calibration.
|
||||
///
|
||||
/// This function will also enable the DCI clock with the provided clock configuration.
|
||||
/// You can use [calculate_dci_divisors] to calculate the divisor values for the given DDR clock,
|
||||
/// or you can hardcode the values if they are fixed.
|
||||
///
|
||||
/// Polling for completion can be disabled. The calibration takes 1-2 ms according to the TRM, so
|
||||
/// the user can set other configuration values which are not reliant on DDR operation before
|
||||
/// polling for completion.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function writes to the DDR IOB related registers. It should only be called once during
|
||||
/// DDR initialization.
|
||||
pub unsafe fn calibrate_iob_impedance(
|
||||
dci_clk_cfg: DciClkConfig,
|
||||
pref_opt2: u3,
|
||||
pref_opt1: u2,
|
||||
nref_opt4: u3,
|
||||
nref_opt2: u3,
|
||||
nref_opt1: u2,
|
||||
poll_for_done: bool,
|
||||
) {
|
||||
// Safety: Only writes to DDR IOB related registers.
|
||||
let mut slcr = unsafe { crate::slcr::Slcr::steal() };
|
||||
slcr.modify(|slcr| {
|
||||
slcr.clk_ctrl().write_dci_clk_ctrl(
|
||||
DciClockControl::builder()
|
||||
.with_divisor_1(dci_clk_cfg.div1)
|
||||
.with_divisor_0(dci_clk_cfg.div0)
|
||||
.with_clk_act(true)
|
||||
.build(),
|
||||
);
|
||||
let mut ddriob = slcr.ddriob();
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_reset(true);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_reset(false);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_reset(true);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_pref_opt2(pref_opt2);
|
||||
val.set_pref_opt1(pref_opt1);
|
||||
val.set_nref_opt4(nref_opt4);
|
||||
val.set_nref_opt2(nref_opt2);
|
||||
val.set_nref_opt1(nref_opt1);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_update_control(false);
|
||||
val
|
||||
});
|
||||
ddriob.modify_dci_ctrl(|mut val| {
|
||||
val.set_enable(true);
|
||||
val
|
||||
});
|
||||
if poll_for_done {
|
||||
while !slcr.ddriob().read_dci_status().done() {
|
||||
// Wait for the DDR IOB impedance calibration to complete.
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Static configuration for DDR IOBs.
|
||||
pub struct DdriobConfigSet {
|
||||
pub addr0: DdriobConfig,
|
||||
pub addr1: DdriobConfig,
|
||||
pub data0: DdriobConfig,
|
||||
pub data1: DdriobConfig,
|
||||
pub diff0: DdriobConfig,
|
||||
pub diff1: DdriobConfig,
|
||||
pub clock: DdriobConfig,
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This function writes to the IOB related registers. It should only be called once during
|
||||
/// DDR initialization.
|
||||
pub unsafe fn configure_iob(cfg_set: &DdriobConfigSet) {
|
||||
// Safety: Only configures IOB related registers.
|
||||
let mut slcr = unsafe { crate::slcr::Slcr::steal() };
|
||||
slcr.modify(|slcr| {
|
||||
let mut ddriob = slcr.ddriob();
|
||||
ddriob.write_ddriob_addr0(cfg_set.addr0);
|
||||
ddriob.write_ddriob_addr1(cfg_set.addr1);
|
||||
|
||||
ddriob.write_ddriob_data0(cfg_set.data0);
|
||||
ddriob.write_ddriob_data1(cfg_set.data1);
|
||||
|
||||
ddriob.write_ddriob_diff0(cfg_set.diff0);
|
||||
ddriob.write_ddriob_diff1(cfg_set.diff1);
|
||||
|
||||
ddriob.write_ddriob_clock(cfg_set.clock);
|
||||
|
||||
// These values were extracted from the ps7_init files and are not documented in the TRM.
|
||||
// zynq-rs uses the same values.. I assume they are constant.
|
||||
ddriob.write_ddriob_drive_slew_addr(DRIVE_SLEW_ADDR_CFG);
|
||||
ddriob.write_ddriob_drive_slew_data(DRIVE_SLEW_DATA_CFG);
|
||||
ddriob.write_ddriob_drive_slew_diff(DRIVE_SLEW_DIFF_CFG);
|
||||
ddriob.write_ddriob_drive_slew_clock(DRIVE_SLEW_CLOCK_CFG);
|
||||
});
|
||||
}
|
||||
|
||||
/// Full static DDRC configuration set.
|
||||
#[derive(Debug)]
|
||||
pub struct DdrcConfigSet {
|
||||
pub ctrl: DdrcControl,
|
||||
pub two_rank: TwoRankConfig,
|
||||
pub hpr: LprHprQueueControl,
|
||||
pub lpr: LprHprQueueControl,
|
||||
pub wr: WriteQueueControl,
|
||||
pub dram_param_0: DramParamReg0,
|
||||
pub dram_param_1: DramParamReg1,
|
||||
pub dram_param_2: DramParamReg2,
|
||||
pub dram_param_3: DramParamReg3,
|
||||
pub dram_param_4: DramParamReg4,
|
||||
pub dram_init_param: DramInitParam,
|
||||
pub dram_emr: DramEmr,
|
||||
pub dram_emr_mr: DramEmrMr,
|
||||
pub dram_burst8_rdwr: DramBurst8ReadWrite,
|
||||
pub disable_dq: DisableDq,
|
||||
pub dram_addr_map_bank: DramAddrMapBank,
|
||||
pub dram_addr_map_col: DramAddrMapColumn,
|
||||
pub dram_addr_map_row: DramAddrMapRow,
|
||||
pub dram_odt: DramOdt,
|
||||
pub phy_cmd_timeout_rddata_cpt: PhyCmdTimeoutRdDataCpt,
|
||||
pub dll_calib: DllCalib,
|
||||
pub odt_delay_hold: OdtDelayHold,
|
||||
pub ctrl_reg1: CtrlReg1,
|
||||
pub ctrl_reg2: CtrlReg2,
|
||||
pub ctrl_reg3: CtrlReg3,
|
||||
pub ctrl_reg4: CtrlReg4,
|
||||
pub ctrl_reg5: CtrlReg5,
|
||||
pub ctrl_reg6: CtrlReg6,
|
||||
pub che_t_zq: CheTZq,
|
||||
pub che_t_zq_short_interval_reg: CheTZqShortInterval,
|
||||
pub deep_powerdown: DeepPowerdown,
|
||||
pub reg_2c: Reg2c,
|
||||
pub reg_2d: Reg2d,
|
||||
pub dfi_timing: DfiTiming,
|
||||
pub che_ecc_ctrl: CheEccControl,
|
||||
pub ecc_scrub: EccScrub,
|
||||
pub phy_receiver_enable: PhyReceiverEnable,
|
||||
pub phy_config: [PhyConfig; 4],
|
||||
pub phy_init_ratio: [PhyInitRatio; 4],
|
||||
pub phy_rd_dqs_config: [PhyDqsConfig; 4],
|
||||
pub phy_wr_dqs_config: [PhyDqsConfig; 4],
|
||||
pub phy_we_cfg: [PhyWriteEnableConfig; 4],
|
||||
pub phy_wr_data_slv: [PhyWriteDataSlaveConfig; 4],
|
||||
pub reg64: Reg64,
|
||||
pub reg65: Reg65,
|
||||
pub page_mask: u32,
|
||||
pub axi_priority_wr_port: [AxiPriorityWritePort; 4],
|
||||
pub axi_priority_rd_port: [AxiPriorityReadPort; 4],
|
||||
pub lpddr_ctrl_0: LpddrControl0,
|
||||
pub lpddr_ctrl_1: LpddrControl1,
|
||||
pub lpddr_ctrl_2: LpddrControl2,
|
||||
pub lpddr_ctrl_3: LpddrControl3,
|
||||
}
|
||||
|
||||
/// This low-level function sets all the configuration registers.
|
||||
///
|
||||
/// It does NOT take care of taking the DDR controller out of reset and polling for DDR
|
||||
/// configuration completion.
|
||||
pub fn configure_ddr_config(ddrc: &mut MmioDdrController<'static>, cfg_set: &DdrcConfigSet) {
|
||||
ddrc.write_ddrc_ctrl(cfg_set.ctrl);
|
||||
// Write all configuration registers.
|
||||
ddrc.write_two_rank_cfg(cfg_set.two_rank);
|
||||
ddrc.write_hpr_queue_ctrl(cfg_set.hpr);
|
||||
ddrc.write_lpr_queue_ctrl(cfg_set.lpr);
|
||||
ddrc.write_wr_reg(cfg_set.wr);
|
||||
ddrc.write_dram_param_reg0(cfg_set.dram_param_0);
|
||||
ddrc.write_dram_param_reg1(cfg_set.dram_param_1);
|
||||
ddrc.write_dram_param_reg2(cfg_set.dram_param_2);
|
||||
ddrc.write_dram_param_reg3(cfg_set.dram_param_3);
|
||||
ddrc.write_dram_param_reg4(cfg_set.dram_param_4);
|
||||
ddrc.write_dram_init_param(cfg_set.dram_init_param);
|
||||
ddrc.write_dram_emr(cfg_set.dram_emr);
|
||||
ddrc.write_dram_emr_mr(cfg_set.dram_emr_mr);
|
||||
ddrc.write_dram_burst8_rdwr(cfg_set.dram_burst8_rdwr);
|
||||
ddrc.write_dram_disable_dq(cfg_set.disable_dq);
|
||||
ddrc.write_phy_cmd_timeout_rddata_cpt(cfg_set.phy_cmd_timeout_rddata_cpt);
|
||||
ddrc.write_dll_calib(cfg_set.dll_calib);
|
||||
ddrc.write_odt_delay_hold(cfg_set.odt_delay_hold);
|
||||
ddrc.write_ctrl_reg1(cfg_set.ctrl_reg1);
|
||||
ddrc.write_ctrl_reg2(cfg_set.ctrl_reg2);
|
||||
ddrc.write_ctrl_reg3(cfg_set.ctrl_reg3);
|
||||
ddrc.write_ctrl_reg4(cfg_set.ctrl_reg4);
|
||||
ddrc.write_ctrl_reg5(cfg_set.ctrl_reg5);
|
||||
ddrc.write_ctrl_reg6(cfg_set.ctrl_reg6);
|
||||
ddrc.write_che_t_zq(cfg_set.che_t_zq);
|
||||
ddrc.write_che_t_zq_short_interval_reg(cfg_set.che_t_zq_short_interval_reg);
|
||||
ddrc.write_deep_powerdown_reg(cfg_set.deep_powerdown);
|
||||
ddrc.write_reg_2c(cfg_set.reg_2c);
|
||||
ddrc.write_reg_2d(cfg_set.reg_2d);
|
||||
ddrc.write_dfi_timing(cfg_set.dfi_timing);
|
||||
ddrc.write_che_ecc_control(cfg_set.che_ecc_ctrl);
|
||||
ddrc.write_ecc_scrub(cfg_set.ecc_scrub);
|
||||
ddrc.write_phy_receiver_enable(cfg_set.phy_receiver_enable);
|
||||
for i in 0..4 {
|
||||
// Safety: Indexes are valid.
|
||||
unsafe {
|
||||
ddrc.write_phy_config_unchecked(i, cfg_set.phy_config[i]);
|
||||
ddrc.write_phy_init_ratio_unchecked(i, cfg_set.phy_init_ratio[i]);
|
||||
ddrc.write_phy_rd_dqs_cfg_unchecked(i, cfg_set.phy_rd_dqs_config[i]);
|
||||
ddrc.write_phy_wr_dqs_cfg_unchecked(i, cfg_set.phy_wr_dqs_config[i]);
|
||||
ddrc.write_phy_we_cfg_unchecked(i, cfg_set.phy_we_cfg[i]);
|
||||
ddrc.write_phy_wr_data_slave_unchecked(i, cfg_set.phy_wr_data_slv[i]);
|
||||
}
|
||||
}
|
||||
ddrc.write_reg_64(cfg_set.reg64);
|
||||
ddrc.write_reg_65(cfg_set.reg65);
|
||||
ddrc.write_page_mask(cfg_set.page_mask);
|
||||
for i in 0..4 {
|
||||
// Safety: Indexes are valid.
|
||||
unsafe {
|
||||
ddrc.write_axi_priority_wr_port_unchecked(i, cfg_set.axi_priority_wr_port[i]);
|
||||
ddrc.write_axi_priority_rd_port_unchecked(i, cfg_set.axi_priority_rd_port[i]);
|
||||
}
|
||||
}
|
||||
ddrc.write_lpddr_ctrl_0(cfg_set.lpddr_ctrl_0);
|
||||
ddrc.write_lpddr_ctrl_1(cfg_set.lpddr_ctrl_1);
|
||||
ddrc.write_lpddr_ctrl_2(cfg_set.lpddr_ctrl_2);
|
||||
ddrc.write_lpddr_ctrl_3(cfg_set.lpddr_ctrl_3);
|
||||
}
|
||||
227
zynq/zynq7000-hal/src/ddr/mod.rs
Normal file
227
zynq/zynq7000-hal/src/ddr/mod.rs
Normal file
@@ -0,0 +1,227 @@
|
||||
use arbitrary_int::u6;
|
||||
use zynq7000::ddrc::MmioDdrController;
|
||||
|
||||
use crate::{
|
||||
BootMode,
|
||||
clocks::pll::{PllConfig, configure_ddr_pll},
|
||||
time::Hertz,
|
||||
};
|
||||
|
||||
pub mod ll;
|
||||
|
||||
pub use ll::{DdrcConfigSet, DdriobConfigSet};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DdrClockSetupConfig {
|
||||
pub ps_clk: Hertz,
|
||||
pub ddr_clk: Hertz,
|
||||
pub ddr_3x_div: u6,
|
||||
pub ddr_2x_div: u6,
|
||||
}
|
||||
|
||||
impl DdrClockSetupConfig {
|
||||
pub const fn new(ps_clk: Hertz, ddr_clk: Hertz, ddr_3x_div: u6, ddr_2x_div: u6) -> Self {
|
||||
Self {
|
||||
ps_clk,
|
||||
ddr_clk,
|
||||
ddr_3x_div,
|
||||
ddr_2x_div,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This completely sets up the DDR module for DDR3 operation.
|
||||
///
|
||||
/// It performs the following steps accoridng to the functional programming model of the
|
||||
/// DDR memory controller, TRM p.323.
|
||||
///
|
||||
/// 1. Configures the DDR PLL to the target clock frequency.
|
||||
/// 2. Configures the DDR clocks based on the user-provided configuration.
|
||||
/// 3. Configures and sets up the DDR I/O buffers (DDR IOB) module.
|
||||
/// 4. Configures and sets up the DDR controller (DDRC) module.
|
||||
///
|
||||
/// This function consumes the DDRC register block once and thus provides a safe interface for DDR
|
||||
/// initialization.
|
||||
pub fn configure_ddr_for_ddr3(
|
||||
mut ddrc_regs: MmioDdrController<'static>,
|
||||
boot_mode: BootMode,
|
||||
clk_setup_cfg: DdrClockSetupConfig,
|
||||
ddriob_cfg: &DdriobConfigSet,
|
||||
ddr_cfg: &DdrcConfigSet,
|
||||
) {
|
||||
// Set the DDR PLL output frequency to an even multiple of the operating frequency,
|
||||
// as recommended by the DDR documentation.
|
||||
configure_ddr_pll(
|
||||
boot_mode,
|
||||
PllConfig::new_from_target_clock(clk_setup_cfg.ps_clk, clk_setup_cfg.ddr_clk).unwrap(),
|
||||
);
|
||||
// Safety: Only done once here during start-up.
|
||||
let ddr_clks = unsafe {
|
||||
crate::clocks::DdrClocks::new_with_2x_3x_init(
|
||||
clk_setup_cfg.ddr_clk,
|
||||
clk_setup_cfg.ddr_3x_div,
|
||||
clk_setup_cfg.ddr_2x_div,
|
||||
)
|
||||
};
|
||||
let dci_clk_cfg = ll::calculate_dci_divisors(&ddr_clks);
|
||||
|
||||
ddrc_regs.modify_ddrc_ctrl(|mut val| {
|
||||
val.set_soft_reset(zynq7000::ddrc::regs::SoftReset::Reset);
|
||||
val
|
||||
});
|
||||
|
||||
// Safety: This is only called once during DDR initialization.
|
||||
unsafe {
|
||||
ll::configure_iob(ddriob_cfg);
|
||||
// Do not wait for completion, it takes a bit of time. We can set all the DDR config registers
|
||||
// before polling for completion.
|
||||
ll::calibrate_iob_impedance_for_ddr3(dci_clk_cfg, false);
|
||||
}
|
||||
ll::configure_ddr_config(&mut ddrc_regs, ddr_cfg);
|
||||
// Safety: This is only called once during DDR initialization, and we only modify DDR related
|
||||
// registers.
|
||||
let slcr = unsafe { crate::slcr::Slcr::steal() };
|
||||
let ddriob_shared = slcr.regs().ddriob_shared();
|
||||
// Wait for DDR IOB impedance calibration to complete first.
|
||||
while !ddriob_shared.read_dci_status().done() {
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
log::debug!("DDR IOB impedance calib done");
|
||||
|
||||
// Now take the DDR out of reset.
|
||||
ddrc_regs.modify_ddrc_ctrl(|mut val| {
|
||||
val.set_soft_reset(zynq7000::ddrc::regs::SoftReset::Active);
|
||||
val
|
||||
});
|
||||
// Wait until the DDR setup has completed.
|
||||
while ddrc_regs.read_mode_status().operating_mode()
|
||||
!= zynq7000::ddrc::regs::OperatingMode::NormalOperation
|
||||
{
|
||||
// Wait for the soft reset to complete.
|
||||
cortex_ar::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
pub mod memtest {
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum MemTestError {
|
||||
#[error("memory address is not aligned to 4 bytes")]
|
||||
AddrNotAligned,
|
||||
#[error("memory test error")]
|
||||
Memory {
|
||||
addr: usize,
|
||||
expected: u32,
|
||||
found: u32,
|
||||
},
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This tests writes and reads on a memory block starting at the base address
|
||||
/// with the size `words` times 4.
|
||||
pub unsafe fn walking_zero_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
|
||||
unsafe { walking_value_test(true, base_addr, words) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This tests writes and reads on a memory block starting at the base address
|
||||
/// with the size `words` times 4.
|
||||
pub unsafe fn walking_one_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
|
||||
unsafe { walking_value_test(true, base_addr, words) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This tests writes and reads on a memory block starting at the base address
|
||||
/// with the size `words` times 4.
|
||||
pub unsafe fn walking_value_test(
|
||||
walking_zero: bool,
|
||||
base_addr: usize,
|
||||
words: usize,
|
||||
) -> Result<(), MemTestError> {
|
||||
if words == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
if !base_addr.is_multiple_of(4) {
|
||||
return Err(MemTestError::AddrNotAligned);
|
||||
}
|
||||
let base_ptr = base_addr as *mut u32;
|
||||
|
||||
// For each bit position 0..31 generate pattern = 1 << bit
|
||||
for bit in 0..32 {
|
||||
let pattern = if walking_zero {
|
||||
!(1u32 << bit)
|
||||
} else {
|
||||
1u32 << bit
|
||||
};
|
||||
|
||||
// write pass
|
||||
for i in 0..words {
|
||||
unsafe {
|
||||
let p = base_ptr.add(i);
|
||||
core::ptr::write_volatile(p, pattern);
|
||||
}
|
||||
}
|
||||
|
||||
// read/verify pass
|
||||
for i in 0..words {
|
||||
let val;
|
||||
unsafe {
|
||||
let p = base_ptr.add(i) as *const u32;
|
||||
val = core::ptr::read_volatile(p);
|
||||
}
|
||||
if val != pattern {
|
||||
return Err(MemTestError::Memory {
|
||||
addr: base_addr + i * 4,
|
||||
expected: pattern,
|
||||
found: val,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This tests writes and reads on a memory block starting at the base address
|
||||
/// with the size `words` times 4.
|
||||
pub unsafe fn checkerboard_test(base_addr: usize, words: usize) -> Result<(), MemTestError> {
|
||||
if words == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
if !base_addr.is_multiple_of(4) {
|
||||
return Err(MemTestError::AddrNotAligned);
|
||||
}
|
||||
|
||||
let base_ptr = base_addr as *mut u32;
|
||||
let patterns = [0xAAAAAAAAu32, 0x55555555u32];
|
||||
|
||||
for &pattern in &patterns {
|
||||
// Write pass
|
||||
for i in 0..words {
|
||||
let value = if i % 2 == 0 { pattern } else { !pattern };
|
||||
unsafe {
|
||||
core::ptr::write_volatile(base_ptr.add(i), value);
|
||||
}
|
||||
}
|
||||
|
||||
// Read/verify pass
|
||||
for i in 0..words {
|
||||
let expected = if i % 2 == 0 { pattern } else { !pattern };
|
||||
let val = unsafe { core::ptr::read_volatile(base_ptr.add(i)) };
|
||||
|
||||
if val != expected {
|
||||
return Err(MemTestError::Memory {
|
||||
addr: base_addr + i * 4,
|
||||
expected,
|
||||
found: val,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
64
zynq/zynq7000-hal/src/devcfg.rs
Normal file
64
zynq/zynq7000-hal/src/devcfg.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("unaligned address: {0}")]
|
||||
pub struct UnalignedAddrError(usize);
|
||||
|
||||
/// Configures the bitstream using the PCAP interface in non-secure mode.
|
||||
///
|
||||
/// Blocking function which only returns when the bitstream configuration is complete.
|
||||
pub fn configure_bitstream_non_secure(
|
||||
init_pl: bool,
|
||||
bitstream: &[u8],
|
||||
) -> Result<(), UnalignedAddrError> {
|
||||
if !(bitstream.as_ptr() as usize).is_multiple_of(64) {
|
||||
return Err(UnalignedAddrError(bitstream.as_ptr() as usize));
|
||||
}
|
||||
if bitstream.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut devcfg = unsafe { zynq7000::devcfg::DevCfg::new_mmio_fixed() };
|
||||
devcfg.modify_control(|mut val| {
|
||||
val.set_config_access_select(zynq7000::devcfg::PlConfigAccess::ConfigAccessPort);
|
||||
val.set_access_port_select(zynq7000::devcfg::ConfigAccessPortSelect::Pcap);
|
||||
val
|
||||
});
|
||||
devcfg.write_interrupt_status(zynq7000::devcfg::Interrupt::new_with_raw_value(0xFFFF_FFFF));
|
||||
if init_pl {
|
||||
devcfg.modify_control(|mut val| {
|
||||
val.set_prog_b_bit(true);
|
||||
val
|
||||
});
|
||||
devcfg.modify_control(|mut val| {
|
||||
val.set_prog_b_bit(false);
|
||||
val
|
||||
});
|
||||
while devcfg.read_status().pcfg_init() {}
|
||||
devcfg.modify_control(|mut val| {
|
||||
val.set_prog_b_bit(true);
|
||||
val
|
||||
});
|
||||
devcfg.write_interrupt_status(
|
||||
zynq7000::devcfg::Interrupt::ZERO.with_pl_programming_done(true),
|
||||
);
|
||||
}
|
||||
while !devcfg.read_status().pcfg_init() {}
|
||||
if !init_pl {
|
||||
while devcfg.read_status().dma_command_queue_full() {}
|
||||
}
|
||||
devcfg.modify_misc_control(|mut val| {
|
||||
val.set_loopback(false);
|
||||
val
|
||||
});
|
||||
devcfg.modify_control(|mut val| {
|
||||
val.set_pcap_rate_enable(false);
|
||||
val
|
||||
});
|
||||
devcfg.write_dma_source_addr(bitstream.as_ptr() as u32);
|
||||
devcfg.write_dma_dest_addr(0xFFFF_FFFF);
|
||||
devcfg.write_dma_source_len(bitstream.len() as u32);
|
||||
devcfg.write_dma_dest_len(bitstream.len() as u32);
|
||||
|
||||
while !devcfg.read_interrupt_status().dma_done() {}
|
||||
// TODO: Check for errors.
|
||||
while !devcfg.read_interrupt_status().pl_programming_done() {}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
use arbitrary_int::{Number, u6};
|
||||
use arbitrary_int::{prelude::*, u6};
|
||||
use zynq7000::{
|
||||
eth::{InterruptControl, NetworkControl, RxStatus, TxStatus},
|
||||
slcr::reset::EthernetReset,
|
||||
};
|
||||
|
||||
use crate::{clocks::IoClocks, enable_amba_periph_clk, slcr::Slcr, time::Hertz};
|
||||
use crate::{clocks::IoClocks, enable_amba_peripheral_clock, slcr::Slcr, time::Hertz};
|
||||
|
||||
use super::{EthernetId, PsEthernet as _};
|
||||
|
||||
@@ -239,7 +239,7 @@ impl EthernetLowLevel {
|
||||
EthernetId::Eth0 => crate::PeriphSelect::Gem0,
|
||||
EthernetId::Eth1 => crate::PeriphSelect::Gem1,
|
||||
};
|
||||
enable_amba_periph_clk(periph_sel);
|
||||
enable_amba_peripheral_clock(periph_sel);
|
||||
}
|
||||
|
||||
/// Completely configures the clock based on the provided [ClockConfig].
|
||||
@@ -105,132 +105,132 @@ impl PsEthernet for MmioEthernet<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TxClk: MioPin {
|
||||
pub trait TxClockPin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait TxCtrl: MioPin {
|
||||
pub trait TxControlPin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait TxData0: MioPin {
|
||||
pub trait TxData0Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait TxData1: MioPin {
|
||||
pub trait TxData1Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait TxData2: MioPin {
|
||||
pub trait TxData2Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait TxData3: MioPin {
|
||||
pub trait TxData3Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait RxClk: MioPin {
|
||||
pub trait RxClockPin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait RxCtrl: MioPin {
|
||||
pub trait RxControlPin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait RxData0: MioPin {
|
||||
pub trait RxData0Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait RxData1: MioPin {
|
||||
pub trait RxData1Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait RxData2: MioPin {
|
||||
pub trait RxData2Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
pub trait RxData3: MioPin {
|
||||
pub trait RxData3Pin: MioPin {
|
||||
const ETH_ID: EthernetId;
|
||||
}
|
||||
|
||||
pub trait MdClk: MioPin {}
|
||||
pub trait MdIo: MioPin {}
|
||||
pub trait MdClockPin: MioPin {}
|
||||
pub trait MdIoPin: MioPin {}
|
||||
|
||||
impl MdClk for Pin<Mio52> {}
|
||||
impl MdIo for Pin<Mio53> {}
|
||||
impl MdClockPin for Pin<Mio52> {}
|
||||
impl MdIoPin for Pin<Mio53> {}
|
||||
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl TxClk for Pin<Mio16> {
|
||||
impl TxClockPin for Pin<Mio16> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl TxCtrl for Pin<Mio21> {
|
||||
impl TxControlPin for Pin<Mio21> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl TxData0 for Pin<Mio17> {
|
||||
impl TxData0Pin for Pin<Mio17> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl TxData1 for Pin<Mio18> {
|
||||
impl TxData1Pin for Pin<Mio18> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl TxData2 for Pin<Mio19> {
|
||||
impl TxData2Pin for Pin<Mio19> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl TxData3 for Pin<Mio20> {
|
||||
impl TxData3Pin for Pin<Mio20> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl RxClk for Pin<Mio22> {
|
||||
impl RxClockPin for Pin<Mio22> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl RxCtrl for Pin<Mio27> {
|
||||
impl RxControlPin for Pin<Mio27> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl RxData0 for Pin<Mio23> {
|
||||
impl RxData0Pin for Pin<Mio23> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl RxData1 for Pin<Mio24> {
|
||||
impl RxData1Pin for Pin<Mio24> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl RxData2 for Pin<Mio25> {
|
||||
impl RxData2Pin for Pin<Mio25> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||
impl RxData3 for Pin<Mio26> {
|
||||
impl RxData3Pin for Pin<Mio26> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth0;
|
||||
}
|
||||
|
||||
impl TxClk for Pin<Mio28> {
|
||||
impl TxClockPin for Pin<Mio28> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl TxCtrl for Pin<Mio33> {
|
||||
impl TxControlPin for Pin<Mio33> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl TxData0 for Pin<Mio29> {
|
||||
impl TxData0Pin for Pin<Mio29> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl TxData1 for Pin<Mio30> {
|
||||
impl TxData1Pin for Pin<Mio30> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl TxData2 for Pin<Mio31> {
|
||||
impl TxData2Pin for Pin<Mio31> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl TxData3 for Pin<Mio32> {
|
||||
impl TxData3Pin for Pin<Mio32> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl RxClk for Pin<Mio34> {
|
||||
impl RxClockPin for Pin<Mio34> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl RxCtrl for Pin<Mio39> {
|
||||
impl RxControlPin for Pin<Mio39> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl RxData0 for Pin<Mio35> {
|
||||
impl RxData0Pin for Pin<Mio35> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl RxData1 for Pin<Mio36> {
|
||||
impl RxData1Pin for Pin<Mio36> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl RxData2 for Pin<Mio37> {
|
||||
impl RxData2Pin for Pin<Mio37> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
impl RxData3 for Pin<Mio38> {
|
||||
impl RxData3Pin for Pin<Mio38> {
|
||||
const ETH_ID: EthernetId = EthernetId::Eth1;
|
||||
}
|
||||
|
||||
@@ -328,30 +328,30 @@ impl Ethernet {
|
||||
/// configuring all the necessary MIO pins.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_with_mio<
|
||||
TxClkPin: TxClk,
|
||||
TxCtrlPin: TxCtrl,
|
||||
TxData0Pin: TxData0,
|
||||
TxData1Pin: TxData1,
|
||||
TxData2Pin: TxData2,
|
||||
TxData3Pin: TxData3,
|
||||
RxClkPin: RxClk,
|
||||
RxCtrlPin: RxCtrl,
|
||||
RxData0Pin: RxData0,
|
||||
RxData1Pin: RxData1,
|
||||
RxData2Pin: RxData2,
|
||||
RxData3Pin: RxData3,
|
||||
MdClkPin: MdClk,
|
||||
MdIoPin: MdIo,
|
||||
TxClock: TxClockPin,
|
||||
TxControl: TxControlPin,
|
||||
TxData0: TxData0Pin,
|
||||
TxData1: TxData1Pin,
|
||||
TxData2: TxData2Pin,
|
||||
TxData3: TxData3Pin,
|
||||
RxClock: RxClockPin,
|
||||
RxControl: RxControlPin,
|
||||
RxData0: RxData0Pin,
|
||||
RxData1: RxData1Pin,
|
||||
RxData2: RxData2Pin,
|
||||
RxData3: RxData3Pin,
|
||||
MdClock: MdClockPin,
|
||||
MdIo: MdIoPin,
|
||||
>(
|
||||
mut ll: ll::EthernetLowLevel,
|
||||
config: EthernetConfig,
|
||||
tx_clk: TxClkPin,
|
||||
tx_ctrl: TxCtrlPin,
|
||||
tx_data: (TxData0Pin, TxData1Pin, TxData2Pin, TxData3Pin),
|
||||
rx_clk: RxClkPin,
|
||||
rx_ctrl: RxCtrlPin,
|
||||
rx_data: (RxData0Pin, RxData1Pin, RxData2Pin, RxData3Pin),
|
||||
md_pins: Option<(MdClkPin, MdIoPin)>,
|
||||
tx_clk: TxClock,
|
||||
tx_ctrl: TxControl,
|
||||
tx_data: (TxData0, TxData1, TxData2, TxData3),
|
||||
rx_clk: RxClock,
|
||||
rx_ctrl: RxControl,
|
||||
rx_data: (RxData0, RxData1, RxData2, RxData3),
|
||||
md_pins: Option<(MdClock, MdIo)>,
|
||||
) -> Self {
|
||||
Self::common_init(&mut ll, config.mac_address);
|
||||
let tx_mio_config = zynq7000::slcr::mio::Config::builder()
|
||||
@@ -4,7 +4,7 @@ use core::{cell::UnsafeCell, mem::MaybeUninit, sync::atomic::AtomicBool};
|
||||
use crate::{cache::clean_and_invalidate_data_cache_range, eth::AlignedBuffer};
|
||||
|
||||
pub use super::shared::Ownership;
|
||||
use arbitrary_int::{Number, u2, u3, u13, u30};
|
||||
use arbitrary_int::{prelude::*, u2, u3, u13, u30};
|
||||
use vcell::VolatileCell;
|
||||
|
||||
static RX_DESCR_TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
@@ -6,7 +6,7 @@
|
||||
//! # Examples
|
||||
//!
|
||||
//! - [GTC ticks](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/bin/gtc-ticks.rs)
|
||||
use arbitrary_int::Number;
|
||||
use arbitrary_int::prelude::*;
|
||||
|
||||
use cortex_ar::interrupt;
|
||||
use zynq7000::gic::{
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user