finished QSPI flasher
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled

This commit is contained in:
2025-09-27 15:53:20 +02:00
committed by Robin Mueller
parent 8463296c3f
commit dd15acbda3
151 changed files with 1345 additions and 225 deletions

View File

@@ -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.

View File

@@ -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
View File

@@ -1,4 +1,5 @@
/target
target
/app.map
/xsct-output.log
/.vscode

View File

@@ -1,33 +0,0 @@
[workspace]
resolver = "3"
members = [
"zynq7000-rt",
"zynq7000",
"zynq7000-hal",
"zynq7000-embassy",
"examples/simple",
"examples/embassy",
"examples/zedboard",
"zynq-mmu",
"zedboard-fsbl",
"zedboard-bsp",
"zynq-boot-image", "zedboard-qspi-flasher",
]
exclude = [
"zynq-boot-image/tester",
]
# cargo build/run --release
[profile.release]
codegen-units = 1
debug = 2
debug-assertions = false # <-
incremental = false
lto = true
opt-level = 3 # <-
overflow-checks = false # <-
[profile.small]
inherits = "release"
opt-level = "z"

View File

@@ -6,17 +6,23 @@ family of SoCs.
# List of crates
This workspace contains the following crates:
This project contains the following crates:
## Zynq Workspace
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/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.
crate containing basic low-level register definitions.
- The [`zynq7000-mmu`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/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/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.
crate containing support for running the embassy-rs asynchronous run-time.
It also contains the following helper crates:
@@ -25,13 +31,21 @@ It also contains the following helper crates:
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

View File

@@ -1,3 +1,35 @@
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:

312
tools/Cargo.lock generated Normal file
View File

@@ -0,0 +1,312 @@
# 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 = "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 = "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 = "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 = "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 = "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-boot-image"
version = "0.1.0"
dependencies = [
"arbitrary-int 2.0.0",
"bitbybit",
"thiserror",
]
[[package]]
name = "zynq7000-ps7init-extract"
version = "0.1.0"
dependencies = [
"clap",
]

6
tools/Cargo.toml Normal file
View File

@@ -0,0 +1,6 @@
[workspace]
resolver = "3"
members = [
"boot-image-test",
"zynq7000-ps7init-extract",
]

View 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"] }

View File

@@ -2,7 +2,7 @@
use std::{io::Read, path::Path};
use clap::Parser as _;
use zynq_boot_image::{BootHeader, FIXED_BOOT_HEADER_SIZE};
use zynq7000_boot_image::{BootHeader, FIXED_BOOT_HEADER_SIZE};
#[derive(clap::Parser, Debug)]
#[command(version, about)]

251
tools/zynq7000-ps7init-extract/Cargo.lock generated Normal file
View 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",
]

View File

@@ -1,10 +1,7 @@
[workspace]
[package]
name = "tester"
name = "zynq7000-ps7init-extract"
version = "0.1.0"
edition = "2024"
[dependencies]
zynq-boot-image= { path = ".." }
clap = { version = "4", features = ["derive"] }

View File

@@ -0,0 +1,20 @@
use std::path::Path;
use clap::Parser as _;
#[derive(clap::Parser, Debug)]
#[command(version, about)]
pub struct Cli {
/// Path to ps7init.tcl file.
#[arg(short, long)]
path: String,
}
fn main() {
let cli = Cli::parse();
let ps7init_tcl = Path::new(&cli.path);
if !ps7init_tcl.exists() {
eprintln!("File not found: {}", ps7init_tcl.display());
std::process::exit(1);
}
}

View File

@@ -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.

View File

@@ -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", branch = "bump-arbitrary-int" }
[features]
tools = []

18
zynq/.cargo/config.toml Normal file
View File

@@ -0,0 +1,18 @@
[target.armv7a-none-eabihf]
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
View File

@@ -0,0 +1 @@
Cargo.lock

21
zynq/Cargo.toml Normal file
View 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",
]

View File

@@ -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" }
@@ -20,19 +20,35 @@ dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev =
static_cell = "2"
critical-section = "1"
heapless = "0.9"
embedded-io = "0.6"
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
log = "0.4"
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

View File

@@ -79,7 +79,7 @@ async fn main(spawner: Spawner) -> ! {
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 {

View File

@@ -8,7 +8,7 @@ use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zynq7000_hal::{clocks, gic, gpio, gtc, time::Hertz, uart, BootMode, InteruptConfig};
use zynq7000_hal::{BootMode, InteruptConfig, clocks, gic, gpio, gtc, time::Hertz, uart};
use zynq7000_rt as _;

View File

@@ -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"

View File

@@ -11,7 +11,7 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2", features = ["critical-section-single-core"] }
cortex-ar = "0.3"
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
@@ -19,7 +19,7 @@ zynq7000-embassy = { path = "../../zynq7000-embassy" }
zedboard-bsp = { path = "../../zedboard-bsp" }
num_enum = { version = "0.7", default-features = false }
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" }
embedded-io = "0.6"
embedded-io = "0.7"
bitbybit = "1.4"
arbitrary-int = "2"
embedded-io-async = "0.6"

View File

@@ -10,7 +10,7 @@ use embedded_io::Write;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zedboard_bsp::qspi_spansion;
use zynq7000_hal::{clocks, gic, gpio, gtc, prelude::*, qspi, uart, BootMode};
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, prelude::*, qspi, uart};
use zynq7000_rt as _;

View File

@@ -9,7 +9,7 @@ use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{clocks, gic, gpio, gtc, uart, BootMode};
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, uart};
use zynq7000_rt as _;

View File

@@ -1,3 +1,2 @@
[toolchain]
# channel = "stable"
channel = "nightly"

View File

@@ -64,6 +64,8 @@ pub enum SectorArchictecture {
Hybrid = 0x01,
}
pub const PAGE_SIZE: usize = 256;
#[derive(Debug, Clone, Copy)]
pub struct BaseDeviceId {
manufacturer_id: u8,
@@ -221,6 +223,8 @@ pub enum ProgramPageError {
ProgrammingErrorBitSet,
#[error("address error: {0}")]
Addr(#[from] AddrError),
#[error("data is larger than page size {PAGE_SIZE}")]
DataLargerThanPage,
}
pub struct QspiSpansionS25Fl256SIoMode(RefCell<QspiIoMode>);
@@ -428,14 +432,17 @@ impl QspiSpansionS25Fl256SIoMode {
/// This function also takes care of enabling writes before programming the page.
/// This function will block until the operation has completed.
///
/// TODO: Allow smaller write size
pub fn program_page(&mut self, addr: u32, data: &[u8; 256]) -> Result<(), ProgramPageError> {
/// The data length max not exceed the page size [PAGE_SIZE].
pub fn program_page(&mut self, addr: u32, data: &[u8]) -> Result<(), ProgramPageError> {
if addr + data.len() as u32 > u24::MAX.as_u32() {
return Err(AddrError::OutOfRange.into());
}
if !addr.is_multiple_of(0x100) {
return Err(AddrError::Alignment.into());
}
if data.len() > PAGE_SIZE {
return Err(ProgramPageError::DataLargerThanPage);
}
self.write_enable();
let qspi = self.0.get_mut();
let mut transfer = qspi.transfer_guard();
@@ -448,8 +455,9 @@ impl QspiSpansionS25Fl256SIoMode {
transfer.write_word_txd_00(u32::from_ne_bytes(raw_word));
let mut read_index: u32 = 0;
let mut current_byte_index = 0;
let fifo_writes = data.len().div_ceil(4);
// Fill the FIFO until it is full.
for _ in 0..FIFO_DEPTH - 1 {
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(u32::from_ne_bytes(
data[current_byte_index..current_byte_index + 4]
.try_into()
@@ -470,25 +478,38 @@ impl QspiSpansionS25Fl256SIoMode {
}
};
// Immediately fill the FIFO again with the remaining 8 bytes.
wait_for_tx_slot(&mut transfer);
while current_byte_index < data.len() {
// Immediately fill the FIFO again with the remaining 8 bytes.
wait_for_tx_slot(&mut transfer);
transfer.write_word_txd_00(u32::from_ne_bytes(
data[current_byte_index..current_byte_index + 4]
.try_into()
.unwrap(),
));
current_byte_index += 4;
let word = match core::cmp::min(4, data.len() - current_byte_index) {
1 => {
let mut bytes = [0; 4];
bytes[0] = data[current_byte_index];
u32::from_ne_bytes(bytes)
}
2 => {
let mut bytes = [0; 4];
bytes[0..2].copy_from_slice(&data[current_byte_index..current_byte_index + 2]);
u32::from_ne_bytes(bytes)
}
3 => {
let mut bytes = [0; 4];
bytes[0..3].copy_from_slice(&data[current_byte_index..current_byte_index + 3]);
u32::from_ne_bytes(bytes)
}
4 => u32::from_ne_bytes(
data[current_byte_index..current_byte_index + 4]
.try_into()
.unwrap(),
),
_ => unreachable!(),
};
transfer.write_word_txd_00(word);
current_byte_index += 4;
}
wait_for_tx_slot(&mut transfer);
transfer.write_word_txd_00(u32::from_ne_bytes(
data[current_byte_index..current_byte_index + 4]
.try_into()
.unwrap(),
));
while read_index < 256 {
while read_index < data.len() as u32 {
if transfer.read_status().rx_above_threshold() {
transfer.read_rx_data();
read_index = read_index.wrapping_add(4);
@@ -528,11 +549,7 @@ impl QspiSpansionS25Fl256SIoMode {
if dummy_byte {
bytes_to_write += 1;
}
let fifo_writes = if bytes_to_write.is_multiple_of(4) {
bytes_to_write / 4
} else {
(bytes_to_write / 4) + 1
};
let fifo_writes = bytes_to_write.div_ceil(4);
// Fill the FIFO until it is full or all 0 bytes have been written.
for _ in 0..core::cmp::min(fifo_writes, FIFO_DEPTH - 1) {
transfer.write_word_txd_00(0);

View File

@@ -9,13 +9,13 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2", features = ["critical-section-single-core"] }
cortex-ar = { 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" }
zynq-boot-image = { path = "../zynq-boot-image" }
embedded-io = "0.6"
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
log = "0.4"

View File

@@ -8,7 +8,8 @@ use cortex_ar::asm::nop;
use embedded_io::Write as _;
use log::{error, info};
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
use zynq_boot_image::DestinationDevice;
use zynq7000_boot_image::DestinationDevice;
use zynq7000_hal::priv_tim;
use zynq7000_hal::{
BootMode,
clocks::{
@@ -16,9 +17,9 @@ use zynq7000_hal::{
pll::{PllConfig, configure_arm_pll, configure_io_pll},
},
ddr::{DdrClockSetupConfig, configure_ddr_for_ddr3, memtest},
gic, gpio, l2_cache,
devcfg, gic, gpio, l2_cache,
prelude::*,
qspi,
qspi::{self, QSPI_START_ADDRESS},
time::Hertz,
uart::{ClockConfig, Config, Uart},
};
@@ -80,35 +81,37 @@ pub fn main() -> ! {
PllConfig::new_from_target_clock(PS_CLK, IO_CLK).unwrap(),
);
let mut dp = zynq7000::Peripherals::take().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(dp.gpio);
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 uart = Uart::new_with_mio(
dp.uart_1,
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();
uart.write_all(b"-- Zedboard Rust FSBL --\n\r").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(
uart,
logger_uart,
log::LevelFilter::Trace,
false,
)
};
// Set up the global interrupt controller.
let mut gic = gic::GicConfigurator::new_with_init(dp.gicc, dp.gicd);
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();
@@ -117,7 +120,7 @@ pub fn main() -> ! {
info!("Configuring DDR..");
configure_ddr_for_ddr3(
dp.ddrc,
periphs.ddrc,
boot_mode,
DdrClockSetupConfig {
ps_clk: PS_CLK,
@@ -132,9 +135,11 @@ pub fn main() -> ! {
info!("L2 cache init..");
// Set up the L2 cache now that the DDR is in normal operation mode.
l2_cache::init_with_defaults(&mut dp.l2c);
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..");
@@ -154,7 +159,7 @@ pub fn main() -> ! {
)
.expect("QSPI clock calculation failed");
let qspi = qspi::Qspi::new_single_qspi_with_feedback(
dp.qspi,
periphs.qspi,
qspi_clock_config,
embedded_hal::spi::MODE_0,
qspi::IoType::LvCmos33,
@@ -168,19 +173,19 @@ pub fn main() -> ! {
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);
qspi_boot(spansion_lqspi, priv_tim);
}
loop {
cortex_ar::asm::nop();
}
}
fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode) -> ! {
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,
zynq_boot_image::FIXED_BOOT_HEADER_SIZE,
zynq7000_boot_image::FIXED_BOOT_HEADER_SIZE,
)
};
let read_guard = qspi.read_guard();
@@ -189,12 +194,12 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode) -> ! {
core::ptr::copy_nonoverlapping(
QspiSpansionS25Fl256SLinearMode::BASE_ADDR as *mut u8,
boot_header_slice.as_mut_ptr(),
zynq_boot_image::FIXED_BOOT_HEADER_SIZE,
zynq7000_boot_image::FIXED_BOOT_HEADER_SIZE,
);
}
drop(read_guard);
let boot_header = zynq_boot_image::BootHeader::new(boot_header_slice).unwrap();
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) };
@@ -203,45 +208,126 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode) -> ! {
let read_guard = qspi.read_guard();
unsafe {
core::ptr::copy_nonoverlapping(
(QspiSpansionS25Fl256SLinearMode::BASE_ADDR + zynq_boot_image::FIXED_BOOT_HEADER_SIZE)
as *mut u8,
boot_header_slice[zynq_boot_image::FIXED_BOOT_HEADER_SIZE..].as_mut_ptr(),
fsbl_offset - zynq_boot_image::FIXED_BOOT_HEADER_SIZE,
(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 = zynq_boot_image::BootHeader::new_unchecked(boot_header_slice);
let boot_header = zynq7000_boot_image::BootHeader::new_unchecked(boot_header_slice);
let mut name_buf: [u8; 256] = [0; 256];
for image_header in boot_header.image_header_iterator().unwrap() {
let 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();
for partition in image_header
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 let Ok(dest_dev) = section_attrs.destination_device() {
match dest_dev {
DestinationDevice::Pl => {
info!("Loading image '{name}' to PL (FPGA)..");
// TODO: Load the bitstream.
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");
}
DestinationDevice::Ps => {
// TODO: Load the binary into DDR. Jump at lowest load address after all
// partitions were parsed.
}
_ => {
error!("Unsupported destination device {dest_dev:?}");
continue;
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;
}
}
}
}
loop {
cortex_ar::asm::nop();
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!"),
}
}

View File

@@ -4,11 +4,13 @@ version = "0.1.0"
edition = "2024"
[dependencies]
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", branch = "main" }
cortex-ar = { version = "0.3" }
zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" }
zynq7000-boot-image = { path = "../../zynq7000-boot-image" }
zedboard-bsp = { path = "../zedboard-bsp" }
embedded-io = "0.6"
embedded-io = "0.7"
embedded-hal = "1"
log = "0.4"
libm = "0.2"

View 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");
}

View 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
}

View 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"

View File

@@ -7,10 +7,11 @@ use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use embedded_hal::{delay::DelayNs as _, digital::StatefulOutputPin as _};
use embedded_io::Write as _;
use log::info;
use log::{error, info};
use zedboard_bsp::qspi_spansion;
use zynq7000_boot_image::BootHeader;
use zynq7000_hal::{
clocks, gpio, prelude::*, priv_tim, qspi, time::Hertz, uart, BootMode, LevelShifterConfig,
BootMode, LevelShifterConfig, clocks, gpio, prelude::*, priv_tim, qspi, time::Hertz, uart,
};
use zynq7000_rt as _;
@@ -19,6 +20,15 @@ use zynq7000_rt as _;
// Not required for the PAC mode, is required for clean delays in HAL mode.
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_333);
// TODO: Make this configurable somehow?
const BOOT_BIN_BASE_ADDR: usize = 0x1000_0000;
const BOOT_BIN_SIZE_ADDR: usize = 0x900_000;
// Maximum of 16 MB is allowed for now.
const MAX_BOOT_BIN_SIZE: usize = 16 * 1024 * 1024;
const VERIFY_PROGRAMMING: bool = true;
#[allow(dead_code)]
const QSPI_DEV_COMBINATION: qspi::QspiDeviceCombination = qspi::QspiDeviceCombination {
vendor: qspi::QspiVendor::WinbondAndSpansion,
@@ -67,7 +77,7 @@ pub fn main() -> ! {
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
log::LevelFilter::Info,
false,
)
};
@@ -96,7 +106,83 @@ pub fn main() -> ! {
let qspi_io_mode = qspi.into_io_mode(false);
let _spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let mut spansion_qspi = qspi_spansion::QspiSpansionS25Fl256SIoMode::new(qspi_io_mode, true);
let mut boot_bin_slice = unsafe {
core::slice::from_raw_parts(BOOT_BIN_BASE_ADDR as *const _, BootHeader::FIXED_SIZED_PART)
};
// This perform some basic validity checks.
let _boot_header = BootHeader::new(&boot_bin_slice[0..BootHeader::FIXED_SIZED_PART])
.expect("failed to parse boot header");
let boot_bin_size =
unsafe { core::ptr::read_volatile(BOOT_BIN_SIZE_ADDR as *const u32) as usize };
if boot_bin_size == 0 || boot_bin_size > MAX_BOOT_BIN_SIZE {
panic!(
"boot binary size read at address {:#x} is invalid: found {}, must be in range [0, {}]",
BOOT_BIN_SIZE_ADDR, boot_bin_size, MAX_BOOT_BIN_SIZE
);
}
boot_bin_slice =
unsafe { core::slice::from_raw_parts(BOOT_BIN_BASE_ADDR as *const _, boot_bin_size) };
info!(
"flashing boot binary with {} bytes to QSPI address 0x0",
boot_bin_size
);
let mut current_addr = 0;
let mut read_buf = [0u8; 256];
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 {

View File

@@ -11,9 +11,9 @@ keywords = ["no-std", "hal", "amd", "zynq7000", "xilinx", "bare-metal"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", branch = "bump-arbitrary-int" }
cortex-ar = { version = "0.3" }
zynq7000 = { path = "../zynq7000" }
zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" }
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1.0" }
static_assertions = "1.1"
bitbybit = "1.4"
@@ -22,7 +22,7 @@ thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false }
ringbuf = { version = "0.4.8", default-features = false }
embedded-hal-nb = "1"
embedded-io = "0.6"
embedded-io = "0.7"
embedded-hal = "1"
embedded-hal-async = "1"
heapless = "0.9"
@@ -39,7 +39,7 @@ embassy-net-driver = "0.2"
smoltcp = { version = "0.12", default-features = false, features = ["proto-ipv4", "medium-ethernet", "socket-raw"] }
vcell = "0.1"
raw-slicee = "0.1"
embedded-io-async = "0.6"
embedded-io-async = "0.7"
[features]
std = ["thiserror/std", "alloc"]

View File

@@ -14,8 +14,8 @@ extern crate alloc;
use slcr::Slcr;
use zynq7000::{
slcr::{BootModeRegister, BootPllConfig, LevelShifterRegister},
SpiClockPhase, SpiClockPolarity,
slcr::{BootModeRegister, BootPllConfig, LevelShifterRegister},
};
pub mod cache;

View File

@@ -1,13 +1,23 @@
//! # Simple logging providers.
use core::sync::atomic::{AtomicBool, AtomicU8};
static LOGGER_INIT_DONE: AtomicBool = AtomicBool::new(false);
const LOG_SEL_LOCKED: u8 = 1;
const LOG_SEL_UNSAFE_SINGLE_CORE: u8 = 2;
static LOG_SEL: AtomicU8 = AtomicU8::new(0);
/// Blocking UART loggers.
pub mod uart_blocking {
use super::*;
use core::cell::{Cell, RefCell, UnsafeCell};
use embedded_io::Write as _;
use cortex_ar::register::Cpsr;
use critical_section::Mutex;
use log::{LevelFilter, set_logger, set_max_level};
use log::{LevelFilter, Log, set_logger, set_max_level};
use crate::uart::Uart;
@@ -27,7 +37,10 @@ pub mod uart_blocking {
/// For async applications, it is strongly recommended to use the asynchronous ring buffer
/// logger instead.
pub fn init_with_locks(uart: Uart, level: LevelFilter) {
// TODO: Impl debug for Uart
if LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
LOG_SEL.swap(LOG_SEL_LOCKED, core::sync::atomic::Ordering::Relaxed);
critical_section::with(|cs| {
let inner = UART_LOGGER_BLOCKING.0.borrow(cs);
inner.replace(Some(uart));
@@ -53,7 +66,16 @@ pub mod uart_blocking {
})
}
fn flush(&self) {}
fn flush(&self) {
critical_section::with(|cs| {
let mut opt_logger = self.0.borrow(cs).borrow_mut();
if opt_logger.is_none() {
return;
}
let logger = opt_logger.as_mut().unwrap();
logger.flush().unwrap();
});
}
}
pub struct UartLoggerUnsafeSingleThread {
@@ -70,23 +92,6 @@ pub mod uart_blocking {
uart: UnsafeCell::new(None),
};
/// Initialize the logger with a blocking UART instance.
///
/// For async applications, it is strongly recommended to use the asynchronous ring buffer
/// logger instead.
///
/// # Safety
///
/// This is a blocking logger which performs a write WITHOUT a critical section. This logger is
/// NOT thread-safe. Users must ensure that this logger is not used inside a pre-emptive
/// multi-threading context and interrupt handlers.
pub unsafe fn create_unsafe_single_thread_logger(uart: Uart) -> UartLoggerUnsafeSingleThread {
UartLoggerUnsafeSingleThread {
skip_in_isr: Cell::new(false),
uart: UnsafeCell::new(Some(uart)),
}
}
/// Initialize the logger with a blocking UART instance which does not use locks.
///
/// # Safety
@@ -95,6 +100,13 @@ pub mod uart_blocking {
/// NOT thread-safe, which might lead to garbled output. Log output in ISRs can optionally be
/// surpressed.
pub unsafe fn init_unsafe_single_core(uart: Uart, level: LevelFilter, skip_in_isr: bool) {
if LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
LOG_SEL.swap(
LOG_SEL_UNSAFE_SINGLE_CORE,
core::sync::atomic::Ordering::Relaxed,
);
let opt_uart = unsafe { &mut *UART_LOGGER_UNSAFE_SINGLE_THREAD.uart.get() };
opt_uart.replace(uart);
UART_LOGGER_UNSAFE_SINGLE_THREAD
@@ -134,7 +146,22 @@ pub mod uart_blocking {
.unwrap();
}
fn flush(&self) {}
fn flush(&self) {
let uart_mut = unsafe { &mut *self.uart.get() }.as_mut();
if uart_mut.is_none() {
return;
}
uart_mut.unwrap().flush().unwrap();
}
}
// Flush the selected logger instance.
pub fn flush() {
match LOG_SEL.load(core::sync::atomic::Ordering::Relaxed) {
val if val == LOG_SEL_LOCKED => UART_LOGGER_BLOCKING.flush(),
val if val == LOG_SEL_UNSAFE_SINGLE_CORE => UART_LOGGER_UNSAFE_SINGLE_THREAD.flush(),
_ => (),
}
}
}
@@ -205,6 +232,9 @@ pub mod rb {
}
pub fn init(level: LevelFilter) {
if super::LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
critical_section::with(|cs| {
let rb = StaticRb::<u8, 4096>::default();
let rb_ref = LOGGER_RB.ring_buf.borrow(cs);

Some files were not shown because too many files have changed in this diff Show More