Compare commits

..

3 Commits

Author SHA1 Message Date
Björn Stein 606fef6d5c add mutex for print and println macros 2019-08-30 15:57:56 +08:00
Björn Stein 1f4add397b mailbox: fix race condition 2019-08-30 15:56:44 +08:00
Björn Stein 60bab77a19 multiprocessing demo 2019-08-30 15:56:11 +08:00
124 changed files with 2535 additions and 37309 deletions

View File

@ -1,4 +1,6 @@
[target.armv7-none-eabihf] [target.armv7-none-eabihf]
runner = "./runner.sh"
linker = "arm-none-eabihf-gcc"
rustflags = [ rustflags = [
"-C", "link-arg=-Tlink.x", "-C", "link-arg=-Tlink.x",
"-C", "target-feature=a9,armv7-a,neon", "-C", "target-feature=a9,armv7-a,neon",

1
.gitignore vendored
View File

@ -1 +0,0 @@
/target

229
Cargo.lock generated
View File

@ -2,213 +2,47 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "bit_field" name = "bit_field"
version = "0.10.1" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.3.4" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "compiler_builtins" name = "compiler_builtins"
version = "0.1.35" version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/rust-lang-nursery/compiler-builtins#36da64f20e96206ac279f700586817c8abe3bdf8"
[[package]]
name = "core_io"
version = "0.1.20200410"
dependencies = [
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "embedded-hal"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "experiments"
version = "0.0.0"
dependencies = [
"embedded-hal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libasync 0.0.0",
"libboard_zynq 0.0.0",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"libsupport_zynq 0.0.0",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fatfs"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"core_io 0.1.20200410",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libasync"
version = "0.0.0"
dependencies = [
"embedded-hal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libcortex_a9 0.0.0",
"nb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libboard_zynq"
version = "0.0.0"
dependencies = [
"bit_field 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"embedded-hal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"nb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libconfig"
version = "0.1.0"
dependencies = [
"core_io 0.1.20200410",
"fatfs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"libboard_zynq 0.0.0",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libcortex_a9"
version = "0.0.0"
dependencies = [
"bit_field 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libregister 0.0.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libregister"
version = "0.0.0"
dependencies = [
"bit_field 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libsupport_zynq"
version = "0.0.0"
dependencies = [
"compiler_builtins 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"libboard_zynq 0.0.0",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"linked_list_allocator 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "linked_list_allocator"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "managed" name = "managed"
version = "0.7.2" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nb 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nb"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "r0" name = "r0"
version = "1.0.0" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "smoltcp" name = "smoltcp"
version = "0.6.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "szl"
version = "0.1.0"
dependencies = [
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"core_io 0.1.20200410",
"libboard_zynq 0.0.0",
"libconfig 0.1.0",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"libsupport_zynq 0.0.0",
"log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "vcell" name = "vcell"
version = "0.1.2" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -216,26 +50,27 @@ name = "volatile-register"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zc706"
version = "0.0.0"
dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"compiler_builtins 0.1.19 (git+https://github.com/rust-lang-nursery/compiler-builtins)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[metadata] [metadata]
"checksum bit_field 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" "checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum compiler_builtins 0.1.19 (git+https://github.com/rust-lang-nursery/compiler-builtins)" = "<none>"
"checksum compiler_builtins 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455" "checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
"checksum embedded-hal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum fatfs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "93079df23039e52059e1f03b4c29fb0c72da2c792aad91bb2236c9fb81d3592e" "checksum smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fef582369edb298c6c41319a544ca9c4e83622f226055ccfcb35974fbb55ed34"
"checksum linked_list_allocator 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "660b26e6156a7d00eefb19052fe1943cf5ab2f353a723a577fad6ba2f99d1f90" "checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
"checksum managed 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
"checksum nb 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
"checksum nb 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
"checksum r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211"
"checksum smoltcp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" "checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

View File

@ -1,24 +1,25 @@
[workspace] [package]
members = [ name = "zc706"
"libregister", version = "0.0.0"
"libcortex_a9", authors = ["Astro <astro@spaceboyz.net>"]
"libboard_zynq", edition = "2018"
"libsupport_zynq",
"libasync", [profile.dev]
"libconfig", panic = "abort"
"libcoreio", lto = false
"experiments",
"szl",
]
[profile.release] [profile.release]
panic = "abort" panic = "abort"
debug = true debug = true
codegen-units = 1
opt-level = 'z'
lto = true
debug-assertions = false
overflow-checks = false
[patch.crates-io] [features]
core_io = { path = "./libcoreio" } target_zc706 = []
target_cora_z7_10 = []
default = ["target_zc706"]
[dependencies]
r0 = "0.2"
volatile-register = "0.2"
bit_field = "0.10"
compiler_builtins = { git = "https://github.com/rust-lang-nursery/compiler-builtins", no-default-features = true, features = ["mem", "no-lang-items"]}
smoltcp = { version = "0.5", default-features = false, features = ["proto-ipv4", "socket-tcp"] }

View File

@ -1,31 +1,53 @@
# Build # Build
```shell ```shell
nix-shell --command "cargo xbuild --release -p experiments" nix-shell --command "cargo xbuild --release"
``` ```
Currently the ELF output is placed at `target/armv7-none-eabihf/release/experiments`
# Debug # Debug
## Running on the ZC706 ## Using the Xilinx toolchain
Tested with the ZC706 board.
Run the Xilinx Microprocessor Debugger:
```shell
/opt/Xilinx/14.7/ISE_DS/EDK/bin/lin64/xmd
```
Connect to target (given it is connected and you have permissions):
```tcl
connect arm hw
```
Leave xmd running.
Start the Xilinx version of the GNU debugger with your latest build:
```shell
/opt/Xilinx/14.7/ISE_DS/EDK/gnu/arm/lin/bin/arm-xilinx-linux-gnueabi-gdb zc706
```
Connect the debugger to xmd over TCP on localhost:
```gdb
target remote :1234
```
Proceed using gdb with `load`, `c`
## Using OpenOCD
### Running on the ZC706
```shell ```shell
nix-shell --command "cargo xbuild --release -p experiments" nix-shell --command "cargo xbuild --release"
cd openocd cd openocd
openocd -f zc706.cfg openocd -f zc706.cfg
``` ```
## Running on the Cora Z7-10 ### Running on the Cora Z7-10
```shell ```shell
nix-shell --command "cd experiments && cargo xbuild --release --no-default-features --features=target_cora_z7_10" nix-shell --command "cargo xbuild --release --no-default-features --features=target_cora_z7_10"
cd openocd cd openocd
openocd -f cora-z7-10.cfg openocd -f cora-z7-10.cfg
``` ```
## Loading a bitstream into volatile memory
```shell
openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit"
```

View File

@ -12,7 +12,7 @@ fn main() {
.unwrap(); .unwrap();
println!("cargo:rustc-link-search={}", out.display()); println!("cargo:rustc-link-search={}", out.display());
// Only re-run the build script when link.x is changed, // Only re-run the build script when memory.x is changed,
// instead of when any part of the source code changes. // instead of when any part of the source code changes.
println!("cargo:rerun-if-changed=link.x"); println!("cargo:rerun-if-changed=link.x");
} }

View File

@ -1,38 +1,27 @@
{ # Use master branch of the overlay by default
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
}:
let let
pkgs = import <nixpkgs> { overlays = [ (import ./nix/mozilla-overlay.nix) ]; }; pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
rustPlatform = (import ./nix/rust-platform.nix { inherit pkgs; });
build-crate = name: crate: features:
rustPlatform.buildRustPackage rec {
name = "${crate}";
src = ./.;
cargoSha256 = "1f2psa1g41pl2j8n60hhik2s2pqdfjhr5capimvajf81kxrnn2ck";
nativeBuildInputs = [ pkgs.cargo-xbuild ];
buildPhase = ''
export XARGO_RUST_SRC="${rustPlatform.rust.rustc.src}/src"
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
pushd ${crate}
cargo xbuild --release --frozen \
--no-default-features \
--features=${features}
popd
'';
installPhase = ''
mkdir -p $out $out/nix-support
cp target/armv7-none-eabihf/release/${name} $out/${name}.elf
echo file binary-dist $out/${name}.elf >> $out/nix-support/hydra-build-products
'';
doCheck = false;
dontFixup = true;
};
in in
{ with pkgs;
zc706-experiments = build-crate "zc706-experiments" "experiments" "target_zc706"; let
cora-experiments = build-crate "cora-experiments" "experiments" "target_cora_z7_10"; rustcSrc = fetchgit {
redpitaya-experiments = build-crate "redpitaya-experiments" "experiments" "target_redpitaya"; url = https://github.com/rust-lang/rust.git;
zc706-fsbl = (import ./nix/fsbl.nix { inherit pkgs; }); # master of 2019-08-18
zc706-szl = build-crate "zc706-szl" "szl" "target_zc706"; rev = "ea52be482ab4945fda63cb65b6a198309a041e3c";
} sha256 = "1spifrkvyyrh1gazqrby29fjqsdbwvajv9k9f6mk2ldrdghlsd21";
fetchSubmodules = true;
};
targets = [
];
rust =
rustChannelOfTargets "nightly" null targets;
rustPlatform = recurseIntoAttrs (makeRustPlatform {
rustc = rust // { src = rustcSrc; };
cargo = rust;
});
in {
inherit pkgs rustPlatform rustcSrc;
}

View File

@ -1,21 +0,0 @@
[package]
name = "experiments"
description = "Developing bare-metal Rust on Zynq"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[features]
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706"]
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10", "libsupport_zynq/target_cora_z7_10"]
target_redpitaya = ["libboard_zynq/target_redpitaya", "libsupport_zynq/target_redpitaya"]
default = ["target_zc706"]
[dependencies]
log = "0.4"
embedded-hal = "0.2"
libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" }
libboard_zynq = { path = "../libboard_zynq" }
libsupport_zynq = { path = "../libsupport_zynq", default-features = false, features = ["panic_handler"]}
libasync = { path = "../libasync" }

View File

@ -1,62 +0,0 @@
ENTRY(Reset);
MEMORY
{
/* 256 kB On-Chip Memory */
OCM : ORIGIN = 0, LENGTH = 0x30000
OCM3 : ORIGIN = 0xFFFF0000, LENGTH = 0x10000
}
SECTIONS
{
.text :
{
KEEP(*(.text.exceptions));
*(.text.boot);
*(.text .text.*);
} > OCM
.rodata : ALIGN(4)
{
*(.rodata .rodata.*);
} > OCM
.data : ALIGN(4)
{
*(.data .data.*);
} > OCM
.bss (NOLOAD) : ALIGN(4)
{
__bss_start = .;
*(.bss .bss.*);
. = ALIGN(4);
__bss_end = .;
} > OCM3
.stack1 (NOLOAD) : ALIGN(8) {
__stack1_end = .;
. += 0x200;
__stack1_start = .;
} > OCM3
.stack0 (NOLOAD) : ALIGN(8) {
__stack0_end = .;
. = ORIGIN(OCM3) + LENGTH(OCM3) - 8;
__stack0_start = .;
/* unused heap0 to prevent the linker from complaining*/
__heap0_start = .;
__heap0_end = .;
} > OCM3
/DISCARD/ :
{
/* Unused exception related info that only wastes space */
*(.ARM.exidx);
*(.ARM.exidx.*);
*(.ARM.extab.*);
}
}
ASSERT(SIZEOF(.stack0) >= 0x1000, "less than 4 KB left for stack");

View File

@ -1,308 +0,0 @@
#![no_std]
#![no_main]
#![feature(const_in_array_repeat_expressions)]
#![feature(naked_functions)]
extern crate alloc;
use alloc::collections::BTreeMap;
use libasync::{
delay,
smoltcp::{Sockets, TcpStream},
task,
};
use libboard_zynq::{
self as zynq,
clocks::source::{ArmPll, ClockSource, IoPll},
clocks::Clocks,
println, stdio,
mpcore,
gic,
smoltcp::{
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
time::Instant,
wire::{EthernetAddress, IpAddress, IpCidr},
},
time::Milliseconds,
};
#[cfg(feature = "target_zc706")]
use libboard_zynq::print;
use libcortex_a9::{
mutex::Mutex,
l2c::enable_l2_cache,
sync_channel::{Sender, Receiver},
sync_channel,
regs::{MPIDR, SP},
spin_lock_yield, notify_spin_lock,
asm
};
use libregister::{RegisterR, RegisterW};
use libsupport_zynq::{
boot, ram,
};
use log::{info, warn};
use core::sync::atomic::{AtomicBool, Ordering};
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
static mut CORE1_REQ: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
static mut CORE1_RES: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
extern "C" {
static mut __stack1_start: u32;
}
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn IRQ() {
if MPIDR.read().cpu_id() == 1{
let mpcore = mpcore::RegisterBlock::mpcore();
let mut gic = gic::InterruptController::gic(mpcore);
let id = gic.get_interrupt_id();
if id.0 == 0 {
gic.end_interrupt(id);
asm::exit_irq();
SP.write(&mut __stack1_start as *mut _ as u32);
asm::enable_irq();
CORE1_RESTART.store(false, Ordering::Relaxed);
notify_spin_lock();
main_core1();
}
}
stdio::drop_uart();
println!("IRQ");
loop {}
}
pub fn restart_core1() {
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
CORE1_RESTART.store(true, Ordering::Relaxed);
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
while CORE1_RESTART.load(Ordering::Relaxed) {
spin_lock_yield();
}
}
#[no_mangle]
pub fn main_core0() {
// zynq::clocks::CpuClocks::enable_io(1_250_000_000);
enable_l2_cache();
println!("\nZynq experiments");
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
interrupt_controller.enable_interrupts();
// ps7_init::apply();
libboard_zynq::stdio::drop_uart();
libboard_zynq::logger::init().unwrap();
log::set_max_level(log::LevelFilter::Trace);
info!(
"Boot mode: {:?}",
zynq::slcr::RegisterBlock::slcr()
.boot_mode
.read()
.boot_mode_pins()
);
#[cfg(feature = "target_zc706")]
const CPU_FREQ: u32 = 800_000_000;
#[cfg(feature = "target_cora_z7_10")]
const CPU_FREQ: u32 = 650_000_000;
#[cfg(feature = "target_redpitaya")]
const CPU_FREQ: u32 = 800_000_000;
info!("Setup clock sources...");
ArmPll::setup(2 * CPU_FREQ);
Clocks::set_cpu_freq(CPU_FREQ);
#[cfg(feature = "target_zc706")]
{
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart();
}
#[cfg(feature = "target_cora_z7_10")]
{
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart();
}
#[cfg(feature = "target_redpitaya")]
{
IoPll::setup(1_000_000_000);
libboard_zynq::stdio::drop_uart();
}
info!("PLLs set up");
let clocks = zynq::clocks::Clocks::get();
info!(
"CPU Clocks: {}/{}/{}/{}",
clocks.cpu_6x4x(),
clocks.cpu_3x2x(),
clocks.cpu_2x(),
clocks.cpu_1x()
);
let timer = libboard_zynq::timer::GlobalTimer::start();
let mut ddr = zynq::ddr::DdrRam::ddrram();
#[cfg(not(feature = "target_zc706"))]
ddr.memtest();
ram::init_alloc_ddr(&mut ddr);
boot::Core1::start(false);
let core1_req = unsafe { &mut CORE1_REQ.0 };
let core1_res = unsafe { &mut CORE1_RES.1 };
task::block_on(async {
for i in 0..10 {
restart_core1();
core1_req.async_send(i).await;
let j = core1_res.async_recv().await;
println!("{} -> {}", i, j);
}
});
unsafe {
core1_req.drop_elements();
}
// Test I2C
#[cfg(feature = "target_zc706")]
{
let mut i2c = zynq::i2c::I2c::i2c0();
i2c.init().unwrap();
println!("I2C bit-banging enabled");
let mut eeprom = zynq::i2c::eeprom::EEPROM::new(&mut i2c, 16);
// Write to 0x00 and 0x08
let eeprom_buffer: [u8; 22] = [
0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,
];
eeprom.write(0x00, &eeprom_buffer[0..6]).unwrap();
eeprom.write(0x08, &eeprom_buffer[6..22]).unwrap();
println!("Data written to EEPROM");
let mut eeprom_buffer = [0u8; 24];
// Read from 0x00
eeprom.read(0x00, &mut eeprom_buffer).unwrap();
print!("Data read from EEPROM @ 0x00: (hex) ");
for i in 0..6 {
print!("{:02x} ", eeprom_buffer[i]);
}
println!("");
// Read from 0x08
eeprom.read(0x08, &mut eeprom_buffer).unwrap();
print!("Data read from EEPROM @ 0x08: (hex) ");
for i in 0..16 {
print!("{:02x} ", eeprom_buffer[i]);
}
println!("");
}
let eth = zynq::eth::Eth::eth0(HWADDR.clone());
println!("Eth on");
const RX_LEN: usize = 4096;
// Number of transmission buffers (minimum is two because with
// one, duplicate packet transmission occurs)
const TX_LEN: usize = 4096;
let eth = eth.start_rx(RX_LEN);
let mut eth = eth.start_tx(TX_LEN);
let ethernet_addr = EthernetAddress(HWADDR);
// IP stack
let local_addr = IpAddress::v4(192, 168, 1, 51);
let mut ip_addrs = [IpCidr::new(local_addr, 24)];
let routes = Routes::new(BTreeMap::new());
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let mut iface = EthernetInterfaceBuilder::new(&mut eth)
.ethernet_addr(ethernet_addr)
.ip_addrs(&mut ip_addrs[..])
.routes(routes)
.neighbor_cache(neighbor_cache)
.finalize();
Sockets::init(32);
const TCP_PORT: u16 = 19;
// (rx, tx)
let stats = alloc::rc::Rc::new(core::cell::RefCell::new((0, 0)));
let stats_tx = stats.clone();
task::spawn(async move {
while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
let stats_tx = stats_tx.clone();
task::spawn(async move {
let tx_data = (0..=255).take(4096).collect::<alloc::vec::Vec<u8>>();
loop {
// const CHUNK_SIZE: usize = 65536;
// match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
match stream.send_slice(&tx_data[..]).await {
Ok(len) => stats_tx.borrow_mut().1 += tx_data.len(), //CHUNK_SIZE,
Err(e) => {
warn!("tx: {:?}", e);
break
}
}
}
});
}
});
let stats_rx = stats.clone();
task::spawn(async move {
while let Ok(stream) = TcpStream::accept(TCP_PORT+1, 0x10_0000, 0x10_0000).await {
let stats_rx = stats_rx.clone();
task::spawn(async move {
loop {
match stream.recv(|buf| (buf.len(), buf.len())).await {
Ok(len) => stats_rx.borrow_mut().0 += len,
Err(e) => {
warn!("rx: {:?}", e);
break
}
}
}
});
}
});
let mut countdown = timer.countdown();
task::spawn(async move {
loop {
delay(&mut countdown, Milliseconds(1000)).await;
let timestamp = timer.get_us().0;
let seconds = timestamp / 1_000_000;
let micros = timestamp % 1_000_000;
let (rx, tx) = {
let mut stats = stats.borrow_mut();
let result = *stats;
*stats = (0, 0);
result
};
info!("time: {:6}.{:06}s, rx: {}k/s, tx: {}k/s", seconds, micros, rx / 1024, tx / 1024);
}
});
Sockets::run(&mut iface, || {
Instant::from_millis(timer.get_time().0 as i64)
})
}
static DONE: Mutex<bool> = Mutex::new(false);
#[no_mangle]
pub fn main_core1() {
println!("Hello from core1!");
let mut interrupt_controller = gic::InterruptController::gic(mpcore::RegisterBlock::mpcore());
interrupt_controller.enable_interrupts();
let req = unsafe { &mut CORE1_REQ.1 };
let res = unsafe { &mut CORE1_RES.0 };
for i in req {
res.send(i * i);
}
println!("core1 done!");
*DONE.lock() = true;
loop {}
}

View File

@ -1,18 +0,0 @@
[package]
name = "libasync"
description = "low-level async support"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[dependencies]
#futures = { version = "0.3", default-features = false }
pin-utils = "0.1.0-alpha.4"
embedded-hal = "0.2"
nb = "0.1"
libcortex_a9 = { path = "../libcortex_a9" }
[dependencies.smoltcp]
version = "0.6"
default-features = false
features = ["alloc"]

View File

@ -1,7 +0,0 @@
use embedded_hal::timer::CountDown;
use crate::block_async;
pub async fn delay<T: CountDown<Time=C>, C>(timer: &mut T, count: C) {
timer.start(count);
let _ = block_async!(timer.wait()).await;
}

View File

@ -1,157 +0,0 @@
use core::{
cell::{RefCell, UnsafeCell},
future::Future,
mem::MaybeUninit,
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
use alloc::{boxed::Box, vec::Vec};
//use futures::future::FutureExt;
use pin_utils::pin_mut;
// NOTE `*const ()` is &AtomicBool
static VTABLE: RawWakerVTable = {
unsafe fn clone(p: *const ()) -> RawWaker {
RawWaker::new(p, &VTABLE)
}
unsafe fn wake(p: *const ()) {
wake_by_ref(p)
}
unsafe fn wake_by_ref(p: *const ()) {
(*(p as *const AtomicBool)).store(true, Ordering::Relaxed)
}
unsafe fn drop(_: *const ()) {
// no-op
}
RawWakerVTable::new(clone, wake, wake_by_ref, drop)
};
/// ready should not move as long as this waker references it. That is
/// the reason for keeping Tasks in a pinned box.
fn wrap_waker(ready: &AtomicBool) -> Waker {
unsafe { Waker::from_raw(RawWaker::new(ready as *const _ as *const (), &VTABLE)) }
}
/// A single-threaded executor
///
/// This is a singleton
pub struct Executor {
// Entered block_on() already?
in_block_on: RefCell<bool>,
/// Tasks reside on the heap, so that we just queue pointers. They
/// must also be pinned in memory because our RawWaker is a pointer
/// to their `ready` field.
tasks: RefCell<Vec<Pin<Box<Task>>>>,
}
impl Executor {
/// Creates a new instance of the executor
pub fn new() -> Self {
Self {
in_block_on: RefCell::new(false),
tasks: RefCell::new(Vec::new()),
}
}
pub fn block_on<T>(&self, f: impl Future<Output = T>) -> T {
// we want to avoid reentering `block_on` because then all the code
// below has to become more complex. It's also likely that the
// application will only call `block_on` once on an infinite task
// (`Future<Output = !>`)
{
let mut in_block_on = self.in_block_on.borrow_mut();
if *in_block_on {
panic!("nested `block_on`");
}
*in_block_on = true;
}
pin_mut!(f);
let ready = AtomicBool::new(true);
let waker = wrap_waker(&ready);
let mut backup = Vec::new();
let val = loop {
// advance the main task
if ready.load(Ordering::Relaxed) {
ready.store(false, Ordering::Relaxed);
// println!("run block_on");
let mut cx = Context::from_waker(&waker);
if let Poll::Ready(val) = f.as_mut().poll(&mut cx) {
break val;
}
// println!("ran block_on");
}
// advance all tasks
core::mem::swap(&mut *self.tasks.borrow_mut(), &mut backup);
for mut task in backup.drain(..) {
// NOTE we don't need a CAS operation here because `wake` invocations that come from
// interrupt handlers (the only source of 'race conditions' (!= data races)) are
// "oneshot": they'll issue a `wake` and then disable themselves to not run again
// until the woken task has made more work
if task.ready.load(Ordering::Relaxed) {
// we are about to service the task so switch the `ready` flag to `false`
task.ready.store(false, Ordering::Relaxed);
let waker = wrap_waker(&task.ready);
let mut cx = Context::from_waker(&waker);
let ready = task.f.as_mut().poll(&mut cx).is_ready();
if ready {
// Task is finished, do not requeue
continue;
}
}
// Requeue
self.tasks.borrow_mut().push(task);
}
// // try to sleep; this will be a no-op if any of the previous tasks generated a SEV or an
// // interrupt ran (regardless of whether it generated a wake-up or not)
// asm::wfe();
};
self.in_block_on.replace(false);
val
}
pub fn spawn(&self, f: impl Future + 'static) {
let task = Box::pin(Task::new(f));
self.tasks.borrow_mut().push(task);
}
}
pub struct Task {
ready: AtomicBool,
f: Pin<Box<dyn Future<Output = ()>>>,
}
impl Task {
fn new(f: impl Future + 'static) -> Self {
Task {
ready: AtomicBool::new(true),
f: Box::pin(async { f.await; }),
}
}
}
/// Returns a handle to the executor singleton
///
/// This lazily initializes the executor and allocator when first called
pub(crate) fn current() -> &'static Executor {
static INIT: AtomicBool = AtomicBool::new(false);
static mut EXECUTOR: UnsafeCell<MaybeUninit<Executor>> = UnsafeCell::new(MaybeUninit::uninit());
if INIT.load(Ordering::Relaxed) {
unsafe { &*(EXECUTOR.get() as *const Executor) }
} else {
unsafe {
let executorp = EXECUTOR.get() as *mut Executor;
executorp.write(Executor::new());
INIT.store(true, Ordering::Relaxed);
&*executorp
}
}
}

View File

@ -1,36 +0,0 @@
#![no_std]
extern crate alloc;
pub mod task;
pub mod executor;
mod delay;
pub use delay::delay;
pub mod smoltcp;
/// Reexport for macro use
pub use nb;
/// The `nb` crate's `block!` macro adapted for async fns
///
/// Call `.await` on the result!
#[macro_export]
macro_rules! block_async {
($e:expr) => {
async {
loop {
#[allow(unreachable_patterns)]
match $e {
Err($crate::nb::Error::Other(e)) => {
#[allow(unreachable_code)]
break Err(e)
},
Err($crate::nb::Error::WouldBlock) =>
$crate::task::r#yield().await,
Ok(x) => break Ok(x),
}
}
}
}
}

View File

@ -1,94 +0,0 @@
use core::{
cell::RefCell,
task::Waker,
};
use alloc::vec::Vec;
use smoltcp::{
iface::EthernetInterface,
phy::Device,
socket::SocketSet,
time::Instant,
};
use crate::task;
mod tcp_stream;
pub use tcp_stream::TcpStream;
static mut SOCKETS: Option<Sockets> = None;
pub struct Sockets {
sockets: RefCell<SocketSet<'static, 'static, 'static>>,
wakers: RefCell<Vec<Waker>>,
}
impl Sockets {
pub fn init(max_sockets: usize) {
let mut sockets_storage = Vec::with_capacity(max_sockets);
for _ in 0..max_sockets {
sockets_storage.push(None);
}
let sockets = RefCell::new(SocketSet::new(sockets_storage));
let wakers = RefCell::new(Vec::new());
let instance = Sockets {
sockets,
wakers,
};
// println!("sockets initialized");
unsafe { SOCKETS = Some(instance); }
}
/// Block and run executor indefinitely while polling the smoltcp
/// iface
pub fn run<'b, 'c, 'e, D: for<'d> Device<'d>>(
iface: &mut EthernetInterface<'b, 'c, 'e, D>,
mut get_time: impl FnMut() -> Instant,
) -> ! {
task::block_on(async {
loop {
let instant = get_time();
Self::instance().poll(iface, instant);
task::r#yield().await;
}
})
}
pub(crate) fn instance() -> &'static Self {
unsafe { SOCKETS.as_ref().expect("Sockets") }
}
fn poll<'b, 'c, 'e, D: for<'d> Device<'d>>(
&self,
iface: &mut EthernetInterface<'b, 'c, 'e, D>,
instant: Instant
) {
let processed = {
let mut sockets = self.sockets.borrow_mut();
match iface.poll(&mut sockets, instant) {
Ok(processed) => processed,
Err(_) => true,
}
};
if processed {
let mut wakers = self.wakers.borrow_mut();
for waker in wakers.drain(..) {
waker.wake();
}
}
}
/// TODO: this was called through eg. TcpStream, another poll()
/// might want to send packets before sleeping for an interrupt.
pub(crate) fn register_waker(waker: Waker) {
let mut wakers = Self::instance().wakers.borrow_mut();
for (i, w) in wakers.iter().enumerate() {
if w.will_wake(&waker) {
let last = wakers.len() - 1;
wakers.swap(i, last);
return;
}
}
wakers.push(waker);
}
}

View File

@ -1,283 +0,0 @@
//! async TCP interface
//!
//! TODO: implement futures AsyncRead/AsyncWrite/Stream/Sink interfaces
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use alloc::vec::Vec;
use smoltcp::{
Error, Result,
socket::{
SocketHandle, SocketRef,
TcpSocketBuffer, TcpSocket, TcpState,
},
time::Duration,
};
use crate::task;
use super::Sockets;
/// References a smoltcp TcpSocket
pub struct TcpStream {
handle: SocketHandle,
}
/// Wait while letting `$f()` poll a stream's socket
macro_rules! poll_stream {
($stream: expr, $output: ty, $f: expr) => (async {
struct Adhoc<'a> {
stream: &'a TcpStream,
}
impl<'a> Future for Adhoc<'a> {
type Output = $output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let result = self.stream.with_socket($f);
if !result.is_ready() {
Sockets::register_waker(cx.waker().clone());
}
result
}
}
Adhoc { stream: $stream }.await
})
}
impl TcpStream {
/// Allocates sockets and its buffers, registers it in the
/// SocketSet.
///
/// Not `pub` as the result can not yet be used. Use `listen()` or
/// `connect()` to obtain a valid TcpStream.
fn new(rx_bufsize: usize, tx_bufsize: usize) -> Self {
fn uninit_vec<T>(size: usize) -> Vec<T> {
let mut result = Vec::with_capacity(size);
unsafe {
result.set_len(size);
}
result
}
let rx_buffer = TcpSocketBuffer::new(uninit_vec(rx_bufsize));
let tx_buffer = TcpSocketBuffer::new(uninit_vec(tx_bufsize));
let socket = TcpSocket::new(rx_buffer, tx_buffer);
let handle = Sockets::instance().sockets.borrow_mut()
.add(socket);
TcpStream { handle }
}
/// Operate on the referenced TCP socket
fn with_socket<F, R>(&self, f: F) -> R
where
F: FnOnce(SocketRef<TcpSocket>) -> R,
{
let mut sockets = Sockets::instance().sockets.borrow_mut();
let socket_ref = sockets.get::<TcpSocket>(self.handle);
f(socket_ref)
}
/// Listen for the next incoming connection on a TCP
/// port. Succeeds on connection attempt.
///
/// Calling this serially in a loop will cause slow/botched
/// connection attempts stall any more new connections. Use
/// `listen()` with a backlog instead.
pub async fn accept(port: u16, rx_bufsize: usize, tx_bufsize: usize) -> Result<Self> {
let stream = Self::new(rx_bufsize, tx_bufsize);
// Set socket to listen
stream.with_socket(|mut s| s.listen(port))?;
// Wait for a connection
poll_stream!(&stream, (), |socket| {
if socket.state() != TcpState::Listen {
Poll::Ready(())
} else {
Poll::Pending
}
}).await;
Ok(stream)
}
/// Probe the receive buffer
///
/// Your callback will only be called when there is some data available,
/// and it must consume at least one byte. It returns a tuple with the
/// number of bytes it consumed, and a user-defined return value of type R.
pub async fn recv<F, R>(&self, f: F) -> Result<R>
where
F: Fn(&[u8]) -> (usize, R),
{
struct Recv<'a, F: FnOnce(&[u8]) -> (usize, R), R> {
stream: &'a TcpStream,
f: F,
}
impl<'a, F: Fn(&[u8]) -> (usize, R), R> Future for Recv<'a, F, R> {
type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let result = self.stream.with_socket(|mut socket| {
if socket_is_handhshaking(&socket) {
return Ok(Poll::Pending);
}
socket.recv(|buf| {
if buf.len() > 0 {
let (amount, result) = (self.f)(buf);
assert!(amount > 0);
(amount, Poll::Ready(Ok(result)))
} else {
(0, Poll::Pending)
}
})
});
match result {
Ok(Poll::Pending) => {
Sockets::register_waker(cx.waker().clone());
Poll::Pending
}
Ok(result) => {
result
}
Err(e) =>
Poll::Ready(Err(e)),
}
}
}
Recv {
stream: self,
f,
}.await
}
/// Wait until there is any space in the socket's send queue
async fn wait_can_send(&self) -> Result<()> {
poll_stream!(self, Result<()>, |socket| {
if socket_is_handhshaking(&socket) {
Poll::Pending
} else if socket.can_send() {
Poll::Ready(Ok(()))
} else if ! socket.may_send() {
Poll::Ready(Err(Error::Truncated))
} else {
Poll::Pending
}
}).await
}
/// Yields to wait for more buffer space
pub async fn send<I: IntoIterator<Item = u8>>(&self, data: I) -> Result<()> {
let mut data = data.into_iter();
let mut done = false;
while !done {
self.wait_can_send().await?;
self.with_socket(|mut socket| {
socket.send(|buf| {
for i in 0..buf.len() {
if let Some(byte) = data.next() {
buf[i] = byte;
} else {
done = true;
return (i, ())
}
}
(buf.len(), ())
})
})?;
}
Ok(())
}
/// Yields to wait for more buffer space
pub async fn send_slice(&self, mut data: &'_ [u8]) -> Result<()> {
while data.len() > 0 {
self.wait_can_send().await?;
data = self.with_socket(|mut socket| {
socket.send(|buf| {
let len = buf.len().min(data.len());
buf[..len].copy_from_slice(&data[..len]);
data = &data[len..];
(len, data)
})
})?;
}
Ok(())
}
/// Wait for all queued data to be sent and ACKed
///
/// **Warning:** this may not work as immediately as expected! The
/// other side may wait until it sends packets to you for
/// piggybacking the ACKs.
pub async fn flush(&self) -> Result<()> {
poll_stream!(self, Result<()>, |socket| {
if socket_is_handhshaking(&socket) {
Poll::Pending
} else if socket.may_send() && socket.send_queue() > 0 {
Poll::Pending
} else if socket.may_send() {
Poll::Ready(Ok(()))
} else {
Poll::Ready(Err(Error::Truncated))
}
}).await
}
/// Close the transmit half of the connection
pub async fn close(&self) {
self.with_socket(|mut socket| socket.close());
// Yield for one iface.poll() to send the packet
task::r#yield().await;
}
/// Destroy the socket, sending the RST
pub async fn abort(self) {
self.with_socket(|mut socket| socket.abort());
// Yield for one iface.poll() to send the packet
task::r#yield().await;
}
pub fn keep_alive(&self) -> Option<Duration> {
self.with_socket(|socket| socket.keep_alive())
}
pub fn set_keep_alive(&mut self, interval: Option<Duration>) {
self.with_socket(|mut socket| socket.set_keep_alive(interval));
}
pub fn timeout(&self) -> Option<Duration> {
self.with_socket(|socket| socket.timeout())
}
pub fn set_timeout(&mut self, duration: Option<Duration>) {
self.with_socket(|mut socket| socket.set_timeout(duration));
}
}
impl Drop for TcpStream {
/// Free item in the socket set, which leads to deallocation of
/// the rx/tx buffers associated with this socket.
fn drop(&mut self) {
Sockets::instance().sockets.borrow_mut()
.remove(self.handle);
}
}
fn socket_is_handhshaking(socket: &SocketRef<TcpSocket>) -> bool {
match socket.state() {
TcpState::SynSent | TcpState::SynReceived =>
true,
_ =>
false,
}
}

View File

@ -1,47 +0,0 @@
//! Asynchronous tasks
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use super::executor;
/// Drives the future `f` to completion
///
/// This also makes any previously `spawn`-ed future make progress
pub fn block_on<T>(f: impl Future<Output = T>) -> T {
executor::current().block_on(f)
}
/// Spawns a task onto the executor
///
/// The spawned task will not make any progress until `block_on` is called.
pub fn spawn(f: impl Future + 'static) {
executor::current().spawn(f)
}
/// Use `r#yield.await` to suspend the execution of a task
pub async fn r#yield() {
struct Yield {
yielded: bool,
}
impl Future for Yield {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.yielded {
Poll::Ready(())
} else {
self.yielded = true;
// wake ourselves
cx.waker().wake_by_ref();
//asm::sev();
Poll::Pending
}
}
}
Yield { yielded: false }.await
}

View File

@ -1,27 +0,0 @@
[package]
name = "libboard_zynq"
description = "Drivers for peripherals in the Zynq PS"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[features]
target_zc706 = []
target_cora_z7_10 = []
target_redpitaya = []
ipv6 = [ "smoltcp/proto-ipv6" ]
[dependencies]
volatile-register = "0.2"
bit_field = "0.10"
embedded-hal = "0.2"
nb = "0.1"
void = { version = "1", default-features = false }
log = "0.4"
libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" }
[dependencies.smoltcp]
version = "0.6"
features = ["ethernet", "proto-ipv4", "socket-tcp"]
default-features = false

View File

@ -1,2 +0,0 @@
pub const M_AXI_GP0: usize = 0x4000_0000;
pub const M_AXI_GP1: usize = 0x8000_0000;

View File

@ -1,59 +0,0 @@
//! AXI_HP Interface (AFI)
use volatile_register::RW;
use libregister::{register, register_bit, register_bits};
pub unsafe fn axi_hp0() -> &'static RegisterBlock {
&*(0xF8008000 as *const _)
}
pub unsafe fn axi_hp1() -> &'static RegisterBlock {
&*(0xF8009000 as *const _)
}
pub unsafe fn axi_hp2() -> &'static RegisterBlock {
&*(0xF800A000 as *const _)
}
pub unsafe fn axi_hp3() -> &'static RegisterBlock {
&*(0xF800B000 as *const _)
}
#[repr(C)]
pub struct RegisterBlock {
/// Read Channel Control Register
pub rdchan_ctrl: RdchanCtrl,
/// Read Issuing Capability Register
pub rdchan_issuingcap: RW<u32>,
/// QOS Read Channel Register
pub rdqos: RW<u32>,
/// Read Data FIFO Level Register
pub rddatafifo_level: RW<u32>,
/// Read Channel Debug Register
pub rddebug: RW<u32>,
/// Write Channel Control Register
pub wrchan_ctrl: WrchanCtrl,
/// Write Issuing Capability Register
pub wrchan_issuingcap: RW<u32>,
/// QOS Write Channel Register
pub wrqos: RW<u32>,
/// Write Data FIFO Level Register
pub wrdatafifo_level: RW<u32>,
/// Write Channel Debug Register
pub wrdebug: RW<u32>,
}
register!(rdchan_ctrl, RdchanCtrl, RW, u32);
register_bit!(rdchan_ctrl, en_32bit, 0);
register_bit!(rdchan_ctrl, fabric_qos_en, 1);
register_bit!(rdchan_ctrl, fabric_out_cmd_en, 2);
register_bit!(rdchan_ctrl, qos_head_of_cmd_q_en, 3);
register!(wrchan_ctrl, WrchanCtrl, RW, u32);
register_bit!(wrchan_ctrl, en_32bit, 0);
register_bit!(wrchan_ctrl, fabric_qos_en, 1);
register_bit!(wrchan_ctrl, fabric_out_cmd_en, 2);
register_bit!(wrchan_ctrl, qos_head_of_cmd_q_en, 3);
register_bits!(wrchan_ctrl, wr_cmd_release_mode, u8, 4, 5);
register_bits!(wrchan_ctrl, wr_data_threshold, u8, 8, 11);

View File

@ -1,174 +0,0 @@
use log::debug;
use libregister::{RegisterR, RegisterW, RegisterRW};
use super::slcr;
#[cfg(feature = "target_zc706")]
pub const PS_CLK: u32 = 33_333_333;
#[cfg(feature = "target_cora_z7_10")]
pub const PS_CLK: u32 = 50_000_000;
#[cfg(feature = "target_redpitaya")]
pub const PS_CLK: u32 = 33_333_333;
/// (pll_fdiv_max, (pll_cp, pll_res, lock_cnt))
const PLL_FDIV_LOCK_PARAM: &[(u16, (u8, u8, u16))] = &[
(13, (2, 6, 750)),
(14, (2, 6, 700)),
(15, (2, 6, 650)),
(16, (2, 10, 625)),
(17, (2, 10, 575)),
(18, (2, 10, 550)),
(19, (2, 10, 525)),
(20, (2, 12, 500)),
(21, (2, 12, 475)),
(22, (2, 12, 450)),
(23, (2, 12, 425)),
(25, (2, 12, 400)),
(26, (2, 12, 375)),
(28, (2, 12, 350)),
(30, (2, 12, 325)),
(33, (2, 2, 300)),
(36, (2, 2, 275)),
(40, (2, 2, 250)),
(47, (3, 12, 250)),
(66, (2, 4, 250)),
];
pub trait ClockSource {
/// picks this ClockSource's registers from the SLCR block
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
);
/// query PLL lock status
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool;
/// get configured frequency
fn freq() -> u32 {
let mut slcr = slcr::RegisterBlock::slcr();
let (pll_ctrl, _, _) = Self::pll_regs(&mut slcr);
u32::from(pll_ctrl.read().pll_fdiv()) * PS_CLK
}
fn name() -> &'static str;
/// Zynq-7000 AP SoC Technical Reference Manual:
/// 25.10.4 PLLs
fn setup(target_freq: u32) {
let fdiv = (target_freq / PS_CLK).min(66) as u16;
let (pll_res, pll_cp, lock_cnt) = PLL_FDIV_LOCK_PARAM.iter()
.filter(|(fdiv_max, _)| fdiv <= *fdiv_max)
.nth(0)
.expect("PLL_FDIV_LOCK_PARAM")
.1.clone();
debug!("Set {} to {} Hz", Self::name(), target_freq);
slcr::RegisterBlock::unlocked(|slcr| {
let (pll_ctrl, pll_cfg, pll_status) = Self::pll_regs(slcr);
// Bypass
pll_ctrl.modify(|_, w| w
.pll_pwrdwn(false)
.pll_bypass_force(true)
.pll_fdiv(fdiv)
);
// Configure
pll_cfg.write(
slcr::PllCfg::zeroed()
.pll_res(pll_res)
.pll_cp(pll_cp)
.lock_cnt(lock_cnt)
);
// Reset
pll_ctrl.modify(|_, w| w.pll_reset(true));
pll_ctrl.modify(|_, w| w.pll_reset(false));
// Wait for PLL lock
while ! Self::pll_locked(pll_status) {}
// Remove bypass
pll_ctrl.modify(|_, w| w
.pll_bypass_force(false)
.pll_bypass_qual(false)
);
});
}
}
/// ARM PLL: Recommended clock source for the CPUs and the interconnect
pub struct ArmPll;
impl ClockSource for ArmPll {
#[inline]
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
) {
(&mut slcr.arm_pll_ctrl,
&mut slcr.arm_pll_cfg,
&mut slcr.pll_status
)
}
#[inline]
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool {
pll_status.read().arm_pll_lock()
}
fn name() -> &'static str {
&"ARM_PLL"
}
}
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
pub struct DdrPll;
impl ClockSource for DdrPll {
#[inline]
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
) {
(&mut slcr.ddr_pll_ctrl,
&mut slcr.ddr_pll_cfg,
&mut slcr.pll_status
)
}
#[inline]
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool {
pll_status.read().ddr_pll_lock()
}
fn name() -> &'static str {
&"DDR_PLL"
}
}
/// I/O PLL: Recommended clock for I/O peripherals
pub struct IoPll;
impl ClockSource for IoPll {
#[inline]
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
) {
(&mut slcr.io_pll_ctrl,
&mut slcr.io_pll_cfg,
&mut slcr.pll_status
)
}
#[inline]
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool {
pll_status.read().io_pll_lock()
}
fn name() -> &'static str {
&"IO_PLL"
}
}

View File

@ -1,389 +0,0 @@
use libregister::{RegisterR, RegisterW, RegisterRW};
use log::{debug, info, error};
use crate::{print, println};
use super::slcr::{self, DdriobVrefSel};
use super::clocks::{Clocks, source::{DdrPll, ClockSource}};
mod regs;
#[cfg(feature = "target_zc706")]
/// Micron MT41J256M8HX-15E: 667 MHz DDR3
const DDR_FREQ: u32 = 666_666_666;
#[cfg(feature = "target_cora_z7_10")]
/// Micron MT41K256M16HA-125: 800 MHz DDR3L, max supported 533 MHz
const DDR_FREQ: u32 = 525_000_000;
#[cfg(feature = "target_redpitaya")]
/// Alliance Memory AS4C256M16D3B: 800 MHz DDR3
const DDR_FREQ: u32 = 800_000_000;
/// MT41K256M16HA-125
const DCI_FREQ: u32 = 10_000_000;
pub struct DdrRam {
regs: &'static mut regs::RegisterBlock,
}
impl DdrRam {
pub fn ddrram() -> Self {
let clocks = Self::clock_setup();
Self::calibrate_iob_impedance(&clocks);
Self::configure_iob();
let regs = regs::RegisterBlock::ddrc();
let mut ddr = DdrRam { regs };
ddr.configure();
ddr.reset_ddrc();
ddr
}
/// Zynq-7000 AP SoC Technical Reference Manual:
/// 10.6.1 DDR Clock Initialization
fn clock_setup() -> Clocks {
DdrPll::setup(2 * DDR_FREQ);
let clocks = Clocks::get();
let ddr3x_clk_divisor = 2;
let ddr2x_clk_divisor = 3;
debug!("DDR 3x/2x clocks: {}/{}", clocks.ddr / u32::from(ddr3x_clk_divisor), clocks.ddr / u32::from(ddr2x_clk_divisor));
slcr::RegisterBlock::unlocked(|slcr| {
slcr.ddr_clk_ctrl.write(
slcr::DdrClkCtrl::zeroed()
.ddr_2xclkact(true)
.ddr_3xclkact(true)
.ddr_2xclk_divisor(ddr2x_clk_divisor)
.ddr_3xclk_divisor(ddr3x_clk_divisor)
);
});
clocks
}
fn calculate_dci_divisors(clocks: &Clocks) -> (u8, u8) {
let target = (DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ;
let mut best = None;
let mut best_error = 0;
for divisor0 in 1..63 {
for divisor1 in 1..63 {
let current = (divisor0 as u32) * (divisor1 as u32);
let error = if current > target {
current - target
} else {
target - current
};
if best.is_none() || best_error > error {
best = Some((divisor0, divisor1));
best_error = error;
}
}
}
best.unwrap()
}
/// Zynq-7000 AP SoC Technical Reference Manual:
/// 10.6.2 DDR IOB Impedance Calibration
fn calibrate_iob_impedance(clocks: &Clocks) {
let (divisor0, divisor1) = Self::calculate_dci_divisors(clocks);
debug!("DDR DCI clock: {} Hz (divisors={}*{})",
clocks.ddr / u32::from(divisor0) / u32::from(divisor1),
divisor0, divisor1);
slcr::RegisterBlock::unlocked(|slcr| {
// Step 1.
slcr.dci_clk_ctrl.write(
slcr::DciClkCtrl::zeroed()
.clkact(true)
.divisor0(divisor0)
.divisor1(divisor1)
);
// Step 2.a.
slcr.ddriob_dci_ctrl.modify(|_, w|
w.reset(false)
);
slcr.ddriob_dci_ctrl.modify(|_, w|
w.reset(true)
);
// Step 3.b. for DDR3/DDR3L
slcr.ddriob_dci_ctrl.modify(|_, w|
w.nref_opt1(0)
.nref_opt2(0)
.nref_opt4(1)
.pref_opt1(0)
.pref_opt2(0)
);
// Step 2.c.
slcr.ddriob_dci_ctrl.modify(|_, w|
w.update_control(false)
);
// Step 2.d.
slcr.ddriob_dci_ctrl.modify(|_, w|
w.enable(true)
);
// Step 2.e.
while ! slcr.ddriob_dci_status.read().done() {}
});
}
/// Zynq-7000 AP SoC Technical Reference Manual:
/// 10.6.3 DDR IOB Configuration
fn configure_iob() {
slcr::RegisterBlock::unlocked(|slcr| {
let addr_config = slcr::DdriobConfig::zeroed()
.output_en(slcr::DdriobOutputEn::Obuf);
slcr.ddriob_addr0.write(addr_config.clone());
slcr.ddriob_addr1.write(addr_config);
#[cfg(feature = "target_zc706")]
let data0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::VrefDifferential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_zc706")]
let data1_config = data0_config.clone();
#[cfg(feature = "target_cora_z7_10")]
let data0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::VrefDifferential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_cora_z7_10")]
let data1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
#[cfg(feature = "target_redpitaya")]
let data0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::VrefDifferential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_redpitaya")]
let data1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
slcr.ddriob_data0.write(data0_config);
slcr.ddriob_data1.write(data1_config);
#[cfg(feature = "target_zc706")]
let diff0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::Differential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_zc706")]
let diff1_config = diff0_config.clone();
#[cfg(feature = "target_cora_z7_10")]
let diff0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::Differential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_cora_z7_10")]
let diff1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
#[cfg(feature = "target_redpitaya")]
let diff0_config = slcr::DdriobConfig::zeroed()
.inp_type(slcr::DdriobInputType::Differential)
.term_en(true)
.dci_type(slcr::DdriobDciType::Termination)
.output_en(slcr::DdriobOutputEn::Obuf);
#[cfg(feature = "target_redpitaya")]
let diff1_config = slcr::DdriobConfig::zeroed()
.pullup_en(true);
slcr.ddriob_diff0.write(diff0_config);
slcr.ddriob_diff1.write(diff1_config);
slcr.ddriob_clock.write(
slcr::DdriobConfig::zeroed()
.output_en(slcr::DdriobOutputEn::Obuf)
);
unsafe {
// Not documented in Technical Reference Manual
slcr.ddriob_drive_slew_addr.write(0x0018C61C);
slcr.ddriob_drive_slew_data.write(0x00F9861C);
slcr.ddriob_drive_slew_diff.write(0x00F9861C);
slcr.ddriob_drive_slew_clock.write(0x00F9861C);
}
// Enable external V[REF]
#[cfg(feature = "target_cora_z7_10")]
slcr.ddriob_ddr_ctrl.modify(|_, w| w
.vref_int_en(false)
.vref_ext_en_lower(true)
.vref_ext_en_upper(false)
);
#[cfg(feature = "target_zc706")]
slcr.ddriob_ddr_ctrl.modify(|_, w| w
.vref_int_en(true)
.vref_sel(DdriobVrefSel::Vref0_75V)
.vref_ext_en_lower(false)
.vref_ext_en_upper(false)
);
#[cfg(feature = "target_redpitaya")]
slcr.ddriob_ddr_ctrl.modify(|_, w| w
.vref_int_en(false)
.vref_ext_en_lower(true)
.vref_ext_en_upper(false)
);
});
}
fn configure(&mut self) {
self.regs.dram_param0.write(
regs::DramParam0::zeroed()
.t_rc(0x1b)
.t_rfc_min(0x56)
.post_selfref_gap_x32(0x10)
);
self.regs.dram_param2.write(
regs::DramParam2::zeroed()
.write_latency(0x5)
.rd2wr(0x7)
.wr2rd(0xe)
.t_xp(0x4)
.pad_pd(0x0)
.rd2pre(0x4)
.t_rcd(0x7)
);
self.regs.dram_emr_mr.write(
regs::DramEmrMr::zeroed()
.mr(0x930)
.emr(0x4)
);
self.regs.phy_cmd_timeout_rddata_cpt.modify(
|_, w| w
.rd_cmd_to_data(0x0)
.wr_cmd_to_data(0x0)
.we_to_re_delay(0x8)
.rdc_fifo_rst_disable(false)
.use_fixed_re(true)
.rdc_fifo_rst_err_cnt_clr(false)
.dis_phy_ctrl_rstn(false)
.clk_stall_level(false)
.gatelvl_num_of_dq0(0x7)
.wrlvl_num_of_dq0(0x7)
);
self.regs.reg_2c.write(
regs::Reg2C::zeroed()
.wrlvl_max_x1024(0xfff)
.rdlvl_max_x1024(0xfff)
.twrlvl_max_error(false)
.trdlvl_max_error(false)
.dfi_wr_level_en(true)
.dfi_rd_dqs_gate_level(true)
.dfi_rd_data_eye_train(true)
);
self.regs.dfi_timing.write(
regs::DfiTiming::zeroed()
.rddata_en(0x6)
.ctrlup_min(0x3)
.ctrlup_max(0x40)
);
self.regs.phy_init_ratio3.write(
regs::PhyInitRatio::zeroed()
.wrlvl_init_ratio(0x21)
.gatelvl_init_ratio(0xee)
);
self.regs.reg_65.write(
regs::Reg65::zeroed()
.wr_rl_delay(0x2)
.rd_rl_delay(0x4)
.dll_lock_diff(0xf)
.use_wr_level(true)
.use_rd_dqs_gate_level(true)
.use_rd_data_eye_level(true)
.dis_calib_rst(false)
.ctrl_slave_delay(0x0)
);
}
/// Reset DDR controller
fn reset_ddrc(&mut self) {
#[cfg(feature = "target_zc706")]
unsafe {
// row/column address bits
self.regs.dram_addr_map_bank.write(0x00000777);
self.regs.dram_addr_map_col.write(0xFFF00000);
self.regs.dram_addr_map_row.write(0x0F666666);
}
#[cfg(feature = "target_zc706")]
let width = regs::DataBusWidth::Width32bit;
#[cfg(feature = "target_cora_z7_10")]
let width = regs::DataBusWidth::Width16bit;
#[cfg(feature = "target_redpitaya")]
let width = regs::DataBusWidth::Width16bit;
self.regs.ddrc_ctrl.modify(|_, w| w
.soft_rstb(false)
.powerdown_en(false)
.data_bus_width(width)
);
self.regs.ddrc_ctrl.modify(|_, w| w
.soft_rstb(true)
.powerdown_en(false)
.data_bus_width(width)
);
while self.status() == regs::ControllerStatus::Init {}
}
pub fn status(&self) -> regs::ControllerStatus {
self.regs.mode_sts.read().operating_mode()
}
pub fn ptr<T>(&mut self) -> *mut T {
0x0010_0000 as *mut _
}
/// actually there's 1 MB more but starting at 0x0000_0000
/// overlaps with OCM.
pub fn size(&self) -> usize {
#[cfg(feature = "target_zc706")]
let megabytes = 1023;
#[cfg(feature = "target_cora_z7_10")]
let megabytes = 511;
#[cfg(feature = "target_redpitaya")]
let megabytes = 511;
megabytes * 1024 * 1024
}
pub fn memtest(&mut self) {
let slice = unsafe {
core::slice::from_raw_parts_mut(self.ptr(), self.size())
};
let patterns: &'static [u32] = &[0xffff_ffff, 0x5555_5555, 0xaaaa_aaaa, 0];
let mut expected = None;
for (i, pattern) in patterns.iter().enumerate() {
info!("memtest phase {} (status: {:?})", i, self.status());
for megabyte in 0..slice.len() / (1024 * 1024) {
let start = megabyte * 1024 * 1024 / 4;
let end = (megabyte + 1) * 1024 * 1024 / 4;
for b in slice[start..end].iter_mut() {
expected.map(|expected| {
let read: u32 = *b;
if read != expected {
error!("{:08X}: expected {:08X}, read {:08X}", b as *mut _ as usize, expected, read);
}
});
*b = *pattern;
}
print!("\r{} MB", megabyte);
}
println!(" Ok");
expected = Some(*pattern);
}
}
}

View File

@ -1,233 +0,0 @@
use volatile_register::{RO, RW};
use libregister::{register, register_at, register_bit, register_bits, register_bits_typed};
#[allow(unused)]
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum DataBusWidth {
Width32bit = 0b00,
Width16bit = 0b01,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum ControllerStatus {
Init = 0,
Normal = 1,
Powerdown = 2,
SelfRefresh = 3,
Powerdown1 = 4,
Powerdown2 = 5,
Powerdown3 = 6,
Powerdown4 = 7,
}
#[repr(C)]
pub struct RegisterBlock {
pub ddrc_ctrl: DdrcCtrl,
pub two_rank_cfg: RW<u32>,
pub hpr: RW<u32>,
pub lpr: RW<u32>,
pub wr: RW<u32>,
pub dram_param0: DramParam0,
pub dram_param1: RW<u32>,
pub dram_param2: DramParam2,
pub dram_param3: RW<u32>,
pub dram_param4: RW<u32>,
pub dram_init_param: RW<u32>,
pub dram_emr: RW<u32>,
pub dram_emr_mr: DramEmrMr,
pub dram_burst8_rdwr: RW<u32>,
pub dram_disable_dq: RW<u32>,
pub dram_addr_map_bank: RW<u32>,
pub dram_addr_map_col: RW<u32>,
pub dram_addr_map_row: RW<u32>,
pub dram_odt: RW<u32>,
pub phy_dbg: RW<u32>,
pub phy_cmd_timeout_rddata_cpt: PhyCmdTimeoutRddataCpt,
pub mode_sts: ModeStsReg,
pub dll_calib: RW<u32>,
pub odt_delay_hold: RW<u32>,
pub ctrl1: RW<u32>,
pub ctrl2: RW<u32>,
pub ctrl3: RW<u32>,
pub ctrl4: RW<u32>,
_unused0: [RO<u32>; 2],
pub ctrl5: RW<u32>,
pub ctrl6: RW<u32>,
_unused1: [RO<u32>; 8],
pub che_refresh_timer01: RW<u32>,
pub che_t_zq: RW<u32>,
pub che_t_zq_short_interval: RW<u32>,
pub deep_pwrdwn: RW<u32>,
pub reg_2c: Reg2C,
pub reg_2d: RW<u32>,
pub dfi_timing: DfiTiming,
_unused2: [RO<u32>; 2],
pub che_ecc_control_offset: RW<u32>,
pub che_corr_ecc_log_offset: RW<u32>,
pub che_corr_ecc_addr_offset: RW<u32>,
pub che_corr_ecc_data_31_0_offset: RW<u32>,
pub che_corr_ecc_data_63_32_offset: RW<u32>,
pub che_corr_ecc_data_71_64_offset: RW<u32>,
pub che_uncorr_ecc_log_offset: RW<u32>,
pub che_uncorr_ecc_addr_offset: RW<u32>,
pub che_uncorr_ecc_data_31_0_offset: RW<u32>,
pub che_uncorr_ecc_data_63_32_offset: RW<u32>,
pub che_uncorr_ecc_data_71_64_offset: RW<u32>,
pub che_ecc_stats_offset: RW<u32>,
pub ecc_scrub: RW<u32>,
pub che_ecc_corr_bit_mask_31_0_offset: RW<u32>,
pub che_ecc_corr_bit_mask_63_32_offset: RW<u32>,
_unused3: [RO<u32>; 5],
pub phy_rcvr_enable: RW<u32>,
pub phy_config0: RW<u32>,
pub phy_config1: RW<u32>,
pub phy_config2: RW<u32>,
pub phy_config3: RW<u32>,
_unused4: RO<u32>,
pub phy_init_ratio0: PhyInitRatio,
pub phy_init_ratio1: PhyInitRatio,
pub phy_init_ratio2: PhyInitRatio,
pub phy_init_ratio3: PhyInitRatio,
_unused5: RO<u32>,
pub phy_rd_dqs_cfg0: RW<u32>,
pub phy_rd_dqs_cfg1: RW<u32>,
pub phy_rd_dqs_cfg2: RW<u32>,
pub phy_rd_dqs_cfg3: RW<u32>,
_unused6: RO<u32>,
pub phy_wr_dqs_cfg0: RW<u32>,
pub phy_wr_dqs_cfg1: RW<u32>,
pub phy_wr_dqs_cfg2: RW<u32>,
pub phy_wr_dqs_cfg3: RW<u32>,
_unused7: RO<u32>,
pub phy_we_cfg0: RW<u32>,
pub phy_we_cfg1: RW<u32>,
pub phy_we_cfg2: RW<u32>,
pub phy_we_cfg3: RW<u32>,
_unused8: RO<u32>,
pub wr_data_slv0: RW<u32>,
pub wr_data_slv1: RW<u32>,
pub wr_data_slv2: RW<u32>,
pub wr_data_slv3: RW<u32>,
_unused9: RO<u32>,
pub reg_64: RW<u32>,
pub reg_65: Reg65,
_unused10: [RO<u32>; 3],
pub reg69_6a0: RW<u32>,
pub reg69_6a1: RW<u32>,
_unused11: RO<u32>,
pub reg6c_6d2: RW<u32>,
pub reg6c_6d3: RW<u32>,
pub reg6e_710: RW<u32>,
pub reg6e_711: RW<u32>,
pub reg6e_712: RW<u32>,
pub reg6e_713: RW<u32>,
pub phy_dll_sts0: RW<u32>,
_unused12: RO<u32>,
pub phy_dll_sts1: RW<u32>,
pub phy_dll_sts2: RW<u32>,
pub phy_dll_sts3: RW<u32>,
_unused13: RO<u32>,
pub dll_lock_sts: RW<u32>,
pub phy_ctrl_sts: RW<u32>,
pub phy_ctrl_sts2: RW<u32>,
_unused14: [RO<u32>; 5],
pub axi_id: RW<u32>,
pub page_mask: RW<u32>,
pub axi_priority_wr_port0: RW<u32>,
pub axi_priority_wr_port1: RW<u32>,
pub axi_priority_wr_port2: RW<u32>,
pub axi_priority_wr_port3: RW<u32>,
pub axi_priority_rd_port0: RW<u32>,
pub axi_priority_rd_port1: RW<u32>,
pub axi_priority_rd_port2: RW<u32>,
pub axi_priority_rd_port3: RW<u32>,
_unused15: [RO<u32>; 27],
pub excl_access_cfg0: RW<u32>,
pub excl_access_cfg1: RW<u32>,
pub excl_access_cfg2: RW<u32>,
pub excl_access_cfg3: RW<u32>,
pub mode_reg_read: RW<u32>,
pub lpddr_ctrl0: RW<u32>,
pub lpddr_ctrl1: RW<u32>,
pub lpddr_ctrl2: RW<u32>,
pub lpddr_ctrl3: RW<u32>,
}
register_at!(RegisterBlock, 0xF8006000, ddrc);
register!(ddrc_ctrl, DdrcCtrl, RW, u32);
register_bit!(ddrc_ctrl,
/// `false` resets controller, `true` continues
soft_rstb, 0);
register_bit!(ddrc_ctrl, powerdown_en, 1);
register_bits_typed!(ddrc_ctrl, data_bus_width, u8, DataBusWidth, 2, 3);
// (ddrc_ctrl) ...
register!(dram_param0, DramParam0, RW, u32);
register_bits!(dram_param0, t_rc, u8, 0, 5);
register_bits!(dram_param0, t_rfc_min, u8, 6, 13);
register_bits!(dram_param0, post_selfref_gap_x32, u8, 14, 20);
register!(dram_param2, DramParam2, RW, u32);
register_bits!(dram_param2, write_latency, u8, 0, 4);
register_bits!(dram_param2, rd2wr, u8, 5, 9);
register_bits!(dram_param2, wr2rd, u8, 10, 14);
register_bits!(dram_param2, t_xp, u8, 15, 19);
register_bits!(dram_param2, pad_pd, u8, 20, 22);
register_bits!(dram_param2, rd2pre, u8, 23, 27);
register_bits!(dram_param2, t_rcd, u8, 28, 31);
register!(dram_emr_mr, DramEmrMr, RW, u32);
register_bits!(dram_emr_mr, mr, u16, 0, 15);
register_bits!(dram_emr_mr, emr, u16, 16, 31);
register!(phy_cmd_timeout_rddata_cpt, PhyCmdTimeoutRddataCpt, RW, u32);
register_bits!(phy_cmd_timeout_rddata_cpt, rd_cmd_to_data, u8, 0, 3);
register_bits!(phy_cmd_timeout_rddata_cpt, wr_cmd_to_data, u8, 4, 7);
register_bits!(phy_cmd_timeout_rddata_cpt, we_to_re_delay, u8, 8, 11);
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_disable, 15);
register_bit!(phy_cmd_timeout_rddata_cpt, use_fixed_re, 16);
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_err_cnt_clr, 17);
register_bit!(phy_cmd_timeout_rddata_cpt, dis_phy_ctrl_rstn, 18);
register_bit!(phy_cmd_timeout_rddata_cpt, clk_stall_level, 19);
register_bits!(phy_cmd_timeout_rddata_cpt, gatelvl_num_of_dq0, u8, 24, 27);
register_bits!(phy_cmd_timeout_rddata_cpt, wrlvl_num_of_dq0, u8, 28, 31);
register!(reg_2c, Reg2C, RW, u32);
register_bits!(reg_2c, wrlvl_max_x1024, u16, 0, 11);
register_bits!(reg_2c, rdlvl_max_x1024, u16, 12, 23);
register_bit!(reg_2c, twrlvl_max_error, 24);
register_bit!(reg_2c, trdlvl_max_error, 25);
register_bit!(reg_2c, dfi_wr_level_en, 26);
register_bit!(reg_2c, dfi_rd_dqs_gate_level, 27);
register_bit!(reg_2c, dfi_rd_data_eye_train, 28);
register!(dfi_timing, DfiTiming, RW, u32);
register_bits!(dfi_timing, rddata_en, u8, 0, 4);
register_bits!(dfi_timing, ctrlup_min, u16, 5, 14);
register_bits!(dfi_timing, ctrlup_max, u16, 15, 24);
register!(phy_init_ratio, PhyInitRatio, RW, u32);
register_bits!(phy_init_ratio, wrlvl_init_ratio, u16, 0, 9);
register_bits!(phy_init_ratio, gatelvl_init_ratio, u16, 10, 19);
register!(reg_65, Reg65, RW, u32);
register_bits!(reg_65, wr_rl_delay, u8, 0, 4);
register_bits!(reg_65, rd_rl_delay, u8, 5, 9);
register_bits!(reg_65, dll_lock_diff, u8, 10, 13);
register_bit!(reg_65, use_wr_level, 14);
register_bit!(reg_65, use_rd_dqs_gate_level, 15);
register_bit!(reg_65, use_rd_data_eye_level, 16);
register_bit!(reg_65, dis_calib_rst, 17);
register_bits!(reg_65, ctrl_slave_delay, u8, 18, 19);
// Controller operation mode status
register!(mode_sts_reg,
ModeStsReg, RO, u32);
register_bits_typed!(mode_sts_reg, operating_mode, u8, ControllerStatus, 0, 2);
// (mode_sts_reg) ...

View File

@ -1,341 +0,0 @@
use super::time::Milliseconds;
use crate::slcr;
use embedded_hal::timer::CountDown;
use libcortex_a9::cache;
use libregister::*;
use log::{debug, trace};
mod regs;
pub struct DevC {
regs: &'static mut regs::RegisterBlock,
enabled: bool,
count_down: super::timer::global::CountDown<Milliseconds>,
timeout_ms: Milliseconds,
}
/// DMA transfer type for PCAP
/// All insecure, we do not implement encrypted transfer
#[derive(PartialEq, Clone, Copy)]
pub enum TransferType {
PcapWrite,
PcapReadback,
ConcurrentReadWrite,
}
pub enum TransferTarget<'a> {
/// From/To PL, with length in bytes.
PL(u32),
/// Source target, immutable.
SliceSrc(&'a [u8]),
/// Last source target, immutable.
SliceSrcLast(&'a [u8]),
/// Destination target, mutable.
SliceDest(&'a mut [u8]),
/// Last destination target, mutable.
SliceDestLast(&'a mut [u8]),
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum DevcError {
NotInitialized,
ResetTimeout,
DmaBusy,
DmaTimeout,
DoneTimeout,
Unknown(u32),
}
impl core::fmt::Display for DevcError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use DevcError::*;
match self {
NotInitialized => write!(f, "DevC driver not initialized properly."),
ResetTimeout => write!(f, "DevC driver reset timeout."),
DmaBusy => write!(f, "DevC driver DMA busy."),
DmaTimeout => write!(f, "DevC driver DMA timeout."),
DoneTimeout => write!(
f,
"FPGA DONE signal timeout. Check if the bitstream is correct."
),
Unknown(reg) => write!(f, "Unknown error, interrupt status register = 0x{:0X}", reg),
}
}
}
impl DevC {
/// Create a new DevC peripheral handle with default timeout = 500ms.
pub fn new() -> Self {
Self::new_timeout(Milliseconds(500))
}
/// Create a new DevC peripheral handle.
/// `timeout_ms`: timeout for operations like initialize and DMA transfer.
pub fn new_timeout(timeout_ms: Milliseconds) -> Self {
DevC {
regs: regs::RegisterBlock::devc(),
enabled: false,
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
timeout_ms,
}
}
/// Enable the devc driver, must be called before `program` or
/// `start_dma_transaction`.
pub fn enable(&mut self) {
const UNLOCK_PATTERN: u32 = 0x757BDF0D;
unsafe {
// unlock register with magic pattern
self.regs.unlock.write(UNLOCK_PATTERN);
}
self.regs
.control
.modify(|_, w| w.pcap_mode(true).pcap_pr(true));
self.regs
.int_mask
.write(self::regs::int_mask::Write { inner: 0xFFFFFFFF });
self.clear_interrupts();
self.enabled = true;
}
/// Disable the devc driver.
/// `enable` has to be called before further `program` or
/// `start_dma_transaction`.
pub fn disable(&mut self) {
self.regs
.control
.modify(|_, w| w.pcap_mode(false).pcap_pr(false));
self.enabled = false;
}
/// Check if the FPGA programming is done.
pub fn is_done(&self) -> bool {
// Note: contrary to what the TRM says, this appears to be simply the
// state of the DONE signal.
self.regs.int_sts.read().ixr_pcfg_done()
}
/// Wait on a certain condition with hardcoded timeout.
fn wait_condition<F: Fn(&mut Self) -> bool>(
&mut self,
fun: F,
err: DevcError,
) -> Result<(), DevcError> {
self.count_down.start(self.timeout_ms);
while let Err(nb::Error::WouldBlock) = self.count_down.wait() {
if fun(self) {
return Ok(());
} else if self.has_error() {
return Err(DevcError::Unknown(self.regs.int_sts.read().inner));
}
}
Err(err)
}
/// Program the FPGA.
/// Note that the user should make sure that the bitstream loaded is
/// correct.
pub fn program(&mut self, src: &[u8]) -> Result<(), DevcError> {
if !self.enabled {
panic!("Attempting to use devc when it is not enabled");
}
self.clear_interrupts();
debug!("Invalidate DCache for bitstream buffer");
cache::dcci_slice(src);
debug!("Init preload FPGA");
slcr::RegisterBlock::unlocked(|slcr| {
slcr.init_preload_fpga();
});
debug!("Toggling PROG_B");
// set PCFG_PROG_B to high low high
self.regs.control.modify(|_, w| w.pcfg_prog_b(true));
self.regs.control.modify(|_, w| w.pcfg_prog_b(false));
// wait until init is false
self.wait_condition(
|s| !s.regs.status.read().pcfg_init(),
DevcError::ResetTimeout,
)?;
self.regs.control.modify(|_, w| w.pcfg_prog_b(true));
// wait until init is true
self.wait_condition(
|s| s.regs.status.read().pcfg_init(),
DevcError::ResetTimeout,
)?;
self.regs.int_sts.write(
self::regs::IntSts::zeroed()
.pss_cfg_reset_b_int(true)
.ixr_pcfg_cfg_rst(true),
);
self.dma_transfer(
TransferTarget::SliceSrcLast(src),
TransferTarget::PL(src.len() as u32),
TransferType::PcapWrite,
)?;
debug!("Waiting for done");
self.wait_condition(|s| s.is_done(), DevcError::DoneTimeout)?;
debug!("Init postload FPGA");
slcr::RegisterBlock::unlocked(|slcr| {
slcr.init_postload_fpga();
});
Ok(())
}
/// Initiate DMA transaction
/// This function only sets the src and dest registers, and should not be used directly.
fn initiate_dma(&mut self, src: TransferTarget, dest: TransferTarget) {
use TransferTarget::*;
const INVALID_ADDR: u32 = 0xFFFFFFFF;
if let (PL(_), PL(_)) = (&src, &dest) {
panic!("Only one of src/dest can be PL");
}
let (src_addr, src_len): (u32, u32) = match src {
PL(l) => (INVALID_ADDR, l / 4),
SliceSrc(s) => (s.as_ptr() as u32, s.len() as u32 / 4),
SliceDest(s) => (s.as_ptr() as u32, s.len() as u32 / 4),
SliceSrcLast(s) => ((s.as_ptr() as u32) | 0x01, s.len() as u32 / 4),
SliceDestLast(s) => ((s.as_ptr() as u32) | 0x01, s.len() as u32 / 4),
};
let (dest_addr, dest_len): (u32, u32) = match dest {
PL(l) => (INVALID_ADDR, l / 4),
SliceDest(s) => (s.as_ptr() as u32, s.len() as u32 / 4),
SliceDestLast(s) => ((s.as_ptr() as u32) | 0x01, s.len() as u32 / 4),
SliceSrc(_) | SliceSrcLast(_) => {
panic!("Destination cannot be SliceSrc/SliceSrcLast, it must be mutable.")
}
};
self.regs.dma_src_addr.modify(|_, w| w.src_addr(src_addr));
self.regs
.dma_dest_addr
.modify(|_, w| w.dest_addr(dest_addr));
self.regs.dma_src_len.modify(|_, w| w.dma_len(src_len));
self.regs.dma_dest_len.modify(|_, w| w.dma_len(dest_len));
}
/// Blocking DMA transfer
/// ## Note
/// This is blocking because there seems to be no other way to guarantee
/// safety, and I don't think requiring static is a solution here due to the
/// large buffer size.
/// See https://docs.rust-embedded.org/embedonomicon/dma.html for details.
///
/// The following checks are implemented in runtime (panic).
/// * Dest would *NOT* accept src type, as the slices are immutable.
/// * At most one of src and dest can be PL type.
pub fn dma_transfer(
&mut self,
src: TransferTarget,
dest: TransferTarget,
transfer_type: TransferType,
) -> Result<(), DevcError> {
if !self.enabled {
panic!("Attempting to use devc when it is not enabled");
}
if self.regs.status.read().dma_cmd_q_f() {
return Err(DevcError::DmaBusy);
}
if transfer_type != TransferType::ConcurrentReadWrite
&& !self.regs.status.read().pcfg_init()
{
return Err(DevcError::NotInitialized);
}
match &transfer_type {
TransferType::PcapReadback => {
// clear internal PCAP loopback
self.regs.mctrl.modify(|_, w| w.pcap_lpbk(false));
// send READ frame command
self.initiate_dma(src, TransferTarget::PL(0));
// wait until DMA done
self.wait_dma_transfer_complete()?;
// initiate the DMA write
self.initiate_dma(TransferTarget::PL(0), dest);
}
TransferType::PcapWrite | TransferType::ConcurrentReadWrite => {
self.regs
.mctrl
.modify(|_, w| w.pcap_lpbk(transfer_type == TransferType::ConcurrentReadWrite));
// PCAP data transmitted every clock
self.regs.control.modify(|_, w| w.pcap_rate_en(false));
self.initiate_dma(src, dest);
}
}
self.wait_dma_transfer_complete()?;
Ok(())
}
fn wait_dma_transfer_complete(&mut self) -> Result<(), DevcError> {
trace!("Wait for DMA done");
self.wait_condition(
|s| s.regs.int_sts.read().ixr_dma_done(),
DevcError::DmaTimeout,
)?;
self.regs
.int_sts
.write(self::regs::IntSts::zeroed().ixr_dma_done(true));
Ok(())
}
/// Dump useful registers for devc block.
pub fn dump_registers(&self) {
debug!("Mctrl: 0x{:0X}", self.regs.mctrl.read().inner);
debug!("Control: 0x{:0X}", self.regs.control.read().inner);
debug!("Status: 0x{:0X}", self.regs.status.read().inner);
debug!("INT STS: 0x{:0X}", self.regs.int_sts.read().inner);
}
/// Clear interrupt status for devc.
pub fn clear_interrupts(&mut self) {
self.regs.int_sts.modify(|_, w| {
w.pss_gts_usr_b_int(true)
.pss_fst_cfg_b_int(true)
.pss_gpwrdwn_b_int(true)
.pss_gts_cfg_b_int(true)
.pss_cfg_reset_b_int(true)
.ixr_axi_wto(true)
.ixr_axi_werr(true)
.ixr_axi_rto(true)
.ixr_axi_rerr(true)
.ixr_rx_fifo_ov(true)
.ixr_wr_fifo_lvl(true)
.ixr_rd_fifo_lvl(true)
.ixr_dma_cmd_err(true)
.ixr_dma_q_ov(true)
.ixr_dma_done(true)
.ixr_d_p_done(true)
.ixr_p2d_len_err(true)
.ixr_pcfg_hmac_err(true)
.ixr_pcfg_seu_err(true)
.ixr_pcfg_por_b(true)
.ixr_pcfg_cfg_rst(true)
.ixr_pcfg_done(true)
.ixr_pcfg_init_pe(true)
.ixr_pcfg_init_ne(true)
})
}
fn has_error(&self) -> bool {
let status = self.regs.int_sts.read();
status.ixr_axi_wto()
|| status.ixr_axi_werr()
|| status.ixr_axi_rto()
|| status.ixr_axi_rerr()
|| status.ixr_rx_fifo_ov()
|| status.ixr_dma_cmd_err()
|| status.ixr_dma_q_ov()
|| status.ixr_p2d_len_err()
}
}

View File

@ -1,213 +0,0 @@
use libregister::{
register, register_at,
register_bit, register_bits, register_bits_typed,
};
use volatile_register::WO;
#[repr(C)]
pub struct RegisterBlock {
pub control: Control,
pub lock: Lock,
pub cfg: Cfg,
pub int_sts: IntSts,
pub int_mask: IntMask,
pub status: Status,
pub dma_src_addr: DmaSrcAddr,
pub dma_dest_addr: DmaDestAddr,
pub dma_src_len: DmaSrcLen,
pub dma_dest_len: DmaDestLen,
unused0: u32,
pub multiboot_addr: MultibootAddr,
unused1: u32,
pub unlock: WO<u32>,
unused2: [u32; 18],
pub mctrl: MCtrl,
unused3: [u32; 31],
pub xadcif_cfg: XADCIfCfg,
pub xadcif_int_sts: XADCIfIntSts,
pub xadcif_int_mask: XADCIfIntMask,
pub xadcif_msts: XADCIf_Msts,
pub xadcif_cmdfifo: XADCIf_CmdFIFO,
pub xadcif_rdfifo: XADCIf_RdFIFO,
pub xadcif_mctl: XADCIf_MCtl,
}
register_at!(RegisterBlock, 0xF8007000, devc);
register!(control, Control, RW, u32);
register_bit!(control, force_rst, 31);
register_bit!(control, pcfg_prog_b, 30);
register_bit!(control, pcfg_pro_cnt_4k, 29);
register_bit!(control, pcap_pr, 27);
register_bit!(control, pcap_mode, 26);
register_bit!(control, pcap_rate_en, 25);
register_bit!(control, multiboot_en, 24);
register_bit!(control, jtag_chain_dis, 23);
register_bit!(control, pcfg_aes_fuse, 12);
register_bits!(control, pcfg_aes_en, u8, 9, 11);
register_bit!(control, seu_en, 8);
register_bit!(control, sec_en, 7);
register_bit!(control, spniden, 6);
register_bit!(control, spiden, 5);
register_bit!(control, niden, 4);
register_bit!(control, dbgen, 3);
register_bits!(control, dap_en, u8, 0, 2);
register!(lock, Lock, RW, u32);
register_bit!(lock, aes_fuse_lock, 4);
register_bit!(lock, aes_en, 3);
register_bit!(lock, seu, 2);
register_bit!(lock, sec, 1);
register_bit!(lock, dbg, 0);
register!(cfg, Cfg, RW, u32);
#[allow(unused)]
#[repr(u8)]
pub enum RFifoTh {
OneFourthFull = 0b00, // One fourth full for read
HalfFull = 0b01, // Half full for read
ThreeFourthFull = 0b10, // Three fourth full for read
Full = 0b11, // Full for read
}
register_bits_typed!(cfg, rfifo_th, u8, RFifoTh, 10, 11);
#[allow(unused)]
#[repr(u8)]
pub enum WFifoTh {
OneFourthEmpty = 0b00, // One fourth empty for write
HalfEmpty = 0b01, // Half empty for write
ThreeFourthEmpty = 0b10, // Three fourth empty for write
Empty = 0b11, // Empty for write
}
register_bits_typed!(cfg, wfifo_th, u8, WFifoTh, 8, 9);
register_bit!(cfg, rclk_edge, 7);
register_bit!(cfg, wclk_edge, 6);
register_bit!(cfg, disable_src_inc, 5);
register_bit!(cfg, disable_dst_inc, 4);
register!(int_sts, IntSts, RW, u32);
register_bit!(int_sts, pss_gts_usr_b_int, 31);
register_bit!(int_sts, pss_fst_cfg_b_int, 30);
register_bit!(int_sts, pss_gpwrdwn_b_int, 29);
register_bit!(int_sts, pss_gts_cfg_b_int, 28);
register_bit!(int_sts, pss_cfg_reset_b_int, 27);
register_bit!(int_sts, ixr_axi_wto, 23);
register_bit!(int_sts, ixr_axi_werr, 22);
register_bit!(int_sts, ixr_axi_rto, 21);
register_bit!(int_sts, ixr_axi_rerr, 20);
register_bit!(int_sts, ixr_rx_fifo_ov, 18);
register_bit!(int_sts, ixr_wr_fifo_lvl, 17);
register_bit!(int_sts, ixr_rd_fifo_lvl, 16);
register_bit!(int_sts, ixr_dma_cmd_err, 15);
register_bit!(int_sts, ixr_dma_q_ov, 14);
register_bit!(int_sts, ixr_dma_done, 13);
register_bit!(int_sts, ixr_d_p_done, 12);
register_bit!(int_sts, ixr_p2d_len_err, 11);
register_bit!(int_sts, ixr_pcfg_hmac_err, 6);
register_bit!(int_sts, ixr_pcfg_seu_err, 5);
register_bit!(int_sts, ixr_pcfg_por_b, 4);
register_bit!(int_sts, ixr_pcfg_cfg_rst, 3);
register_bit!(int_sts, ixr_pcfg_done, 2);
register_bit!(int_sts, ixr_pcfg_init_pe, 1);
register_bit!(int_sts, ixr_pcfg_init_ne, 0);
register!(int_mask, IntMask, RW, u32);
register_bit!(int_mask, m_pss_gts_usr_b_int, 31);
register_bit!(int_mask, m_pss_fst_cfg_b_int, 30);
register_bit!(int_mask, m_pss_gpwrdwn_b_int, 29);
register_bit!(int_mask, m_pss_gts_cfg_b_int, 28);
register_bit!(int_mask, m_pss_cfg_reset_b_int, 27);
register_bit!(int_mask, ixr_axi_wto, 23);
register_bit!(int_mask, ixr_axi_werr, 22);
register_bit!(int_mask, ixr_axi_rto, 21);
register_bit!(int_mask, ixr_axi_rerr, 20);
register_bit!(int_mask, ixr_rx_fifo_ov, 18);
register_bit!(int_mask, ixr_wr_fifo_lvl, 17);
register_bit!(int_mask, ixr_rd_fifo_lvl, 16);
register_bit!(int_mask, ixr_dma_cmd_err, 15);
register_bit!(int_mask, ixr_dma_q_ov, 14);
register_bit!(int_mask, ixr_dma_done, 13);
register_bit!(int_mask, ixr_d_p_done, 12);
register_bit!(int_mask, ixr_p2d_len_err, 11);
register_bit!(int_mask, ixr_pcfg_hmac_err, 6);
register_bit!(int_mask, ixr_pcfg_seu_err, 5);
register_bit!(int_mask, ixr_pcfg_por_b, 4);
register_bit!(int_mask, ixr_pcfg_cfg_rst, 3);
register_bit!(int_mask, ixr_pcfg_done, 2);
register_bit!(int_mask, ixr_pcfg_init_pe, 1);
register_bit!(int_mask, ixr_pcfg_init_ne, 0);
register!(status, Status, RO, u32);
register_bit!(status, dma_cmd_q_f, 31);
register_bit!(status, dma_cmd_q_e, 30);
register_bits!(status, dma_done_cnt, u8, 28, 29);
register_bits!(status, rx_fifo_lvl, u8, 20, 24);
register_bits!(status, tx_fifo_lvl, u8, 12, 18);
register_bit!(status, pss_gts_usr_b, 11);
register_bit!(status, pss_fst_cfg_b, 10);
register_bit!(status, pss_gpwrdwn_b, 9);
register_bit!(status, pss_gts_cfg_b, 8);
register_bit!(status, secure_rst, 7);
register_bit!(status, illegal_apb_access , 6);
register_bit!(status, pss_cfg_reset_b, 5);
register_bit!(status, pcfg_init, 4);
register_bit!(status, efuse_sw_reserve, 3);
register_bit!(status, efuse_sec_en, 2);
register_bit!(status, efuse_jtag_dis, 1);
register!(dma_src_addr, DmaSrcAddr, RW, u32);
register_bits!(dma_src_addr, src_addr, u32, 0, 31);
register!(dma_dest_addr, DmaDestAddr, RW, u32);
register_bits!(dma_dest_addr, dest_addr, u32, 0, 31);
register!(dma_src_len, DmaSrcLen, RW, u32);
register_bits!(dma_src_len, dma_len, u32, 0, 26);
register!(dma_dest_len, DmaDestLen, RW, u32);
register_bits!(dma_dest_len, dma_len, u32, 0, 26);
register!(multiboot_addr, MultibootAddr, RW, u32);
register_bits!(multiboot_addr, multiboot_addr, u8, 0, 12);
register!(mctrl, MCtrl, RW, u32);
register_bits!(mctrl, ps_version, u8, 28, 31);
register_bit!(mctrl, pcfg_por_b, 8);
register_bit!(mctrl, pcap_lpbk, 4);
register!(xadcif_cfg, XADCIfCfg, RW, u32);
register_bit!(xadcif_cfg, enable, 31);
register_bits!(xadcif_cfg, cfifoth, u8, 20, 23);
register_bits!(xadcif_cfg, dfifoth, u8, 16, 19);
register_bit!(xadcif_cfg, wedge, 13);
register_bit!(xadcif_cfg, redge, 13);
register_bits!(xadcif_cfg, tckrate, u8, 8, 9);
register_bits!(xadcif_cfg, igap, u8, 0, 4);
register!(xadcif_int_sts, XADCIfIntSts, RW, u32);
register_bit!(xadcif_int_sts, cfifo_lth, 9);
register_bit!(xadcif_int_sts, dfifo_gth, 8);
register_bit!(xadcif_int_sts, ot, 7);
register_bits!(xadcif_int_sts, alm, u8, 0, 6);
register!(xadcif_int_mask, XADCIfIntMask, RW, u32);
register_bit!(xadcif_int_mask, m_cfifo_lth, 9);
register_bit!(xadcif_int_mask, m_dfifo_gth, 8);
register_bit!(xadcif_int_mask, m_ot, 7);
register_bits!(xadcif_int_mask, m_alm, u8, 0, 6);
register!(xadcif_msts, XADCIf_Msts, RO, u32);
register_bits!(xadcif_msts, cfifo_lvl, u8, 16, 19);
register_bits!(xadcif_msts, dfifo_lvl, u8, 12, 15);
register_bit!(xadcif_msts, cfifof, 11);
register_bit!(xadcif_msts, cfifoe, 10);
register_bit!(xadcif_msts, dfifof, 9);
register_bit!(xadcif_msts, dfifoe, 8);
register_bit!(xadcif_msts, ot, 7);
register_bits!(xadcif_msts, alm, u8, 0, 6);
register!(xadcif_cmdfifo, XADCIf_CmdFIFO, WO, u32);
register_bits!(xadcif_cmdfifo, cmd, u8, 0, 31);
register!(xadcif_rdfifo, XADCIf_RdFIFO, RO, u32);
register_bits!(xadcif_rdfifo, rddata, u8, 0, 31);
register!(xadcif_mctl, XADCIf_MCtl, RW, u32);
register_bit!(xadcif_mctl, reset, 4);

View File

@ -1,52 +0,0 @@
use bit_field::BitField;
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
#[derive(Clone, Copy, Debug)]
/// PHY-Specific Status Register
pub struct PSSR(pub u16);
impl PSSR {
pub fn link(&self) -> bool {
self.0.get_bit(10)
}
pub fn duplex(&self) -> LinkDuplex {
if self.0.get_bit(13) {
LinkDuplex::Full
} else {
LinkDuplex::Half
}
}
pub fn speed(&self) -> Option<LinkSpeed> {
match self.0.get_bits(14..=15) {
0b00 => Some(LinkSpeed::S10),
0b01 => Some(LinkSpeed::S100),
0b10 => Some(LinkSpeed::S1000),
_ => None,
}
}
pub fn get_link(&self) -> Option<Link> {
if self.link() {
Some(Link {
speed: self.speed()?,
duplex: self.duplex(),
})
} else {
None
}
}
}
impl PhyRegister for PSSR {
fn addr() -> u8 {
0x11
}
}
impl From<u16> for PSSR {
fn from(value: u16) -> Self {
PSSR(value)
}
}

View File

@ -1,150 +0,0 @@
//! ARM Generic Interrupt Controller
use bit_field::BitField;
use libregister::{RegisterW, RegisterRW, RegisterR};
use super::mpcore;
#[derive(Debug, Clone, Copy)]
pub struct InterruptId(pub u8);
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum CPUCore {
Core0 = 0b01,
Core1 = 0b10
}
#[derive(Debug, Clone, Copy)]
pub struct TargetCPU(u8);
impl TargetCPU {
pub const fn none() -> TargetCPU {
TargetCPU(0)
}
pub const fn and(self, other: TargetCPU) -> TargetCPU {
TargetCPU(self.0 | other.0)
}
}
impl From<CPUCore> for TargetCPU {
fn from(core: CPUCore) -> Self {
TargetCPU(core as u8)
}
}
pub enum TargetList {
CPUList(TargetCPU),
Others,
This
}
impl From<CPUCore> for TargetList {
fn from(core: CPUCore) -> Self {
TargetList::CPUList(TargetCPU(core as u8))
}
}
impl From<TargetCPU> for TargetList {
fn from(cpu: TargetCPU) -> Self {
TargetList::CPUList(cpu)
}
}
#[derive(Debug, Clone, Copy)]
pub enum InterruptSensitivity {
Level,
Edge,
}
pub struct InterruptController {
mpcore: &'static mut mpcore::RegisterBlock,
}
impl InterruptController {
pub fn gic(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
InterruptController { mpcore }
}
pub fn disable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(false)
.enable_s(false));
// FIXME: Should we disable the distributor globally when we disable interrupt (for a single
// core)?
// self.mpcore.icddcr.modify(|_, w| w.enable_secure(false)
// .enable_non_secure(false));
}
/// enable interrupt signaling
pub fn enable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(true)
.enable_s(true));
self.mpcore.icddcr.modify(|_, w| w.enable_secure(true));
// Enable all interrupts except those of the lowest priority.
self.mpcore.iccpmr.write(mpcore::ICCPMR::zeroed().priority(0xFF));
}
/// send software generated interrupt
pub fn send_sgi(&mut self, id: InterruptId, targets: TargetList) {
assert!(id.0 < 16);
self.mpcore.icdsgir.modify(|_, w| match targets {
TargetList::CPUList(list) => w.target_list_filter(0).cpu_target_list(list.0),
TargetList::Others => w.target_list_filter(0b01),
TargetList::This => w.target_list_filter(0b10)
}.sgiintid(id.0).satt(false));
}
/// enable the interrupt *for this core*.
/// Not needed for SGI.
pub fn enable(&mut self, id: InterruptId, target_cpu: CPUCore, sensitivity: InterruptSensitivity, priority: u8) {
// only 5 bits of the priority is useful
assert!(priority < 32);
self.disable_interrupts();
// enable
let m = (id.0 >> 5) as usize;
let n = (id.0 & 0x1F) as usize;
assert!(m < 3);
unsafe {
self.mpcore.icdiser[m].modify(|mut icdiser| *icdiser.set_bit(n, true));
}
// target cpu
let m = (id.0 >> 2) as usize;
let n = (8 * (id.0 & 3)) as usize;
unsafe {
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32 + 1));
}
// sensitivity
let m = (id.0 >> 4) as usize;
let n = (2 * (id.0 & 0xF)) as usize;
unsafe {
self.mpcore.icdicfr[m].modify(|mut icdicfr| *icdicfr.set_bits(n..=n+1, match sensitivity {
InterruptSensitivity::Level => 0b00,
InterruptSensitivity::Edge => 0b10,
}));
}
// priority
let offset = (id.0 % 4) * 8;
let priority: u32 = (priority as u32) << (offset + 3);
let mask: u32 = 0xFFFFFFFF ^ (0xFF << offset);
unsafe {
self.mpcore.icdipr[id.0 as usize / 4].modify(|v| (v & mask) | priority);
}
self.enable_interrupts();
}
pub fn end_interrupt(&mut self, id: InterruptId) {
self.mpcore.icceoir.modify(|_, w| w.eoiintid(id.0 as u32));
}
pub fn get_interrupt_id(&self) -> InterruptId {
InterruptId(self.mpcore.icciar.read().ackintid() as u8)
}
}

View File

@ -1,107 +0,0 @@
use super::I2c;
use crate::time::Milliseconds;
use embedded_hal::timer::CountDown;
pub struct EEPROM<'a> {
i2c: &'a mut I2c,
port: u8,
address: u8,
page_size: u8,
count_down: crate::timer::global::CountDown<Milliseconds>
}
impl<'a> EEPROM<'a> {
#[cfg(feature = "target_zc706")]
pub fn new(i2c: &'a mut I2c, page_size: u8) -> Self {
EEPROM {
i2c: i2c,
port: 2,
address: 0b1010100,
page_size: page_size,
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
}
}
#[cfg(feature = "target_zc706")]
fn select(&mut self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
self.i2c.pca9548_select(0b1110100, mask as u8)?;
Ok(())
}
/// Random read
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
self.select()?;
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr)?;
self.i2c.restart()?;
self.i2c.write((self.address << 1) | 1)?;
let buf_len = buf.len();
for (i, byte) in buf.iter_mut().enumerate() {
*byte = self.i2c.read(i < buf_len - 1)?;
}
self.i2c.stop()?;
Ok(())
}
/// Smart multi-page writing
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
/// any roll-over when writing ambiguous memory regions.
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
self.select()?;
let buf_len = buf.len();
let mut pb: u8 = addr % self.page_size;
for (i, byte) in buf.iter().enumerate() {
if (i == 0) || (pb == 0) {
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr + (i as u8))?;
}
self.i2c.write(*byte)?;
pb += 1;
if (i == buf_len-1) || (pb == self.page_size) {
self.i2c.stop()?;
self.poll(1_000)?;
pb = 0;
}
}
Ok(())
}
/// Poll
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
self.select()?;
self.count_down.start(Milliseconds(timeout_ms));
loop {
self.i2c.start()?;
let ack = self.i2c.write(self.address << 1)?;
self.i2c.stop()?;
if ack {
break
};
if !self.count_down.waiting() {
return Err("I2C polling timeout")
}
}
Ok(())
}
pub fn read_eui48<'r>(&mut self) -> Result<[u8; 6], &'static str> {
let mut buffer = [0u8; 6];
self.read(0xFA, &mut buffer)?;
Ok(buffer)
}
}

View File

@ -1,231 +0,0 @@
//! I2C Bit-banging Controller
mod regs;
pub mod eeprom;
use super::slcr;
use super::time::Microseconds;
use embedded_hal::timer::CountDown;
use libregister::{RegisterR, RegisterRW, RegisterW};
pub struct I2c {
regs: regs::RegisterBlock,
count_down: super::timer::global::CountDown<Microseconds>
}
impl I2c {
#[cfg(feature = "target_zc706")]
pub fn i2c0() -> Self {
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
slcr::RegisterBlock::unlocked(|slcr| {
// SCL
slcr.mio_pin_50.write(
slcr::MioPin50::zeroed()
.l3_sel(0b000) // as GPIO 50
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// SDA
slcr.mio_pin_51.write(
slcr::MioPin51::zeroed()
.l3_sel(0b000) // as GPIO 51
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// Reset
slcr.gpio_rst_ctrl.reset_gpio();
});
Self::i2c_common(0xFFFF - 0x000C)
}
fn i2c_common(gpio_output_mask: u16) -> Self {
// Setup register block
let self_ = Self {
regs: regs::RegisterBlock::i2c(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
};
// Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| {
w.mask(gpio_output_mask)
});
// Setup GPIO driver direction
self_.regs.gpio_direction.modify(|_, w| {
w.scl(true).sda(true)
});
self_
}
/// Delay for I2C operations, simple wrapper for nb.
fn delay_us(&mut self, us: u64) {
self.count_down.start(Microseconds(us));
nb::block!(self.count_down.wait()).unwrap();
}
fn half_period(&mut self) { self.delay_us(100) }
fn sda_i(&mut self) -> bool {
self.regs.gpio_input.read().sda()
}
fn scl_i(&mut self) -> bool {
self.regs.gpio_input.read().scl()
}
fn sda_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.sda(oe)
})
}
fn sda_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.sda_o(o)
})
}
fn scl_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.scl(oe)
})
}
fn scl_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.scl_o(o)
})
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.scl_oe(false);
self.sda_oe(false);
self.scl_o(false);
self.sda_o(false);
// Check the I2C bus is ready
self.half_period();
self.half_period();
if !self.sda_i() {
// Try toggling SCL a few times
for _bit in 0..8 {
self.scl_oe(true);
self.half_period();
self.scl_oe(false);
self.half_period();
}
}
if !self.sda_i() {
return Err("SDA is stuck low and doesn't get unstuck");
}
if !self.scl_i() {
return Err("SCL is stuck low and doesn't get unstuck");
}
// postcondition: SCL and SDA high
Ok(())
}
pub fn start(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA high
if !self.scl_i() {
return Err("SCL is stuck low and doesn't get unstuck");
}
if !self.sda_i() {
return Err("SDA arbitration lost");
}
self.sda_oe(true);
self.half_period();
self.scl_oe(true);
// postcondition: SCL and SDA low
Ok(())
}
pub fn restart(&mut self) -> Result<(), &'static str> {
// precondition SCL and SDA low
self.sda_oe(false);
self.half_period();
self.scl_oe(false);
self.half_period();
self.start()?;
// postcondition: SCL and SDA low
Ok(())
}
pub fn stop(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA low
self.half_period();
self.scl_oe(false);
self.half_period();
self.sda_oe(false);
self.half_period();
if !self.sda_i() {
return Err("SDA arbitration lost");
}
// postcondition: SCL and SDA high
Ok(())
}
pub fn write(&mut self, data: u8) -> Result<bool, &'static str> {
// precondition: SCL and SDA low
// MSB first
for bit in (0..8).rev() {
self.sda_oe(data & (1 << bit) == 0);
self.half_period();
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
}
self.sda_oe(false);
self.half_period();
self.scl_oe(false);
self.half_period();
// Read ack/nack
let ack = !self.sda_i();
self.scl_oe(true);
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(ack)
}
pub fn read(&mut self, ack: bool) -> Result<u8, &'static str> {
// precondition: SCL and SDA low
self.sda_oe(false);
let mut data: u8 = 0;
// MSB first
for bit in (0..8).rev() {
self.half_period();
self.scl_oe(false);
self.half_period();
if self.sda_i() { data |= 1 << bit }
self.scl_oe(true);
}
// Send ack/nack
self.sda_oe(ack);
self.half_period();
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(data)
}
pub fn pca9548_select(&mut self, address: u8, channels: u8) -> Result<(), &'static str> {
self.start()?;
if !self.write(address << 1)? {
return Err("PCA9548 failed to ack write address")
}
if !self.write(channels)? {
return Err("PCA9548 failed to ack control word")
}
self.stop()?;
Ok(())
}
}

View File

@ -1,91 +0,0 @@
use libregister::{
register, register_at,
register_bit, register_bits
};
// With reference to:
//
// artiq:artiq/gateware/targets/kasli.py:
// self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
//
// misoc:misoc/cores/gpio.py:
// class GPIOTristate(Module, AutoCSR):
// def __init__(self, signals, reset_out=0, reset_oe=0):
// l = len(signals)
// self._in = CSRStatus(l)
// self._out = CSRStorage(l, reset=reset_out)
// self._oe = CSRStorage(l, reset=reset_oe)
//
// Hence, using GPIOs as SCL and SDA GPIOs respectively.
//
// Current compatibility:
// zc706: GPIO 50, 51 == SCL, SDA
pub struct RegisterBlock {
pub gpio_output_mask: &'static mut GPIOOutputMask,
pub gpio_input: &'static mut GPIOInput,
pub gpio_direction: &'static mut GPIODirection,
pub gpio_output_enable: &'static mut GPIOOutputEnable,
}
impl RegisterBlock {
pub fn i2c() -> Self {
Self {
gpio_output_mask: GPIOOutputMask::new(),
gpio_input: GPIOInput::new(),
gpio_direction: GPIODirection::new(),
gpio_output_enable: GPIOOutputEnable::new()
}
}
}
// MASK_DATA_1_MSW:
// Maskable output data for MIO[53:48]
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputMask, 0xE000A00C, new);
// Output for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, scl_o, 2);
// Output for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, sda_o, 3);
// Mask for keeping bits except SCL and SDA unchanged
#[cfg(feature = "target_zc706")]
register_bits!(gpio_output_mask, mask, u16, 16, 31);
// DATA_1_RO:
// Input data for MIO[53:32]
register!(gpio_input, GPIOInput, RO, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOInput, 0xE000A064, new);
// Input for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, scl, 18);
// Input for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, sda, 19);
// DIRM_1:
// Direction mode for MIO[53:32]; 0/1 = in/out
register!(gpio_direction, GPIODirection, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIODirection, 0xE000A244, new);
// Direction for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, scl, 18);
// Direction for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, sda, 19);
// OEN_1:
// Output enable for MIO[53:32]
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputEnable, 0xE000A248, new);
// Output enable for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, scl, 18);
// Output enable for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, sda, 19);

View File

@ -1,25 +0,0 @@
#![no_std]
extern crate alloc;
/// Re-export so that dependents can always use the same version
pub use smoltcp;
pub mod slcr;
pub mod clocks;
pub mod uart;
pub mod devc;
pub mod stdio;
pub mod eth;
pub mod axi_hp;
pub mod axi_gp;
pub mod ddr;
pub mod mpcore;
pub mod gic;
pub mod time;
pub mod timer;
pub mod sdio;
#[cfg(feature = "target_zc706")]
pub mod i2c;
pub mod logger;
pub mod ps7_init;

View File

@ -1,34 +0,0 @@
//! A logger for the `log` crate
use crate::{println, stdio, timer::GlobalTimer};
pub static LOGGER: Logger = Logger;
pub struct Logger;
pub fn init() -> Result<(), log::SetLoggerError> {
log::set_logger(&LOGGER)
}
impl log::Log for Logger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= log::Level::Trace
}
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
let timestamp = unsafe {
GlobalTimer::get()
}.get_us().0;
let seconds = timestamp / 1_000_000;
let micros = timestamp % 1_000_000;
println!("[{:6}.{:06}s] {:>5}({}): {}",
seconds, micros, record.level(), record.target(), record.args());
}
}
fn flush(&self) {
let uart = stdio::get_uart();
while !uart.tx_idle() {}
}
}

View File

@ -1,321 +0,0 @@
///! Register definitions for Application Processing Unit (mpcore)
use volatile_register::{RO, RW};
use libregister::{
register, register_at, register_bit, register_bits,
RegisterW, RegisterRW,
};
#[repr(C)]
pub struct RegisterBlock {
/// SCU Control Register
pub scu_control: ScuControl,
/// SCU Configuration Register
pub scu_config: ScuConfig,
/// SCU CPU Power Status Register
pub scu_cpu_power_status: SCUCPUPowerStatusRegister,
/// SCU Invalidate All Registers in Secure State
pub scu_invalidate: ScuInvalidate,
unused0: [u32; 12],
/// Filtering Start Address Register
pub filtering_start_address: FilteringStartAddressRegister,
/// Defined by FILTEREND input
pub filtering_end_address: FilteringEndAddressRegister,
unused1: [u32; 2],
/// SCU Access Control (SAC) Register
pub scu_access_control_sac: SCUAccessControlRegisterSAC,
/// SCU Non-secure Access Control Register SNSAC
pub scu_non_secure_access_control: SCUNonSecureAccessControlRegister,
unused2: [u32; 42],
/// CPU Interface Control Register
pub iccicr: ICCICR,
/// Interrupt Priority Mask Register
pub iccpmr: ICCPMR,
/// Binary Point Register
pub iccbpr: ICCBPR,
/// Interrupt Acknowledge Register
pub icciar: ICCIAR,
/// End Of Interrupt Register
pub icceoir: ICCEOIR,
/// Running Priority Register
pub iccrpr: ICCRPR,
/// Highest Pending Interrupt Register
pub icchpir: ICCHPIR,
/// Aliased Non-secure Binary Point Register
pub iccabpr: ICCABPR,
unused3: [u32; 55],
/// CPU Interface Implementer Identification Register
pub iccidr: ICCIDR,
/// Global Timer Counter Register 0
pub global_timer_counter0: ValueRegister,
pub global_timer_counter1: ValueRegister,
/// Global Timer Control Register
pub global_timer_control: GlobalTimerControl,
/// Global Timer Interrupt Status Register
pub global_timer_interrupt_status: GlobalTimerInterruptStatusRegister,
/// Comparator Value Register_0
pub comparator_value0: ValueRegister,
pub comparator_value1: ValueRegister,
/// Auto-increment Register
pub auto_increment: RW<u32>,
unused4: [u32; 249],
/// Private Timer Load Register
pub private_timer_load: RW<u32>,
/// Private Timer Counter Register
pub private_timer_counter: RW<u32>,
/// Private Timer Control Register
pub private_timer_control: PrivateTimerControlRegister,
/// Private Timer Interrupt Status Register
pub private_timer_interrupt_status: PrivateTimerInterruptStatusRegister,
unused5: [u32; 4],
/// Watchdog Load Register
pub watchdog_load: RW<u32>,
/// Watchdog Counter Register
pub watchdog_counter: RW<u32>,
/// Watchdog Control Register
pub watchdog_control: WatchdogControlRegister,
/// Watchdog Interrupt Status Register
pub watchdog_interrupt_status: WatchdogInterruptStatusRegister,
/// Watchdog Reset Status Register
pub watchdog_reset_status: WatchdogResetStatusRegister,
/// Watchdog Disable Register
pub watchdog_disable: RW<u32>,
unused6: [u32; 626],
/// Distributor Control Register
pub icddcr: ICDDCR,
/// Interrupt Controller Type Register
pub icdictr: ICDICTR,
/// Distributor Implementer Identification Register
pub icdiidr: ICDIIDR,
unused7: [u32; 29],
/// Interrupt Security Register
pub icdisr0: RW<u32>,
pub icdisr1: RW<u32>,
pub icdisr2: RW<u32>,
unused8: [u32; 29],
/// Interrupt Set-enable Registers
pub icdiser: [RW<u32>; 3],
unused9: [u32; 29],
/// Interrupt Clear-Enable Register 0
pub icdicer0: RW<u32>,
/// Interrupt Clear-Enable Register 1
pub icdicer1: RW<u32>,
/// Interrupt Clear-Enable Register 2
pub icdicer2: RW<u32>,
unused10: [u32; 29],
/// Interrupt Set-pending Register
pub icdispr0: RW<u32>,
pub icdispr1: RW<u32>,
pub icdispr2: RW<u32>,
unused11: [u32; 29],
/// Interrupt Clear-Pending Register
pub icdicpr0: RW<u32>,
pub icdicpr1: RW<u32>,
pub icdicpr2: RW<u32>,
unused12: [u32; 29],
/// Active Bit register
pub icdabr0: RW<u32>,
pub icdabr1: RW<u32>,
pub icdabr2: RW<u32>,
unused13: [u32; 61],
/// Interrupt Priority Register
pub icdipr: [RW<u32>; 24],
unused14: [u32; 232],
/// Interrupt Processor Targets Registers
pub icdiptr: [RW<u32>; 24],
unused15: [u32; 232],
/// Interrupt Configuration Registers
pub icdicfr: [RW<u32>; 6],
unused16: [u32; 58],
/// PPI Status Register
pub ppi_status: PpiStatus,
/// SPI Status Register 0
pub spi_status_0: RO<u32>,
/// SPI Status Register 1
pub spi_status_1: RO<u32>,
unused17: [u32; 125],
/// Software Generated Interrupt Register
pub icdsgir: ICDSGIR,
}
register_at!(RegisterBlock, 0xF8F00000, mpcore);
register!(value_register, ValueRegister, RW, u32);
register_bits!(value_register, value, u32, 0, 31);
register!(scu_control, ScuControl, RW, u32);
register_bit!(scu_control, ic_standby_enable, 6);
register_bit!(scu_control, scu_standby_enable, 5);
register_bit!(scu_control, force_to_port0_enable, 4);
register_bit!(scu_control, scu_speculative_linefill_enable, 3);
register_bit!(scu_control, scu_rams_parity_enable, 2);
register_bit!(scu_control, address_filtering_enable, 1);
register_bit!(scu_control, enable, 0);
impl ScuControl {
pub fn start(&mut self) {
self.modify(|_, w| w.enable(true).scu_speculative_linefill_enable(true));
}
}
register!(scu_config, ScuConfig, RO, u32);
register_bits!(scu_config, tag_ram_sizes, u8, 8, 15);
register_bits!(scu_config, cpus_smp, u8, 4, 7);
register_bits!(scu_config, cpu_number, u8, 0, 1);
register!(scu_cpu_power_status, SCUCPUPowerStatusRegister, RW, u32);
register_bits!(scu_cpu_power_status, cpu3_status, u8, 24, 25);
register_bits!(scu_cpu_power_status, cpu2_status, u8, 16, 17);
register_bits!(scu_cpu_power_status, cpu1_status, u8, 8, 9);
register_bits!(scu_cpu_power_status, cpu0_status, u8, 0, 1);
register!(scu_invalidate, ScuInvalidate, WO, u32);
register_bits!(scu_invalidate, cpu0_ways, u8, 0, 3);
register_bits!(scu_invalidate, cpu1_ways, u8, 4, 7);
register_bits!(scu_invalidate, cpu2_ways, u8, 8, 11);
register_bits!(scu_invalidate, cpu3_ways, u8, 12, 15);
impl ScuInvalidate {
pub fn invalidate_all_cores(&mut self) {
self.write(ScuInvalidate::zeroed()
.cpu0_ways(0xf)
.cpu1_ways(0xf)
.cpu2_ways(0xf)
.cpu3_ways(0xf)
);
}
pub fn invalidate_core1(&mut self) {
self.write(ScuInvalidate::zeroed()
.cpu1_ways(0xf)
);
}
}
register!(filtering_start_address, FilteringStartAddressRegister, RW, u32);
register_bits!(filtering_start_address, filtering_start_address, u32, 20, 31);
register_bits!(filtering_start_address, sbz, u32, 0, 19);
register!(filtering_end_address, FilteringEndAddressRegister, RW, u32);
register_bits!(filtering_end_address, filtering_end_address, u32, 20, 31);
register_bits!(filtering_end_address, sbz, u32, 0, 19);
register!(scu_access_control_sac, SCUAccessControlRegisterSAC, RW, u32);
register_bit!(scu_access_control_sac, cp_u3, 3);
register_bit!(scu_access_control_sac, cp_u2, 2);
register_bit!(scu_access_control_sac, cp_u1, 1);
register_bit!(scu_access_control_sac, cp_u0, 0);
register!(scu_non_secure_access_control, SCUNonSecureAccessControlRegister, RO, u32);
register_bits!(scu_non_secure_access_control, sbz, u32, 12, 31);
register_bit!(scu_non_secure_access_control, cpu3_global_timer, 11);
register_bit!(scu_non_secure_access_control, cpu2_global_timer, 10);
register_bit!(scu_non_secure_access_control, cpu1_global_timer, 9);
register_bit!(scu_non_secure_access_control, cpu0_global_timer, 8);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu3, 7);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu2, 6);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu1, 5);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu0, 4);
register_bit!(scu_non_secure_access_control, component_access_for_cpu3, 3);
register_bit!(scu_non_secure_access_control, component_access_for_cpu2, 2);
register_bit!(scu_non_secure_access_control, component_access_for_cpu1, 1);
register_bit!(scu_non_secure_access_control, component_access_for_cpu0, 0);
register!(iccicr, ICCICR, RW, u32);
register_bit!(iccicr, sbpr, 4);
register_bit!(iccicr, fiq_en, 3);
register_bit!(iccicr, ack_ctl, 2);
register_bit!(iccicr, enable_ns, 1);
register_bit!(iccicr, enable_s, 0);
register!(iccpmr, ICCPMR, RW, u32);
register_bits!(iccpmr, priority, u8, 0, 7);
register!(iccbpr, ICCBPR, RW, u32);
register_bits!(iccbpr, binary_point, u8, 0, 2);
register!(icciar, ICCIAR, RW, u32);
register_bits!(icciar, cpuid, u8, 10, 12);
register_bits!(icciar, ackintid, u32, 0, 9);
register!(icceoir, ICCEOIR, RW, u32);
register_bits!(icceoir, cpuid, u8, 10, 12);
register_bits!(icceoir, eoiintid, u32, 0, 9);
register!(iccrpr, ICCRPR, RW, u32);
register_bits!(iccrpr, priority, u8, 0, 7);
register!(icchpir, ICCHPIR, RW, u32);
register_bits!(icchpir, cpuid, u8, 10, 12);
register_bits!(icchpir, pendintid, u32, 0, 9);
register!(iccabpr, ICCABPR, RW, u32);
register_bits!(iccabpr, binary_point, u8, 0, 2);
register!(iccidr, ICCIDR, RO, u32);
register_bits!(iccidr, part_number, u32, 20, 31);
register_bits!(iccidr, architecture_version, u8, 16, 19);
register_bits!(iccidr, revision_number, u8, 12, 15);
register_bits!(iccidr, implementer, u32, 0, 11);
register!(global_timer_control, GlobalTimerControl, RW, u32);
register_bits!(global_timer_control, prescaler, u8, 8, 15);
register_bit!(global_timer_control, auto_increment_mode, 3);
register_bit!(global_timer_control, irq_enable, 2);
register_bit!(global_timer_control, comp_enablea, 1);
register_bit!(global_timer_control, timer_enable, 0);
register!(global_timer_interrupt_status, GlobalTimerInterruptStatusRegister, RW, u32);
register_bit!(global_timer_interrupt_status, event_flag, 0);
register!(private_timer_control, PrivateTimerControlRegister, RW, u32);
register_bits!(private_timer_control, sbzp, u32, 16, 31);
register_bits!(private_timer_control, prescaler, u8, 8, 15);
register_bits!(private_timer_control, unk_sbzp, u8, 3, 7);
register_bit!(private_timer_control, irq_enable, 2);
register_bit!(private_timer_control, auto_reload, 1);
register_bit!(private_timer_control, timer_enable, 0);
register!(private_timer_interrupt_status, PrivateTimerInterruptStatusRegister, RW, u32);
register_bits!(private_timer_interrupt_status, unk_sbzp, u32, 1, 31);
register!(watchdog_control, WatchdogControlRegister, RW, u32);
register_bits!(watchdog_control, prescaler, u8, 8, 15);
register_bit!(watchdog_control, watchdog_mode, 3);
register_bit!(watchdog_control, it_enable, 2);
register_bit!(watchdog_control, auto_reload, 1);
register_bit!(watchdog_control, watchdog_enable, 0);
register!(watchdog_interrupt_status, WatchdogInterruptStatusRegister, RW, u32);
register_bit!(watchdog_interrupt_status, event_flag, 0);
register!(watchdog_reset_status, WatchdogResetStatusRegister, RW, u32);
register_bit!(watchdog_reset_status, reset_flag, 0);
register!(icddcr, ICDDCR, RW, u32);
register_bit!(icddcr, enable_non_secure, 1);
register_bit!(icddcr, enable_secure, 0);
register!(icdictr, ICDICTR, RO, u32);
register_bits!(icdictr, lspi, u8, 11, 15);
register_bit!(icdictr, security_extn, 10);
register_bits!(icdictr, sbz, u8, 8, 9);
register_bits!(icdictr, cpu_number, u8, 5, 7);
register_bits!(icdictr, it_lines_number, u8, 0, 4);
register!(icdiidr, ICDIIDR, RO, u32);
register_bits!(icdiidr, implementation_version, u8, 24, 31);
register_bits!(icdiidr, revision_number, u32, 12, 23);
register_bits!(icdiidr, implementer, u32, 0, 11);
register!(ppi_status, PpiStatus, RO, u32);
register_bits!(ppi_status, ppi_status, u8, 11, 15);
register_bits!(ppi_status, sbz, u32, 0, 10);
register!(icdsgir, ICDSGIR, RW, u32);
register_bits!(icdsgir, target_list_filter, u8, 24, 25);
register_bits!(icdsgir, cpu_target_list, u8, 16, 23);
register_bit!(icdsgir, satt, 15);
register_bits!(icdsgir, sbz, u32, 4, 14);
register_bits!(icdsgir, sgiintid, u8, 0, 3);

View File

@ -1,108 +0,0 @@
#![cfg(feature = "target_zc706")]
use crate::println;
mod zc706;
// mod cora_z7_10;
#[cfg(feature = "target_zc706")]
use zc706 as target;
// #[cfg(feature = "target_cora_z7_10")]
// use cora_z7_10 as target;
pub fn report_differences() {
for (i, op) in target::INIT_DATA.iter().enumerate() {
let address = op.address();
let overwritten_later = target::INIT_DATA[(i + 1)..].iter()
.any(|later_op| later_op.address() == address);
if !overwritten_later {
op.report_difference();
}
}
}
pub fn apply() {
for op in target::INIT_DATA {
op.apply();
}
}
#[derive(Clone, Debug)]
pub enum InitOp {
MaskWrite(usize, usize, usize),
MaskPoll(usize, usize),
MaskDelay(usize, usize),
}
impl InitOp {
fn address(&self) -> usize {
match self {
InitOp::MaskWrite(address, _, _) => *address,
InitOp::MaskPoll(address, _) => *address,
InitOp::MaskDelay(address, _) => *address,
}
}
fn read(&self) -> usize {
unsafe { *(self.address() as *const usize) }
}
fn difference(&self) -> Option<(usize, usize)> {
let expected = match self {
InitOp::MaskWrite(_, mask, expected) =>
Some((*mask, *expected)),
InitOp::MaskPoll(_, mask) =>
Some((*mask, *mask)),
_ => None,
};
match expected {
Some((mask, expected)) => {
let actual = self.read();
if actual & mask == expected {
None
} else {
Some((actual & mask, expected))
}
}
None =>
None
}
}
pub fn report_difference(&self) {
if let Some((actual, expected)) = self.difference() {
println!(
"Register {:08X} is {:08X}&={:08X} != {:08X} expected",
self.address(),
self.read(),
actual,
expected
);
}
}
pub fn apply(&self) {
let reg = self.address() as *mut usize;
println!("apply {:?}", self);
match self {
InitOp::MaskWrite(_, mask, val) =>
unsafe {
*reg = (val & mask) | (*reg & !mask);
},
InitOp::MaskPoll(_, mask) =>
while unsafe { *reg } & mask == 0 {},
InitOp::MaskDelay(_, mask) => {
let delay = get_number_of_cycles_for_delay(*mask);
while unsafe { *reg } < delay {
println!("W");
}
}
}
}
}
fn get_number_of_cycles_for_delay(delay: usize) -> usize {
const APU_FREQ: usize = 666666687;
APU_FREQ * delay/ (2 * 1000)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,76 +0,0 @@
/// ADMA library
use core::mem::MaybeUninit;
use super::Sdio;
use libcortex_a9::cache;
use libregister::{
register, register_bit,
RegisterR, RegisterW, RegisterRW, VolatileCell,
};
#[repr(C, align(4))]
pub struct Adma2Desc32 {
attribute: Desc32Attribute,
length: VolatileCell<u16>,
address: VolatileCell<u32>,
}
const DESC_MAX_LENGTH: u32 = 65536;
register!(desc32_attribute, Desc32Attribute, VolatileCell, u16);
register_bit!(desc32_attribute, trans, 5);
register_bit!(desc32_attribute, int, 2);
register_bit!(desc32_attribute, end, 1);
register_bit!(desc32_attribute, valid, 0);
pub struct Adma2DescTable([Adma2Desc32; 32]);
impl Adma2DescTable {
pub fn new() -> Self {
let table = MaybeUninit::zeroed();
let table = unsafe { table.assume_init() };
Adma2DescTable(table)
}
/// Initialize the table and setup `adma_system_address`
pub fn setup(&mut self, sdio: &mut Sdio, blk_cnt: u32, buffer: &[u8]) {
let descr_table = &mut self.0;
let blk_size = sdio
.regs
.block_size_block_count
.read()
.transfer_block_size() as u32;
let total_desc_lines = if blk_size * blk_cnt < DESC_MAX_LENGTH {
1
} else {
blk_size * blk_cnt / DESC_MAX_LENGTH
+ if (blk_size * blk_cnt) % DESC_MAX_LENGTH == 0 {
0
} else {
1
}
} as usize;
let ptr = buffer.as_ptr() as u32;
for desc_num in 0..total_desc_lines {
descr_table[desc_num].address.set(ptr + (desc_num as u32) * DESC_MAX_LENGTH);
descr_table[desc_num].attribute.write(
Desc32Attribute::zeroed()
.trans(true)
.valid(true)
);
// 0 is the max length (65536)
descr_table[desc_num].length.set(0);
}
descr_table[total_desc_lines - 1].attribute.modify(|_, w| w.end(true));
descr_table[total_desc_lines - 1].length.set(
(blk_cnt * blk_size - ((total_desc_lines as u32) - 1) * DESC_MAX_LENGTH) as u16,
);
unsafe {
sdio.regs
.adma_system_address
.write(descr_table.as_ptr() as u32);
}
cache::dcci_slice(descr_table);
}
}

View File

@ -1,133 +0,0 @@
use super::regs;
const APP_CMD_PREFIX: u8 = 0x80;
#[allow(unused)]
pub mod args {
pub const CMD8_VOL_PATTERN: u32 = 0x1AA;
pub const RESPOCR_READY: u32 = 0x80000000;
pub const ACMD41_HCS: u32 = 0x40000000;
pub const ACMD41_3V3: u32 = 0x00300000;
pub const CMD1_HIGH_VOL: u32 = 0x00FF8000;
pub const OCR_S18: u32 = 1 << 24;
}
#[allow(unused)]
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum SdCmd {
CMD0 = 0x00,
CMD1 = 0x01,
CMD2 = 0x02,
CMD3 = 0x03,
CMD4 = 0x04,
CMD5 = 0x05,
CMD6 = 0x06,
ACMD6 = APP_CMD_PREFIX + 0x06,
CMD7 = 0x07,
CMD8 = 0x08,
CMD9 = 0x09,
CMD10 = 0x0A,
CMD11 = 0x0B,
CMD12 = 0x0C,
ACMD13 = APP_CMD_PREFIX + 0x0D,
CMD16 = 0x10,
CMD17 = 0x11,
CMD18 = 0x12,
CMD19 = 0x13,
CMD21 = 0x15,
CMD23 = 0x17,
ACMD23 = APP_CMD_PREFIX + 0x17,
CMD24 = 0x18,
CMD25 = 0x19,
CMD41 = 0x29,
ACMD41 = APP_CMD_PREFIX + 0x29,
ACMD42 = APP_CMD_PREFIX + 0x2A,
ACMD51 = APP_CMD_PREFIX + 0x33,
CMD52 = 0x34,
CMD55 = 0x37,
CMD58 = 0x3A,
}
pub fn require_dat(cmd: SdCmd, is_sd_card: bool) -> bool {
use SdCmd::*;
match cmd {
CMD6 => is_sd_card,
CMD8 => !is_sd_card,
ACMD13 | CMD17 | CMD18 | CMD19 | CMD21 | CMD23 | ACMD23 | CMD24 | CMD25 | ACMD51 => true,
_ => false,
}
}
type CmdReg = regs::transfer_mode_command::Write;
fn resp_r1(w: CmdReg) -> CmdReg {
w.response_type_select(regs::ResponseTypeSelect::Length48)
.crc_check_en(true)
.index_check_en(true)
}
fn resp_r1b(w: CmdReg) -> CmdReg {
w.response_type_select(regs::ResponseTypeSelect::Legnth48Check)
.crc_check_en(true)
.index_check_en(true)
}
fn resp_r2(w: CmdReg) -> CmdReg {
w.response_type_select(regs::ResponseTypeSelect::Length136)
.crc_check_en(true)
}
fn resp_r3(w: CmdReg) -> CmdReg {
w.response_type_select(regs::ResponseTypeSelect::Length48)
}
fn resp_r6(w: CmdReg) -> CmdReg {
w.response_type_select(regs::ResponseTypeSelect::Legnth48Check)
.crc_check_en(true)
.index_check_en(true)
}
pub fn set_cmd_reg(cmd: SdCmd, is_sd_card: bool, w: CmdReg) -> CmdReg {
use SdCmd::*;
let w = w.command_index(cmd as u8 & 0x3F);
match cmd {
CMD1 => resp_r3(w),
CMD2 => resp_r2(w),
CMD3 => {
if is_sd_card {
resp_r6(w)
} else {
resp_r1(w)
}
}
CMD5 => resp_r1b(w),
CMD6 => {
if is_sd_card {
resp_r1(w).data_present_select(true)
} else {
resp_r1b(w)
}
}
ACMD6 => resp_r1(w),
CMD7 => resp_r1(w),
CMD8 => {
if is_sd_card {
resp_r1(w)
} else {
resp_r1(w).data_present_select(true)
}
}
CMD9 => resp_r2(w),
CMD10 | CMD11 | CMD12 => resp_r1(w),
ACMD13 => resp_r1(w).data_present_select(true),
CMD16 => resp_r1(w),
CMD17 | CMD18 | CMD19 | CMD21 | CMD23 | ACMD23 | CMD24 | CMD25 => {
resp_r1(w).data_present_select(true)
}
ACMD41 => resp_r3(w),
ACMD42 => resp_r1(w),
ACMD51 => resp_r1(w).data_present_select(true),
CMD52 | CMD55 => resp_r1(w),
_ => w,
}
}

View File

@ -1,461 +0,0 @@
pub mod sd_card;
mod adma;
mod cmd;
mod regs;
use super::clocks::Clocks;
use super::slcr;
use super::time::Milliseconds;
use embedded_hal::timer::CountDown;
use libregister::{RegisterR, RegisterRW, RegisterW};
use log::{trace, debug};
use nb;
/// Basic SDIO Struct with common low-level functions.
pub struct Sdio {
regs: &'static mut regs::RegisterBlock,
count_down: super::timer::global::CountDown<Milliseconds>,
input_clk_hz: u32,
card_type: CardType,
card_detect: bool,
}
#[derive(Debug)]
pub enum CmdTransferError {
CmdInhibited,
DatLineInhibited,
CmdTimeout,
Other(regs::interrupt_status::Read),
}
impl core::fmt::Display for CmdTransferError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use CmdTransferError::*;
write!(f, "Command transfer error: ")?;
match self {
CmdInhibited => write!(f, "Command line inhibited."),
DatLineInhibited => write!(f, "Data line inhibited, possibly due to ongonging data transfer."),
CmdTimeout => write!(f, "Command timeout, check if the card is inserted properly."),
Other(x) => write!(f, "Unknown Error, interrupt status = 0x{:0X}", x.inner),
}
}
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum CardType {
CardNone,
CardSd,
CardMmc,
}
impl Sdio {
/// Initialize SDIO0
/// card_detect means if we would use the card detect pin,
/// false to disable card detection (assume there is card inserted)
pub fn sdio0(card_detect: bool) -> Self {
// initialization according to ps7_init.c
slcr::RegisterBlock::unlocked(|slcr| {
slcr.mio_pin_40.write(
slcr::MioPin40::zeroed()
.l3_sel(0b100)
.io_type(slcr::IoBufferType::Lvcmos18)
.speed(true),
);
slcr.mio_pin_41.write(
slcr::MioPin41::zeroed()
.l3_sel(0b100)
.io_type(slcr::IoBufferType::Lvcmos18)
.speed(true),
);
slcr.mio_pin_42.write(
slcr::MioPin42::zeroed()
.l3_sel(0b100)
.io_type(slcr::IoBufferType::Lvcmos18)
.speed(true),
);
slcr.mio_pin_43.write(
slcr::MioPin43::zeroed()
.l3_sel(0b100)
.io_type(slcr::IoBufferType::Lvcmos18)
.speed(true),
);
slcr.mio_pin_44.write(
slcr::MioPin44::zeroed()
.l3_sel(0b100)
.io_type(slcr::IoBufferType::Lvcmos18)
.speed(true),
);
slcr.mio_pin_45.write(
slcr::MioPin45::zeroed()
.l3_sel(0b100)
.io_type(slcr::IoBufferType::Lvcmos18)
.speed(true),
);
// zc706 card detect pin
#[cfg(feature = "target_zc706")]
{
unsafe {
slcr.sd0_wp_cd_sel.write(0x000E000F);
}
slcr.mio_pin_14.write(
slcr::MioPin14::zeroed()
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.tri_enable(true),
);
}
// cora card detect pin
#[cfg(feature = "target_cora_z7_10")]
{
unsafe {
slcr.sd0_wp_cd_sel.write(47 << 16);
}
slcr.mio_pin_47.write(
slcr::MioPin47::zeroed()
.io_type(slcr::IoBufferType::Lvcmos18)
.speed(true),
);
}
// redpitaya card detect pin
#[cfg(feature = "target_redpitaya")]
{
unsafe {
slcr.sd0_wp_cd_sel.write(46 << 16);
}
slcr.mio_pin_46.write(
slcr::MioPin46::zeroed()
.io_type(slcr::IoBufferType::Lvcmos25)
.speed(true),
);
}
slcr.sdio_rst_ctrl.reset_sdio0();
slcr.aper_clk_ctrl.enable_sdio0();
slcr.sdio_clk_ctrl.enable_sdio0();
});
let clocks = Clocks::get();
let mut self_ = Sdio {
regs: regs::RegisterBlock::sdio0(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
input_clk_hz: clocks.sdio_ref_clk(),
card_type: CardType::CardNone,
card_detect,
};
self_.init();
self_
}
/// Change clock frequency to the value less than or equal to the given value.
/// From XSdPs_Change_ClkFreq in xsdps_options.c. SPEC_V3 related code is removed as
/// our board would only be V1 or V2.
fn change_clk_freq(&mut self, freq: u32) {
debug!("Changing clock frequency to {}", freq);
self.regs
.clock_control
.modify(|_, w| w.sd_clk_en(false).internal_clk_en(false));
const XSDPS_CC_MAX_DIV_CNT: u32 = 256;
// calculate clock divisor
let mut div_cnt: u32 = 0x1;
let mut divisor = 0;
while div_cnt <= XSDPS_CC_MAX_DIV_CNT {
if (self.input_clk_hz / div_cnt) <= freq {
divisor = div_cnt / 2;
break;
}
div_cnt <<= 1;
}
if div_cnt > XSDPS_CC_MAX_DIV_CNT {
panic!("No valid divisor!");
}
// enable internal clock
self.regs
.clock_control
.modify(|_, w| w.sdclk_freq_divisor(divisor as u8).internal_clk_en(true));
while !self.regs.clock_control.read().internal_clk_stable() {}
// enable SD clock
self.regs.clock_control.modify(|_, w| w.sd_clk_en(true));
}
/// Initialization based on XSdPs_CfgInitialize function in xsdps.c
fn init(&mut self) {
// poweroff
self.regs
.control
.modify(|_, w| w.bus_voltage(regs::BusVoltage::V0).bus_power(false));
if self.regs.misc_reg.read().spec_ver() == regs::SpecificationVersion::V3 {
// The documentation said the field can only be V1 or V2,
// so the code is written for V1 and V2. V3 requires special handling
// which is currently not implemented.
// I hope that this would never trigger but it is safer to put a check here.
panic!("The code written is for V1 and V2");
}
// delay to poweroff card
self.delay(1);
// reset all
debug!("Reset SDIO!");
self.regs
.clock_control
.modify(|_, w| w.software_reset_all(true));
while self.regs.clock_control.read().software_reset_all() {}
// set power to 3.3V
self.regs
.control
.modify(|_, w| w.bus_voltage(regs::BusVoltage::V33).bus_power(true));
// set clock frequency
self.change_clk_freq(400_000);
// select voltage
let capabilities = self.regs.capabilities.read();
let voltage = if capabilities.voltage_3_3() {
regs::BusVoltage::V33
} else if capabilities.voltage_3_0() {
regs::BusVoltage::V30
} else if capabilities.voltage_1_8() {
regs::BusVoltage::V18
} else {
regs::BusVoltage::V0
};
self.regs.control.modify(|_, w| w.bus_voltage(voltage));
self.regs
.control
.modify(|_, w| w.dma_select(regs::DmaSelect::ADMA2_32));
// enable all interrupt status except card interrupt
self.regs.interrupt_status_en.write(
(regs::interrupt_status_en::Write { inner: 0xFFFFFFFF })
.card_interrupt_status_en(false),
);
// disable all interrupt signals
self.regs
.interrupt_signal_en
.write(regs::InterruptSignalEn::zeroed());
// set block size to 512 by default
self.regs
.block_size_block_count
.modify(|_, w| w.transfer_block_size(512));
}
/// Delay for SDIO operations, simple wrapper for nb.
pub fn delay(&mut self, ms: u64) {
self.count_down.start(Milliseconds(ms));
nb::block!(self.count_down.wait()).unwrap();
}
/// Send SD command. Basically `cmd_transfer_with_mode` with mode
/// `regs::TransferModeCommand::zeroed()`.
/// Return: Ok if success, Err(status) if failed.
fn cmd_transfer(
&mut self,
cmd: cmd::SdCmd,
arg: u32,
block_cnt: u16,
) -> Result<(), CmdTransferError> {
self.cmd_transfer_with_mode(cmd, arg, block_cnt, regs::TransferModeCommand::zeroed())
}
/// Send SD Command with additional transfer mode.
/// This function would block until response is ready.
/// Return: Ok if success, Err(status) if failed.
fn cmd_transfer_with_mode(
&mut self,
cmd: cmd::SdCmd,
arg: u32,
block_cnt: u16,
transfer_mode: regs::transfer_mode_command::Write,
) -> Result<(), CmdTransferError> {
trace!("Send Cmd {:?}", cmd);
let state = self.regs.present_state.read();
if state.command_inhibit_cmd() {
return Err(CmdTransferError::CmdInhibited);
}
self.regs
.block_size_block_count
.modify(|_, w| w.blocks_count(block_cnt));
self.regs
.clock_control
.modify(|_, w| w.timeout_counter_value(0xE));
unsafe {
self.regs.argument.write(arg);
}
self.regs
.interrupt_status_en
.write(regs::interrupt_status_en::Write { inner: 0xFFFFFFFF });
let is_sd_card = self.card_type == CardType::CardSd;
// Check DAT Line
if cmd != cmd::SdCmd::CMD21 && cmd != cmd::SdCmd::CMD19 {
if self.regs.present_state.read().command_inhibit_dat()
&& cmd::require_dat(cmd, is_sd_card)
{
return Err(CmdTransferError::DatLineInhibited);
}
}
// Set the command registers.
self.regs
.transfer_mode_command
.write(cmd::set_cmd_reg(cmd, is_sd_card, transfer_mode));
// polling for response
loop {
let status = self.regs.interrupt_status.read();
if cmd == cmd::SdCmd::CMD21 || cmd == cmd::SdCmd::CMD19 {
if status.buffer_read_ready() {
self.regs
.interrupt_status
.modify(|_, w| w.buffer_read_ready());
break;
}
}
if status.command_complete() {
break;
}
self.check_error(&status)?;
}
// wait for command complete
while !self.regs.interrupt_status.read().command_complete() {}
self.regs
.interrupt_status
.modify(|_, w| w.command_complete());
Ok(())
}
/// Check if card is inserted.
pub fn is_card_inserted(&self) -> bool {
!self.card_detect || self.regs.present_state.read().card_inserted()
}
/// Switch voltage from 3.3V to 1.8V.
fn switch_voltage(&mut self) -> Result<(), CmdTransferError> {
use cmd::SdCmd::*;
// send switch voltage command
self.cmd_transfer(CMD11, 0, 0)?;
// wait for the lines to go low
let mut state = self.regs.present_state.read();
while state.cmd_line_level()
|| state.dat0_level()
|| state.dat1_level()
|| state.dat2_level()
|| state.dat3_level()
{
state = self.regs.present_state.read();
}
// stop the clock
self.regs
.clock_control
.modify(|_, w| w.sd_clk_en(false).internal_clk_en(false));
// enabling 1.8v in controller
self.regs
.control
.modify(|_, w| w.bus_voltage(regs::BusVoltage::V18));
// wait minimum 5ms
self.delay(5);
if self.regs.control.read().bus_voltage() != regs::BusVoltage::V18 {
// I should not wrap the error of this function into another type later.
// actually this is not correct.
return Err(CmdTransferError::CmdTimeout);
}
// wait for internal clock to stabilize
self.regs
.clock_control
.modify(|_, w| w.internal_clk_en(true));
while !self.regs.clock_control.read().internal_clk_stable() {}
// enable SD clock
self.regs.clock_control.modify(|_, w| w.sd_clk_en(true));
// wait for 1ms
self.delay(1);
// wait for CMD and DATA line to go high
state = self.regs.present_state.read();
while !state.cmd_line_level()
|| !state.dat0_level()
|| !state.dat1_level()
|| !state.dat2_level()
|| !state.dat3_level()
{
state = self.regs.present_state.read();
}
Ok(())
}
/// Detect inserted card type, and set the corresponding field.
/// Return Ok(CardType) on success, Err(CmdTransferError) when failed to identify.
pub fn identify_card(&mut self) -> Result<CardType, CmdTransferError> {
use cmd::{args::*, SdCmd::*};
// actually the delay for this one is unclear in the xilinx code.
self.delay(10);
self.cmd_transfer(CMD0, 0, 0)?;
self.card_type = match self.cmd_transfer(CMD1, ACMD41_HCS | CMD1_HIGH_VOL, 0) {
Ok(()) => CardType::CardMmc,
Err(_) => CardType::CardSd,
};
// clear all status
self.regs
.interrupt_status
.write(regs::interrupt_status::Write { inner: 0xF3FFFFFF });
self.regs
.clock_control
.modify(|_, w| w.software_reset_cmd(true));
// wait for reset completion
while self.regs.clock_control.read().software_reset_cmd() {}
Ok(self.card_type)
}
/// Modify transfer block size.
fn set_block_size(&mut self, block_size: u16) -> Result<(), CmdTransferError> {
use cmd::SdCmd::*;
let state = self.regs.present_state.read();
if state.command_inhibit_cmd()
|| state.command_inhibit_dat()
|| state.write_transfer_active()
|| state.read_transfer_active()
{
return Err(CmdTransferError::CmdInhibited);
}
debug!("Set block size to {}", block_size);
// send block write command
self.cmd_transfer(CMD16, block_size as u32, 0)?;
// set block size
self.regs
.block_size_block_count
.modify(|_, w| w.transfer_block_size(block_size));
Ok(())
}
/// Check if error occured, and reset the error status.
/// Return Err(CmdTransferError) if error occured, Ok(()) otherwise.
fn check_error(
&mut self,
status: &regs::interrupt_status::Read,
) -> Result<(), CmdTransferError> {
if status.error_interrupt() {
let err_status = if status.inner & 0xFFFE0000 == 0 {
CmdTransferError::CmdTimeout
} else {
CmdTransferError::Other(regs::interrupt_status::Read {
inner: status.inner,
})
};
// reset all error status
self.regs
.interrupt_status
.write(regs::interrupt_status::Write { inner: 0xF3FF0000 });
return Err(err_status);
}
Ok(())
}
}

View File

@ -1,548 +0,0 @@
use core::fmt;
use libregister::{register, register_at, register_bit, register_bits, register_bits_typed};
use volatile_register::{RO, RW};
#[allow(unused)]
#[repr(C)]
pub struct RegisterBlock {
pub sdma_system_address: RW<u32>,
pub block_size_block_count: BlockSizeBlockCount,
pub argument: RW<u32>,
pub transfer_mode_command: TransferModeCommand,
pub responses: [RO<u32>; 4],
pub buffer: RW<u32>,
pub present_state: PresentState,
/// Host. power, block gap, wakeup control
pub control: Control,
/// Clock and timeout control, and software reset register.
pub clock_control: ClockControl,
pub interrupt_status: InterruptStatus,
pub interrupt_status_en: InterruptStatusEn,
pub interrupt_signal_en: InterruptSignalEn,
pub auto_cmd12_error_status: AutoCmd12ErrorStatus,
pub capabilities: Capabilities,
pub unused0: RO<u32>,
pub max_current_capabilities: MaxCurrentCapabilities,
pub unused1: RO<u32>,
pub force_event: ForceEvent,
pub adma_error_status: AdmaErrorStatus,
pub adma_system_address: RW<u32>,
pub unused2: RO<u32>,
pub boot_data_timeout_counter: RW<u32>,
pub debug_selection: DebugSelection,
pub unused3: [RO<u32>; 34],
pub spi_interrupt_support: SpiInterruptSupport,
pub unused4: [RO<u32>; 2],
pub misc_reg: MiscReg,
}
#[allow(unused)]
#[repr(u8)]
pub enum CommandType {
Normal = 0b00,
Suspend = 0b01,
Resume = 0b10,
Abort = 0b11,
}
#[allow(unused)]
#[repr(u8)]
pub enum ResponseTypeSelect {
NoResponse = 0b00,
Length136 = 0b01,
Length48 = 0b10,
Legnth48Check = 0b11,
}
#[allow(unused)]
#[repr(u8)]
#[derive(PartialEq, Debug)]
pub enum BusVoltage {
/// 3.3V
V33 = 0b111,
/// 3.0V, typ.
V30 = 0b110,
/// 1.8V, typ.
V18 = 0b101,
/// No power,
V0 = 0b000,
}
#[allow(unused)]
#[repr(u8)]
pub enum DmaSelect {
SDMA = 0b00,
ADMA1 = 0b01,
ADMA2_32 = 0b10,
ADMA2_64 = 0b11,
}
#[allow(unused)]
#[repr(u8)]
pub enum AdmaErrorState {
StStop = 0b00,
StFds = 0b01,
StTfr = 0b11,
}
#[allow(unused)]
#[repr(u8)]
#[derive(PartialEq)]
pub enum SpecificationVersion {
V1 = 0,
V2 = 1,
V3 = 2,
}
register_at!(RegisterBlock, 0xE0100000, sdio0);
register_at!(RegisterBlock, 0xE0101000, sdio1);
register!(block_size_block_count, BlockSizeBlockCount, RW, u32);
register_bits!(
block_size_block_count,
/// Current transfer block count.
blocks_count,
u16,
16,
31
);
register_bits!(
block_size_block_count,
/// Host SDMA Buffer Size, size = 2^(val + 2) KB.
dma_buffer_size,
u8,
12,
14
);
register_bits!(
block_size_block_count,
/// Block size for data transfer. Unit: byte.
transfer_block_size,
u16,
0,
11
);
register!(transfer_mode_command, TransferModeCommand, RW, u32);
register_bits!(
transfer_mode_command,
/// Command Number.
command_index,
u8,
24,
29
);
register_bits_typed!(
transfer_mode_command,
/// Command type register.
command_type,
u8,
CommandType,
22,
23
);
register_bit!(
transfer_mode_command,
/// 1 if data is present and shall be transferred using the DAT line.
data_present_select,
21
);
register_bit!(
transfer_mode_command,
/// If the index field shall be checked.
index_check_en,
20
);
register_bit!(
transfer_mode_command,
/// If CRC shall be checked.
crc_check_en,
19
);
register_bits_typed!(
transfer_mode_command,
/// Different type of response.
response_type_select,
u8,
ResponseTypeSelect,
16,
17
);
register_bit!(
transfer_mode_command,
/// Enables the multi block DAT line data transfer.
multi_block_en,
5
);
register_bit!(
transfer_mode_command,
/// 1 if read (card to host), 0 if write (host to card).
direction_select,
4
);
register_bit!(
transfer_mode_command,
/// If CMD12 shall be issued automatically when last block transfer is completed.
auto_cmd12_en,
2
);
register_bit!(
transfer_mode_command,
/// Enable the block count register.
block_count_en,
1
);
register_bit!(
transfer_mode_command,
/// Enable DMA,
dma_en,
0
);
register!(present_state, PresentState, RO, u32);
register_bit!(
present_state,
/// CMD Line Signal Level.
cmd_line_level,
24
);
register_bit!(
present_state,
/// Signal level in DAT[3]
dat3_level,
23
);
register_bit!(
present_state,
/// Signal level in DAT[2]
dat2_level,
22
);
register_bit!(
present_state,
/// Signal level in DAT[1]
dat1_level,
21
);
register_bit!(
present_state,
/// Signal level in DAT[0]
dat0_level,
20
);
register_bit!(
present_state,
/// Write enabled and inverse of SDx_WP pin level.
write_enabled,
19
);
register_bit!(
present_state,
/// Card detected and inverse of SDx_CDn pin level.
card_detected,
18
);
register_bit!(present_state, card_state_stable, 17);
register_bit!(present_state, card_inserted, 16);
register_bit!(present_state, buffer_read_en, 11);
register_bit!(present_state, buffer_write_en, 10);
register_bit!(present_state, read_transfer_active, 9);
register_bit!(present_state, write_transfer_active, 8);
register_bit!(present_state, dat_line_active, 2);
register_bit!(present_state, command_inhibit_dat, 1);
register_bit!(present_state, command_inhibit_cmd, 0);
register!(control, Control, RW, u32);
register_bit!(
control,
/// Enable wakeup event via SD card removal assertion.
wakeup_on_removal,
26
);
register_bit!(
control,
/// Enable wakeup event via SD card insertion assertion.
wakeup_on_insertion,
25
);
register_bit!(
control,
/// Enable wakeup event via card interrupt assertion.
wakeup_on_interrupt,
24
);
register_bit!(
control,
///Enable interrupt detection at the block gap for a multiple block transfer.
interrupt_at_block_gap,
19
);
register_bit!(
control,
/// Enable the use of the read wait protocol.
read_wait_control,
18
);
register_bit!(
control,
/// Restart a trasaction which was stopped using the stop at block gap request.
continue_req,
17
);
register_bit!(
control,
/// Stop executing a transaction at the next block gap.
stop_at_block_gap_req,
16
);
register_bits_typed!(control, bus_voltage, u8, BusVoltage, 9, 11);
register_bit!(control, bus_power, 8);
register_bit!(
control,
/// Selects source for card detection. 0 for SDCD#, 1 for card detect test level.
card_detect_signal,
7
);
register_bit!(
control,
/// Indicates card inserted or not. Enabled when card detect signal is 1.
card_detect_test_level,
6
);
register_bits_typed!(control, dma_select, u8, DmaSelect, 3, 4);
register_bit!(control, high_speed_en, 2);
register_bit!(
control,
/// Select the data width of the HC. 1 for 4-bit, 0 for 1-bit.
data_width_select,
1
);
register_bit!(
control,
/// 1 for LED on, 0 for LED off.
led_control,
0
);
register!(clock_control, ClockControl, RW, u32);
register_bit!(
clock_control,
/// Software reset for DAT line.
software_reset_dat,
26
);
register_bit!(
clock_control,
/// Software reset for CMD line.
software_reset_cmd,
25
);
register_bit!(
clock_control,
/// Software reset for ALL.
software_reset_all,
24
);
register_bits!(
clock_control,
/// Determines the interval by which DAT line time-outs are detected.
/// Interval = TMCLK * 2^(13 + val)
/// Note: 0b1111 is reserved.
timeout_counter_value,
u8,
16,
19
);
register_bits!(
clock_control,
/// Selects the frequency divisor, thus the clock frequency for SDCLK.
/// Choose the smallest possible divisor which results in a clock frequency
/// that is less than or equal to the target frequency.
sdclk_freq_divisor,
u8,
8,
15
);
register_bit!(clock_control, sd_clk_en, 2);
register_bit!(
clock_control,
/// 1 when SD clock is stable.
/// Note that this field is read-only.
internal_clk_stable,
1,
RO
);
register_bit!(clock_control, internal_clk_en, 0);
register!(interrupt_status, InterruptStatus, RW, u32, 1 << 15 | 1 << 8);
register_bit!(interrupt_status, ceata_error, 29, WTC);
register_bit!(interrupt_status, target_response_error, 28, WTC);
register_bit!(interrupt_status, adma_error, 25, WTC);
register_bit!(interrupt_status, auto_cmd12_error, 24, WTC);
register_bit!(interrupt_status, current_limit_error, 23, WTC);
register_bit!(interrupt_status, data_end_bit_error, 22, WTC);
register_bit!(interrupt_status, data_crc_error, 21, WTC);
register_bit!(interrupt_status, data_timeout_error, 20, WTC);
register_bit!(interrupt_status, command_index_error, 19, WTC);
register_bit!(interrupt_status, command_end_bit_error, 18, WTC);
register_bit!(interrupt_status, command_crc_error, 17, WTC);
register_bit!(interrupt_status, command_timeout_error, 16, WTC);
register_bit!(interrupt_status, error_interrupt, 15, RO);
register_bit!(interrupt_status, boot_terminate_interrupt, 10, WTC);
register_bit!(interrupt_status, boot_ack_rcv, 9, WTC);
register_bit!(interrupt_status, card_interrupt, 8, RO);
register_bit!(interrupt_status, card_removal, 7, WTC);
register_bit!(interrupt_status, card_insertion, 6, WTC);
register_bit!(interrupt_status, buffer_read_ready, 5, WTC);
register_bit!(interrupt_status, buffer_write_ready, 4, WTC);
register_bit!(interrupt_status, dma_interrupt, 3, WTC);
register_bit!(interrupt_status, block_gap_event, 2, WTC);
register_bit!(interrupt_status, transfer_complete, 1, WTC);
register_bit!(interrupt_status, command_complete, 0, WTC);
register!(interrupt_status_en, InterruptStatusEn, RW, u32);
register_bit!(interrupt_status_en, ceata_error_status_en, 29);
register_bit!(interrupt_status_en, target_response_error_status_en, 28);
register_bit!(interrupt_status_en, adma_error_status_en, 25);
register_bit!(interrupt_status_en, auto_cmd12_error_status_en, 24);
register_bit!(interrupt_status_en, current_limit_error_status_en, 23);
register_bit!(interrupt_status_en, data_end_bit_error_status_en, 22);
register_bit!(interrupt_status_en, data_crc_error_status_en, 21);
register_bit!(interrupt_status_en, data_timeout_error_status_en, 20);
register_bit!(interrupt_status_en, cmd_index_error_status_en, 19);
register_bit!(interrupt_status_en, cmd_end_bit_error_status_en, 18);
register_bit!(interrupt_status_en, cmd_crc_error_status_en, 17);
register_bit!(interrupt_status_en, cmd_timeout_error_status_en, 16);
register_bit!(interrupt_status_en, fixed_to_0, 15, RO);
register_bit!(interrupt_status_en, boot_terminate_interrupt_en, 10);
register_bit!(interrupt_status_en, boot_ack_rcv_en, 9);
register_bit!(interrupt_status_en, card_interrupt_status_en, 8);
register_bit!(interrupt_status_en, card_removal_status_en, 7);
register_bit!(interrupt_status_en, card_insertion_status_en, 6);
register_bit!(interrupt_status_en, buffer_read_ready_status_en, 5);
register_bit!(interrupt_status_en, buffer_write_ready_status_en, 4);
register_bit!(interrupt_status_en, dma_interrupt_status_en, 3);
register_bit!(interrupt_status_en, block_gap_evt_status_en, 2);
register_bit!(interrupt_status_en, transfer_complete_status_en, 1);
register_bit!(interrupt_status_en, cmd_complete_status_en, 0);
register!(interrupt_signal_en, InterruptSignalEn, RW, u32);
register_bit!(interrupt_signal_en, ceata_error_signal_en, 29);
register_bit!(interrupt_signal_en, target_response_error_signal_en, 28);
register_bit!(interrupt_signal_en, adma_error_signal_en, 25);
register_bit!(interrupt_signal_en, auto_cmd12_error_signal_en, 24);
register_bit!(interrupt_signal_en, current_limit_error_signal_en, 23);
register_bit!(interrupt_signal_en, data_end_bit_error_signal_en, 22);
register_bit!(interrupt_signal_en, data_crc_error_signal_en, 21);
register_bit!(interrupt_signal_en, data_timeout_error_signal_en, 20);
register_bit!(interrupt_signal_en, cmd_index_error_signal_en, 19);
register_bit!(interrupt_signal_en, cmd_end_bit_error_signal_en, 18);
register_bit!(interrupt_signal_en, cmd_crc_error_signal_en, 17);
register_bit!(interrupt_signal_en, cmd_timeout_error_signal_en, 16);
register_bit!(interrupt_signal_en, fixed_to_0, 15, RO);
register_bit!(interrupt_signal_en, boot_terminate_interrupt_signal_en, 10);
register_bit!(interrupt_signal_en, boot_ack_rcv_signal_en, 9);
register_bit!(interrupt_signal_en, card_interrupt_signal_en, 8);
register_bit!(interrupt_signal_en, card_removal_signal_en, 7);
register_bit!(interrupt_signal_en, card_insertion_signal_en, 6);
register_bit!(interrupt_signal_en, buffer_read_ready_signal_en, 5);
register_bit!(interrupt_signal_en, buffer_write_ready_signal_en, 4);
register_bit!(interrupt_signal_en, dma_interrupt_signal_en, 3);
register_bit!(interrupt_signal_en, block_gap_evt_signal_en, 2);
register_bit!(interrupt_signal_en, transfer_complete_signal_en, 1);
register_bit!(interrupt_signal_en, cmd_complete_signal_en, 0);
register!(auto_cmd12_error_status, AutoCmd12ErrorStatus, RO, u32);
register_bit!(
auto_cmd12_error_status,
cmd_not_issued_by_auto_cmd12_error,
7
);
register_bit!(auto_cmd12_error_status, index_error, 4);
register_bit!(auto_cmd12_error_status, end_bit_error, 3);
register_bit!(auto_cmd12_error_status, crc_error, 2);
register_bit!(auto_cmd12_error_status, timeout_error, 1);
register_bit!(auto_cmd12_error_status, not_executed, 0);
register!(capabilities, Capabilities, RO, u32);
register_bit!(capabilities, spi_block_mode, 30);
register_bit!(capabilities, spi_mode, 29);
register_bit!(capabilities, support_64bit, 28);
register_bit!(capabilities, interrupt_mode, 27);
register_bit!(capabilities, voltage_1_8, 26);
register_bit!(capabilities, voltage_3_0, 25);
register_bit!(capabilities, voltage_3_3, 24);
register_bit!(capabilities, suspend_resume, 23);
register_bit!(capabilities, sdma, 22);
register_bit!(capabilities, hgih_speed, 21);
register_bit!(capabilities, adma2, 19);
register_bit!(capabilities, extended_media_bus, 18);
register_bits!(
capabilities,
/// Length = 2^(9 + v) bytes.
max_block_len,
u8,
16,
17
);
register_bit!(capabilities, timeout_clock_unit, 7);
register!(max_current_capabilities, MaxCurrentCapabilities, RO, u32);
register_bits!(max_current_capabilities, max_current_1_8v, u8, 16, 23);
register_bits!(max_current_capabilities, max_current_3_0v, u8, 8, 15);
register_bits!(max_current_capabilities, max_current_3_3v, u8, 0, 7);
register!(force_event, ForceEvent, WO, u32);
register_bit!(force_event, ceata_error, 29);
register_bit!(force_event, target_response_error, 28);
register_bit!(force_event, adma_error, 25);
register_bit!(force_event, auto_cmd12_error, 24);
register_bit!(force_event, current_limit_error, 23);
register_bit!(force_event, data_end_bit_error, 22);
register_bit!(force_event, data_crc_error, 21);
register_bit!(force_event, data_timeout_error, 20);
register_bit!(force_event, cmd_index_error, 19);
register_bit!(force_event, cmd_end_bit_error, 18);
register_bit!(force_event, cmd_crc_error, 17);
register_bit!(force_event, cmd_timeout_error, 16);
register_bit!(force_event, cmd_not_issued_by_auto_cmd12_error, 7);
register_bit!(force_event, auto_cmd12_index_error, 4);
register_bit!(force_event, auto_cmd12_end_bit_error, 3);
register_bit!(force_event, auto_cmd12_crc_error, 2);
register_bit!(force_event, auto_cmd12_timeout_error, 1);
register_bit!(force_event, auto_cmd12_not_executed, 0);
register!(adma_error_status, AdmaErrorStatus, RW, u32, 0b11);
register_bit!(adma_error_status, length_mismatch_error, 2, WTC);
register_bits_typed!(adma_error_status, error_state, u8, AdmaErrorState, 0, 1);
register!(debug_selection, DebugSelection, WO, u32);
register_bit!(debug_selection, debug_select, 0);
register!(spi_interrupt_support, SpiInterruptSupport, RW, u32);
register_bits!(
spi_interrupt_support,
/// There should be a problem with the documentation of this field.
spi_int_support,
u8,
0,
7
);
register!(misc_reg, MiscReg, RO, u32);
register_bits!(misc_reg, vendor_version_num, u8, 24, 31);
register_bits_typed!(misc_reg, spec_ver, u8, SpecificationVersion, 16, 23);
register_bits!(
misc_reg,
/// Logical OR of interrupt signal and wakeup signal for each slot.
slot_interrupt_signal,
u8,
0,
7
);
impl fmt::Debug for interrupt_status::Read {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_fmt(format_args!("status: {:0X}", self.inner))
}
}

View File

@ -1,379 +0,0 @@
use super::{adma::Adma2DescTable, cmd, CardType, CmdTransferError, Sdio};
use libcortex_a9::cache;
use libregister::{RegisterR, RegisterRW, RegisterW};
use log::{trace, debug};
#[derive(Debug)]
pub enum CardInitializationError {
AlreadyInitialized,
NoCardInserted,
InitializationFailedOther,
InitializationFailedCmd(CmdTransferError),
}
impl core::fmt::Display for CardInitializationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use CardInitializationError::*;
write!(f, "Card initialization error: ")?;
match self {
AlreadyInitialized => write!(f, "Card already initialized."),
NoCardInserted => write!(f, "No card inserted, check if the card is inserted properly."),
InitializationFailedOther => write!(f, "Unknown error. Please check the debug messages."),
InitializationFailedCmd(x) => write!(f, "{}", x)
}
}
}
impl From<CmdTransferError> for CardInitializationError {
fn from(error: CmdTransferError) -> Self {
CardInitializationError::InitializationFailedCmd(error)
}
}
#[derive(Debug)]
enum CardVersion {
SdVer1,
SdVer2,
}
pub struct SdCard {
sdio: Sdio,
adma2_desc_table: Adma2DescTable,
card_version: CardVersion,
hcs: bool,
card_id: [u32; 4],
rel_card_addr: u32,
sector_cnt: u32,
switch_1v8: bool,
width_4_bit: bool,
}
const BLK_SIZE_MASK: u16 = 0x00000FFF;
impl core::fmt::Display for SdCard {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "SdCard: \n card version: {:?}\n hcs: {}\n card id: {:?}\n rel card addr: {}\n sector count: {}",
self.card_version, self.hcs, self.card_id, self.rel_card_addr, self.sector_cnt)
}
}
impl SdCard {
fn sd_card_initialize(&mut self) -> Result<(), CardInitializationError> {
use cmd::{args::*, SdCmd::*};
if !self.sdio.is_card_inserted() {
return Err(CardInitializationError::NoCardInserted);
}
// CMD0
self.sdio.cmd_transfer(CMD0, 0, 0)?;
match self.sdio.cmd_transfer(CMD8, CMD8_VOL_PATTERN, 0) {
Err(CmdTransferError::CmdTimeout) => {
// reset
self.sdio
.regs
.clock_control
.modify(|_, w| w.software_reset_cmd(true));
// wait until reset is completed
while self.sdio.regs.clock_control.read().software_reset_cmd() {}
}
// for other error, return initialization failed
Err(e) => return Err(CardInitializationError::from(e)),
_ => (),
}
self.card_version = if self.sdio.regs.responses[0].read() != CMD8_VOL_PATTERN {
CardVersion::SdVer1
} else {
CardVersion::SdVer2
};
// send ACMD41 while card is still busy with power up
loop {
self.sdio.cmd_transfer(CMD55, 0, 0)?;
self.sdio
.cmd_transfer(ACMD41, ACMD41_HCS | ACMD41_3V3 | (0x1FF << 15), 0)?;
if (self.sdio.regs.responses[0].read() & RESPOCR_READY) != 0 {
break;
}
}
let response = self.sdio.regs.responses[0].read();
// update HCS support flag
self.hcs = (response & ACMD41_HCS) != 0;
if (response & OCR_S18) != 0 {
self.switch_1v8 = true;
self.sdio.switch_voltage()?;
}
self.sdio.cmd_transfer(CMD2, 0, 0)?;
for i in 0..=3 {
self.card_id[i] = self.sdio.regs.responses[i].read();
}
self.rel_card_addr = 0;
while self.rel_card_addr == 0 {
self.sdio.cmd_transfer(CMD3, 0, 0)?;
self.rel_card_addr = self.sdio.regs.responses[0].read() & 0xFFFF0000;
}
self.sdio.cmd_transfer(CMD9, self.rel_card_addr, 0)?;
self.sdio
.regs
.interrupt_status
.modify(|_, w| w.transfer_complete());
let mut csd: [u32; 4] = [0, 0, 0, 0];
for i in 0..=3 {
csd[i] = self.sdio.regs.responses[i].read();
trace!("CSD[{}] = {:0X}", i, csd[i]);
}
const CSD_STRUCT_MSK: u32 = 0x00C00000;
const C_SIZE_MULT_MASK: u32 = 0x00000380;
const C_SIZE_LOWER_MASK: u32 = 0xFFC00000;
const C_SIZE_UPPER_MASK: u32 = 0x00000003;
const READ_BLK_LEN_MASK: u32 = 0x00000F00;
const CSD_V2_C_SIZE_MASK: u32 = 0x3FFFFF00;
const XSDPS_BLK_SIZE_512_MASK: u32 = 0x200;
if ((csd[3] & CSD_STRUCT_MSK) >> 22) == 0 {
let blk_len = 1 << ((csd[2] & READ_BLK_LEN_MASK) >> 8);
let mult = 1 << (((csd[1] & C_SIZE_MULT_MASK) >> 7) + 2);
let mut device_size = (csd[1] & C_SIZE_LOWER_MASK) >> 22;
device_size |= (csd[2] & C_SIZE_UPPER_MASK) << 10;
device_size = (device_size + 1) * mult;
device_size = device_size * blk_len;
self.sector_cnt = device_size / XSDPS_BLK_SIZE_512_MASK;
} else if ((csd[3] & CSD_STRUCT_MSK) >> 22) == 1 {
self.sector_cnt = (((csd[1] & CSD_V2_C_SIZE_MASK) >> 8) + 1) * 1024;
} else {
return Err(CardInitializationError::InitializationFailedOther);
}
self.sdio.change_clk_freq(25_000_000);
// CMD7: select card
self.sdio.cmd_transfer(CMD7, self.rel_card_addr, 0)?;
// pull up
self.sdio.cmd_transfer(CMD55, self.rel_card_addr, 0)?;
self.sdio.cmd_transfer(ACMD42, 0, 0)?;
let mut scr: [u8; 32] = [0; 32];
self.get_bus_width(&mut scr)?;
trace!("SCR={:?}", scr);
if scr[1] & 0x4 != 0 {
// 4bit support
debug!("4 bit support");
self.change_bus_width()?;
}
self.sdio.set_block_size(512)?;
Ok(())
}
/// Convert Sdio into SdCard struct, error if no card inserted or it is not an SD card.
pub fn from_sdio(mut sdio: Sdio) -> Result<Self, CardInitializationError> {
match sdio.identify_card()? {
CardType::CardSd => (),
_ => return Err(CardInitializationError::NoCardInserted),
};
let mut _self = SdCard {
sdio,
adma2_desc_table: Adma2DescTable::new(),
card_version: CardVersion::SdVer1,
hcs: false,
card_id: [0, 0, 0, 0],
rel_card_addr: 0,
sector_cnt: 0,
switch_1v8: false,
width_4_bit: false,
};
_self.sd_card_initialize()?;
Ok(_self)
}
/// Convert SdCard struct back to Sdio struct.
pub fn to_sdio(self) -> Sdio {
self.sdio
}
/// read blocks starting from an address. Each block has length 512 byte.
/// Note that the address is block address, i.e. 0 for 0~512, 1 for 512~1024, etc.
pub fn read_block(
&mut self,
address: u32,
block_cnt: u16,
buffer: &mut [u8],
) -> Result<(), CmdTransferError> {
assert!(buffer.len() >= (block_cnt as usize) * 512);
// set block size if not set already
if self
.sdio
.regs
.block_size_block_count
.read()
.transfer_block_size()
!= 512
{
self.sdio.set_block_size(512)?;
}
let real_addr = if self.hcs {
address
} else {
// standard capacity card uses byte address
address * 0x200
};
self.adma2_desc_table.setup(&mut self.sdio, block_cnt as u32, buffer);
// invalidate D cache, required for ZC706, not sure for Cora Z7 10
cache::dcci_slice(buffer);
let cmd = if block_cnt == 1 {
cmd::SdCmd::CMD17
} else {
cmd::SdCmd::CMD18
};
let mode = if block_cnt == 1 {
super::regs::TransferModeCommand::zeroed()
.block_count_en(true)
.direction_select(true)
.dma_en(true)
} else {
super::regs::TransferModeCommand::zeroed()
.auto_cmd12_en(true)
.block_count_en(true)
.direction_select(true)
.multi_block_en(true)
.dma_en(true)
};
self.sdio
.cmd_transfer_with_mode(cmd, real_addr, block_cnt, mode)?;
self.wait_transfer_complete()?;
cache::dcci_slice(buffer);
Ok(())
}
/// write blocks starting from an address. Each block has length 512 byte.
/// Note that the address is block address, i.e. 0 for 0~512, 1 for 512~1024, etc.
pub fn write_block(
&mut self,
address: u32,
block_cnt: u16,
buffer: &[u8],
) -> Result<(), CmdTransferError> {
assert!(buffer.len() >= (block_cnt as usize) * 512);
// set block size if not set already
if self
.sdio
.regs
.block_size_block_count
.read()
.transfer_block_size()
!= 512
{
self.sdio.set_block_size(512)?;
}
let real_addr = if self.hcs {
address
} else {
// standard capacity card uses byte address
address * 0x200
};
self.adma2_desc_table.setup(&mut self.sdio, block_cnt as u32, buffer);
// invalidate D cache, required for ZC706, not sure for Cora Z7 10
cache::dcci_slice(buffer);
let cmd = if block_cnt == 1 {
cmd::SdCmd::CMD24
} else {
cmd::SdCmd::CMD25
};
let mode = if block_cnt == 1 {
super::regs::TransferModeCommand::zeroed()
.block_count_en(true)
.dma_en(true)
} else {
super::regs::TransferModeCommand::zeroed()
.auto_cmd12_en(true)
.block_count_en(true)
.multi_block_en(true)
.dma_en(true)
};
self.sdio
.cmd_transfer_with_mode(cmd, real_addr, block_cnt, mode)?;
// wait for transfer complete interrupt
self.wait_transfer_complete()?;
cache::dcci_slice(buffer);
Ok(())
}
fn get_bus_width(&mut self, buf: &mut [u8]) -> Result<(), CmdTransferError> {
use cmd::SdCmd::*;
debug!("Getting bus width");
for i in 0..8 {
buf[i] = 0;
}
// send block write command
self.sdio.cmd_transfer(CMD55, self.rel_card_addr, 0)?;
let blk_cnt: u16 = 1;
let blk_size: u16 = 8 & BLK_SIZE_MASK;
self.sdio
.regs
.block_size_block_count
.modify(|_, w| w.transfer_block_size(blk_size));
self.adma2_desc_table.setup(&mut self.sdio, blk_cnt as u32, buf);
cache::dcci_slice(buf);
self.sdio.cmd_transfer_with_mode(
ACMD51,
0,
blk_cnt,
super::regs::TransferModeCommand::zeroed()
.dma_en(true)
.direction_select(true),
)?;
self.wait_transfer_complete()?;
cache::dcci_slice(buf);
Ok(())
}
fn change_bus_width(&mut self) -> Result<(), CmdTransferError> {
use cmd::SdCmd::*;
debug!("Changing bus width");
self.sdio.cmd_transfer(CMD55, self.rel_card_addr, 0)?;
self.width_4_bit = true;
self.sdio.cmd_transfer(ACMD6, 0x2, 0)?;
self.sdio.delay(1);
self.sdio
.regs
.control
.modify(|_, w| w.data_width_select(true));
Ok(())
}
fn wait_transfer_complete(&mut self) -> Result<(), CmdTransferError> {
trace!("Wait for transfer complete");
let mut status = self.sdio.regs.interrupt_status.read();
while !status.transfer_complete() {
self.sdio.check_error(&status)?;
status = self.sdio.regs.interrupt_status.read();
}
trace!("Clearing transfer complete");
self.sdio
.regs
.interrupt_status
.modify(|_, w| w.transfer_complete());
Ok(())
}
}

View File

@ -1,80 +0,0 @@
use core::ops::{Deref, DerefMut};
use libcortex_a9::{asm, mutex::{Mutex, MutexGuard}};
use crate::uart::Uart;
const UART_RATE: u32 = 115_200;
static mut UART: Mutex<LazyUart> = Mutex::new(LazyUart::Uninitialized);
#[doc(hidden)]
pub fn get_uart<'a>() -> MutexGuard<'a, LazyUart> {
unsafe { UART.lock() }
}
/// Deinitialize so that the Uart will be reinitialized on next
/// output.
///
/// Delays so that an outstanding transmission can finish.
pub fn drop_uart() {
for _ in 0..1_000_000 {
asm::nop();
}
unsafe { UART = Mutex::new(LazyUart::Uninitialized); }
}
/// Initializes the UART on first use through `.deref_mut()` for debug
/// output through the `print!` and `println!` macros.
pub enum LazyUart {
Uninitialized,
Initialized(Uart),
}
impl Deref for LazyUart {
type Target = Uart;
fn deref(&self) -> &Uart {
match self {
LazyUart::Uninitialized =>
panic!("stdio not initialized!"),
LazyUart::Initialized(uart) =>
uart,
}
}
}
impl DerefMut for LazyUart {
fn deref_mut(&mut self) -> &mut Uart {
match self {
LazyUart::Uninitialized => {
#[cfg(any(feature = "target_cora_z7_10", feature = "target_redpitaya"))]
let uart = Uart::uart0(UART_RATE);
#[cfg(feature = "target_zc706")]
let uart = Uart::uart1(UART_RATE);
*self = LazyUart::Initialized(uart);
self
}
LazyUart::Initialized(uart) =>
uart,
}
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
let mut uart = $crate::stdio::get_uart();
let _ = write!(uart, $($arg)*);
})
}
#[macro_export]
macro_rules! println {
($($arg:tt)*) => ({
use core::fmt::Write;
let mut uart = $crate::stdio::get_uart();
let _ = write!(uart, $($arg)*);
let _ = write!(uart, "\n");
// flush after the newline
while !uart.tx_idle() {}
})
}

View File

@ -1,25 +0,0 @@
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Milliseconds(pub u64);
impl core::ops::Add for Milliseconds {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Milliseconds(self.0 + rhs.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Microseconds(pub u64);
impl core::ops::Add for Microseconds {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Microseconds(self.0 + rhs.0)
}
}
pub trait TimeSource<U> {
fn now(&self) -> U;
}

View File

@ -1,170 +0,0 @@
use core::ops::Add;
use void::Void;
use libregister::{RegisterR, RegisterW};
use crate::{
clocks::Clocks,
mpcore,
time::{Milliseconds, Microseconds, TimeSource},
};
/// "uptime"
#[derive(Clone, Copy)]
pub struct GlobalTimer {
regs: &'static mpcore::RegisterBlock,
}
impl GlobalTimer {
/// Get the potentially uninitialized timer
pub unsafe fn get() -> GlobalTimer {
let regs = mpcore::RegisterBlock::mpcore();
GlobalTimer { regs }
}
/// Get the timer with a reset
pub fn start() -> GlobalTimer {
let mut regs = mpcore::RegisterBlock::mpcore();
Self::reset(&mut regs);
GlobalTimer { regs }
}
fn reset(regs: &mut mpcore::RegisterBlock) {
// Disable
regs.global_timer_control.write(
mpcore::GlobalTimerControl::zeroed()
);
// Reset counters
regs.global_timer_counter0.write(
mpcore::ValueRegister::zeroed()
);
regs.global_timer_counter1.write(
mpcore::ValueRegister::zeroed()
);
// find a prescaler value that matches CPU speed / 2 to us
let clocks = Clocks::get();
let mut prescaler = clocks.cpu_3x2x() / 1_000_000;
while prescaler > 256 {
prescaler /= 2;
}
// Start
regs.global_timer_control.write(
mpcore::GlobalTimerControl::zeroed()
.prescaler((prescaler - 1) as u8)
.auto_increment_mode(true)
.timer_enable(true)
);
}
/// read the raw counter value
pub fn get_counter(&self) -> u64 {
loop {
let c1_pre = self.regs.global_timer_counter1.read().value();
let c0 = self.regs.global_timer_counter0.read().value();
let c1_post = self.regs.global_timer_counter1.read().value();
if c1_pre == c1_post {
return ((c1_pre as u64) << 32) | (c0 as u64);
}
// retry if c0 has wrapped while reading.
}
}
/// read and convert to time
pub fn get_time(&self) -> Milliseconds {
let prescaler = self.regs.global_timer_control.read().prescaler() as u64;
let clocks = Clocks::get();
Milliseconds(self.get_counter() * (prescaler + 1) / (clocks.cpu_3x2x() as u64 / 1000))
}
/// read with high precision
pub fn get_us(&self) -> Microseconds {
let prescaler = self.regs.global_timer_control.read().prescaler() as u64;
let clocks = Clocks::get();
Microseconds(1_000_000 * self.get_counter() * (prescaler + 1) / clocks.cpu_3x2x() as u64)
}
/// return a handle that has implements
/// `embedded_hal::timer::CountDown`
pub fn countdown<U>(&self) -> CountDown<U>
where
Self: TimeSource<U>,
{
CountDown {
timer: self.clone(),
timeout: self.now(),
}
}
}
impl TimeSource<Milliseconds> for GlobalTimer {
fn now(&self) -> Milliseconds {
self.get_time()
}
}
impl TimeSource<Microseconds> for GlobalTimer {
fn now(&self) -> Microseconds {
self.get_us()
}
}
#[derive(Clone)]
pub struct CountDown<U> {
timer: GlobalTimer,
timeout: U,
}
/// embedded-hal async API
impl<U: Add<Output=U> + PartialOrd> embedded_hal::timer::CountDown for CountDown<U>
where
GlobalTimer: TimeSource<U>,
{
type Time = U;
fn start<T: Into<Self::Time>>(&mut self, count: T) {
self.timeout = self.timer.now() + count.into();
}
fn wait(&mut self) -> nb::Result<(), Void> {
if self.timer.now() <= self.timeout {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
}
}
impl<U: PartialOrd> CountDown<U>
where
GlobalTimer: TimeSource<U>,
{
pub fn waiting(&self) -> bool {
self.timer.now() <= self.timeout
}
}
/// embedded-hal sync API
impl embedded_hal::blocking::delay::DelayMs<u64> for GlobalTimer {
fn delay_ms(&mut self, ms: u64) {
use embedded_hal::timer::CountDown;
let mut countdown = self.countdown::<Milliseconds>();
countdown.start(Milliseconds(ms));
nb::block!(countdown.wait()).unwrap();
}
}
/// embedded-hal sync API
impl embedded_hal::blocking::delay::DelayUs<u64> for GlobalTimer {
fn delay_us(&mut self, us: u64) {
use embedded_hal::timer::CountDown;
let mut countdown = self.countdown::<Microseconds>();
countdown.start(Microseconds(us));
nb::block!(countdown.wait()).unwrap();
}
}

View File

@ -1,2 +0,0 @@
pub mod global;
pub use global::GlobalTimer;

View File

@ -1,15 +0,0 @@
[package]
name = "libconfig"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2018"
[dependencies]
libboard_zynq = { path = "../libboard_zynq" }
core_io = { version = "0.1", features = ["collections"] }
fatfs = { version = "0.3", features = ["core_io"], default-features = false }
log = "0.4"
[features]
ipv6 = []

View File

@ -1,181 +0,0 @@
use alloc::vec::Vec;
use core_io::{Error, Read, Seek, SeekFrom};
use libboard_zynq::devc;
use log::debug;
#[derive(Debug)]
pub enum BootgenLoadingError {
InvalidBootImageHeader,
MissingPartition,
EncryptedBitstream,
IoError(Error),
DevcError(devc::DevcError),
}
impl From<Error> for BootgenLoadingError {
fn from(error: Error) -> Self {
BootgenLoadingError::IoError(error)
}
}
impl From<devc::DevcError> for BootgenLoadingError {
fn from(error: devc::DevcError) -> Self {
BootgenLoadingError::DevcError(error)
}
}
impl core::fmt::Display for BootgenLoadingError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use BootgenLoadingError::*;
match self {
InvalidBootImageHeader => write!(
f,
"Invalid boot image header. Check if the file is correct."
),
MissingPartition => write!(f, "Partition not found. Check your compile configuration."),
EncryptedBitstream => write!(f, "Encrypted bitstream is not supported."),
IoError(e) => write!(f, "Error while reading: {}", e),
DevcError(e) => write!(f, "PCAP interface error: {}", e),
}
}
}
#[repr(C)]
struct PartitionHeader {
pub encrypted_length: u32,
pub unencrypted_length: u32,
pub word_length: u32,
pub dest_load_addr: u32,
pub dest_exec_addr: u32,
pub data_offset: u32,
pub attribute_bits: u32,
pub section_count: u32,
pub checksum_offset: u32,
pub header_offset: u32,
pub cert_offset: u32,
pub reserved: [u32; 4],
pub checksum: u32,
}
/// Read a u32 word from the reader.
fn read_u32<Reader: Read>(reader: &mut Reader) -> Result<u32, BootgenLoadingError> {
let mut buffer: [u8; 4] = [0; 4];
reader.read_exact(&mut buffer)?;
let mut result: u32 = 0;
for i in 0..4 {
result |= (buffer[i] as u32) << (i * 8);
}
Ok(result)
}
/// Load PL partition header.
fn load_pl_header<File: Read + Seek>(
file: &mut File,
) -> Result<Option<PartitionHeader>, BootgenLoadingError> {
let mut buffer: [u8; 0x40] = [0; 0x40];
file.read_exact(&mut buffer)?;
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) };
if header.attribute_bits & (2 << 4) != 0 {
Ok(Some(header))
} else {
Ok(None)
}
}
fn load_ps_header<File: Read + Seek>(
file: &mut File,
) -> Result<Option<PartitionHeader>, BootgenLoadingError> {
let mut buffer: [u8; 0x40] = [0; 0x40];
file.read_exact(&mut buffer)?;
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) };
if header.attribute_bits & (1 << 4) != 0 {
Ok(Some(header))
} else {
Ok(None)
}
}
/// Locate the partition from the image, and return the size (in bytes) of the partition if successful.
/// This function would seek the file to the location of the partition.
fn locate<
File: Read + Seek,
F: Fn(&mut File) -> Result<Option<PartitionHeader>, BootgenLoadingError>,
>(
file: &mut File,
f: F,
) -> Result<usize, BootgenLoadingError> {
file.seek(SeekFrom::Start(0))?;
const BOOT_HEADER_SIGN: u32 = 0x584C4E58;
// read boot header signature
file.seek(SeekFrom::Start(0x24))?;
if read_u32(file)? != BOOT_HEADER_SIGN {
return Err(BootgenLoadingError::InvalidBootImageHeader);
}
// find fsbl offset
file.seek(SeekFrom::Start(0x30))?;
// the length is in bytes, we have to convert it to words to compare with the partition offset
// later
let fsbl = read_u32(file)? / 4;
// read partition header offset
file.seek(SeekFrom::Start(0x9C))?;
let ptr = read_u32(file)?;
debug!("Partition header pointer = {:0X}", ptr);
file.seek(SeekFrom::Start(ptr as u64))?;
// at most 3 partition headers
for _ in 0..3 {
if let Some(header) = f(file)? {
let encrypted_length = header.encrypted_length;
let unencrypted_length = header.unencrypted_length;
debug!("Unencrypted length = {:0X}", unencrypted_length);
if encrypted_length != unencrypted_length {
return Err(BootgenLoadingError::EncryptedBitstream);
}
let start_addr = header.data_offset;
// skip fsbl
if start_addr == fsbl {
continue;
}
debug!("Partition start address: {:0X}", start_addr);
file.seek(SeekFrom::Start(start_addr as u64 * 4))?;
return Ok(unencrypted_length as usize * 4);
}
}
Err(BootgenLoadingError::MissingPartition)
}
/// Load bitstream from bootgen file.
/// This function parses the file, locate the bitstream and load it through the PCAP driver.
/// It requires a large buffer, please enable the DDR RAM before using it.
pub fn load_bitstream<File: Read + Seek>(file: &mut File) -> Result<(), BootgenLoadingError> {
let size = locate(file, load_pl_header)?;
unsafe {
// align to 64 bytes
let ptr = alloc::alloc::alloc(alloc::alloc::Layout::from_size_align(size, 64).unwrap());
let buffer = core::slice::from_raw_parts_mut(ptr, size);
file.read_exact(buffer).map_err(|e| {
core::ptr::drop_in_place(ptr);
e
})?;
let mut devcfg = devc::DevC::new();
devcfg.enable();
devcfg.program(&buffer).map_err(|e| {
core::ptr::drop_in_place(ptr);
e
})?;
core::ptr::drop_in_place(ptr);
Ok(())
}
}
pub fn get_runtime<File: Read + Seek>(file: &mut File) -> Result<Vec<u8>, BootgenLoadingError> {
let size = locate(file, load_ps_header)?;
let mut buffer = Vec::with_capacity(size);
unsafe {
buffer.set_len(size);
}
file.read_exact(&mut buffer)?;
Ok(buffer)
}

View File

@ -1,287 +0,0 @@
#![no_std]
extern crate alloc;
use core::fmt;
use alloc::{string::FromUtf8Error, string::String, vec::Vec, rc::Rc, str};
use core_io::{self as io, BufRead, BufReader, Read, Write, Seek, ErrorKind, SeekFrom};
use libboard_zynq::sdio;
pub mod sd_reader;
pub mod net_settings;
pub mod bootgen;
#[derive(Debug)]
pub enum Error<'a> {
SdError(sdio::sd_card::CardInitializationError),
IoError(io::Error),
Utf8Error(FromUtf8Error),
KeyNotFoundError(&'a str),
NoConfig,
}
pub type Result<'a, T> = core::result::Result<T, Error<'a>>;
impl<'a> fmt::Display for Error<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::SdError(error) => write!(f, "SD error: {}", error),
Error::IoError(error) => write!(f, "I/O error: {}", error),
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
Error::KeyNotFoundError(name) => write!(f, "Configuration key `{}` not found", name),
Error::NoConfig => write!(f, "Configuration not present"),
}
}
}
impl<'a> From<sdio::sd_card::CardInitializationError> for Error<'a> {
fn from(error: sdio::sd_card::CardInitializationError) -> Self {
Error::SdError(error)
}
}
impl<'a> From<io::Error> for Error<'a> {
fn from(error: io::Error) -> Self {
Error::IoError(error)
}
}
impl<'a> From<FromUtf8Error> for Error<'a> {
fn from(error: FromUtf8Error) -> Self {
Error::Utf8Error(error)
}
}
fn parse_config<'a>(
key: &'a str,
buffer: &mut Vec<u8>,
file: fatfs::File<sd_reader::SdReader>,
) -> Result<'a, ()> {
let prefix = [key, "="].concat();
for line in BufReader::new(file).lines() {
let line = line?;
if line.starts_with(&prefix) {
buffer.extend(line[prefix.len()..].as_bytes());
return Ok(());
}
}
Err(Error::KeyNotFoundError(key))
}
fn delete_old_entry<'a>(
key: &str,
file: fatfs::File<sd_reader::SdReader>,
mut file_tmp: fatfs::File<sd_reader::SdReader>,
) -> Result<'a, ()> {
let prefix = [key, "="].concat();
let buf_reader = BufReader::new(file);
for line in buf_reader.lines() {
let line = line?;
if !line.starts_with(&prefix) {
file_tmp.write_all(&[line.as_str(), "\n"].concat().as_bytes())?;
}
}
Ok(())
}
fn rename_file<'a>(dir: &fatfs::Dir<sd_reader::SdReader>, old_file_name: &str, new_file_name: &str) -> Result<'a, ()>{
{
let old_file = dir.open_file(old_file_name)?;
let mut new_file = dir.create_file(new_file_name)?;
new_file.truncate()?;
for line in BufReader::new(old_file).lines() {
let line = line?;
new_file.write_all(&[line.as_str(), "\n"].concat().as_bytes())?;
}
}
dir.remove(old_file_name)?;
Ok(())
}
pub struct Config {
fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>,
}
impl Config {
pub fn new() -> Result<'static, Self> {
let sdio = sdio::Sdio::sdio0(true);
if !sdio.is_card_inserted() {
Err(sdio::sd_card::CardInitializationError::NoCardInserted)?;
}
let sd = sdio::sd_card::SdCard::from_sdio(sdio)?;
let reader = sd_reader::SdReader::new(sd);
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?;
Ok(Config { fs: Some(Rc::new(fs)) })
}
pub fn from_fs(fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>) -> Self {
Config { fs }
}
pub fn new_dummy() -> Self {
Config { fs: None }
}
pub fn read<'b>(&self, key: &'b str) -> Result<'b, Vec<u8>> {
if let Some(fs) = &self.fs {
let root_dir = fs.root_dir();
let mut buffer: Vec<u8> = Vec::new();
match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) {
Ok(mut f) => f.read_to_end(&mut buffer).map(|_| ())?,
Err(_) => match root_dir.open_file("/CONFIG.TXT") {
Ok(f) => parse_config(key, &mut buffer, f)?,
Err(_) => return Err(Error::KeyNotFoundError(key)),
},
};
Ok(buffer)
} else {
Err(Error::NoConfig)
}
}
pub fn read_str<'b>(&self, key: &'b str) -> Result<'b, String> {
Ok(String::from_utf8(self.read(key)?)?)
}
pub fn erase<'b>(&mut self) -> Result<'b, ()>{
if let Some(fs) = &self.fs {
let root_dir = fs.root_dir();
match root_dir.create_file("/CONFIG.TXT") {
Ok(mut file) => {
file.truncate()?;
},
Err(e) => {
return Err(Error::IoError(e));
}
};
let dir = root_dir.create_dir("/CONFIG")?;
for r in dir.iter() {
let entry = r?;
if entry.is_file() {
dir.remove(str::from_utf8(entry.short_file_name_as_bytes()).unwrap())?;
}
}
Ok(())
} else {
Err(Error::NoConfig)
}
}
pub fn remove_config_txt<'b>(&mut self, key: &str) -> Result<'b, ()>{
if let Some(fs) = &self.fs {
let root_dir = fs.root_dir();
let config_txt_tmp = "/CONFIG.TMP";
let mut need_to_rename = false;
match root_dir.open_file("/CONFIG.TXT") {
Ok(file) => {
need_to_rename = true;
let mut file_tmp = root_dir.create_file(config_txt_tmp)?;
file_tmp.truncate()?;
delete_old_entry(key, file, file_tmp)?;
},
Err(e) => match e.kind() {
ErrorKind::NotFound => {},
_ => {
return Err(Error::IoError(e));
}
}
};
if need_to_rename {
rename_file(&root_dir, config_txt_tmp, "/CONFIG.TXT")?;
}
Ok(())
} else {
Err(Error::NoConfig)
}
}
pub fn remove_config_key_bin<'b>(&mut self, key: &str) -> Result<'b, ()>{
if let Some(fs) = &self.fs {
let root_dir = fs.root_dir();
let config_key_bin = &["/CONFIG/", key, ".BIN"].concat();
match root_dir.remove(config_key_bin) {
Ok(_) => {},
Err(e) => match e.kind() {
ErrorKind::NotFound => {},
_ => {
return Err(Error::IoError(e));
}
}
};
Ok(())
} else {
Err(Error::NoConfig)
}
}
pub fn remove<'b>(&mut self, key: &str) -> Result<'b, ()>{
self.remove_config_txt(key)?;
self.remove_config_key_bin(key)?;
Ok(())
}
pub fn write_str<'b>(&mut self, key: &str, data: &str) -> Result<'b, ()>{
let mut call_remove_config_key_bin = false;
let mut call_remove_config_txt = false;
if let Some(fs) = &self.fs {
let root_dir = fs.root_dir();
let config_key_bin = &["/CONFIG/", key, ".BIN"].concat();
let config_txt_tmp = "/CONFIG.TMP";
if data.is_ascii() & (data.len() <= 100) {
match root_dir.create_file("/CONFIG.TXT") {
Ok(file) => {
let mut file_tmp = root_dir.create_file(config_txt_tmp)?;
file_tmp.truncate()?;
delete_old_entry(key, file, file_tmp)?;
},
Err(e) => {
return Err(Error::IoError(e));
}
};
rename_file(&root_dir, config_txt_tmp, "/CONFIG.TXT")?;
let mut file = root_dir.open_file("/CONFIG.TXT")?;
file.seek(SeekFrom::End(0))?;
file.write_all(&["\n", key, "=", data, "\n"].concat().as_bytes())?;
call_remove_config_key_bin = true;
} else {
root_dir.create_dir("/CONFIG")?;
match root_dir.create_file(config_key_bin) {
Ok(mut file) => {
file.truncate()?;
file.write_all(&[data, "\n"].concat().as_bytes())?;
},
Err(e) => {
return Err(Error::IoError(e));
}
};
call_remove_config_txt = true;
}
} else {
return Err(Error::NoConfig);
}
if call_remove_config_key_bin {
self.remove_config_key_bin(key)?;
}
if call_remove_config_txt {
self.remove_config_txt(key)?;
}
Ok(())
}
}

View File

@ -1,62 +0,0 @@
use core::fmt;
use libboard_zynq::smoltcp::wire::{EthernetAddress, IpAddress};
use super::Config;
pub struct NetAddresses {
pub hardware_addr: EthernetAddress,
pub ipv4_addr: IpAddress,
#[cfg(feature = "ipv6")]
pub ipv6_ll_addr: IpAddress,
#[cfg(feature = "ipv6")]
pub ipv6_addr: Option<IpAddress>
}
impl fmt::Display for NetAddresses {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MAC={} IPv4={} ",
self.hardware_addr, self.ipv4_addr)?;
#[cfg(feature = "ipv6")]
{
write!(f, "IPv6-LL={}", self.ipv6_ll_addr)?;
match self.ipv6_addr {
Some(addr) => write!(f, " {}", addr)?,
None => write!(f, " IPv6: no configured address")?
}
}
Ok(())
}
}
pub fn get_adresses(cfg: &Config) -> NetAddresses {
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x52]);
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 52);
if let Ok(Ok(addr)) = cfg.read_str("mac").map(|s| s.parse()) {
hardware_addr = addr;
}
if let Ok(Ok(addr)) = cfg.read_str("ip").map(|s| s.parse()) {
ipv4_addr = addr;
}
#[cfg(feature = "ipv6")]
let ipv6_addr = cfg.read_str("ipv6").ok().and_then(|s| s.parse().ok());
#[cfg(feature = "ipv6")]
let ipv6_ll_addr = IpAddress::v6(
0xfe80, 0x0000, 0x0000, 0x0000,
(((hardware_addr.0[0] ^ 0x02) as u16) << 8) | (hardware_addr.0[1] as u16),
((hardware_addr.0[2] as u16) << 8) | 0x00ff,
0xfe00 | (hardware_addr.0[3] as u16),
((hardware_addr.0[4] as u16) << 8) | (hardware_addr.0[5] as u16));
NetAddresses {
hardware_addr,
ipv4_addr,
#[cfg(feature = "ipv6")]
ipv6_ll_addr,
#[cfg(feature = "ipv6")]
ipv6_addr
}
}

View File

@ -1,303 +0,0 @@
use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write};
use fatfs;
use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError};
use log::debug;
use alloc::vec::Vec;
const MBR_SIGNATURE: [u8; 2] = [0x55, 0xAA];
const PARTID_FAT12: u8 = 0x01;
const PARTID_FAT16_LESS32M: u8 = 0x04;
const PARTID_FAT16: u8 = 0x06;
const PARTID_FAT32: u8 = 0x0B;
const PARTID_FAT32_LBA: u8 = 0x0C;
fn cmd_error_to_io_error(_: CmdTransferError) -> Error {
Error::new(ErrorKind::Other, "Command transfer error")
}
const BLOCK_SIZE: usize = 512;
/// SdReader struct implementing `Read + BufRead + Write + Seek` traits for `core_io`.
/// Used as an adaptor for fatfs crate, but could be used directly for raw data access.
///
/// Implementation: all read/writes would be split into unaligned and block-aligned parts,
/// unaligned read/writes would do a buffered read/write using a block-sized internal buffer,
/// while aligned transactions would be sent to the SD card directly for performance reason.
pub struct SdReader {
/// Internal SdCard handle.
sd: SdCard,
/// Read buffer with the size of 1 block.
buffer: Vec<u8>,
/// Address for the next byte.
byte_addr: u32,
/// Internal index for the next byte.
/// Normally in range `[0, BLOCK_SIZE - 1]`.
///
/// `index = BLOCK_SIZE` means that the `buffer` is invalid for the current `byte_addr`,
/// the next `fill_buf` call would fill the buffer.
index: usize,
/// Dirty flag indicating the content has to be flushed.
dirty: bool,
/// Base offset for translation from logical address to physical address.
offset: u32,
}
#[derive(Copy, Clone)]
#[allow(unused)]
// Partition entry enum, normally we would use entry1.
pub enum PartitionEntry {
Entry1 = 0x1BE,
Entry2 = 0x1CE,
Entry3 = 0x1DE,
Entry4 = 0x1EE,
}
impl SdReader {
/// Create SdReader from SdCard
pub fn new(sd: SdCard) -> SdReader {
let mut vec: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
unsafe {
vec.set_len(vec.capacity());
}
SdReader {
sd,
buffer: vec,
byte_addr: 0,
index: BLOCK_SIZE,
dirty: false,
offset: 0,
}
}
/// Internal read function for unaligned read.
/// The read must not cross block boundary.
fn read_unaligned(&mut self, buf: &mut [u8]) -> IoResult<usize> {
if buf.len() == 0 {
return Ok(0);
}
let filled_buffer = self.fill_buf()?;
for (dest, src) in buf.iter_mut().zip(filled_buffer.iter()) {
*dest = *src;
}
self.consume(buf.len());
Ok(buf.len())
}
/// Internal write function for unaligned write.
/// The write must not cross block boundary.
fn write_unaligned(&mut self, buf: &[u8]) -> IoResult<usize> {
if buf.len() == 0 {
return Ok(0);
}
// update buffer if needed, as we will flush the entire block later.
self.fill_buf()?;
self.dirty = true;
let dest_buffer = &mut self.buffer[self.index..];
for (src, dest) in buf.iter().zip(dest_buffer.iter_mut()) {
*dest = *src;
}
self.consume(buf.len());
Ok(buf.len())
}
/// Split the slice into three segments, with the middle block-aligned.
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
fn block_align<'b>(&self, buf: &'b [u8]) -> (&'b [u8], &'b [u8], &'b [u8]) {
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
if head_len > buf.len() {
(buf, &[], &[])
} else {
let remaining_length = buf.len() - head_len;
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
let (head, remaining) = buf.split_at(head_len);
let (mid, tail) = remaining.split_at(mid_length);
(head, mid, tail)
}
}
/// Split the mutable slice into three segments, with the middle block-aligned.
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
fn block_align_mut<'b>(&self, buf: &'b mut [u8]) -> (&'b mut [u8], &'b mut [u8], &'b mut [u8]) {
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
if head_len > buf.len() {
(buf, &mut [], &mut [])
} else {
let remaining_length = buf.len() - head_len;
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
let (head, remaining) = buf.split_at_mut(head_len);
let (mid, tail) = remaining.split_at_mut(mid_length);
(head, mid, tail)
}
}
/// Invalidate the buffer, so later unaligned read/write would reload the buffer from SD card.
fn invalidate_buffer(&mut self) {
self.index = BLOCK_SIZE;
}
/// Set the base offset of the SD card, to transform from physical address to logical address.
fn set_base_offset(&mut self, offset: u32) -> IoResult<u64> {
self.offset = offset;
self.seek(SeekFrom::Start(0))
}
/// Mount fatfs from partition entry, and return the fatfs object if success.
/// This takes the ownership of self, so currently there is no way to recover from an error,
/// except creating a new SD card instance.
pub fn mount_fatfs(mut self, entry: PartitionEntry) -> IoResult<fatfs::FileSystem<Self>> {
let mut buffer: [u8; 4] = [0; 4];
self.seek(SeekFrom::Start(0x1FE))?;
self.read_exact(&mut buffer[..2])?;
// check MBR signature
if buffer[..2] != MBR_SIGNATURE {
return Err(Error::new(
ErrorKind::InvalidData,
"Incorrect signature for MBR sector.",
));
}
// Read partition ID.
self.seek(SeekFrom::Start(entry as u64 + 0x4))?;
self.read_exact(&mut buffer[..1])?;
debug!("Partition ID: {:0X}", buffer[0]);
match buffer[0] {
PARTID_FAT12 | PARTID_FAT16_LESS32M | PARTID_FAT16 |
PARTID_FAT32 | PARTID_FAT32_LBA => {}
_ => {
return Err(Error::new(
ErrorKind::InvalidData,
"No FAT partition found for the specified entry.",
));
}
}
// Read LBA
self.seek(SeekFrom::Current(0x3))?;
self.read_exact(&mut buffer)?;
let mut lba: u32 = 0;
// Little endian
for i in 0..4 {
lba |= (buffer[i] as u32) << (i * 8);
}
// Set to logical address
self.set_base_offset(lba * BLOCK_SIZE as u32)?;
// setup fatfs
fatfs::FileSystem::new(self, fatfs::FsOptions::new())
}
}
impl Read for SdReader {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
let total_length = buf.len();
let (a, b, c) = self.block_align_mut(buf);
self.read_unaligned(a)?;
if b.len() > 0 {
// invalidate internal buffer
self.invalidate_buffer();
if let Err(_) = self.sd.read_block(
self.byte_addr / BLOCK_SIZE as u32,
(b.len() / BLOCK_SIZE) as u16,
b,
) {
// we have to allow partial read, as per the trait required
return Ok(a.len());
}
self.byte_addr += b.len() as u32;
}
if let Err(_) = self.read_unaligned(c) {
// we have to allow partial read, as per the trait required
return Ok(a.len() + b.len());
}
Ok(total_length)
}
}
impl BufRead for SdReader {
fn fill_buf(&mut self) -> IoResult<&[u8]> {
if self.index == BLOCK_SIZE {
// flush the buffer if it is dirty before overwriting it with new data
if self.dirty {
self.flush()?;
}
// reload buffer
self.sd
.read_block(self.byte_addr / (BLOCK_SIZE as u32), 1, &mut self.buffer)
.map_err(cmd_error_to_io_error)?;
self.index = (self.byte_addr as usize) % BLOCK_SIZE;
}
Ok(&self.buffer[self.index..])
}
fn consume(&mut self, amt: usize) {
self.index += amt;
self.byte_addr += amt as u32;
}
}
impl Write for SdReader {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
let (a, b, c) = self.block_align(buf);
self.write_unaligned(a)?;
if b.len() > 0 {
self.flush()?;
self.invalidate_buffer();
if let Err(_) = self.sd.write_block(
self.byte_addr / BLOCK_SIZE as u32,
(b.len() / BLOCK_SIZE) as u16,
b,
) {
return Ok(a.len());
}
self.byte_addr += b.len() as u32;
}
if let Err(_) = self.write_unaligned(c) {
return Ok(a.len() + b.len());
}
Ok(buf.len())
}
fn flush(&mut self) -> IoResult<()> {
if self.dirty {
let block_addr = (self.byte_addr - self.index as u32) / (BLOCK_SIZE as u32);
self.sd
.write_block(block_addr, 1, &self.buffer)
.map_err(cmd_error_to_io_error)?;
self.dirty = false;
}
Ok(())
}
}
impl Seek for SdReader {
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
let raw_target = match pos {
SeekFrom::Start(x) => self.offset as i64 + x as i64,
SeekFrom::Current(x) => self.byte_addr as i64 + x,
SeekFrom::End(_) => panic!("SD card does not support seek from end"),
};
if raw_target < self.offset as i64 || raw_target > core::u32::MAX as i64 {
return Err(Error::new(ErrorKind::InvalidInput, "Invalid address"));
}
let target_byte_addr = raw_target as u32;
let address_same_block =
self.byte_addr / (BLOCK_SIZE as u32) == target_byte_addr / (BLOCK_SIZE as u32);
// if the buffer was invalidated, we consider seek as different block
let same_block = address_same_block && self.index != BLOCK_SIZE;
if !same_block {
self.flush()?;
}
self.byte_addr = target_byte_addr;
self.index = if same_block {
target_byte_addr as usize % BLOCK_SIZE
} else {
// invalidate the buffer as we moved to a different block
BLOCK_SIZE
};
Ok((self.byte_addr - self.offset) as u64)
}
}
impl Drop for SdReader {
fn drop(&mut self) {
// just try to flush it, ignore error if any
self.flush().unwrap_or(());
}
}

View File

@ -1,14 +0,0 @@
[package]
authors = ["M-Labs"]
name = "core_io"
version = "0.1.20200410"
[lib]
name = "core_io"
[dependencies]
memchr = { version = "2", default-features = false, optional = true }
[features]
alloc = []
collections = ["alloc", "memchr"]

File diff suppressed because it is too large Load Diff

View File

@ -1,896 +0,0 @@
use crate::io::prelude::*;
use core::cmp;
use crate::io::{self, Error, ErrorKind, Initializer, SeekFrom};
#[cfg(feature = "collections")]
use core::convert::TryInto;
#[cfg(feature="collections")]
use collections::vec::Vec;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
/// A `Cursor` wraps an in-memory buffer and provides it with a
/// [`Seek`] implementation.
///
/// `Cursor`s are used with in-memory buffers, anything implementing
/// `AsRef<[u8]>`, to allow them to implement [`Read`] and/or [`Write`],
/// allowing these buffers to be used anywhere you might use a reader or writer
/// that does actual I/O.
///
/// The standard library implements some I/O traits on various types which
/// are commonly used as a buffer, like `Cursor<`[`Vec`]`<u8>>` and
/// `Cursor<`[`&[u8]`][bytes]`>`.
///
/// # Examples
///
/// We may want to write bytes to a [`File`] in our production
/// code, but use an in-memory buffer in our tests. We can do this with
/// `Cursor`:
///
/// [`Seek`]: trait.Seek.html
/// [`Read`]: ../../std/io/trait.Read.html
/// [`Write`]: ../../std/io/trait.Write.html
/// [`Vec`]: ../../std/vec/struct.Vec.html
/// [bytes]: ../../std/primitive.slice.html
/// [`File`]: ../fs/struct.File.html
///
/// ```no_run
/// use std::io::prelude::*;
/// use std::io::{self, SeekFrom};
/// use std::fs::File;
///
/// // a library function we've written
/// fn write_ten_bytes_at_end<W: Write + Seek>(writer: &mut W) -> io::Result<()> {
/// writer.seek(SeekFrom::End(-10))?;
///
/// for i in 0..10 {
/// writer.write(&[i])?;
/// }
///
/// // all went well
/// Ok(())
/// }
///
/// # fn foo() -> io::Result<()> {
/// // Here's some code that uses this library function.
/// //
/// // We might want to use a BufReader here for efficiency, but let's
/// // keep this example focused.
/// let mut file = File::create("foo.txt")?;
///
/// write_ten_bytes_at_end(&mut file)?;
/// # Ok(())
/// # }
///
/// // now let's write a test
/// #[test]
/// fn test_writes_bytes() {
/// // setting up a real File is much slower than an in-memory buffer,
/// // let's use a cursor instead
/// use std::io::Cursor;
/// let mut buff = Cursor::new(vec![0; 15]);
///
/// write_ten_bytes_at_end(&mut buff).unwrap();
///
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
/// }
/// ```
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Cursor<T> {
inner: T,
pos: u64,
}
impl<T> Cursor<T> {
/// Creates a new cursor wrapping the provided underlying in-memory buffer.
///
/// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`)
/// is not empty. So writing to cursor starts with overwriting `Vec`
/// content, not with appending to it.
///
/// # Examples
///
/// ```
/// use std::io::Cursor;
///
/// let buff = Cursor::new(Vec::new());
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
/// # force_inference(&buff);
/// ```
pub fn new(inner: T) -> Cursor<T> {
Cursor { pos: 0, inner }
}
/// Consumes this cursor, returning the underlying value.
///
/// # Examples
///
/// ```
/// use std::io::Cursor;
///
/// let buff = Cursor::new(Vec::new());
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
/// # force_inference(&buff);
///
/// let vec = buff.into_inner();
/// ```
pub fn into_inner(self) -> T {
self.inner
}
/// Gets a reference to the underlying value in this cursor.
///
/// # Examples
///
/// ```
/// use std::io::Cursor;
///
/// let buff = Cursor::new(Vec::new());
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
/// # force_inference(&buff);
///
/// let reference = buff.get_ref();
/// ```
pub fn get_ref(&self) -> &T {
&self.inner
}
/// Gets a mutable reference to the underlying value in this cursor.
///
/// Care should be taken to avoid modifying the internal I/O state of the
/// underlying value as it may corrupt this cursor's position.
///
/// # Examples
///
/// ```
/// use std::io::Cursor;
///
/// let mut buff = Cursor::new(Vec::new());
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
/// # force_inference(&buff);
///
/// let reference = buff.get_mut();
/// ```
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
/// Returns the current position of this cursor.
///
/// # Examples
///
/// ```
/// use std::io::Cursor;
/// use std::io::prelude::*;
/// use std::io::SeekFrom;
///
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
///
/// assert_eq!(buff.position(), 0);
///
/// buff.seek(SeekFrom::Current(2)).unwrap();
/// assert_eq!(buff.position(), 2);
///
/// buff.seek(SeekFrom::Current(-1)).unwrap();
/// assert_eq!(buff.position(), 1);
/// ```
pub fn position(&self) -> u64 {
self.pos
}
/// Sets the position of this cursor.
///
/// # Examples
///
/// ```
/// use std::io::Cursor;
///
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
///
/// assert_eq!(buff.position(), 0);
///
/// buff.set_position(2);
/// assert_eq!(buff.position(), 2);
///
/// buff.set_position(4);
/// assert_eq!(buff.position(), 4);
/// ```
pub fn set_position(&mut self, pos: u64) {
self.pos = pos;
}
}
impl<T> io::Seek for Cursor<T>
where
T: AsRef<[u8]>,
{
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
let (base_pos, offset) = match style {
SeekFrom::Start(n) => {
self.pos = n;
return Ok(n);
}
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
SeekFrom::Current(n) => (self.pos, n),
};
let new_pos = if offset >= 0 {
base_pos.checked_add(offset as u64)
} else {
base_pos.checked_sub((offset.wrapping_neg()) as u64)
};
match new_pos {
Some(n) => {
self.pos = n;
Ok(self.pos)
}
None => Err(Error::new(
ErrorKind::InvalidInput,
"invalid seek to a negative or overflowing position",
)),
}
}
fn stream_len(&mut self) -> io::Result<u64> {
Ok(self.inner.as_ref().len() as u64)
}
fn stream_position(&mut self) -> io::Result<u64> {
Ok(self.pos)
}
}
impl<T> Read for Cursor<T>
where
T: AsRef<[u8]>,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = Read::read(&mut self.get_ref().as_ref(), buf)?;
self.pos += n as u64;
Ok(n)
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
let n = buf.len();
Read::read_exact(&mut self.get_ref().as_ref(), buf)?;
self.pos += n as u64;
Ok(())
}
#[inline]
unsafe fn initializer(&self) -> Initializer {
Initializer::nop()
}
}
#[cfg(feature = "collections")]
impl<T> BufRead for Cursor<T>
where
T: AsRef<[u8]>,
{
fn fill_buf(&mut self) -> io::Result<&[u8]> {
let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64);
Ok(&self.inner.as_ref()[(amt as usize)..])
}
fn consume(&mut self, amt: usize) {
self.pos += amt as u64;
}
}
// Non-resizing write implementation
#[inline]
fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> {
let pos = cmp::min(*pos_mut, slice.len() as u64);
let amt = (&mut slice[(pos as usize)..]).write(buf)?;
*pos_mut += amt as u64;
Ok(amt)
}
// Resizing write implementation
#[cfg(feature = "collections")]
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> {
let pos: usize = (*pos_mut).try_into().map_err(|_| {
Error::new(
ErrorKind::InvalidInput,
"cursor position exceeds maximum possible vector length",
)
})?;
// Make sure the internal buffer is as least as big as where we
// currently are
let len = vec.len();
if len < pos {
// use `resize` so that the zero filling is as efficient as possible
vec.resize(pos, 0);
}
// Figure out what bytes will be used to overwrite what's currently
// there (left), and what will be appended on the end (right)
{
let space = vec.len() - pos;
let (left, right) = buf.split_at(cmp::min(space, buf.len()));
vec[pos..pos + left.len()].copy_from_slice(left);
vec.extend_from_slice(right);
}
// Bump us forward
*pos_mut = (pos + buf.len()) as u64;
Ok(buf.len())
}
impl Write for Cursor<&mut [u8]> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
slice_write(&mut self.pos, self.inner, buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(feature = "collections")]
impl Write for Cursor<&mut Vec<u8>> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
vec_write(&mut self.pos, self.inner, buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(feature = "collections")]
impl Write for Cursor<Vec<u8>> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
vec_write(&mut self.pos, &mut self.inner, buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Write for Cursor<Box<[u8]>> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
slice_write(&mut self.pos, &mut self.inner, buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::io::prelude::*;
use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
#[test]
fn test_vec_writer() {
let mut writer = Vec::new();
assert_eq!(writer.write(&[0]).unwrap(), 1);
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
assert_eq!(
writer
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
.unwrap(),
3
);
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(writer, b);
}
#[test]
fn test_mem_writer() {
let mut writer = Cursor::new(Vec::new());
assert_eq!(writer.write(&[0]).unwrap(), 1);
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
assert_eq!(
writer
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
.unwrap(),
3
);
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(&writer.get_ref()[..], b);
}
#[test]
fn test_mem_mut_writer() {
let mut vec = Vec::new();
let mut writer = Cursor::new(&mut vec);
assert_eq!(writer.write(&[0]).unwrap(), 1);
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
assert_eq!(
writer
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
.unwrap(),
3
);
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(&writer.get_ref()[..], b);
}
#[test]
fn test_box_slice_writer() {
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
assert_eq!(writer.position(), 0);
assert_eq!(writer.write(&[0]).unwrap(), 1);
assert_eq!(writer.position(), 1);
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write(&[]).unwrap(), 0);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
assert_eq!(writer.write(&[10]).unwrap(), 0);
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(&**writer.get_ref(), b);
}
#[test]
fn test_box_slice_writer_vectored() {
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
assert_eq!(writer.position(), 0);
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
assert_eq!(writer.position(), 1);
assert_eq!(
writer
.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),])
.unwrap(),
7,
);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(&**writer.get_ref(), b);
}
#[test]
fn test_buf_writer() {
let mut buf = [0 as u8; 9];
{
let mut writer = Cursor::new(&mut buf[..]);
assert_eq!(writer.position(), 0);
assert_eq!(writer.write(&[0]).unwrap(), 1);
assert_eq!(writer.position(), 1);
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write(&[]).unwrap(), 0);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
assert_eq!(writer.write(&[10]).unwrap(), 0);
}
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(buf, b);
}
#[test]
fn test_buf_writer_vectored() {
let mut buf = [0 as u8; 9];
{
let mut writer = Cursor::new(&mut buf[..]);
assert_eq!(writer.position(), 0);
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
assert_eq!(writer.position(), 1);
assert_eq!(
writer
.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7])],)
.unwrap(),
7,
);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
assert_eq!(writer.position(), 8);
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
}
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(buf, b);
}
#[test]
fn test_buf_writer_seek() {
let mut buf = [0 as u8; 8];
{
let mut writer = Cursor::new(&mut buf[..]);
assert_eq!(writer.position(), 0);
assert_eq!(writer.write(&[1]).unwrap(), 1);
assert_eq!(writer.position(), 1);
assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2);
assert_eq!(writer.position(), 2);
assert_eq!(writer.write(&[2]).unwrap(), 1);
assert_eq!(writer.position(), 3);
assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1);
assert_eq!(writer.position(), 1);
assert_eq!(writer.write(&[3]).unwrap(), 1);
assert_eq!(writer.position(), 2);
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
assert_eq!(writer.position(), 7);
assert_eq!(writer.write(&[4]).unwrap(), 1);
assert_eq!(writer.position(), 8);
}
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
assert_eq!(buf, b);
}
#[test]
fn test_buf_writer_error() {
let mut buf = [0 as u8; 2];
let mut writer = Cursor::new(&mut buf[..]);
assert_eq!(writer.write(&[0]).unwrap(), 1);
assert_eq!(writer.write(&[0, 0]).unwrap(), 1);
assert_eq!(writer.write(&[0, 0]).unwrap(), 0);
}
#[test]
fn test_mem_reader() {
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
let mut buf = [];
assert_eq!(reader.read(&mut buf).unwrap(), 0);
assert_eq!(reader.position(), 0);
let mut buf = [0];
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.position(), 1);
let b: &[_] = &[0];
assert_eq!(buf, b);
let mut buf = [0; 4];
assert_eq!(reader.read(&mut buf).unwrap(), 4);
assert_eq!(reader.position(), 5);
let b: &[_] = &[1, 2, 3, 4];
assert_eq!(buf, b);
assert_eq!(reader.read(&mut buf).unwrap(), 3);
let b: &[_] = &[5, 6, 7];
assert_eq!(&buf[..3], b);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_mem_reader_vectored() {
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
let mut buf = [];
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
assert_eq!(reader.position(), 0);
let mut buf = [0];
assert_eq!(
reader
.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
.unwrap(),
1,
);
assert_eq!(reader.position(), 1);
let b: &[_] = &[0];
assert_eq!(buf, b);
let mut buf1 = [0; 4];
let mut buf2 = [0; 4];
assert_eq!(
reader
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
.unwrap(),
7,
);
let b1: &[_] = &[1, 2, 3, 4];
let b2: &[_] = &[5, 6, 7];
assert_eq!(buf1, b1);
assert_eq!(&buf2[..3], b2);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_boxed_slice_reader() {
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
let mut buf = [];
assert_eq!(reader.read(&mut buf).unwrap(), 0);
assert_eq!(reader.position(), 0);
let mut buf = [0];
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.position(), 1);
let b: &[_] = &[0];
assert_eq!(buf, b);
let mut buf = [0; 4];
assert_eq!(reader.read(&mut buf).unwrap(), 4);
assert_eq!(reader.position(), 5);
let b: &[_] = &[1, 2, 3, 4];
assert_eq!(buf, b);
assert_eq!(reader.read(&mut buf).unwrap(), 3);
let b: &[_] = &[5, 6, 7];
assert_eq!(&buf[..3], b);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_boxed_slice_reader_vectored() {
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
let mut buf = [];
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
assert_eq!(reader.position(), 0);
let mut buf = [0];
assert_eq!(
reader
.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
.unwrap(),
1,
);
assert_eq!(reader.position(), 1);
let b: &[_] = &[0];
assert_eq!(buf, b);
let mut buf1 = [0; 4];
let mut buf2 = [0; 4];
assert_eq!(
reader
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
.unwrap(),
7,
);
let b1: &[_] = &[1, 2, 3, 4];
let b2: &[_] = &[5, 6, 7];
assert_eq!(buf1, b1);
assert_eq!(&buf2[..3], b2);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn read_to_end() {
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
let mut v = Vec::new();
reader.read_to_end(&mut v).unwrap();
assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
}
#[test]
fn test_slice_reader() {
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
let reader = &mut &in_buf[..];
let mut buf = [];
assert_eq!(reader.read(&mut buf).unwrap(), 0);
let mut buf = [0];
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.len(), 7);
let b: &[_] = &[0];
assert_eq!(&buf[..], b);
let mut buf = [0; 4];
assert_eq!(reader.read(&mut buf).unwrap(), 4);
assert_eq!(reader.len(), 3);
let b: &[_] = &[1, 2, 3, 4];
assert_eq!(&buf[..], b);
assert_eq!(reader.read(&mut buf).unwrap(), 3);
let b: &[_] = &[5, 6, 7];
assert_eq!(&buf[..3], b);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_slice_reader_vectored() {
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
let reader = &mut &in_buf[..];
let mut buf = [];
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
let mut buf = [0];
assert_eq!(
reader
.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),])
.unwrap(),
1,
);
assert_eq!(reader.len(), 7);
let b: &[_] = &[0];
assert_eq!(buf, b);
let mut buf1 = [0; 4];
let mut buf2 = [0; 4];
assert_eq!(
reader
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
.unwrap(),
7,
);
let b1: &[_] = &[1, 2, 3, 4];
let b2: &[_] = &[5, 6, 7];
assert_eq!(buf1, b1);
assert_eq!(&buf2[..3], b2);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_read_exact() {
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
let reader = &mut &in_buf[..];
let mut buf = [];
assert!(reader.read_exact(&mut buf).is_ok());
let mut buf = [8];
assert!(reader.read_exact(&mut buf).is_ok());
assert_eq!(buf[0], 0);
assert_eq!(reader.len(), 7);
let mut buf = [0, 0, 0, 0, 0, 0, 0];
assert!(reader.read_exact(&mut buf).is_ok());
assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
assert_eq!(reader.len(), 0);
let mut buf = [0];
assert!(reader.read_exact(&mut buf).is_err());
}
#[test]
fn test_buf_reader() {
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
let mut reader = Cursor::new(&in_buf[..]);
let mut buf = [];
assert_eq!(reader.read(&mut buf).unwrap(), 0);
assert_eq!(reader.position(), 0);
let mut buf = [0];
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.position(), 1);
let b: &[_] = &[0];
assert_eq!(buf, b);
let mut buf = [0; 4];
assert_eq!(reader.read(&mut buf).unwrap(), 4);
assert_eq!(reader.position(), 5);
let b: &[_] = &[1, 2, 3, 4];
assert_eq!(buf, b);
assert_eq!(reader.read(&mut buf).unwrap(), 3);
let b: &[_] = &[5, 6, 7];
assert_eq!(&buf[..3], b);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn seek_past_end() {
let buf = [0xff];
let mut r = Cursor::new(&buf[..]);
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
assert_eq!(r.read(&mut [0]).unwrap(), 0);
let mut r = Cursor::new(vec![10]);
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
assert_eq!(r.read(&mut [0]).unwrap(), 0);
let mut buf = [0];
let mut r = Cursor::new(&mut buf[..]);
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
assert_eq!(r.write(&[3]).unwrap(), 0);
let mut r = Cursor::new(vec![10].into_boxed_slice());
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
assert_eq!(r.write(&[3]).unwrap(), 0);
}
#[test]
fn seek_past_i64() {
let buf = [0xff];
let mut r = Cursor::new(&buf[..]);
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
let mut r = Cursor::new(vec![10]);
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
let mut buf = [0];
let mut r = Cursor::new(&mut buf[..]);
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
let mut r = Cursor::new(vec![10].into_boxed_slice());
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
}
#[test]
fn seek_before_0() {
let buf = [0xff];
let mut r = Cursor::new(&buf[..]);
assert!(r.seek(SeekFrom::End(-2)).is_err());
let mut r = Cursor::new(vec![10]);
assert!(r.seek(SeekFrom::End(-2)).is_err());
let mut buf = [0];
let mut r = Cursor::new(&mut buf[..]);
assert!(r.seek(SeekFrom::End(-2)).is_err());
let mut r = Cursor::new(vec![10].into_boxed_slice());
assert!(r.seek(SeekFrom::End(-2)).is_err());
}
#[test]
fn test_seekable_mem_writer() {
let mut writer = Cursor::new(Vec::<u8>::new());
assert_eq!(writer.position(), 0);
assert_eq!(writer.write(&[0]).unwrap(), 1);
assert_eq!(writer.position(), 1);
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
assert_eq!(writer.position(), 8);
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
assert_eq!(&writer.get_ref()[..], b);
assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0);
assert_eq!(writer.position(), 0);
assert_eq!(writer.write(&[3, 4]).unwrap(), 2);
let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
assert_eq!(&writer.get_ref()[..], b);
assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3);
assert_eq!(writer.write(&[0, 1]).unwrap(), 2);
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
assert_eq!(&writer.get_ref()[..], b);
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
assert_eq!(writer.write(&[1, 2]).unwrap(), 2);
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
assert_eq!(&writer.get_ref()[..], b);
assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10);
assert_eq!(writer.write(&[1]).unwrap(), 1);
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
assert_eq!(&writer.get_ref()[..], b);
}
#[test]
fn vec_seek_past_end() {
let mut r = Cursor::new(Vec::new());
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
assert_eq!(r.write(&[3]).unwrap(), 1);
}
#[test]
fn vec_seek_before_0() {
let mut r = Cursor::new(Vec::new());
assert!(r.seek(SeekFrom::End(-2)).is_err());
}
#[test]
#[cfg(target_pointer_width = "32")]
fn vec_seek_and_write_past_usize_max() {
let mut c = Cursor::new(Vec::new());
c.set_position(<usize>::max_value() as u64 + 1);
assert!(c.write_all(&[1, 2, 3]).is_err());
}
#[test]
fn test_partial_eq() {
assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
}
#[test]
fn test_eq() {
struct AssertEq<T: Eq>(pub T);
let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
}
}

View File

@ -1,551 +0,0 @@
#[cfg(feature="alloc")] use alloc::boxed::Box;
#[cfg(not(feature="alloc"))] use ::FakeBox as Box;
use core::convert::Into;
use core::fmt;
use core::marker::{Send, Sync};
use core::option::Option::{self, Some, None};
use core::result;
#[cfg(feature="collections")] use collections::string::String;
#[cfg(not(feature="collections"))] use ::ErrorString as String;
use core::convert::From;
/// A specialized [`Result`](../result/enum.Result.html) type for I/O
/// operations.
///
/// This type is broadly used across [`std::io`] for any operation which may
/// produce an error.
///
/// This typedef is generally used to avoid writing out [`io::Error`] directly and
/// is otherwise a direct mapping to [`Result`].
///
/// While usual Rust style is to import types directly, aliases of [`Result`]
/// often are not, to make it easier to distinguish between them. [`Result`] is
/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias
/// will generally use `io::Result` instead of shadowing the prelude's import
/// of [`std::result::Result`][`Result`].
///
/// [`std::io`]: ../io/index.html
/// [`io::Error`]: ../io/struct.Error.html
/// [`Result`]: ../result/enum.Result.html
///
/// # Examples
///
/// A convenience function that bubbles an `io::Result` to its caller:
///
/// ```
/// use std::io;
///
/// fn get_string() -> io::Result<String> {
/// let mut buffer = String::new();
///
/// io::stdin().read_line(&mut buffer)?;
///
/// Ok(buffer)
/// }
/// ```
pub type Result<T> = result::Result<T, Error>;
/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and
/// associated traits.
///
/// Errors mostly originate from the underlying OS, but custom instances of
/// `Error` can be created with crafted error messages and a particular value of
/// [`ErrorKind`].
///
/// [`Read`]: ../io/trait.Read.html
/// [`Write`]: ../io/trait.Write.html
/// [`Seek`]: ../io/trait.Seek.html
/// [`ErrorKind`]: enum.ErrorKind.html
pub struct Error {
repr: Repr,
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.repr, f)
}
}
enum Repr {
Os(i32),
Simple(ErrorKind),
#[cfg(feature="alloc")]
Custom(Box<Custom>),
#[cfg(not(feature="alloc"))]
Custom(Custom),
}
#[derive(Debug)]
struct Custom {
kind: ErrorKind,
error: String,
}
/// A list specifying general categories of I/O error.
///
/// This list is intended to grow over time and it is not recommended to
/// exhaustively match against it.
///
/// It is used with the [`io::Error`] type.
///
/// [`io::Error`]: struct.Error.html
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[allow(deprecated)]
#[non_exhaustive]
pub enum ErrorKind {
/// An entity was not found, often a file.
NotFound,
/// The operation lacked the necessary privileges to complete.
PermissionDenied,
/// The connection was refused by the remote server.
ConnectionRefused,
/// The connection was reset by the remote server.
ConnectionReset,
/// The connection was aborted (terminated) by the remote server.
ConnectionAborted,
/// The network operation failed because it was not connected yet.
NotConnected,
/// A socket address could not be bound because the address is already in
/// use elsewhere.
AddrInUse,
/// A nonexistent interface was requested or the requested address was not
/// local.
AddrNotAvailable,
/// The operation failed because a pipe was closed.
BrokenPipe,
/// An entity already exists, often a file.
AlreadyExists,
/// The operation needs to block to complete, but the blocking operation was
/// requested to not occur.
WouldBlock,
/// A parameter was incorrect.
InvalidInput,
/// Data not valid for the operation were encountered.
///
/// Unlike [`InvalidInput`], this typically means that the operation
/// parameters were valid, however the error was caused by malformed
/// input data.
///
/// For example, a function that reads a file into a string will error with
/// `InvalidData` if the file's contents are not valid UTF-8.
///
/// [`InvalidInput`]: #variant.InvalidInput
InvalidData,
/// The I/O operation's timeout expired, causing it to be canceled.
TimedOut,
/// An error returned when an operation could not be completed because a
/// call to [`write`] returned [`Ok(0)`].
///
/// This typically means that an operation could only succeed if it wrote a
/// particular number of bytes but only a smaller number of bytes could be
/// written.
///
/// [`write`]: ../../std/io/trait.Write.html#tymethod.write
/// [`Ok(0)`]: ../../std/io/type.Result.html
WriteZero,
/// This operation was interrupted.
///
/// Interrupted operations can typically be retried.
Interrupted,
/// Any I/O error not part of this list.
Other,
/// An error returned when an operation could not be completed because an
/// "end of file" was reached prematurely.
///
/// This typically means that an operation could only succeed if it read a
/// particular number of bytes but only a smaller number of bytes could be
/// read.
UnexpectedEof,
}
impl ErrorKind {
pub(crate) fn as_str(&self) -> &'static str {
match *self {
ErrorKind::NotFound => "entity not found",
ErrorKind::PermissionDenied => "permission denied",
ErrorKind::ConnectionRefused => "connection refused",
ErrorKind::ConnectionReset => "connection reset",
ErrorKind::ConnectionAborted => "connection aborted",
ErrorKind::NotConnected => "not connected",
ErrorKind::AddrInUse => "address in use",
ErrorKind::AddrNotAvailable => "address not available",
ErrorKind::BrokenPipe => "broken pipe",
ErrorKind::AlreadyExists => "entity already exists",
ErrorKind::WouldBlock => "operation would block",
ErrorKind::InvalidInput => "invalid input parameter",
ErrorKind::InvalidData => "invalid data",
ErrorKind::TimedOut => "timed out",
ErrorKind::WriteZero => "write zero",
ErrorKind::Interrupted => "operation interrupted",
ErrorKind::Other => "other os error",
ErrorKind::UnexpectedEof => "unexpected end of file",
}
}
}
/// Intended for use for errors not exposed to the user, where allocating onto
/// the heap (for normal construction via Error::new) is too costly.
impl From<ErrorKind> for Error {
/// Converts an [`ErrorKind`] into an [`Error`].
///
/// This conversion allocates a new error with a simple representation of error kind.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
///
/// let not_found = ErrorKind::NotFound;
/// let error = Error::from(not_found);
/// assert_eq!("entity not found", format!("{}", error));
/// ```
///
/// [`ErrorKind`]: ../../std/io/enum.ErrorKind.html
/// [`Error`]: ../../std/io/struct.Error.html
#[inline]
fn from(kind: ErrorKind) -> Error {
Error { repr: Repr::Simple(kind) }
}
}
impl Error {
/// Creates a new I/O error from a known kind of error as well as an
/// arbitrary error payload.
///
/// This function is used to generically create I/O errors which do not
/// originate from the OS itself. The `error` argument is an arbitrary
/// payload which will be contained in this `Error`.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
///
/// // errors can be created from strings
/// let custom_error = Error::new(ErrorKind::Other, "oh no!");
///
/// // errors can also be created from other errors
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
/// ```
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where
E: Into<String>,
{
Self::_new(kind, error.into())
}
fn _new(kind: ErrorKind, error: String) -> Error {
Error { repr: Repr::Custom(Box::new(Custom { kind, error })) }
}
/// Creates a new instance of an `Error` from a particular OS error code.
///
/// # Examples
///
/// On Linux:
///
/// ```
/// # if cfg!(target_os = "linux") {
/// use std::io;
///
/// let error = io::Error::from_raw_os_error(22);
/// assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
/// # }
/// ```
///
/// On Windows:
///
/// ```
/// # if cfg!(windows) {
/// use std::io;
///
/// let error = io::Error::from_raw_os_error(10022);
/// assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
/// # }
/// ```
pub fn from_raw_os_error(code: i32) -> Error {
Error { repr: Repr::Os(code) }
}
/// Returns the OS error that this error represents (if any).
///
/// If this `Error` was constructed via `last_os_error` or
/// `from_raw_os_error`, then this function will return `Some`, otherwise
/// it will return `None`.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
///
/// fn print_os_error(err: &Error) {
/// if let Some(raw_os_err) = err.raw_os_error() {
/// println!("raw OS error: {:?}", raw_os_err);
/// } else {
/// println!("Not an OS error");
/// }
/// }
///
/// fn main() {
/// // Will print "raw OS error: ...".
/// print_os_error(&Error::last_os_error());
/// // Will print "Not an OS error".
/// print_os_error(&Error::new(ErrorKind::Other, "oh no!"));
/// }
/// ```
pub fn raw_os_error(&self) -> Option<i32> {
match self.repr {
Repr::Os(i) => Some(i),
Repr::Custom(..) => None,
Repr::Simple(..) => None,
}
}
/// Returns a reference to the inner error wrapped by this error (if any).
///
/// If this `Error` was constructed via `new` then this function will
/// return `Some`, otherwise it will return `None`.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
///
/// fn print_error(err: &Error) {
/// if let Some(inner_err) = err.get_ref() {
/// println!("Inner error: {:?}", inner_err);
/// } else {
/// println!("No inner error");
/// }
/// }
///
/// fn main() {
/// // Will print "No inner error".
/// print_error(&Error::last_os_error());
/// // Will print "Inner error: ...".
/// print_error(&Error::new(ErrorKind::Other, "oh no!"));
/// }
/// ```
pub fn get_ref(&self) -> Option<&String> {
match self.repr {
Repr::Os(..) => None,
Repr::Simple(..) => None,
Repr::Custom(ref c) => Some(&c.error),
}
}
/// Returns a mutable reference to the inner error wrapped by this error
/// (if any).
///
/// If this `Error` was constructed via `new` then this function will
/// return `Some`, otherwise it will return `None`.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
/// use std::{error, fmt};
/// use std::fmt::Display;
///
/// #[derive(Debug)]
/// struct MyError {
/// v: String,
/// }
///
/// impl MyError {
/// fn new() -> MyError {
/// MyError {
/// v: "oh no!".to_string()
/// }
/// }
///
/// fn change_message(&mut self, new_message: &str) {
/// self.v = new_message.to_string();
/// }
/// }
///
/// impl error::Error for MyError {}
///
/// impl Display for MyError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "MyError: {}", &self.v)
/// }
/// }
///
/// fn change_error(mut err: Error) -> Error {
/// if let Some(inner_err) = err.get_mut() {
/// inner_err.downcast_mut::<MyError>().unwrap().change_message("I've been changed!");
/// }
/// err
/// }
///
/// fn print_error(err: &Error) {
/// if let Some(inner_err) = err.get_ref() {
/// println!("Inner error: {}", inner_err);
/// } else {
/// println!("No inner error");
/// }
/// }
///
/// fn main() {
/// // Will print "No inner error".
/// print_error(&change_error(Error::last_os_error()));
/// // Will print "Inner error: ...".
/// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new())));
/// }
/// ```
pub fn get_mut(&mut self) -> Option<&mut String> {
match self.repr {
Repr::Os(..) => None,
Repr::Simple(..) => None,
Repr::Custom(ref mut c) => Some(&mut c.error),
}
}
/// Consumes the `Error`, returning its inner error (if any).
///
/// If this `Error` was constructed via `new` then this function will
/// return `Some`, otherwise it will return `None`.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
///
/// fn print_error(err: Error) {
/// if let Some(inner_err) = err.into_inner() {
/// println!("Inner error: {}", inner_err);
/// } else {
/// println!("No inner error");
/// }
/// }
///
/// fn main() {
/// // Will print "No inner error".
/// print_error(Error::last_os_error());
/// // Will print "Inner error: ...".
/// print_error(Error::new(ErrorKind::Other, "oh no!"));
/// }
/// ```
pub fn into_inner(self) -> Option<String> {
match self.repr {
Repr::Os(..) => None,
Repr::Simple(..) => None,
Repr::Custom(c) => Some(c.error),
}
}
/// Returns the corresponding `ErrorKind` for this error.
///
/// # Examples
///
/// ```
/// use std::io::{Error, ErrorKind};
///
/// fn print_error(err: Error) {
/// println!("{:?}", err.kind());
/// }
///
/// fn main() {
/// // Will print "No inner error".
/// print_error(Error::last_os_error());
/// // Will print "Inner error: ...".
/// print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
/// }
/// ```
pub fn kind(&self) -> ErrorKind {
match self.repr {
Repr::Os(_code) => ErrorKind::Other,
Repr::Custom(ref c) => c.kind,
Repr::Simple(kind) => kind,
}
}
}
impl fmt::Debug for Repr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Repr::Os(code) => fmt
.debug_struct("Os")
.field("code", &code)
.finish(),
Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.repr {
Repr::Os(code) => {
write!(fmt, "os error {}", code)
}
Repr::Custom(ref c) => c.error.fmt(fmt),
Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
}
}
}
fn _assert_error_is_sync_send() {
fn _is_sync_send<T: Sync + Send>() {}
_is_sync_send::<Error>();
}
#[cfg(test)]
mod test {
use super::{Custom, Error, ErrorKind, Repr};
use crate::error;
use crate::fmt;
use crate::sys::decode_error_kind;
use crate::sys::os::error_string;
#[test]
fn test_debug_error() {
let code = 6;
let msg = error_string(code);
let kind = decode_error_kind(code);
let err = Error {
repr: Repr::Custom(box Custom {
kind: ErrorKind::InvalidInput,
error: box Error { repr: super::Repr::Os(code) },
}),
};
let expected = format!(
"Custom {{ \
kind: InvalidInput, \
error: Os {{ \
code: {:?}, \
kind: {:?}, \
message: {:?} \
}} \
}}",
code, kind, msg
);
assert_eq!(format!("{:?}", err), expected);
}
#[test]
fn test_downcasting() {
#[derive(Debug)]
struct TestError;
impl fmt::Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("asdf")
}
}
impl error::Error for TestError {}
// we have to call all of these UFCS style right now since method
// resolution won't implicitly drop the Send+Sync bounds
let mut err = Error::new(ErrorKind::Other, TestError);
assert!(err.get_ref().unwrap().is::<TestError>());
assert_eq!("asdf", err.get_ref().unwrap().to_string());
assert!(err.get_mut().unwrap().is::<TestError>());
let extracted = err.into_inner().unwrap();
extracted.downcast::<TestError>().unwrap();
}
}

View File

@ -1,378 +0,0 @@
use core::cmp;
use core::fmt;
use crate::io::{
self, Error, ErrorKind, Initializer, Read, Seek, SeekFrom, Write,
};
#[cfg(feature = "collections")] use crate::io::BufRead;
use core::mem;
#[cfg(feature="collections")]
use collections::{
vec::Vec,
string::String,
};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
// =============================================================================
// Forwarding implementations
impl<R: Read + ?Sized> Read for &mut R {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
#[inline]
unsafe fn initializer(&self) -> Initializer {
(**self).initializer()
}
#[cfg(feature="collections")]
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
(**self).read_to_end(buf)
}
#[cfg(feature="collections")]
#[inline]
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
(**self).read_to_string(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
(**self).read_exact(buf)
}
}
impl<W: Write + ?Sized> Write for &mut W {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(**self).write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
(**self).flush()
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
(**self).write_all(buf)
}
#[inline]
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
(**self).write_fmt(fmt)
}
}
impl<S: Seek + ?Sized> Seek for &mut S {
#[inline]
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
(**self).seek(pos)
}
}
#[cfg(feature = "collections")]
impl<B: BufRead + ?Sized> BufRead for &mut B {
#[inline]
fn fill_buf(&mut self) -> io::Result<&[u8]> {
(**self).fill_buf()
}
#[inline]
fn consume(&mut self, amt: usize) {
(**self).consume(amt)
}
#[cfg(feature="collections")]
#[inline]
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
(**self).read_until(byte, buf)
}
#[cfg(feature="collections")]
#[inline]
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
(**self).read_line(buf)
}
}
#[cfg(feature="alloc")]
#[cfg(feature="collections")]
impl<R: Read + ?Sized> Read for Box<R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
#[cfg(feature="collections")]
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
(**self).read_to_end(buf)
}
#[cfg(feature="collections")]
#[inline]
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
(**self).read_to_string(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
(**self).read_exact(buf)
}
}
#[cfg(feature="alloc")]
#[cfg(feature="collections")]
impl<W: Write + ?Sized> Write for Box<W> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(**self).write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
(**self).flush()
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
(**self).write_all(buf)
}
#[inline]
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
(**self).write_fmt(fmt)
}
}
#[cfg(feature="collections")]
impl<S: Seek + ?Sized> Seek for Box<S> {
#[inline]
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
(**self).seek(pos)
}
}
#[cfg(feature="collections")]
impl<B: BufRead + ?Sized> BufRead for Box<B> {
#[inline]
fn fill_buf(&mut self) -> io::Result<&[u8]> {
(**self).fill_buf()
}
#[inline]
fn consume(&mut self, amt: usize) {
(**self).consume(amt)
}
#[inline]
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
(**self).read_until(byte, buf)
}
#[inline]
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
(**self).read_line(buf)
}
}
// Used by panicking::default_hook
#[cfg(test)]
/// This impl is only used by printing logic, so any error returned is always
/// of kind `Other`, and should be ignored.
#[cfg(feature="collections")]
impl Write for Box<dyn (::realstd::io::Write) + Send> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(**self).write(buf).map_err(|_| ErrorKind::Other.into())
}
fn flush(&mut self) -> io::Result<()> {
(**self).flush().map_err(|_| ErrorKind::Other.into())
}
}
// =============================================================================
// In-memory buffer implementations
/// Read is implemented for `&[u8]` by copying from the slice.
///
/// Note that reading updates the slice to point to the yet unread part.
/// The slice will be empty when EOF is reached.
impl Read for &[u8] {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let amt = cmp::min(buf.len(), self.len());
let (a, b) = self.split_at(amt);
// First check if the amount of bytes we want to read is small:
// `copy_from_slice` will generally expand to a call to `memcpy`, and
// for a single byte the overhead is significant.
if amt == 1 {
buf[0] = a[0];
} else {
buf[..amt].copy_from_slice(a);
}
*self = b;
Ok(amt)
}
#[inline]
unsafe fn initializer(&self) -> Initializer {
Initializer::nop()
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if buf.len() > self.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"));
}
let (a, b) = self.split_at(buf.len());
// First check if the amount of bytes we want to read is small:
// `copy_from_slice` will generally expand to a call to `memcpy`, and
// for a single byte the overhead is significant.
if buf.len() == 1 {
buf[0] = a[0];
} else {
buf.copy_from_slice(a);
}
*self = b;
Ok(())
}
#[cfg(feature="collections")]
#[inline]
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
buf.extend_from_slice(*self);
let len = self.len();
*self = &self[len..];
Ok(len)
}
}
#[cfg(feature="collections")]
impl BufRead for &[u8] {
#[inline]
fn fill_buf(&mut self) -> io::Result<&[u8]> {
Ok(*self)
}
#[inline]
fn consume(&mut self, amt: usize) {
*self = &self[amt..];
}
}
/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting
/// its data.
///
/// Note that writing updates the slice to point to the yet unwritten part.
/// The slice will be empty when it has been completely overwritten.
impl Write for &mut [u8] {
#[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let amt = cmp::min(data.len(), self.len());
let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
a.copy_from_slice(&data[..amt]);
*self = b;
Ok(amt)
}
#[inline]
fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
if self.write(data)? == data.len() {
Ok(())
} else {
Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer"))
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
/// Write is implemented for `Vec<u8>` by appending to the vector.
/// The vector will grow as needed.
#[cfg(feature="collections")]
impl Write for Vec<u8> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.extend_from_slice(buf);
Ok(buf.len())
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.extend_from_slice(buf);
Ok(())
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::io::prelude::*;
#[bench]
fn bench_read_slice(b: &mut test::Bencher) {
let buf = [5; 1024];
let mut dst = [0; 128];
b.iter(|| {
let mut rd = &buf[..];
for _ in 0..8 {
let _ = rd.read(&mut dst);
test::black_box(&dst);
}
})
}
#[bench]
fn bench_write_slice(b: &mut test::Bencher) {
let mut buf = [0; 1024];
let src = [5; 128];
b.iter(|| {
let mut wr = &mut buf[..];
for _ in 0..8 {
let _ = wr.write_all(&src);
test::black_box(&wr);
}
})
}
#[bench]
fn bench_read_vec(b: &mut test::Bencher) {
let buf = vec![5; 1024];
let mut dst = [0; 128];
b.iter(|| {
let mut rd = &buf[..];
for _ in 0..8 {
let _ = rd.read(&mut dst);
test::black_box(&dst);
}
})
}
#[bench]
fn bench_write_vec(b: &mut test::Bencher) {
let mut buf = Vec::with_capacity(1024);
let src = [5; 128];
b.iter(|| {
let mut wr = &mut buf[..];
for _ in 0..8 {
let _ = wr.write_all(&src);
test::black_box(&wr);
}
})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
//! The I/O Prelude
//!
//! The purpose of this module is to alleviate imports of many common I/O traits
//! by adding a glob import to the top of I/O heavy modules:
//!
//! ```
//! # #![allow(unused_imports)]
//! use std::io::prelude::*;
//! ```
pub use super::{Read, Seek, Write};
#[cfg(feature = "collections")] pub use super::BufRead;

View File

@ -1,269 +0,0 @@
#![allow(missing_copy_implementations)]
use core::fmt;
use core::mem;
use crate::io::{self, ErrorKind, Initializer, Read, Write};
#[cfg(feature = "collections")] use crate::io::BufRead;
/// Copies the entire contents of a reader into a writer.
///
/// This function will continuously read data from `reader` and then
/// write it into `writer` in a streaming fashion until `reader`
/// returns EOF.
///
/// On success, the total number of bytes that were copied from
/// `reader` to `writer` is returned.
///
/// If youre wanting to copy the contents of one file to another and youre
/// working with filesystem paths, see the [`fs::copy`] function.
///
/// [`fs::copy`]: ../fs/fn.copy.html
///
/// # Errors
///
/// This function will return an error immediately if any call to `read` or
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
/// handled by this function and the underlying operation is retried.
///
/// # Examples
///
/// ```
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let mut reader: &[u8] = b"hello";
/// let mut writer: Vec<u8> = vec![];
///
/// io::copy(&mut reader, &mut writer)?;
///
/// assert_eq!(&b"hello"[..], &writer[..]);
/// Ok(())
/// }
/// ```
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
where
R: Read,
W: Write,
{
let mut buf = unsafe {
#[allow(deprecated)]
let mut buf: [u8; super::DEFAULT_BUF_SIZE] = mem::uninitialized();
reader.initializer().initialize(&mut buf);
buf
};
let mut written = 0;
loop {
let len = match reader.read(&mut buf) {
Ok(0) => return Ok(written),
Ok(len) => len,
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
};
writer.write_all(&buf[..len])?;
written += len as u64;
}
}
/// A reader which is always at EOF.
///
/// This struct is generally created by calling [`empty`]. Please see
/// the documentation of [`empty()`][`empty`] for more details.
///
/// [`empty`]: fn.empty.html
pub struct Empty {
_priv: (),
}
/// Constructs a new handle to an empty reader.
///
/// All reads from the returned reader will return [`Ok`]`(0)`.
///
/// [`Ok`]: ../result/enum.Result.html#variant.Ok
///
/// # Examples
///
/// A slightly sad example of not reading anything into a buffer:
///
/// ```
/// use std::io::{self, Read};
///
/// let mut buffer = String::new();
/// io::empty().read_to_string(&mut buffer).unwrap();
/// assert!(buffer.is_empty());
/// ```
pub fn empty() -> Empty {
Empty { _priv: () }
}
impl Read for Empty {
#[inline]
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Ok(0)
}
#[inline]
unsafe fn initializer(&self) -> Initializer {
Initializer::nop()
}
}
#[cfg(feature="collections")]
impl BufRead for Empty {
#[inline]
fn fill_buf(&mut self) -> io::Result<&[u8]> {
Ok(&[])
}
#[inline]
fn consume(&mut self, _n: usize) {}
}
impl fmt::Debug for Empty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("Empty { .. }")
}
}
/// A reader which yields one byte over and over and over and over and over and...
///
/// This struct is generally created by calling [`repeat`][repeat]. Please
/// see the documentation of `repeat()` for more details.
///
/// [repeat]: fn.repeat.html
pub struct Repeat {
byte: u8,
}
/// Creates an instance of a reader that infinitely repeats one byte.
///
/// All reads from this reader will succeed by filling the specified buffer with
/// the given byte.
///
/// # Examples
///
/// ```
/// use std::io::{self, Read};
///
/// let mut buffer = [0; 3];
/// io::repeat(0b101).read_exact(&mut buffer).unwrap();
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
/// ```
pub fn repeat(byte: u8) -> Repeat {
Repeat { byte }
}
impl Read for Repeat {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
for slot in &mut *buf {
*slot = self.byte;
}
Ok(buf.len())
}
#[inline]
unsafe fn initializer(&self) -> Initializer {
Initializer::nop()
}
}
impl fmt::Debug for Repeat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("Repeat { .. }")
}
}
/// A writer which will move data into the void.
///
/// This struct is generally created by calling [`sink`][sink]. Please
/// see the documentation of `sink()` for more details.
///
/// [sink]: fn.sink.html
pub struct Sink {
_priv: (),
}
/// Creates an instance of a writer which will successfully consume all data.
///
/// All calls to `write` on the returned instance will return `Ok(buf.len())`
/// and the contents of the buffer will not be inspected.
///
/// # Examples
///
/// ```rust
/// use std::io::{self, Write};
///
/// let buffer = vec![1, 2, 3, 5, 8];
/// let num_bytes = io::sink().write(&buffer).unwrap();
/// assert_eq!(num_bytes, 5);
/// ```
pub fn sink() -> Sink {
Sink { _priv: () }
}
impl Write for Sink {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl fmt::Debug for Sink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("Sink { .. }")
}
}
#[cfg(test)]
mod tests {
use crate::io::prelude::*;
use crate::io::{copy, empty, repeat, sink};
#[test]
fn copy_copies() {
let mut r = repeat(0).take(4);
let mut w = sink();
assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
let mut r = repeat(0).take(1 << 17);
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
}
#[test]
fn sink_sinks() {
let mut s = sink();
assert_eq!(s.write(&[]).unwrap(), 0);
assert_eq!(s.write(&[0]).unwrap(), 1);
assert_eq!(s.write(&[0; 1024]).unwrap(), 1024);
assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024);
}
#[test]
fn empty_reads() {
let mut e = empty();
assert_eq!(e.read(&mut []).unwrap(), 0);
assert_eq!(e.read(&mut [0]).unwrap(), 0);
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
}
#[test]
fn repeat_repeats() {
let mut r = repeat(4);
let mut b = [0; 1024];
assert_eq!(r.read(&mut b).unwrap(), 1024);
assert!(b.iter().all(|b| *b == 4));
}
#[test]
fn take_some_bytes() {
assert_eq!(repeat(4).take(100).bytes().count(), 100);
assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
}
}

View File

@ -1,51 +0,0 @@
//! <p id="core_io-show-docblock"></p>
//! This is just a listing of the functionality available in this crate. See
//! the [std documentation](https://doc.rust-lang.org/nightly/std/io/index.html)
//! for a full description of the functionality.
#![allow(stable_features,unused_features)]
#![feature(question_mark,const_fn,copy_from_slice,try_from,str_internals,align_offset,slice_internals)]
#![cfg_attr(any(feature="alloc",feature="collections"),feature(alloc))]
#![cfg_attr(pattern_guards,feature(bind_by_move_pattern_guards,nll))]
#![cfg_attr(non_exhaustive,feature(non_exhaustive))]
#![cfg_attr(unicode,feature(str_char))]
#![cfg_attr(unicode,feature(unicode))]
#![no_std]
#[cfg_attr(feature="collections",macro_use)]
#[cfg_attr(feature="collections",allow(unused_imports))]
#[cfg(feature="collections")] extern crate alloc as collections;
#[cfg(feature="alloc")] extern crate alloc;
#[cfg(rustc_unicode)]
extern crate rustc_unicode;
#[cfg(std_unicode)]
extern crate std_unicode;
#[cfg(not(feature="collections"))]
pub type ErrorString = &'static str;
// Provide Box::new wrapper
#[cfg(not(feature="alloc"))]
struct FakeBox<T>(core::marker::PhantomData<T>);
#[cfg(not(feature="alloc"))]
impl<T> FakeBox<T> {
fn new(val: T) -> T {
val
}
}
// Needed for older compilers, to ignore vec!/format! macros in tests
#[cfg(not(feature="collections"))]
#[allow(unused)]
macro_rules! vec (
( $ elem : expr ; $ n : expr ) => { () };
( $ ( $ x : expr ) , * ) => { () };
( $ ( $ x : expr , ) * ) => { () };
);
#[cfg(not(feature="collections"))]
#[allow(unused)]
macro_rules! format {
( $ ( $ arg : tt ) * ) => { () };
}
mod io;
pub use io::*;

View File

@ -1,17 +0,0 @@
[package]
name = "libcortex_a9"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[features]
target_zc706 = []
target_cora_z7_10 = []
target_redpitaya = []
power_saving = []
default = ["target_zc706"]
[dependencies]
bit_field = "0.10"
volatile-register = "0.2"
libregister = { path = "../libregister" }

View File

@ -1,76 +0,0 @@
/// The classic no-op
#[inline]
pub fn nop() {
unsafe { llvm_asm!("nop" :::: "volatile") }
}
/// Wait For Event
#[inline]
pub fn wfe() {
unsafe { llvm_asm!("wfe" :::: "volatile") }
}
/// Send Event
#[inline]
pub fn sev() {
unsafe { llvm_asm!("sev" :::: "volatile") }
}
/// Data Memory Barrier
#[inline]
pub fn dmb() {
unsafe { llvm_asm!("dmb" :::: "volatile") }
}
/// Data Synchronization Barrier
#[inline]
pub fn dsb() {
unsafe { llvm_asm!("dsb" :::: "volatile") }
}
/// Instruction Synchronization Barrier
#[inline]
pub fn isb() {
unsafe { llvm_asm!("isb" :::: "volatile") }
}
/// Enable IRQ
#[inline]
pub unsafe fn enable_irq() {
llvm_asm!("cpsie i":::: "volatile");
}
/// Disable IRQ, return if IRQ was originally enabled.
#[inline]
pub unsafe fn enter_critical() -> bool {
let mut cpsr: u32;
llvm_asm!(
"mrs $0, cpsr
cpsid i"
: "=r"(cpsr) ::: "volatile");
(cpsr & (1 << 7)) == 0
}
#[inline]
pub unsafe fn exit_critical(enable: bool) {
// https://stackoverflow.com/questions/40019929/temporarily-disable-interrupts-on-arm
let mask: u32 = if enable {
1 << 7
} else {
0
};
llvm_asm!(
"mrs r1, cpsr
bic r1, r1, $0
msr cpsr_c, r1"
:: "r"(mask) : "r1");
}
/// Exiting IRQ
#[inline]
pub unsafe fn exit_irq() {
llvm_asm!("
mrs r0, SPSR
msr CPSR, r0
" ::: "r0");
}

View File

@ -1,279 +0,0 @@
use super::asm::{dmb, dsb};
use super::l2c::*;
/// Invalidate TLBs
#[inline(always)]
pub fn tlbiall() {
unsafe {
llvm_asm!("mcr p15, 0, $0, c8, c7, 0" :: "r" (0) :: "volatile");
}
}
/// Invalidate I-Cache
#[inline(always)]
pub fn iciallu() {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c5, 0" :: "r" (0) :: "volatile");
}
}
/// Invalidate Branch Predictor Array
#[inline(always)]
pub fn bpiall() {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c5, 6" :: "r" (0) :: "volatile");
}
}
/// Data cache clean by set/way
#[inline(always)]
pub fn dccsw(setway: u32) {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c10, 2" :: "r" (setway) :: "volatile");
}
}
/// Data cache invalidate by set/way
#[inline(always)]
pub fn dcisw(setway: u32) {
unsafe {
// acc. to ARM Architecture Reference Manual, Figure B3-32;
// also see example code (for DCCISW, but DCISW will be
// analogous) "Example code for cache maintenance operations"
// on pages B2-1286 and B2-1287.
llvm_asm!("mcr p15, 0, $0, c7, c6, 2" :: "r" (setway) :: "volatile");
}
}
/// Data cache clean by set/way
#[inline(always)]
pub fn dccisw(setway: u32) {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c14, 2" :: "r" (setway) :: "volatile");
}
}
/// A made-up "instruction": invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dciall_l1() {
// the cache associativity could be read from a register, but will
// always be 4 in L1 data cache of a cortex a9
let ways = 4;
let bit_pos_of_way = 30; // 32 - log2(ways)
// the cache sets could be read from a register, but are always
// 256 for the cores in the zync-7000; in general, 128 or 512 are
// also possible.
let sets = 256;
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
// select L1 data cache
unsafe {
llvm_asm!("mcr p15, 2, $0, c0, c0, 0" :: "r" (0) :: "volatile");
}
// Invalidate entire D-Cache by iterating every set and every way
for set in 0..sets {
for way in 0..ways {
dcisw((set << bit_pos_of_set) | (way << bit_pos_of_way));
}
}
}
/// A made-up "instruction": invalidate all of the L1 L2 D-Cache
#[inline(always)]
pub fn dciall() {
dmb();
l2_cache_invalidate_all();
dciall_l1();
}
/// A made-up "instruction": flush and invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dcciall_l1() {
// the cache associativity could be read from a register, but will
// always be 4 in L1 data cache of a cortex a9
let ways = 4;
let bit_pos_of_way = 30; // 32 - log2(ways)
// the cache sets could be read from a register, but are always
// 256 for the cores in the zync-7000; in general, 128 or 512 are
// also possible.
let sets = 256;
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
// select L1 data cache
unsafe {
llvm_asm!("mcr p15, 2, $0, c0, c0, 0" :: "r" (0) :: "volatile");
}
// Invalidate entire D-Cache by iterating every set and every way
for set in 0..sets {
for way in 0..ways {
dccisw((set << bit_pos_of_set) | (way << bit_pos_of_way));
}
}
}
#[inline(always)]
pub fn dcciall() {
dmb();
dcciall_l1();
dsb();
l2_cache_clean_invalidate_all();
dcciall_l1();
dsb();
}
const CACHE_LINE: usize = 0x20;
const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
#[inline]
fn cache_line_addrs(first_addr: usize, beyond_addr: usize) -> impl Iterator<Item = usize> {
let first_addr = first_addr & !CACHE_LINE_MASK;
let beyond_addr = (beyond_addr | CACHE_LINE_MASK) + 1;
(first_addr..beyond_addr).step_by(CACHE_LINE)
}
fn object_cache_line_addrs<T>(object: &T) -> impl Iterator<Item = usize> {
let first_addr = object as *const _ as usize;
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
cache_line_addrs(first_addr, beyond_addr)
}
fn slice_cache_line_addrs<T>(slice: &[T]) -> impl Iterator<Item = usize> {
let first_addr = &slice[0] as *const _ as usize;
let beyond_addr = (&slice[slice.len() - 1] as *const _ as usize) +
core::mem::size_of_val(&slice[slice.len() - 1]);
cache_line_addrs(first_addr, beyond_addr)
}
/// Data cache clean and invalidate by memory virtual address. This
/// flushes data out to the point of coherency, and invalidates the
/// corresponding cache line (as appropriate when DMA is meant to be
/// writing into it).
#[inline(always)]
pub fn dccimvac(addr: usize) {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c14, 1" :: "r" (addr) :: "volatile");
}
}
/// Data cache clean and invalidate for an object.
pub fn dcci<T>(object: &T) {
// ref: L2C310 TRM 3.3.10
dmb();
for addr in object_cache_line_addrs(object) {
dccmvac(addr);
}
dsb();
for addr in object_cache_line_addrs(object) {
l2_cache_clean_invalidate(addr);
}
l2_cache_sync();
for addr in object_cache_line_addrs(object) {
dccimvac(addr);
}
dsb();
}
pub fn dcci_slice<T>(slice: &[T]) {
dmb();
for addr in slice_cache_line_addrs(slice) {
dccmvac(addr);
}
dsb();
for addr in slice_cache_line_addrs(slice) {
l2_cache_clean_invalidate(addr);
}
l2_cache_sync();
for addr in slice_cache_line_addrs(slice) {
dccimvac(addr);
}
dsb();
}
/// Data cache clean by memory virtual address.
#[inline(always)]
pub fn dccmvac(addr: usize) {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c10, 1" :: "r" (addr) :: "volatile");
}
}
/// Data cache clean for an object.
pub fn dcc<T>(object: &T) {
dmb();
for addr in object_cache_line_addrs(object) {
dccmvac(addr);
}
dsb();
for addr in object_cache_line_addrs(object) {
l2_cache_clean(addr);
}
l2_cache_sync();
}
/// Data cache clean for an object. Panics if not properly
/// aligned and properly sized to be contained in an exact number of
/// cache lines.
pub fn dcc_slice<T>(slice: &[T]) {
if slice.len() == 0 {
return;
}
dmb();
for addr in slice_cache_line_addrs(slice) {
dccmvac(addr);
}
dsb();
for addr in slice_cache_line_addrs(slice) {
l2_cache_clean(addr);
}
l2_cache_sync();
}
/// Data cache invalidate by memory virtual address. This and
/// invalidates the cache line containing the given address. Super
/// unsafe, as this discards a write-back cache line, potentially
/// affecting more data than intended.
#[inline(always)]
pub unsafe fn dcimvac(addr: usize) {
llvm_asm!("mcr p15, 0, $0, c7, c6, 1" :: "r" (addr) :: "volatile");
}
/// Data cache clean and invalidate for an object.
pub unsafe fn dci<T>(object: &mut T) {
let first_addr = object as *const _ as usize;
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
assert_eq!(first_addr & CACHE_LINE_MASK, 0, "dci object first_addr must be aligned");
assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci object beyond_addr must be aligned");
dmb();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
l2_cache_invalidate(addr);
}
l2_cache_sync();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
dcimvac(addr);
}
dsb();
}
pub unsafe fn dci_slice<T>(slice: &mut [T]) {
let first_addr = &slice[0] as *const _ as usize;
let beyond_addr = (&slice[slice.len() - 1] as *const _ as usize) +
core::mem::size_of_val(&slice[slice.len() - 1]);
assert_eq!(first_addr & CACHE_LINE_MASK, 0, "dci slice first_addr must be aligned");
assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci slice beyond_addr must be aligned");
dmb();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
l2_cache_invalidate(addr);
}
l2_cache_sync();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
dcimvac(addr);
}
dsb();
}

View File

@ -1,14 +0,0 @@
/// Enable FPU in the current core.
pub fn enable_fpu() {
unsafe {
llvm_asm!("
mrc p15, 0, r1, c1, c0, 2
orr r1, r1, (0b1111<<20)
mcr p15, 0, r1, c1, c0, 2
vmrs r1, fpexc
orr r1, r1, (1<<30)
vmsr fpexc, r1
":::"r1");
}
}

View File

@ -1,328 +0,0 @@
use libregister::{register, register_at, register_bit, register_bits, RegisterRW, RegisterR, RegisterW};
use super::asm::dmb;
use volatile_register::RW;
pub fn enable_l2_cache() {
dmb();
let regs = RegisterBlock::new();
// disable L2 cache
regs.reg1_control.modify(|_, w| w.l2_enable(false));
regs.reg15_prefetch_ctrl.modify(|_, w|
w.instr_prefetch_en(true)
.data_prefetch_en(true)
.double_linefill_en(true)
.incr_double_linefill_en(true)
.pref_drop_en(true)
);
regs.reg1_aux_control.modify(|_, w| {
w.early_bresp_en(true)
.instr_prefetch_en(true)
.data_prefetch_en(true)
.cache_replace_policy(true)
.way_size(3)
});
regs.reg1_tag_ram_control.modify(|_, w| w.ram_wr_access_lat(1).ram_rd_access_lat(1).ram_setup_lat(1));
regs.reg1_data_ram_control.modify(|_, w| w.ram_wr_access_lat(1).ram_rd_access_lat(2).ram_setup_lat(1));
// invalidate L2 ways
unsafe {
regs.reg7_inv_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
// write to a magic memory location with a magic sequence
// required in UG585 Section 3.4.10 Initialization Sequence
unsafe {
core::ptr::write_volatile(0xF8000008usize as *mut u32, 0xDF0D);
core::ptr::write_volatile(0xF8000A1Cusize as *mut u32, 0x020202);
core::ptr::write_volatile(0xF8000004usize as *mut u32, 0x767B);
}
regs.reg1_control.modify(|_, w| w.l2_enable(true));
dmb();
}
#[inline(always)]
pub fn l2_cache_invalidate_all() {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_inv_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
}
#[inline(always)]
pub fn l2_cache_clean_all() {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
}
#[inline(always)]
pub fn l2_cache_clean_invalidate_all() {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_inv_way.write(0xFFFF);
}
// poll for completion
while regs.reg7_cache_sync.read().c() {}
}
/// L2 cache sync, similar to dsb for L1 cache
#[inline(always)]
pub fn l2_cache_sync() {
let regs = RegisterBlock::new();
regs.reg7_cache_sync.write(Reg7CacheSync::zeroed().c(false));
}
#[inline(always)]
pub fn l2_cache_clean(addr: usize) {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_pa.write(addr as u32);
}
}
#[inline(always)]
pub fn l2_cache_invalidate(addr: usize) {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_inv_pa.write(addr as u32);
}
}
#[inline(always)]
pub fn l2_cache_clean_invalidate(addr: usize) {
let regs = RegisterBlock::new();
unsafe {
regs.reg7_clean_inv_pa.write(addr as u32);
}
}
#[repr(C)]
struct RegisterBlock {
/// cache ID register, Returns the 32-bit device ID code it reads off the CACHEID input bus.
/// The value is specified by the system integrator. Reset value: 0x410000c8
pub reg0_cache_id: Reg0CacheId,
/// cache type register, Returns the 32-bit cache type. Reset value: 0x1c100100
pub reg0_cache_type: Reg0CacheType,
unused0: [u32; 62],
/// control register, reset value: 0x0
pub reg1_control: Reg1Control,
/// auxilary control register, reset value: 0x02020000
pub reg1_aux_control: Reg1AuxControl,
/// Configures Tag RAM latencies
pub reg1_tag_ram_control: Reg1TagRamControl,
/// configures data RAM latencies
pub reg1_data_ram_control: Reg1DataRamControl,
unused1: [u32; 60],
/// Permits the event counters to be enabled and reset.
pub reg2_ev_counter_ctrl: Reg2EvCounterCtrl,
/// Enables event counter 1 to be driven by a specific event. Counter 1 increments when the
/// event occurs.
pub reg2_ev_counter1_cfg: Reg2EvCounter1Cfg,
/// Enables event counter 0 to be driven by a specific event. Counter 0 increments when the
/// event occurs.
pub reg2_ev_counter0_cfg: Reg2EvCounter0Cfg,
/// Enable the programmer to read off the counter value. The counter counts an event as
/// specified by the Counter Configuration Registers. The counter can be preloaded if counting
/// is disabled and reset by the Event Counter Control Register.
pub reg2_ev_counter1: RW<u32>,
/// Enable the programmer to read off the counter value. The counter counts an event as
/// specified by the Counter Configuration Registers. The counter can be preloaded if counting
/// is disabled and reset by the Event Counter Control Register.
pub reg2_ev_counter0: RW<u32>,
/// This register enables or masks interrupts from being triggered on the external pins of the
/// cache controller. Figure 3-8 on page 3-17 shows the register bit assignments. The bit
/// assignments enables the masking of the interrupts on both their individual outputs and the
/// combined L2CCINTR line. Clearing a bit by writing a 0, disables the interrupt triggering on
/// that pin. All bits are cleared by a reset. You must write to the register bits with a 1 to
/// enable the generation of interrupts. 1 = Enabled. 0 = Masked. This is the default.
pub reg2_int_mask: Reg2IntMask,
/// This register is a read-only.It returns the masked interrupt status. This register can be
/// accessed by secure and non-secure operations. The register gives an AND function of the raw
/// interrupt status with the values of the interrupt mask register. All the bits are cleared
/// by a reset. A write to this register is ignored. Bits read can be HIGH or LOW: HIGH If the
/// bits read HIGH, they reflect the status of the input lines triggering an interrupt. LOW If
/// the bits read LOW, either no interrupt has been generated, or the interrupt is masked.
pub reg2_int_mask_status: Reg2IntMaskStatus,
/// The Raw Interrupt Status Register enables the interrupt status that excludes the masking
/// logic. Bits read can be HIGH or LOW: HIGH If the bits read HIGH, they reflect the status of
/// the input lines triggering an interrupt. LOW If the bits read LOW, no interrupt has been
/// generated.
pub reg2_int_raw_status: Reg2IntRawStatus,
/// Clears the Raw Interrupt Status Register bits. When a bit is written as 1, it clears the
/// corresponding bit in the Raw Interrupt Status Register. When a bit is written as 0, it has
/// no effect
pub reg2_int_clear: Reg2IntClear,
unused2: [u32; 323],
/// Drain the STB. Operation complete when all buffers, LRB, LFB, STB, and EB, are empty
pub reg7_cache_sync: Reg7CacheSync,
unused3: [u32; 15],
/// Invalidate Line by PA: Specific L2 cache line is marked as not valid
pub reg7_inv_pa: RW<u32>,
unused4: [u32; 2],
/// Invalidate by Way Invalidate all data in specified ways, including dirty data. An
/// Invalidate by way while selecting all cache ways is equivalent to invalidating all cache
/// entries. Completes as a background task with the way, or ways, locked, preventing
/// allocation.
pub reg7_inv_way: RW<u32>,
unused5: [u32; 12],
/// Clean Line by PA Write the specific L2 cache line to L3 main memory if the line is marked
/// as valid and dirty. The line is marked as not dirty. The valid bit is unchanged
pub reg7_clean_pa: RW<u32>,
unused6: [u32; 1],
/// Clean Line by Set/Way Write the specific L2 cache line within the specified way to L3 main
/// memory if the line is marked as valid and dirty. The line is marked as not dirty. The valid
/// bit is unchanged
pub reg7_clean_index: Reg7CleanIndex,
/// Clean by Way Writes each line of the specified L2 cache ways to L3 main memory if the line
/// is marked as valid and dirty. The lines are marked as not dirty. The valid bits are
/// unchanged. Completes as a background task with the way, or ways, locked, preventing
/// allocation.
pub reg7_clean_way: RW<u32>,
unused7: [u32; 12],
/// Clean and Invalidate Line by PA Write the specific L2 cache line to L3 main memory if the
/// line is marked as valid and dirty. The line is marked as not valid
pub reg7_clean_inv_pa: RW<u32>,
unused8: [u32; 1],
/// Clean and Invalidate Line by Set/Way Write the specific L2 cache line within the specified
/// way to L3 main memory if the line is marked as valid and dirty. The line is marked as not
/// valid
pub reg7_clean_inv_index: Reg7CleanInvIndex,
/// Clean and Invalidate by Way Writes each line of the specified L2 cache ways to L3 main
/// memory if the line is marked as valid and dirty. The lines are marked as not valid.
/// Completes as a background task with the way, or ways, locked, preventing allocation.
pub reg7_clean_inv_way: RW<u32>,
unused9: [u32; 0x1D8],
pub reg15_prefetch_ctrl: Reg15PrefetechCtrl,
}
register_at!(RegisterBlock, 0xF8F02000, new);
register!(reg0_cache_id, Reg0CacheId, RW, u32);
register_bits!(reg0_cache_id, implementer, u8, 24, 31);
register_bits!(reg0_cache_id, cache_id, u8, 10, 15);
register_bits!(reg0_cache_id, part_num, u8, 6, 9);
register_bits!(reg0_cache_id, rtl_release, u8, 0, 5);
register!(reg0_cache_type, Reg0CacheType, RW, u32);
register_bit!(reg0_cache_type, data_banking, 31);
register_bits!(reg0_cache_type, ctype, u8, 25, 28);
register_bit!(reg0_cache_type, h, 24);
register_bits!(reg0_cache_type, dsize_middsize_19, u8, 20, 22);
register_bit!(reg0_cache_type, l2_assoc_d, 18);
register_bits!(reg0_cache_type, l2cache_line_len_disize_11, u8, 12, 13);
register_bits!(reg0_cache_type, isize_midisize_7, u8, 8, 10);
register_bit!(reg0_cache_type, l2_assoc_i, 6);
register_bits!(reg0_cache_type, l2cache_line_len_i, u8, 0, 1);
register!(reg1_control, Reg1Control, RW, u32);
register_bit!(reg1_control, l2_enable, 0);
register!(reg1_aux_control, Reg1AuxControl, RW, u32);
register_bit!(reg1_aux_control, early_bresp_en, 30);
register_bit!(reg1_aux_control, instr_prefetch_en, 29);
register_bit!(reg1_aux_control, data_prefetch_en, 28);
register_bit!(reg1_aux_control, nonsec_inte_access_ctrl, 27);
register_bit!(reg1_aux_control, nonsec_lockdown_en, 26);
register_bit!(reg1_aux_control, cache_replace_policy, 25);
register_bits!(reg1_aux_control, force_write_alloc, u8, 23, 24);
register_bit!(reg1_aux_control, shared_attr_override_en, 22);
register_bit!(reg1_aux_control, parity_en, 21);
register_bit!(reg1_aux_control, event_mon_bus_en, 20);
register_bits!(reg1_aux_control, way_size, u8, 17, 19);
register_bit!(reg1_aux_control, associativity, 16);
register_bit!(reg1_aux_control, shared_attr_inva_en, 13);
register_bit!(reg1_aux_control, ex_cache_config, 12);
register_bit!(reg1_aux_control, store_buff_dev_lim_en, 11);
register_bit!(reg1_aux_control, high_pr_so_dev_rd_en, 10);
register_bit!(reg1_aux_control, full_line_zero_enable, 0);
register!(reg1_tag_ram_control, Reg1TagRamControl, RW, u32);
register_bits!(reg1_tag_ram_control, ram_wr_access_lat, u8, 8, 10);
register_bits!(reg1_tag_ram_control, ram_rd_access_lat, u8, 4, 6);
register_bits!(reg1_tag_ram_control, ram_setup_lat, u8, 0, 2);
register!(reg1_data_ram_control, Reg1DataRamControl, RW, u32);
register_bits!(reg1_data_ram_control, ram_wr_access_lat, u8, 8, 10);
register_bits!(reg1_data_ram_control, ram_rd_access_lat, u8, 4, 6);
register_bits!(reg1_data_ram_control, ram_setup_lat, u8, 0, 2);
register!(reg2_ev_counter_ctrl, Reg2EvCounterCtrl, RW, u32);
register_bit!(reg2_ev_counter_ctrl, ev_ctr_en, 0);
register!(reg2_ev_counter1_cfg, Reg2EvCounter1Cfg, RW, u32);
register_bits!(reg2_ev_counter1_cfg, ctr_ev_src, u8, 2, 5);
register_bits!(reg2_ev_counter1_cfg, ev_ctr_intr_gen, u8, 0, 1);
register!(reg2_ev_counter0_cfg, Reg2EvCounter0Cfg, RW, u32);
register_bits!(reg2_ev_counter0_cfg, ctr_ev_src, u8, 2, 5);
register_bits!(reg2_ev_counter0_cfg, ev_ctr_intr_gen, u8, 0, 1);
register!(reg2_int_mask, Reg2IntMask, RW, u32);
register_bit!(reg2_int_mask, decerr, 8);
register_bit!(reg2_int_mask, slverr, 7);
register_bit!(reg2_int_mask, errrd, 6);
register_bit!(reg2_int_mask, errrt, 5);
register_bit!(reg2_int_mask, errwd, 4);
register_bit!(reg2_int_mask, errwt, 3);
register_bit!(reg2_int_mask, parrd, 2);
register_bit!(reg2_int_mask, parrt, 1);
register_bit!(reg2_int_mask, ecntr, 0);
register!(reg2_int_mask_status, Reg2IntMaskStatus, RW, u32);
register_bit!(reg2_int_mask_status, decerr, 8);
register_bit!(reg2_int_mask_status, slverr, 7);
register_bit!(reg2_int_mask_status, errrd, 6);
register_bit!(reg2_int_mask_status, errrt, 5);
register_bit!(reg2_int_mask_status, errwd, 4);
register_bit!(reg2_int_mask_status, errwt, 3);
register_bit!(reg2_int_mask_status, parrd, 2);
register_bit!(reg2_int_mask_status, parrt, 1);
register_bit!(reg2_int_mask_status, ecntr, 0);
register!(reg2_int_raw_status, Reg2IntRawStatus, RW, u32);
register_bit!(reg2_int_raw_status, decerr, 8);
register_bit!(reg2_int_raw_status, slverr, 7);
register_bit!(reg2_int_raw_status, errrd, 6);
register_bit!(reg2_int_raw_status, errrt, 5);
register_bit!(reg2_int_raw_status, errwd, 4);
register_bit!(reg2_int_raw_status, errwt, 3);
register_bit!(reg2_int_raw_status, parrd, 2);
register_bit!(reg2_int_raw_status, parrt, 1);
register_bit!(reg2_int_raw_status, ecntr, 0);
register!(reg2_int_clear, Reg2IntClear, RW, u32, 0);
register_bit!(reg2_int_clear, decerr, 8, WTC);
register_bit!(reg2_int_clear, slverr, 7, WTC);
register_bit!(reg2_int_clear, errrd, 6, WTC);
register_bit!(reg2_int_clear, errrt, 5, WTC);
register_bit!(reg2_int_clear, errwd, 4, WTC);
register_bit!(reg2_int_clear, errwt, 3, WTC);
register_bit!(reg2_int_clear, parrd, 2, WTC);
register_bit!(reg2_int_clear, parrt, 1, WTC);
register_bit!(reg2_int_clear, ecntr, 0, WTC);
register!(reg7_cache_sync, Reg7CacheSync, RW, u32);
register_bit!(reg7_cache_sync, c, 0);
register!(reg7_clean_index, Reg7CleanIndex, RW, u32);
register_bits!(reg7_clean_index, way, u8, 28, 30);
register_bits!(reg7_clean_index, index, u8, 5, 11);
register_bit!(reg7_clean_index, c, 0);
register!(reg7_clean_inv_index, Reg7CleanInvIndex, RW, u32);
register_bits!(reg7_clean_inv_index, way, u8, 28, 30);
register_bits!(reg7_clean_inv_index, index, u8, 5, 11);
register_bit!(reg7_clean_inv_index, c, 0);
register!(reg15_prefetch_ctrl, Reg15PrefetechCtrl, RW, u32);
register_bit!(reg15_prefetch_ctrl, double_linefill_en, 30);
register_bit!(reg15_prefetch_ctrl, instr_prefetch_en, 29);
register_bit!(reg15_prefetch_ctrl, data_prefetch_en, 28);
register_bit!(reg15_prefetch_ctrl, pref_drop_en, 24);
register_bit!(reg15_prefetch_ctrl, incr_double_linefill_en, 23);

View File

@ -1,37 +0,0 @@
#![no_std]
#![feature(llvm_asm, global_asm)]
#![feature(never_type)]
#![feature(const_fn)]
extern crate alloc;
pub mod asm;
pub mod regs;
pub mod cache;
pub mod mmu;
pub mod mutex;
pub mod sync_channel;
pub mod semaphore;
pub mod l2c;
mod uncached;
mod fpu;
pub use uncached::UncachedSlice;
pub use fpu::enable_fpu;
global_asm!(include_str!("exceptions.s"));
#[inline]
pub fn spin_lock_yield() {
#[cfg(feature = "power_saving")]
asm::wfe();
}
#[inline]
pub fn notify_spin_lock() {
#[cfg(feature = "power_saving")]
{
asm::dsb();
asm::sev();
}
}

View File

@ -1,88 +0,0 @@
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicU32, Ordering};
use core::cell::UnsafeCell;
use super::{
spin_lock_yield, notify_spin_lock,
asm::{enter_critical, exit_critical}
};
const LOCKED: u32 = 1;
const UNLOCKED: u32 = 0;
/// Mutex implementation for Cortex-A9
///
/// [ARM Synchronization Primitives Development Article: Implementing a mutex](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
pub struct Mutex<T> {
locked: AtomicU32,
inner: UnsafeCell<T>,
}
unsafe impl<T: Send> Sync for Mutex<T> {}
unsafe impl<T: Send> Send for Mutex<T> {}
impl<T> Mutex<T> {
/// Constructor, const-fn
pub const fn new(inner: T) -> Self {
Mutex{
locked: AtomicU32::new(UNLOCKED),
inner: UnsafeCell::new(inner),
}
}
/// Lock the Mutex, blocks when already locked
pub fn lock(&self) -> MutexGuard<T> {
let mut irq = unsafe { enter_critical() };
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::AcqRel) != UNLOCKED {
unsafe {
exit_critical(irq);
spin_lock_yield();
irq = enter_critical();
}
}
MutexGuard { mutex: self, irq }
}
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
let irq = unsafe { enter_critical() };
if self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::AcqRel) != UNLOCKED {
unsafe { exit_critical(irq) };
None
} else {
Some(MutexGuard { mutex: self, irq })
}
}
fn unlock(&self) {
self.locked.store(UNLOCKED, Ordering::Release);
notify_spin_lock();
}
}
/// Returned by `Mutex.lock()`, allows access to data via
/// `Deref`/`DerefMutx`
pub struct MutexGuard<'a, T> {
mutex: &'a Mutex<T>,
irq: bool,
}
impl<'a, T> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.mutex.inner.get() }
}
}
impl<'a, T> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.mutex.inner.get() }
}
}
/// Automatically `Mutex.unlock()` when this reference is dropped
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
self.mutex.unlock();
unsafe { exit_critical(self.irq) };
}
}

View File

@ -1,71 +0,0 @@
use super::{spin_lock_yield, notify_spin_lock};
use core::{
task::{Context, Poll},
pin::Pin,
future::Future,
sync::atomic::{AtomicI32, Ordering}
};
pub struct Semaphore {
value: AtomicI32,
max: i32
}
impl Semaphore {
pub const fn new(value: i32, max: i32) -> Self {
Semaphore { value: AtomicI32::new(value), max}
}
pub fn try_wait(&self) -> Option<()> {
loop {
let value = self.value.load(Ordering::Relaxed);
if value > 0 {
if self.value.compare_and_swap(value, value - 1, Ordering::SeqCst) == value {
return Some(());
}
} else {
return None;
}
}
}
pub fn wait(&self) {
while self.try_wait().is_none() {
spin_lock_yield();
}
}
pub async fn async_wait(&self) {
struct Fut<'a>(&'a Semaphore);
impl Future for Fut<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.0.try_wait() {
Some(_) => Poll::Ready(()),
None => {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
Fut(&self).await
}
pub fn signal(&self) {
loop {
let value = self.value.load(Ordering::Relaxed);
if value < self.max {
if self.value.compare_and_swap(value, value + 1, Ordering::SeqCst) == value {
notify_spin_lock();
return;
}
} else {
return;
}
}
}
}

View File

@ -1,187 +0,0 @@
use core::{
pin::Pin,
future::Future,
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
task::{Context, Poll},
};
use alloc::boxed::Box;
use super::{spin_lock_yield, notify_spin_lock};
pub struct Sender<'a, T> where T: Clone {
list: &'a [AtomicPtr<T>],
write: &'a AtomicUsize,
read: &'a AtomicUsize,
}
pub struct Receiver<'a, T> where T: Clone {
list: &'a [AtomicPtr<T>],
write: &'a AtomicUsize,
read: &'a AtomicUsize,
}
impl<'a, T> Sender<'a, T> where T: Clone {
pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
Sender {list, write, read}
}
pub fn try_send<B: Into<Box<T>>>(&mut self, content: B) -> Result<(), B> {
let write = self.write.load(Ordering::Relaxed);
if (write + 1) % self.list.len() == self.read.load(Ordering::Acquire) {
Err(content)
} else {
let ptr = Box::into_raw(content.into());
let entry = &self.list[write];
let prev = entry.swap(ptr, Ordering::Relaxed);
// we allow other end get it first
self.write.store((write + 1) % self.list.len(), Ordering::Release);
notify_spin_lock();
if !prev.is_null() {
unsafe {
Box::from_raw(prev);
}
}
Ok(())
}
}
pub fn send<B: Into<Box<T>>>(&mut self, content: B) {
let mut content = content;
while let Err(back) = self.try_send(content) {
content = back;
spin_lock_yield();
}
}
pub async fn async_send<B: Into<Box<T>>>(&mut self, content: B) {
struct Send<'a, 'b, T> where T: Clone, 'b: 'a {
sender: &'a mut Sender<'b, T>,
content: Result<(), Box<T>>,
}
impl<T> Future for Send<'_, '_, T> where T: Clone {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match core::mem::replace(&mut self.content, Ok(())) {
Err(content) => {
if let Err(content) = self.sender.try_send(content) {
// failure
self.content = Err(content);
cx.waker().wake_by_ref();
Poll::Pending
} else {
// success
Poll::Ready(())
}
}
Ok(_) => panic!("Send future polled after success"),
}
}
}
Send {
sender: self,
content: Err(content.into()),
}.await
}
/// free all items in the queue. It is the user's responsibility to
/// ensure no reader is trying to copy the data.
pub unsafe fn drop_elements(&mut self) {
for v in self.list.iter() {
let original = v.swap(core::ptr::null_mut(), Ordering::Relaxed);
if !original.is_null() {
Box::from_raw(original);
}
}
}
/// Reset the `sync_channel`, *forget* all items in the queue. Affects both the sender and
/// receiver.
pub unsafe fn reset(&mut self) {
self.write.store(0, Ordering::Relaxed);
self.read.store(0, Ordering::Relaxed);
for v in self.list.iter() {
v.store(core::ptr::null_mut(), Ordering::Relaxed);
}
}
}
impl<'a, T> Receiver<'a, T> where T: Clone {
pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
Receiver {list, write, read}
}
pub fn try_recv(&mut self) -> Result<T, ()> {
let read = self.read.load(Ordering::Relaxed);
if read == self.write.load(Ordering::Acquire) {
Err(())
} else {
let entry = &self.list[read];
let data = unsafe {
// we cannot deallocate the box
Box::leak(Box::from_raw(entry.load(Ordering::Relaxed)))
};
let result = data.clone();
self.read.store((read + 1) % self.list.len(), Ordering::Release);
notify_spin_lock();
Ok(result)
}
}
pub fn recv(&mut self) -> T {
loop {
if let Ok(data) = self.try_recv() {
return data;
}
spin_lock_yield();
}
}
pub async fn async_recv(&mut self) -> T {
struct Recv<'a, 'b, T> where T: Clone, 'b: 'a {
receiver: &'a mut Receiver<'b, T>,
}
impl<T> Future for Recv<'_, '_, T> where T: Clone {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Ok(content) = self.receiver.try_recv() {
Poll::Ready(content)
} else {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
Recv {
receiver: self,
}.await
}
}
impl<'a, T> Iterator for Receiver<'a, T> where T: Clone {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
Some(self.recv())
}
}
#[macro_export]
/// Macro for initializing the sync_channel with static buffer and indexes.
/// Note that this requires `#![feature(const_in_array_repeat_expressions)]`
macro_rules! sync_channel {
($t: ty, $cap: expr) => {
{
use core::sync::atomic::{AtomicUsize, AtomicPtr};
use $crate::sync_channel::{Sender, Receiver};
static LIST: [AtomicPtr<$t>; $cap + 1] = [AtomicPtr::new(core::ptr::null_mut()); $cap + 1];
static WRITE: AtomicUsize = AtomicUsize::new(0);
static READ: AtomicUsize = AtomicUsize::new(0);
(Sender::new(&LIST, &WRITE, &READ), Receiver::new(&LIST, &WRITE, &READ))
}
};
}

View File

@ -1,67 +0,0 @@
use core::{
ops::{Deref, DerefMut},
mem::{align_of, size_of},
};
use alloc::alloc::{dealloc, Layout, LayoutErr};
use crate::mmu::{L1_PAGE_SIZE, L1Table};
pub struct UncachedSlice<T: 'static> {
layout: Layout,
slice: &'static mut [T],
}
impl<T> UncachedSlice<T> {
/// allocates in chunks of 1 MB
pub fn new<F: Fn() -> T>(len: usize, default: F) -> Result<Self, LayoutErr> {
// round to full pages
let size = ((len * size_of::<T>() - 1) | (L1_PAGE_SIZE - 1)) + 1;
let align = align_of::<T>()
.max(L1_PAGE_SIZE);
let layout = Layout::from_size_align(size, align)?;
let ptr = unsafe { alloc::alloc::alloc(layout).cast::<T>() };
let start = ptr as usize;
assert_eq!(start & (L1_PAGE_SIZE - 1), 0);
for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) {
// non-shareable device
L1Table::get()
.update(page_start as *const (), |l1_section| {
l1_section.tex = 0b10;
l1_section.cacheable = true;
l1_section.bufferable = false;
});
}
let slice = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
// verify size
assert!(unsafe { slice.get_unchecked(len) } as *const _ as usize <= start + size);
// initialize
for e in slice.iter_mut() {
*e = default();
}
Ok(UncachedSlice { layout, slice })
}
}
/// Does not yet mark the pages cachable again
impl<T> Drop for UncachedSlice<T> {
fn drop(&mut self) {
unsafe {
dealloc(self.slice.as_mut_ptr() as *mut _ as *mut u8, self.layout);
}
}
}
impl<T> Deref for UncachedSlice<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.slice
}
}
impl<T> DerefMut for UncachedSlice<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.slice
}
}

View File

@ -1,10 +0,0 @@
[package]
name = "libregister"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[dependencies]
vcell = "0.1"
volatile-register = "0.2"
bit_field = "0.10"

View File

@ -1,24 +0,0 @@
[package]
name = "libsupport_zynq"
description = "Software support for running in the Zynq PS"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[features]
target_zc706 = ["libboard_zynq/target_zc706"]
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10"]
target_redpitaya = ["libboard_zynq/target_redpitaya"]
panic_handler = []
dummy_irq_handler = []
alloc_core = []
default = ["panic_handler", "dummy_irq_handler"]
[dependencies]
r0 = "1"
compiler_builtins = "0.1"
linked_list_allocator = { version = "0.8", default-features = false }
libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" }
libboard_zynq = { path = "../libboard_zynq" }

View File

@ -1,70 +0,0 @@
use libregister::RegisterR;
use libcortex_a9::regs::{DFSR, MPIDR};
use libboard_zynq::{println, stdio};
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn UndefinedInstruction() {
stdio::drop_uart();
println!("UndefinedInstruction");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn SoftwareInterrupt() {
stdio::drop_uart();
println!("SoftwareInterrupt");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn PrefetchAbort() {
stdio::drop_uart();
println!("PrefetchAbort");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn DataAbort() {
stdio::drop_uart();
println!("DataAbort on core {}", MPIDR.read().cpu_id());
println!("DFSR: {:03X}", DFSR.read());
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn ReservedException() {
stdio::drop_uart();
println!("ReservedException");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
#[cfg(feature = "dummy_irq_handler")]
pub unsafe extern "C" fn IRQ() {
stdio::drop_uart();
println!("IRQ");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn FIQ() {
stdio::drop_uart();
println!("FIQ");
loop {}
}

View File

@ -1,172 +0,0 @@
use r0::zero_bss;
use core::ptr::write_volatile;
use libregister::{
VolatileCell,
RegisterR, RegisterW, RegisterRW,
};
use libcortex_a9::{asm, l2c, regs::*, cache, mmu, spin_lock_yield, notify_spin_lock};
use libboard_zynq::{slcr, mpcore};
extern "C" {
static mut __bss_start: u32;
static mut __bss_end: u32;
static mut __stack0_start: u32;
static mut __stack1_start: u32;
fn main_core0();
fn main_core1();
}
static mut CORE1_ENABLED: VolatileCell<bool> = VolatileCell::new(false);
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn Reset() -> ! {
match MPIDR.read().cpu_id() {
0 => {
SP.write(&mut __stack0_start as *mut _ as u32);
boot_core0();
}
1 => {
while !CORE1_ENABLED.get() {
spin_lock_yield();
}
SP.write(&mut __stack1_start as *mut _ as u32);
boot_core1();
}
_ => unreachable!(),
}
}
#[naked]
#[inline(never)]
unsafe fn boot_core0() -> ! {
l1_cache_init();
let mpcore = mpcore::RegisterBlock::mpcore();
mpcore.scu_invalidate.invalidate_all_cores();
zero_bss(&mut __bss_start, &mut __bss_end);
let mmu_table = mmu::L1Table::get()
.setup_flat_layout();
mmu::with_mmu(mmu_table, || {
mpcore.scu_control.start();
ACTLR.enable_smp();
ACTLR.enable_prefetch();
// TODO: Barriers reqd when core1 is not yet starting?
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core0();
panic!("return from main");
});
}
#[naked]
#[inline(never)]
unsafe fn boot_core1() -> ! {
l1_cache_init();
let mpcore = mpcore::RegisterBlock::mpcore();
mpcore.scu_invalidate.invalidate_core1();
let mmu_table = mmu::L1Table::get();
mmu::with_mmu(mmu_table, || {
ACTLR.enable_smp();
ACTLR.enable_prefetch();
// TODO: Barriers reqd when core1 is not yet starting?
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core1();
panic!("return from main_core1");
});
}
fn l1_cache_init() {
use libcortex_a9::cache::*;
// Invalidate TLBs
tlbiall();
// Invalidate I-Cache
iciallu();
// Invalidate Branch Predictor Array
bpiall();
// Invalidate D-Cache
//
// NOTE: It is both faster and correct to only invalidate instead
// of also flush the cache (as was done before with
// `dccisw()`) and it is correct to perform this operation
// for all of the L1 data cache rather than a (previously
// unspecified) combination of one cache set and one cache
// way.
dciall_l1();
}
pub struct Core1 {
}
impl Core1 {
/// Reset and start core1
pub fn start(sdram: bool) -> Self {
// reset and stop (safe to repeat)
slcr::RegisterBlock::unlocked(|slcr| {
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
});
if sdram {
// Cores always start from OCM no matter what you do.
// Make up a vector table there that just jumps to SDRAM.
for i in 0..8 {
unsafe {
// this is the ARM instruction "b +0x00100000"
write_volatile((i*4) as *mut u32, 0xea03fffe);
}
}
}
unsafe {
CORE1_ENABLED.set(true);
}
// Flush cache-line
cache::dcc(unsafe { &CORE1_ENABLED });
if sdram {
cache::dccmvac(0);
asm::dsb();
l2c::l2_cache_clean(0);
l2c::l2_cache_sync();
}
// wake up core1
slcr::RegisterBlock::unlocked(|slcr| {
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
});
notify_spin_lock();
Core1 {}
}
pub fn disable(&self) {
unsafe {
CORE1_ENABLED.set(false);
cache::dccmvac(&CORE1_ENABLED as *const _ as usize);
asm::dsb();
}
self.restart();
}
pub fn restart(&self) {
slcr::RegisterBlock::unlocked(|slcr| {
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
});
}
}

View File

@ -1,14 +0,0 @@
#![no_std]
#![feature(naked_functions)]
#![feature(alloc_error_handler)]
#![feature(panic_info_message)]
pub extern crate alloc;
pub extern crate compiler_builtins;
pub mod boot;
mod abort;
#[cfg(feature = "panic_handler")]
mod panic;
pub mod ram;

View File

@ -1,18 +0,0 @@
use libboard_zynq::{print, println};
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
print!("panic at ");
if let Some(location) = info.location() {
print!("{}:{}:{}", location.file(), location.line(), location.column());
} else {
print!("unknown location");
}
if let Some(message) = info.message() {
println!(": {}", message);
} else {
println!("");
}
loop {}
}

View File

@ -1,100 +0,0 @@
use alloc::alloc::Layout;
use core::alloc::GlobalAlloc;
use core::ptr::NonNull;
use libcortex_a9::{
mutex::Mutex,
regs::MPIDR
};
use libregister::RegisterR;
use linked_list_allocator::Heap;
#[cfg(not(feature = "alloc_core"))]
use libboard_zynq::ddr::DdrRam;
#[global_allocator]
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(
Mutex::new(Heap::empty()),
Mutex::new(Heap::empty()),
);
struct CortexA9Alloc(Mutex<Heap>, Mutex<Heap>);
unsafe impl Sync for CortexA9Alloc {}
unsafe impl GlobalAlloc for CortexA9Alloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
&self.0
} else {
&self.1
}
.lock()
.allocate_first_fit(layout)
.ok()
.map_or(0 as *mut u8, |allocation| allocation.as_ptr())
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if cfg!(not(feature = "alloc_core"))
|| ((&__heap0_start as *const usize as usize <= ptr as usize)
&& ((ptr as usize) < &__heap0_end as *const usize as usize))
{
&self.0
} else {
&self.1
}
.lock()
.deallocate(NonNull::new_unchecked(ptr), layout)
}
}
#[cfg(not(feature = "alloc_core"))]
pub fn init_alloc_ddr(ddr: &mut DdrRam) {
unsafe {
ALLOCATOR
.0
.lock()
.init(ddr.ptr::<u8>() as usize, ddr.size());
}
}
extern "C" {
static __heap0_start: usize;
static __heap0_end: usize;
#[cfg(feature = "alloc_core")]
static __heap1_start: usize;
#[cfg(feature = "alloc_core")]
static __heap1_end: usize;
}
pub fn init_alloc_core0() {
unsafe {
let start = &__heap0_start as *const usize as usize;
let end = &__heap0_end as *const usize as usize;
ALLOCATOR.0.lock().init(start, end - start);
}
}
#[cfg(feature = "alloc_core")]
pub fn init_alloc_core1() {
unsafe {
let start = &__heap1_start as *const usize as usize;
let end = &__heap1_end as *const usize as usize;
ALLOCATOR.1.lock().init(start, end - start);
}
}
#[alloc_error_handler]
fn alloc_error(layout: core::alloc::Layout) -> ! {
let id = MPIDR.read().cpu_id();
let used = if cfg!(not(feature = "alloc_core")) || id == 0 {
ALLOCATOR.0.lock().used()
} else {
ALLOCATOR.1.lock().used()
};
panic!(
"Core {} alloc_error, layout: {:?}, used memory: {}",
id,
layout,
used
);
}

73
link.x Normal file
View File

@ -0,0 +1,73 @@
ENTRY(_boot_cores);
/* Size of stack for core 0 in bytes */
STACK_SIZE = 0x8000;
/* Provide some defaults */
PROVIDE(Reset = _boot_cores);
PROVIDE(UndefinedInstruction = Reset);
PROVIDE(SoftwareInterrupt = Reset);
PROVIDE(PrefetchAbort = Reset);
PROVIDE(DataAbort = Reset);
PROVIDE(ReservedException = Reset);
PROVIDE(IRQ = Reset);
PROVIDE(FIQ = Reset);
MEMORY
{
/* 256 kB On-Chip Memory */
OCM : ORIGIN = 0, LENGTH = 0x30000
OCM3 : ORIGIN = 0xFFFF0000, LENGTH = 0x10000
}
SECTIONS
{
.exceptions ORIGIN(OCM) :
{
KEEP(*(.text.exceptions));
} > OCM
.__fill (NOLOAD) : {
. = ORIGIN(OCM) + 0x8000;
} > OCM
.text (ORIGIN(OCM) + 0x8000) :
{
*(.text.boot);
*(.text .text.*);
} > OCM
.rodata : ALIGN(4)
{
*(.rodata .rodata.*);
} > OCM
.data : ALIGN(4)
{
*(.data .data.*);
} > OCM
.bss (NOLOAD) : ALIGN(0x4000)
{
/* Aligned to 16 kB */
KEEP(*(.bss.l1_table));
*(.bss .bss.*);
. = ALIGN(4);
} > OCM
__bss_start = ADDR(.bss);
__bss_end = ADDR(.bss) + SIZEOF(.bss);
.stack (NOLOAD) : ALIGN(0x1000) {
. += STACK_SIZE;
} > OCM
__stack_end = ADDR(.stack);
__stack_start = ADDR(.stack) + SIZEOF(.stack) - 4;
/DISCARD/ :
{
/* Unused exception related info that only wastes space */
*(.ARM.exidx);
*(.ARM.exidx.*);
*(.ARM.extab.*);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +0,0 @@
{ pkgs, board ? "zc706" }:
let
gnutoolchain = import ./gnutoolchain.nix { inherit pkgs; };
in
pkgs.stdenv.mkDerivation {
name = "${board}-fsbl";
src = pkgs.fetchFromGitHub {
owner = "Xilinx";
repo = "embeddedsw";
rev = "65c849ed46c88c67457e1fc742744f96db968ff1";
sha256 = "1rvl06ha40dzd6s9aa4sylmksh4xb9dqaxq462lffv1fdk342pda";
};
patches = [ ./fsbl.patch ];
nativeBuildInputs = [
pkgs.gnumake
gnutoolchain.binutils
gnutoolchain.gcc
];
patchPhase =
''
patch -p1 -i ${./fsbl.patch}
patchShebangs lib/sw_apps/zynq_fsbl/misc/copy_bsp.sh
echo 'SEARCH_DIR("${gnutoolchain.newlib}/arm-none-eabi/lib");' >> lib/sw_apps/zynq_fsbl/src/lscript.ld
'';
buildPhase =
''
cd lib/sw_apps/zynq_fsbl/src
make BOARD=${board} "CFLAGS=-DFSBL_DEBUG_INFO -g"
'';
installPhase =
''
mkdir $out
cp fsbl.elf $out
'';
doCheck = false;
dontFixup = true;
}

View File

@ -1,31 +0,0 @@
diff --git a/lib/sw_apps/zynq_fsbl/src/Makefile b/lib/sw_apps/zynq_fsbl/src/Makefile
index 0e3ccdf1c5..a5b02f386e 100644
--- a/lib/sw_apps/zynq_fsbl/src/Makefile
+++ b/lib/sw_apps/zynq_fsbl/src/Makefile
@@ -71,11 +71,14 @@ endif
all: $(EXEC)
$(EXEC): $(LIBS) $(OBJS) $(INCLUDES)
- cp $(BSP_DIR)/$(BOARD)/ps7_init.* .
$(LINKER) $(LD1FLAGS) -o $@ $(OBJS) $(LDFLAGS)
rm -rf $(OBJS)
-
-
+
+.PHONY: ps7_init
+
+ps7_init:
+ cp $(BSP_DIR)/$(BOARD)/ps7_init.* .
+
$(LIBS):
echo "Copying BSP files"
$(BSP_DIR)/copy_bsp.sh $(BOARD) $(CC)
@@ -86,7 +89,7 @@ $(LIBS):
make -C $(BSP_DIR) -k all "CC=armcc" "AR=armar" "C_FLAGS= -O2 -c" "EC_FLAGS=--debug --wchar32"; \
fi;
-%.o:%.c
+%.o:%.c ps7_init
$(CC) $(CC_FLAGS) $(CFLAGS) $(ECFLAGS) -c $< -o $@ $(INCLUDEPATH)
%.o:%.S

View File

@ -1,134 +0,0 @@
{ pkgs ? import <nixpkgs> }:
let
platform = "arm-none-eabi";
binutils-pkg = { stdenv, buildPackages
, fetchurl, zlib
, extraConfigureFlags ? []
}:
stdenv.mkDerivation rec {
basename = "binutils";
version = "2.30";
name = "${basename}-${platform}-${version}";
src = fetchurl {
url = "https://ftp.gnu.org/gnu/binutils/binutils-${version}.tar.bz2";
sha256 = "028cklfqaab24glva1ks2aqa1zxa6w6xmc8q34zs1sb7h22dxspg";
};
configureFlags = [
"--enable-deterministic-archives"
"--target=${platform}"
"--with-cpu=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--with-mode=thumb"
] ++ extraConfigureFlags;
outputs = [ "out" "info" "man" ];
depsBuildBuild = [ buildPackages.stdenv.cc ];
buildInputs = [ zlib ];
enableParallelBuilding = true;
meta = {
description = "Tools for manipulating binaries (linker, assembler, etc.)";
longDescription = ''
The GNU Binutils are a collection of binary tools. The main
ones are `ld' (the GNU linker) and `as' (the GNU assembler).
They also include the BFD (Binary File Descriptor) library,
`gprof', `nm', `strip', etc.
'';
homepage = http://www.gnu.org/software/binutils/;
license = stdenv.lib.licenses.gpl3Plus;
/* Give binutils a lower priority than gcc-wrapper to prevent a
collision due to the ld/as wrappers/symlinks in the latter. */
priority = "10";
};
};
gcc-pkg = { stdenv, buildPackages
, fetchurl, gmp, mpfr, libmpc, platform-binutils
, extraConfigureFlags ? []
}:
stdenv.mkDerivation rec {
basename = "gcc";
version = "9.1.0";
name = "${basename}-${platform}-${version}";
src = fetchurl {
url = "https://ftp.gnu.org/gnu/gcc/gcc-${version}/gcc-${version}.tar.xz";
sha256 = "1817nc2bqdc251k0lpc51cimna7v68xjrnvqzvc50q3ax4s6i9kr";
};
preConfigure =
''
mkdir build
cd build
'';
configureScript = "../configure";
configureFlags =
[ "--target=${platform}"
"--with-arch=armv7-a"
"--with-tune=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--disable-libssp"
"--enable-languages=c"
"--with-as=${platform-binutils}/bin/${platform}-as"
"--with-ld=${platform-binutils}/bin/${platform}-ld" ] ++ extraConfigureFlags;
outputs = [ "out" "info" "man" ];
hardeningDisable = [ "format" "pie" ];
propagatedBuildInputs = [ gmp mpfr libmpc platform-binutils ];
enableParallelBuilding = true;
dontFixup = true;
};
newlib-pkg = { stdenv, fetchurl, buildPackages, platform-binutils, platform-gcc }:
stdenv.mkDerivation rec {
pname = "newlib";
version = "3.1.0";
src = fetchurl {
url = "ftp://sourceware.org/pub/newlib/newlib-${version}.tar.gz";
sha256 = "0ahh3n079zjp7d9wynggwrnrs27440aac04340chf1p9476a2kzv";
};
nativeBuildInputs = [ platform-binutils platform-gcc ];
configureFlags = [
"--target=${platform}"
"--with-cpu=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--with-mode=thumb"
"--enable-interwork"
"--disable-multilib"
"--disable-newlib-supplied-syscalls"
"--with-gnu-ld"
"--with-gnu-as"
"--disable-newlib-io-float"
"--disable-werror"
];
dontFixup = true;
};
in rec {
binutils-bootstrap = pkgs.callPackage binutils-pkg { };
gcc-bootstrap = pkgs.callPackage gcc-pkg {
platform-binutils = binutils-bootstrap;
extraConfigureFlags = [ "--disable-libgcc" ];
};
newlib = pkgs.callPackage newlib-pkg {
platform-binutils = binutils-bootstrap;
platform-gcc = gcc-bootstrap;
};
binutils = pkgs.callPackage binutils-pkg {
extraConfigureFlags = [ "--with-lib-path=${newlib}/arm-none-eabi/lib" ];
};
gcc = pkgs.callPackage gcc-pkg {
platform-binutils = binutils;
extraConfigureFlags = [ "--enable-newlib" "--with-headers=${newlib}/arm-none-eabi/include" ];
};
}

View File

@ -1,24 +0,0 @@
{ pkgs }:
pkgs.stdenv.mkDerivation {
pname = "mkbootimage";
version = "2.2";
src = pkgs.fetchFromGitHub {
owner = "antmicro";
repo = "zynq-mkbootimage";
rev = "4ee42d782a9ba65725ed165a4916853224a8edf7";
sha256 = "1k1mbsngqadqihzjgvwvsrkvryxy5ladpxd9yh9iqn2s7fxqwqa9";
};
propagatedBuildInputs = [ pkgs.libelf pkgs.pcre ];
patchPhase =
''
substituteInPlace Makefile --replace "git rev-parse --short HEAD" "echo nix"
'';
installPhase =
''
mkdir -p $out/bin
cp mkbootimage $out/bin
'';
}

View File

@ -1,10 +0,0 @@
let
pkgs = import <nixpkgs> {};
overlay = pkgs.fetchFromGitHub {
owner = "mozilla";
repo = "nixpkgs-mozilla";
rev = "efda5b357451dbb0431f983cca679ae3cd9b9829";
sha256 = "11wqrg86g3qva67vnk81ynvqyfj0zxk83cbrf0p9hsvxiwxs8469";
};
in
import overlay

View File

@ -1,24 +0,0 @@
{ pkgs }:
let
rustcSrc = pkgs.fetchgit {
url = "https://github.com/rust-lang/rust.git";
# sync with git_commit_hash from pkg.rust in channel-rust-nightly.toml
rev = "5ef299eb9805b4c86b227b718b39084e8bf24454";
sha256 = "0gc9hmb1sfkaf3ba8fsynl1n6bs8nk65hbhhx7ss89dfkrsxrn0x";
fetchSubmodules = true;
};
rustManifest = ./channel-rust-nightly.toml;
targets = [];
rustChannelOfTargets = _channel: _date: targets:
(pkgs.lib.rustLib.fromManifestFile rustManifest {
inherit (pkgs) stdenv fetchurl patchelf;
}).rust.override { inherit targets; };
rust =
rustChannelOfTargets "nightly" null targets;
in
pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
rustc = rust // { src = rustcSrc; };
cargo = rust;
})

View File

@ -1,11 +1,12 @@
source [find interface/ftdi/digilent-hs1.cfg] source [find interface/ftdi/digilent-hs1.cfg]
adapter_khz 10000 adapter_khz 10000
set PL_TAPID 0x13722093 set PL_TAPID 0x03722093
set SMP 1 set SMP 1
source ./zynq-7000.cfg source ./zynq-7000.cfg
source ./xilinx-tcl.cfg source ./xilinx-tcl.cfg
source ./ps7_init.tcl
reset_config srst_only srst_push_pull reset_config srst_only srst_push_pull
@ -30,10 +31,11 @@ pld device virtex2 zynq.tap 1
init init
xc7_program zynq.tap xc7_program zynq.tap
reset halt xilinx_ps7_init
# Disable MMU # Disable MMU
targets $_TARGETNAME_1 targets $_TARGETNAME_1
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd] arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
targets $_TARGETNAME_0 targets $_TARGETNAME_0
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd] arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]

View File

@ -1,12 +0,0 @@
# this is the original file from OpenOCD, but with ftdi_device_desc
# removed because some cables don't have it programmed.
# this supports JTAG-HS2 (and apparently Nexys4 as well)
interface ftdi
ftdi_vid_pid 0x0403 0x6014
ftdi_channel 0
ftdi_layout_init 0x00e8 0x60eb
reset_config none

View File

@ -0,0 +1,19 @@
#
# Digilent JTAG-SMT2-NC
#
# http://store.digilentinc.com/jtag-smt2-nc-surface-mount-programming-module/
# https://reference.digilentinc.com/_media/jtag_smt2nc/jtag-smt2-nc_rm.pdf
#
# Based on reference sheet (above) and Xilinx KCU105 schematics
# https://www.xilinx.com/products/boards-and-kits/kcu105.html#documentation
#
# Note that the digilent_jtag_smt2 layout does not work and hangs while
# the ftdi_device_desc from digilent_hs2 is wrong.
interface ftdi
ftdi_device_desc "Digilent USB Device"
ftdi_vid_pid 0x0403 0x6014
ftdi_channel 0
ftdi_layout_init 0x00e8 0x60eb
reset_config none

771
openocd/ps7_init.tcl Normal file
View File

@ -0,0 +1,771 @@
proc ps7_pll_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000110 0x003FFFF0 0x000FA220
mask_write 0XF8000100 0x0007F000 0x00028000
mask_write 0XF8000100 0x00000010 0x00000010
mask_write 0XF8000100 0x00000001 0x00000001
mask_write 0XF8000100 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000001
mask_write 0XF8000100 0x00000010 0x00000000
mask_write 0XF8000120 0x1F003F30 0x1F000200
mask_write 0XF8000114 0x003FFFF0 0x0012C220
mask_write 0XF8000104 0x0007F000 0x00020000
mask_write 0XF8000104 0x00000010 0x00000010
mask_write 0XF8000104 0x00000001 0x00000001
mask_write 0XF8000104 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000002
mask_write 0XF8000104 0x00000010 0x00000000
mask_write 0XF8000124 0xFFF00003 0x0C200003
mask_write 0XF8000118 0x003FFFF0 0x001452C0
mask_write 0XF8000108 0x0007F000 0x0001E000
mask_write 0XF8000108 0x00000010 0x00000010
mask_write 0XF8000108 0x00000001 0x00000001
mask_write 0XF8000108 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000004
mask_write 0XF8000108 0x00000010 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_clock_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000128 0x03F03F01 0x00700F01
mask_write 0XF8000138 0x00000011 0x00000001
mask_write 0XF8000140 0x03F03F71 0x00100801
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00001402
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200500
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01EC044D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_3_0 {} {
mask_write 0XF8006000 0x0001FFFF 0x00000080
mask_write 0XF8006004 0x0007FFFF 0x00001081
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
mask_write 0XF800600C 0x03FFFFFF 0x02001001
mask_write 0XF8006010 0x03FFFFFF 0x00014001
mask_write 0XF8006014 0x001FFFFF 0x0004159B
mask_write 0XF8006018 0xF7FFFFFF 0x452460D2
mask_write 0XF800601C 0xFFFFFFFF 0x720238E5
mask_write 0XF8006020 0x7FDFFFFC 0x270872D0
mask_write 0XF8006024 0x0FFFFFC3 0x00000000
mask_write 0XF8006028 0x00003FFF 0x00002007
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
mask_write 0XF8006030 0xFFFFFFFF 0x00040930
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
mask_write 0XF8006038 0x00000003 0x00000000
mask_write 0XF800603C 0x000FFFFF 0x00000777
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
mask_write 0XF8006048 0x0003F03F 0x0003C008
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
mask_write 0XF8006058 0x00010000 0x00000000
mask_write 0XF800605C 0x0000FFFF 0x00005003
mask_write 0XF8006060 0x000017FF 0x0000003E
mask_write 0XF8006064 0x00021FE0 0x00020000
mask_write 0XF8006068 0x03FFFFFF 0x00284141
mask_write 0XF800606C 0x0000FFFF 0x00001610
mask_write 0XF8006078 0x03FFFFFF 0x00466111
mask_write 0XF800607C 0x000FFFFF 0x00032222
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
mask_write 0XF80060AC 0x000001FF 0x000001FE
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
mask_write 0XF80060B4 0x00000200 0x00000200
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
mask_write 0XF80060C4 0x00000003 0x00000000
mask_write 0XF80060C8 0x000000FF 0x00000000
mask_write 0XF80060DC 0x00000001 0x00000000
mask_write 0XF80060F0 0x0000FFFF 0x00000000
mask_write 0XF80060F4 0x0000000F 0x00000008
mask_write 0XF8006114 0x000000FF 0x00000000
mask_write 0XF8006118 0x7FFFFFCF 0x40000001
mask_write 0XF800611C 0x7FFFFFCF 0x40000001
mask_write 0XF8006120 0x7FFFFFCF 0x40000001
mask_write 0XF8006124 0x7FFFFFCF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x00000080
mask_write 0XF8006160 0x000FFFFF 0x00000080
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000C0
mask_write 0XF8006188 0x000FFFFF 0x000000C0
mask_write 0XF8006190 0x6FFFFEFE 0x00040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
mask_write 0XF8006208 0x000703FF 0x000003FF
mask_write 0XF800620C 0x000703FF 0x000003FF
mask_write 0XF8006210 0x000703FF 0x000003FF
mask_write 0XF8006214 0x000703FF 0x000003FF
mask_write 0XF8006218 0x000F03FF 0x000003FF
mask_write 0XF800621C 0x000F03FF 0x000003FF
mask_write 0XF8006220 0x000F03FF 0x000003FF
mask_write 0XF8006224 0x000F03FF 0x000003FF
mask_write 0XF80062A8 0x00000FF5 0x00000000
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
mask_write 0XF80062B0 0x003FFFFF 0x00005125
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
mask_poll 0XF8000B74 0x00002000
mask_write 0XF8006000 0x0001FFFF 0x00000081
mask_poll 0XF8006054 0x00000007
}
proc ps7_mio_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B40 0x00000FFF 0x00000600
mask_write 0XF8000B44 0x00000FFF 0x00000600
mask_write 0XF8000B48 0x00000FFF 0x00000672
mask_write 0XF8000B4C 0x00000FFF 0x00000672
mask_write 0XF8000B50 0x00000FFF 0x00000674
mask_write 0XF8000B54 0x00000FFF 0x00000674
mask_write 0XF8000B58 0x00000FFF 0x00000600
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B6C 0x00007FFF 0x00000209
mask_write 0XF8000B70 0x00000001 0x00000001
mask_write 0XF8000B70 0x00000021 0x00000020
mask_write 0XF8000B70 0x07FEFFFF 0x00000823
mask_write 0XF8000700 0x00003FFF 0x00000600
mask_write 0XF8000704 0x00003FFF 0x00000702
mask_write 0XF8000708 0x00003FFF 0x00000702
mask_write 0XF800070C 0x00003FFF 0x00000702
mask_write 0XF8000710 0x00003FFF 0x00000702
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00000600
mask_write 0XF8000728 0x00003FFF 0x00000600
mask_write 0XF800072C 0x00003FFF 0x00000600
mask_write 0XF8000730 0x00003FFF 0x00000600
mask_write 0XF8000734 0x00003FFF 0x00000600
mask_write 0XF8000738 0x00003FFF 0x00000600
mask_write 0XF800073C 0x00003FFF 0x00000600
mask_write 0XF8000740 0x00003FFF 0x00000302
mask_write 0XF8000744 0x00003FFF 0x00000302
mask_write 0XF8000748 0x00003FFF 0x00000302
mask_write 0XF800074C 0x00003FFF 0x00000302
mask_write 0XF8000750 0x00003FFF 0x00000302
mask_write 0XF8000754 0x00003FFF 0x00000302
mask_write 0XF8000758 0x00003FFF 0x00000303
mask_write 0XF800075C 0x00003FFF 0x00000303
mask_write 0XF8000760 0x00003FFF 0x00000303
mask_write 0XF8000764 0x00003FFF 0x00000303
mask_write 0XF8000768 0x00003FFF 0x00000303
mask_write 0XF800076C 0x00003FFF 0x00000303
mask_write 0XF8000770 0x00003FFF 0x00000304
mask_write 0XF8000774 0x00003FFF 0x00000305
mask_write 0XF8000778 0x00003FFF 0x00000304
mask_write 0XF800077C 0x00003FFF 0x00000305
mask_write 0XF8000780 0x00003FFF 0x00000304
mask_write 0XF8000784 0x00003FFF 0x00000304
mask_write 0XF8000788 0x00003FFF 0x00000304
mask_write 0XF800078C 0x00003FFF 0x00000304
mask_write 0XF8000790 0x00003FFF 0x00000305
mask_write 0XF8000794 0x00003FFF 0x00000304
mask_write 0XF8000798 0x00003FFF 0x00000304
mask_write 0XF800079C 0x00003FFF 0x00000304
mask_write 0XF80007A0 0x00003FFF 0x00000380
mask_write 0XF80007A4 0x00003FFF 0x00000380
mask_write 0XF80007A8 0x00003FFF 0x00000380
mask_write 0XF80007AC 0x00003FFF 0x00000380
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x002F002E
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B48 0x00000180 0x00000180
mask_write 0XF8000B4C 0x00000180 0x00000180
mask_write 0XF8000B50 0x00000180 0x00000180
mask_write 0XF8000B54 0x00000180 0x00000180
mwr -force 0XF8000004 0x0000767B
mask_write 0XE0001034 0x000000FF 0x00000006
mask_write 0XE0001018 0x0000FFFF 0x0000003E
mask_write 0XE0001000 0x000001FF 0x00000017
mask_write 0XE0001004 0x000003FF 0x00000020
mask_write 0XE000D000 0x00080000 0x00080000
mask_write 0XF8007000 0x20000000 0x00000000
}
proc ps7_post_config_3_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000900 0x0000000F 0x0000000F
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_debug_3_0 {} {
mwr -force 0XF8898FB0 0xC5ACCE55
mwr -force 0XF8899FB0 0xC5ACCE55
mwr -force 0XF8809FB0 0xC5ACCE55
}
proc ps7_pll_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000110 0x003FFFF0 0x000FA220
mask_write 0XF8000100 0x0007F000 0x00028000
mask_write 0XF8000100 0x00000010 0x00000010
mask_write 0XF8000100 0x00000001 0x00000001
mask_write 0XF8000100 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000001
mask_write 0XF8000100 0x00000010 0x00000000
mask_write 0XF8000120 0x1F003F30 0x1F000200
mask_write 0XF8000114 0x003FFFF0 0x0012C220
mask_write 0XF8000104 0x0007F000 0x00020000
mask_write 0XF8000104 0x00000010 0x00000010
mask_write 0XF8000104 0x00000001 0x00000001
mask_write 0XF8000104 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000002
mask_write 0XF8000104 0x00000010 0x00000000
mask_write 0XF8000124 0xFFF00003 0x0C200003
mask_write 0XF8000118 0x003FFFF0 0x001452C0
mask_write 0XF8000108 0x0007F000 0x0001E000
mask_write 0XF8000108 0x00000010 0x00000010
mask_write 0XF8000108 0x00000001 0x00000001
mask_write 0XF8000108 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000004
mask_write 0XF8000108 0x00000010 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_clock_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000128 0x03F03F01 0x00700F01
mask_write 0XF8000138 0x00000011 0x00000001
mask_write 0XF8000140 0x03F03F71 0x00100801
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00001402
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200500
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01EC044D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_2_0 {} {
mask_write 0XF8006000 0x0001FFFF 0x00000080
mask_write 0XF8006004 0x1FFFFFFF 0x00081081
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
mask_write 0XF800600C 0x03FFFFFF 0x02001001
mask_write 0XF8006010 0x03FFFFFF 0x00014001
mask_write 0XF8006014 0x001FFFFF 0x0004159B
mask_write 0XF8006018 0xF7FFFFFF 0x452460D2
mask_write 0XF800601C 0xFFFFFFFF 0x720238E5
mask_write 0XF8006020 0xFFFFFFFC 0x272872D0
mask_write 0XF8006024 0x0FFFFFFF 0x0000003C
mask_write 0XF8006028 0x00003FFF 0x00002007
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
mask_write 0XF8006030 0xFFFFFFFF 0x00040930
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
mask_write 0XF8006038 0x00001FC3 0x00000000
mask_write 0XF800603C 0x000FFFFF 0x00000777
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
mask_write 0XF8006048 0x3FFFFFFF 0x0003C248
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
mask_write 0XF8006058 0x0001FFFF 0x00000101
mask_write 0XF800605C 0x0000FFFF 0x00005003
mask_write 0XF8006060 0x000017FF 0x0000003E
mask_write 0XF8006064 0x00021FE0 0x00020000
mask_write 0XF8006068 0x03FFFFFF 0x00284141
mask_write 0XF800606C 0x0000FFFF 0x00001610
mask_write 0XF8006078 0x03FFFFFF 0x00466111
mask_write 0XF800607C 0x000FFFFF 0x00032222
mask_write 0XF80060A0 0x00FFFFFF 0x00008000
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
mask_write 0XF80060AC 0x000001FF 0x000001FE
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
mask_write 0XF80060B4 0x000007FF 0x00000200
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
mask_write 0XF80060C4 0x00000003 0x00000000
mask_write 0XF80060C8 0x000000FF 0x00000000
mask_write 0XF80060DC 0x00000001 0x00000000
mask_write 0XF80060F0 0x0000FFFF 0x00000000
mask_write 0XF80060F4 0x0000000F 0x00000008
mask_write 0XF8006114 0x000000FF 0x00000000
mask_write 0XF8006118 0x7FFFFFFF 0x40000001
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x00000080
mask_write 0XF8006160 0x000FFFFF 0x00000080
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000C0
mask_write 0XF8006188 0x000FFFFF 0x000000C0
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
mask_write 0XF8006208 0x000F03FF 0x000803FF
mask_write 0XF800620C 0x000F03FF 0x000803FF
mask_write 0XF8006210 0x000F03FF 0x000803FF
mask_write 0XF8006214 0x000F03FF 0x000803FF
mask_write 0XF8006218 0x000F03FF 0x000003FF
mask_write 0XF800621C 0x000F03FF 0x000003FF
mask_write 0XF8006220 0x000F03FF 0x000003FF
mask_write 0XF8006224 0x000F03FF 0x000003FF
mask_write 0XF80062A8 0x00000FF7 0x00000000
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
mask_write 0XF80062B0 0x003FFFFF 0x00005125
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
mask_poll 0XF8000B74 0x00002000
mask_write 0XF8006000 0x0001FFFF 0x00000081
mask_poll 0XF8006054 0x00000007
}
proc ps7_mio_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B40 0x00000FFF 0x00000600
mask_write 0XF8000B44 0x00000FFF 0x00000600
mask_write 0XF8000B48 0x00000FFF 0x00000672
mask_write 0XF8000B4C 0x00000FFF 0x00000672
mask_write 0XF8000B50 0x00000FFF 0x00000674
mask_write 0XF8000B54 0x00000FFF 0x00000674
mask_write 0XF8000B58 0x00000FFF 0x00000600
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B6C 0x00007FFF 0x00000209
mask_write 0XF8000B70 0x00000021 0x00000021
mask_write 0XF8000B70 0x00000021 0x00000020
mask_write 0XF8000B70 0x07FFFFFF 0x00000823
mask_write 0XF8000700 0x00003FFF 0x00000600
mask_write 0XF8000704 0x00003FFF 0x00000702
mask_write 0XF8000708 0x00003FFF 0x00000702
mask_write 0XF800070C 0x00003FFF 0x00000702
mask_write 0XF8000710 0x00003FFF 0x00000702
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00000600
mask_write 0XF8000728 0x00003FFF 0x00000600
mask_write 0XF800072C 0x00003FFF 0x00000600
mask_write 0XF8000730 0x00003FFF 0x00000600
mask_write 0XF8000734 0x00003FFF 0x00000600
mask_write 0XF8000738 0x00003FFF 0x00000600
mask_write 0XF800073C 0x00003FFF 0x00000600
mask_write 0XF8000740 0x00003FFF 0x00000302
mask_write 0XF8000744 0x00003FFF 0x00000302
mask_write 0XF8000748 0x00003FFF 0x00000302
mask_write 0XF800074C 0x00003FFF 0x00000302
mask_write 0XF8000750 0x00003FFF 0x00000302
mask_write 0XF8000754 0x00003FFF 0x00000302
mask_write 0XF8000758 0x00003FFF 0x00000303
mask_write 0XF800075C 0x00003FFF 0x00000303
mask_write 0XF8000760 0x00003FFF 0x00000303
mask_write 0XF8000764 0x00003FFF 0x00000303
mask_write 0XF8000768 0x00003FFF 0x00000303
mask_write 0XF800076C 0x00003FFF 0x00000303
mask_write 0XF8000770 0x00003FFF 0x00000304
mask_write 0XF8000774 0x00003FFF 0x00000305
mask_write 0XF8000778 0x00003FFF 0x00000304
mask_write 0XF800077C 0x00003FFF 0x00000305
mask_write 0XF8000780 0x00003FFF 0x00000304
mask_write 0XF8000784 0x00003FFF 0x00000304
mask_write 0XF8000788 0x00003FFF 0x00000304
mask_write 0XF800078C 0x00003FFF 0x00000304
mask_write 0XF8000790 0x00003FFF 0x00000305
mask_write 0XF8000794 0x00003FFF 0x00000304
mask_write 0XF8000798 0x00003FFF 0x00000304
mask_write 0XF800079C 0x00003FFF 0x00000304
mask_write 0XF80007A0 0x00003FFF 0x00000380
mask_write 0XF80007A4 0x00003FFF 0x00000380
mask_write 0XF80007A8 0x00003FFF 0x00000380
mask_write 0XF80007AC 0x00003FFF 0x00000380
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x002F002E
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B48 0x00000180 0x00000180
mask_write 0XF8000B4C 0x00000180 0x00000180
mask_write 0XF8000B50 0x00000180 0x00000180
mask_write 0XF8000B54 0x00000180 0x00000180
mwr -force 0XF8000004 0x0000767B
mask_write 0XE0001034 0x000000FF 0x00000006
mask_write 0XE0001018 0x0000FFFF 0x0000003E
mask_write 0XE0001000 0x000001FF 0x00000017
mask_write 0XE0001004 0x00000FFF 0x00000020
mask_write 0XE000D000 0x00080000 0x00080000
mask_write 0XF8007000 0x20000000 0x00000000
}
proc ps7_post_config_2_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000900 0x0000000F 0x0000000F
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_debug_2_0 {} {
mwr -force 0XF8898FB0 0xC5ACCE55
mwr -force 0XF8899FB0 0xC5ACCE55
mwr -force 0XF8809FB0 0xC5ACCE55
}
proc ps7_pll_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000110 0x003FFFF0 0x000FA220
mask_write 0XF8000100 0x0007F000 0x00028000
mask_write 0XF8000100 0x00000010 0x00000010
mask_write 0XF8000100 0x00000001 0x00000001
mask_write 0XF8000100 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000001
mask_write 0XF8000100 0x00000010 0x00000000
mask_write 0XF8000120 0x1F003F30 0x1F000200
mask_write 0XF8000114 0x003FFFF0 0x0012C220
mask_write 0XF8000104 0x0007F000 0x00020000
mask_write 0XF8000104 0x00000010 0x00000010
mask_write 0XF8000104 0x00000001 0x00000001
mask_write 0XF8000104 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000002
mask_write 0XF8000104 0x00000010 0x00000000
mask_write 0XF8000124 0xFFF00003 0x0C200003
mask_write 0XF8000118 0x003FFFF0 0x001452C0
mask_write 0XF8000108 0x0007F000 0x0001E000
mask_write 0XF8000108 0x00000010 0x00000010
mask_write 0XF8000108 0x00000001 0x00000001
mask_write 0XF8000108 0x00000001 0x00000000
mask_poll 0XF800010C 0x00000004
mask_write 0XF8000108 0x00000010 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_clock_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000128 0x03F03F01 0x00700F01
mask_write 0XF8000138 0x00000011 0x00000001
mask_write 0XF8000140 0x03F03F71 0x00100801
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00001402
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200400
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01EC044D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_1_0 {} {
mask_write 0XF8006000 0x0001FFFF 0x00000080
mask_write 0XF8006004 0x1FFFFFFF 0x00081081
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
mask_write 0XF800600C 0x03FFFFFF 0x02001001
mask_write 0XF8006010 0x03FFFFFF 0x00014001
mask_write 0XF8006014 0x001FFFFF 0x0004159B
mask_write 0XF8006018 0xF7FFFFFF 0x452460D2
mask_write 0XF800601C 0xFFFFFFFF 0x720238E5
mask_write 0XF8006020 0xFFFFFFFC 0x272872D0
mask_write 0XF8006024 0x0FFFFFFF 0x0000003C
mask_write 0XF8006028 0x00003FFF 0x00002007
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
mask_write 0XF8006030 0xFFFFFFFF 0x00040930
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
mask_write 0XF8006038 0x00001FC3 0x00000000
mask_write 0XF800603C 0x000FFFFF 0x00000777
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
mask_write 0XF8006048 0x3FFFFFFF 0x0003C248
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
mask_write 0XF8006058 0x0001FFFF 0x00000101
mask_write 0XF800605C 0x0000FFFF 0x00005003
mask_write 0XF8006060 0x000017FF 0x0000003E
mask_write 0XF8006064 0x00021FE0 0x00020000
mask_write 0XF8006068 0x03FFFFFF 0x00284141
mask_write 0XF800606C 0x0000FFFF 0x00001610
mask_write 0XF80060A0 0x00FFFFFF 0x00008000
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
mask_write 0XF80060AC 0x000001FF 0x000001FE
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
mask_write 0XF80060B4 0x000007FF 0x00000200
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
mask_write 0XF80060C4 0x00000003 0x00000000
mask_write 0XF80060C8 0x000000FF 0x00000000
mask_write 0XF80060DC 0x00000001 0x00000000
mask_write 0XF80060F0 0x0000FFFF 0x00000000
mask_write 0XF80060F4 0x0000000F 0x00000008
mask_write 0XF8006114 0x000000FF 0x00000000
mask_write 0XF8006118 0x7FFFFFFF 0x40000001
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
mask_write 0XF800612C 0x000FFFFF 0x00033C03
mask_write 0XF8006130 0x000FFFFF 0x00034003
mask_write 0XF8006134 0x000FFFFF 0x0002F400
mask_write 0XF8006138 0x000FFFFF 0x00030400
mask_write 0XF8006140 0x000FFFFF 0x00000035
mask_write 0XF8006144 0x000FFFFF 0x00000035
mask_write 0XF8006148 0x000FFFFF 0x00000035
mask_write 0XF800614C 0x000FFFFF 0x00000035
mask_write 0XF8006154 0x000FFFFF 0x00000083
mask_write 0XF8006158 0x000FFFFF 0x00000083
mask_write 0XF800615C 0x000FFFFF 0x00000080
mask_write 0XF8006160 0x000FFFFF 0x00000080
mask_write 0XF8006168 0x001FFFFF 0x00000124
mask_write 0XF800616C 0x001FFFFF 0x00000125
mask_write 0XF8006170 0x001FFFFF 0x00000112
mask_write 0XF8006174 0x001FFFFF 0x00000116
mask_write 0XF800617C 0x000FFFFF 0x000000C3
mask_write 0XF8006180 0x000FFFFF 0x000000C3
mask_write 0XF8006184 0x000FFFFF 0x000000C0
mask_write 0XF8006188 0x000FFFFF 0x000000C0
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
mask_write 0XF8006208 0x000F03FF 0x000803FF
mask_write 0XF800620C 0x000F03FF 0x000803FF
mask_write 0XF8006210 0x000F03FF 0x000803FF
mask_write 0XF8006214 0x000F03FF 0x000803FF
mask_write 0XF8006218 0x000F03FF 0x000003FF
mask_write 0XF800621C 0x000F03FF 0x000003FF
mask_write 0XF8006220 0x000F03FF 0x000003FF
mask_write 0XF8006224 0x000F03FF 0x000003FF
mask_write 0XF80062A8 0x00000FF7 0x00000000
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
mask_write 0XF80062B0 0x003FFFFF 0x00005125
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
mask_poll 0XF8000B74 0x00002000
mask_write 0XF8006000 0x0001FFFF 0x00000081
mask_poll 0XF8006054 0x00000007
}
proc ps7_mio_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B40 0x00000FFF 0x00000600
mask_write 0XF8000B44 0x00000FFF 0x00000600
mask_write 0XF8000B48 0x00000FFF 0x00000672
mask_write 0XF8000B4C 0x00000FFF 0x00000672
mask_write 0XF8000B50 0x00000FFF 0x00000674
mask_write 0XF8000B54 0x00000FFF 0x00000674
mask_write 0XF8000B58 0x00000FFF 0x00000600
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
mask_write 0XF8000B6C 0x000073FF 0x00000209
mask_write 0XF8000B70 0x00000021 0x00000021
mask_write 0XF8000B70 0x00000021 0x00000020
mask_write 0XF8000B70 0x07FFFFFF 0x00000823
mask_write 0XF8000700 0x00003FFF 0x00000600
mask_write 0XF8000704 0x00003FFF 0x00000702
mask_write 0XF8000708 0x00003FFF 0x00000702
mask_write 0XF800070C 0x00003FFF 0x00000702
mask_write 0XF8000710 0x00003FFF 0x00000702
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00000600
mask_write 0XF8000728 0x00003FFF 0x00000600
mask_write 0XF800072C 0x00003FFF 0x00000600
mask_write 0XF8000730 0x00003FFF 0x00000600
mask_write 0XF8000734 0x00003FFF 0x00000600
mask_write 0XF8000738 0x00003FFF 0x00000600
mask_write 0XF800073C 0x00003FFF 0x00000600
mask_write 0XF8000740 0x00003FFF 0x00000302
mask_write 0XF8000744 0x00003FFF 0x00000302
mask_write 0XF8000748 0x00003FFF 0x00000302
mask_write 0XF800074C 0x00003FFF 0x00000302
mask_write 0XF8000750 0x00003FFF 0x00000302
mask_write 0XF8000754 0x00003FFF 0x00000302
mask_write 0XF8000758 0x00003FFF 0x00000303
mask_write 0XF800075C 0x00003FFF 0x00000303
mask_write 0XF8000760 0x00003FFF 0x00000303
mask_write 0XF8000764 0x00003FFF 0x00000303
mask_write 0XF8000768 0x00003FFF 0x00000303
mask_write 0XF800076C 0x00003FFF 0x00000303
mask_write 0XF8000770 0x00003FFF 0x00000304
mask_write 0XF8000774 0x00003FFF 0x00000305
mask_write 0XF8000778 0x00003FFF 0x00000304
mask_write 0XF800077C 0x00003FFF 0x00000305
mask_write 0XF8000780 0x00003FFF 0x00000304
mask_write 0XF8000784 0x00003FFF 0x00000304
mask_write 0XF8000788 0x00003FFF 0x00000304
mask_write 0XF800078C 0x00003FFF 0x00000304
mask_write 0XF8000790 0x00003FFF 0x00000305
mask_write 0XF8000794 0x00003FFF 0x00000304
mask_write 0XF8000798 0x00003FFF 0x00000304
mask_write 0XF800079C 0x00003FFF 0x00000304
mask_write 0XF80007A0 0x00003FFF 0x00000380
mask_write 0XF80007A4 0x00003FFF 0x00000380
mask_write 0XF80007A8 0x00003FFF 0x00000380
mask_write 0XF80007AC 0x00003FFF 0x00000380
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x002F002E
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000B48 0x00000180 0x00000180
mask_write 0XF8000B4C 0x00000180 0x00000180
mask_write 0XF8000B50 0x00000180 0x00000180
mask_write 0XF8000B54 0x00000180 0x00000180
mwr -force 0XF8000004 0x0000767B
mask_write 0XE0001034 0x000000FF 0x00000006
mask_write 0XE0001018 0x0000FFFF 0x0000003E
mask_write 0XE0001000 0x000001FF 0x00000017
mask_write 0XE0001004 0x00000FFF 0x00000020
mask_write 0XE000D000 0x00080000 0x00080000
mask_write 0XF8007000 0x20000000 0x00000000
}
proc ps7_post_config_1_0 {} {
mwr -force 0XF8000008 0x0000DF0D
mask_write 0XF8000900 0x0000000F 0x0000000F
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
mwr -force 0XF8000004 0x0000767B
}
proc ps7_debug_1_0 {} {
mwr -force 0XF8898FB0 0xC5ACCE55
mwr -force 0XF8899FB0 0xC5ACCE55
mwr -force 0XF8809FB0 0xC5ACCE55
}
set PCW_SILICON_VER_1_0 "0x0"
set PCW_SILICON_VER_2_0 "0x1"
set PCW_SILICON_VER_3_0 "0x2"
set APU_FREQ 666666667
proc mask_poll { addr mask } {
set count 1
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval & $mask}]
while { $maskedval == 0 } {
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval & $mask}]
set count [ expr { $count + 1 } ]
if { $count == 100000000 } {
puts "Timeout Reached. Mask poll failed at ADDRESS: $addr MASK: $mask"
break
}
}
}
proc mask_delay { addr val } {
set delay [ get_number_of_cycles_for_delay $val ]
perf_reset_and_start_timer
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval < $delay}]
while { $maskedval == 1 } {
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval < $delay}]
}
perf_reset_clock
}
proc ps_version { } {
set si_ver "0x[string range [mrd 0xF8007080] end-8 end]"
set mask_sil_ver "0x[expr {$si_ver >> 28}]"
return $mask_sil_ver;
}
proc ps7_post_config {} {
ps7_post_config_1_0
}
proc ps7_debug {} {
ps7_debug_1_0
}
proc ps7_init {} {
ps7_mio_init_data_1_0
ps7_pll_init_data_1_0
ps7_clock_init_data_1_0
ps7_ddr_init_data_1_0
ps7_peripherals_init_data_1_0
}
# For delay calculation using global timer
# start timer
proc perf_start_clock { } {
#writing SCU_GLOBAL_TIMER_CONTROL register
mask_write 0xF8F00208 0x00000109 0x00000009
}
# stop timer and reset timer count regs
proc perf_reset_clock { } {
perf_disable_clock
mask_write 0xF8F00200 0xFFFFFFFF 0x00000000
mask_write 0xF8F00204 0xFFFFFFFF 0x00000000
}
# Compute mask for given delay in miliseconds
proc get_number_of_cycles_for_delay { delay } {
# GTC is always clocked at 1/2 of the CPU frequency (CPU_3x2x)
variable APU_FREQ
return [ expr ($delay * $APU_FREQ /(2 * 1000))]
}
# stop timer
proc perf_disable_clock {} {
mask_write 0xF8F00208 0xFFFFFFFF 0x00000000
}
proc perf_reset_and_start_timer {} {
perf_reset_clock
perf_start_clock
}

View File

@ -1,38 +0,0 @@
source ./digilent-hs2.cfg
adapter_khz 1000
set PL_TAPID 0x13722093
set SMP 1
source ./zynq-7000.cfg
reset_config none
set XC7_JSHUTDOWN 0x0d
set XC7_JPROGRAM 0x0b
set XC7_JSTART 0x0c
set XC7_BYPASS 0x3f
proc xc7_program {tap} {
global XC7_JSHUTDOWN XC7_JPROGRAM XC7_JSTART XC7_BYPASS
irscan $tap $XC7_JSHUTDOWN
irscan $tap $XC7_JPROGRAM
runtest 60000
#JSTART prevents this from working...
#irscan $tap $XC7_JSTART
runtest 2000
irscan $tap $XC7_BYPASS
runtest 2000
}
pld device virtex2 zynq.tap 1
init
xc7_program zynq.tap
halt
# Disable MMU
targets $_TARGETNAME_1
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
targets $_TARGETNAME_0
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]

61
openocd/xilinx-tcl.cfg Normal file
View File

@ -0,0 +1,61 @@
#
# TCL to allow the Xilinx PS7 Init TCL code to run in OpenOCD.
#
proc mrd { args } {
if {[llength $args] == 0} {
echo "mrd address \[count \[w|h|b\]\]"
echo " Read <count> memory locations starting at <address>. Defaults to one word."
return
}
set addr [lindex $args 0]
set count 1
set bits 32
if {[llength $args] > 1} {
set count [lindex $args 1]
if {[llength $args] > 2} {
switch [lindex $args 2] {
w { set bits 32 }
h { set bits 16 }
b { set bits 8 }
default { set bits 32 }
}
}
}
mem2array x $bits $addr $count
set nibbles [expr {$bits / 4}]
set bytes [expr {$bits / 8}]
set result {}
foreach {idx elmt} $x {
append result [format "%08x: %0*x\n" [expr {$addr + $idx * $bytes}] $nibbles $elmt]
}
return $result
}
proc mwr { args } {
set addr [lindex $args 1]
set data [lindex $args 2]
mww $addr $data
}
proc mask_write { addr mask value } {
set curval "0x[string range [mrd $addr] end-8 end]"
set maskedval [expr {$curval & ~$mask}]
#echo "curval = [format 0x%08x $curval] maskedval = [format 0x%08x $maskedval]"
set writeval(0) [expr {$maskedval | $value}]
#echo " $addr <= [format 0x%08x $writeval(0)] ([format 0x%08x $curval]: [format 0x%08x $mask]/[format 0x%08x $value])"
array2mem writeval 32 $addr 1
}
proc xilinx_ps7_init { } {
poll off
reset init
reset halt
targets zynq.cpu.0
sleep 100
halt
ps7_debug
ps7_init
ps7_post_config
poll on
}

View File

@ -1,14 +1,14 @@
source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg] source ./digilent_jtag_smt2_nc.cfg
adapter_khz 1000 adapter_khz 10000
set PL_TAPID 0x23731093 set PL_TAPID 0x03731093
set SMP 1 set SMP 1
source ./zynq-7000.cfg source ./zynq-7000.cfg
source ./xilinx-tcl.cfg
source ./ps7_init.tcl
reset_config srst_only srst_open_drain reset_config srst_only srst_push_pull
adapter_nsrst_assert_width 250
adapter_nsrst_delay 400
set XC7_JSHUTDOWN 0x0d set XC7_JSHUTDOWN 0x0d
set XC7_JPROGRAM 0x0b set XC7_JPROGRAM 0x0b
@ -31,10 +31,12 @@ pld device virtex2 zynq.tap 1
init init
xc7_program zynq.tap xc7_program zynq.tap
reset halt xilinx_ps7_init
# Disable MMU # Disable MMU
targets $_TARGETNAME_1 targets $_TARGETNAME_1
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd] arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]
targets $_TARGETNAME_0 targets $_TARGETNAME_0
arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd] arm mcr 15 0 1 0 0 [expr [arm mrc 15 0 1 0 0] & ~0xd]

View File

@ -93,3 +93,119 @@ if { $_SMP } {
-dbgbase 0x80092000 -dbgbase 0x80092000
target smp $_TARGETNAME_0 $_TARGETNAME_1 target smp $_TARGETNAME_0 $_TARGETNAME_1
} }
#
# Hack to get the registers into a stable state when first booting a zynq in
# JTAG mode. If r11 is pointing to an invalid address and you use gdb to set a
# register the write will fail because gdb attempts to scan or unwind the
# current frame and the bad address seems to lock the bus up. This code puts
# the registers into the OCM and hopefull safe.
#
proc zynq_clear_registers { target } {
echo "Zynq-7000 Series setup: $target"
set _OCM_END 0x0003FFF0
mww phys 0xF8007000 0x4E00E07F
reg r0 0
reg r1 0
reg r2 0
reg r3 0
reg r4 0
reg r5 0
reg r6 0
reg r7 0
reg r8 0
reg r9 0
reg r10 0
reg r11 $_OCM_END
reg sp_svc $_OCM_END
reg lr_svc $_OCM_END
reg sp_abt $_OCM_END
reg lr_abt $_OCM_END
reg sp_und $_OCM_END
reg lr_und $_OCM_END
}
proc zynq_disable_mmu_and_caches { target } {
# arm mcr pX op1 CRn CRm op2 value
echo "Disable MMU and caches"
# Invalidate caches
catch {
$target arm mcr 15 0 7 5 0 0
$target arm mcr 15 0 7 7 0 0
# Invalidate all TLBs
$target arm mcr 15 0 8 5 0 0
$target arm mcr 15 0 8 6 0 0
$target arm mcr 15 0 8 7 0 0
$target arm mcr 15 4 8 3 0 0
$target arm mcr 15 4 8 7 0 0
set cp [$target arm mrc 15 0 1 0 0]
echo "SCTRL => [format 0x%x $cp]"
set mask [expr 1 << 29 | 1 << 12 | 1 << 11 | 1 << 2 | 1 << 1 | 1 << 0]
set cp [expr ($cp & ~$mask)]
$target arm mcr 15 0 1 0 0 $cp
echo "SCTRL <= [format 0x%x $cp]"
}
}
proc zynq_boot_ocm_setup { } {
#
# Enable the OCM
#
echo "Zynq Boot OCM setup"
catch {
mww phys 0xF8000008 0xDF0D
mww phys 0xF8000238 0
mww phys 0xF8000910 0xC
}
}
proc zynq_rtems_setup { } {
cache_config l2x 0xF8F02000 8
cortex_a maskisr on
}
proc zynq_restart { wait } {
global _SMP
global _TARGETNAME_0
global _TARGETNAME_1
set target0 $_TARGETNAME_0
set target1 $_TARGETNAME_1
echo "Zynq reset, resetting the board ... "
poll off
#
# Issue the reset via the SLCR
#
catch {
mww phys 0xF8000008 0xDF0D
mww phys 0xF8000200 1
}
echo "Zynq reset waiting for $wait msecs ... "
sleep $wait
#
# Reconnect the DAP etc due to the reset.
#
$target0 cortex_a dbginit
$target0 arm core_state arm
if { $_SMP } {
$target1 arm core_state arm
$target1 cortex_a dbginit
cortex_a smp_off
}
poll on
#
# We can now halt the core.
#
if { $_SMP } {
targets $target1
halt
}
targets $target0
halt
zynq_rtems_setup
}
proc zynq_gdb_attach { target } {
catch {
halt
}
}

6
qemu.gdb Normal file
View File

@ -0,0 +1,6 @@
target remote :1234
# print demangled symbols by default
set print asm-demangle on
load

View File

@ -1,21 +0,0 @@
#!/usr/bin/env bash
set -e
target_host="rpi-4.m-labs.hk"
while getopts "h:i" opt; do
case "$opt" in
\?) exit 0
;;
h) target_host=$OPTARG
;;
esac
done
target_folder=/tmp/zynq-\$USER
ssh $target_host "mkdir -p $target_folder"
rsync openocd/* $target_host:$target_folder
rsync target/armv7-none-eabihf/release/experiments $target_host:$target_folder/experiments.elf
ssh $target_host "cd $target_folder; openocd -f zc706.cfg -c 'load_image experiments.elf; resume 0; exit'"

11
runner.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e -m
ELF=$1
IMAGE=$ELF.bin
arm-none-eabihf-objcopy -O binary $ELF $IMAGE
qemu-system-arm -M xilinx-zynq-a9 -s -kernel $IMAGE -chardev file,id=uart0,path=/tmp/qemu.serial &
sleep 1
gdb -x qemu.gdb $ELF
kill -KILL %1

View File

@ -1,24 +1,27 @@
let let
pkgs = import <nixpkgs> { overlays = [ (import ./nix/mozilla-overlay.nix) ]; }; mozillaOverlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz);
rustPlatform = (import ./nix/rust-platform.nix { inherit pkgs; }); pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
in in
pkgs.stdenv.mkDerivation { with pkgs;
name = "zynq-env"; let
buildInputs = [ project = callPackage ./default.nix {};
rustPlatform.rust.rustc in
rustPlatform.rust.cargo with project;
pkgs.cacert stdenv.mkDerivation {
pkgs.cargo-xbuild name = "adc2tcp-env";
buildInputs = with rustPlatform.rust; [
pkgs.openocd pkgs.gdb rustc cargo
pkgs.openssh pkgs.rsync cargo-xbuild rustcSrc
pkgsCross.armhf-embedded.buildPackages.gcc
(import ./nix/mkbootimage.nix { inherit pkgs; }) #pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc
#pkgsCross.armhf-embedded.buildPackages.binutils
]; ];
XARGO_RUST_SRC = "${rustPlatform.rust.rustc.src}/src"; # Set Environment Variables
RUST_BACKTRACE = 1;
XARGO_RUST_SRC = "${rustcSrc}/src";
shellHook = '' shellHook = ''
echo "Run 'cargo xbuild --release -p ...' to build." echo "Run 'cargo xbuild --release' to build."
''; '';
} }

View File

@ -1,9 +1,10 @@
use libregister::{RegisterR, RegisterRW}; use crate::slcr;
use super::slcr; use crate::regs::RegisterR;
pub use slcr::ArmPllSource;
pub mod source; #[cfg(feature = "target_zc706")]
use source::*; const PS_CLK: u32 = 33_333_333;
#[cfg(feature = "target_cora_z7_10")]
const PS_CLK: u32 = 50_000_000;
enum CpuClockMode { enum CpuClockMode {
/// Clocks run in 4:2:2:1 mode /// Clocks run in 4:2:2:1 mode
@ -14,7 +15,7 @@ enum CpuClockMode {
impl CpuClockMode { impl CpuClockMode {
pub fn get() -> Self { pub fn get() -> Self {
let regs = slcr::RegisterBlock::slcr(); let regs = slcr::RegisterBlock::new();
if regs.clk_621_true.read().clk_621_true() { if regs.clk_621_true.read().clk_621_true() {
CpuClockMode::C621 CpuClockMode::C621
} else { } else {
@ -24,7 +25,7 @@ impl CpuClockMode {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Clocks { pub struct CpuClocks {
/// ARM PLL: Recommended clock source for the CPUs and the interconnect /// ARM PLL: Recommended clock source for the CPUs and the interconnect
pub arm: u32, pub arm: u32,
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces /// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
@ -33,38 +34,22 @@ pub struct Clocks {
pub io: u32, pub io: u32,
} }
impl Clocks { impl CpuClocks {
pub fn get() -> Self { pub fn get() -> Self {
Clocks { let regs = slcr::RegisterBlock::new();
arm: ArmPll::freq(), let arm = u32::from(regs.arm_pll_ctrl.read().pll_fdiv()) * PS_CLK;
ddr: DdrPll::freq(), let ddr = u32::from(regs.ddr_pll_ctrl.read().pll_fdiv()) * PS_CLK;
io: IoPll::freq(), let io = u32::from(regs.io_pll_ctrl.read().pll_fdiv()) * PS_CLK;
} CpuClocks { arm, ddr, io }
}
pub fn set_cpu_freq(target_freq: u32) {
let arm_pll = ArmPll::freq();
// 1 and 3 cannot be used
let mut div = 2u8;
while div == 3 || (div < 63 && arm_pll / u32::from(div) > target_freq) {
div += 1;
}
slcr::RegisterBlock::unlocked(|slcr| {
slcr.arm_clk_ctrl.modify(|_, w| w
.srcsel(ArmPllSource::ArmPll)
.divisor(div)
);
})
} }
pub fn cpu_6x4x(&self) -> u32 { pub fn cpu_6x4x(&self) -> u32 {
let slcr = slcr::RegisterBlock::slcr(); let regs = slcr::RegisterBlock::new();
let arm_clk_ctrl = slcr.arm_clk_ctrl.read(); let arm_clk_ctrl = regs.arm_clk_ctrl.read();
let pll = match arm_clk_ctrl.srcsel() { let pll = match arm_clk_ctrl.srcsel() {
ArmPllSource::ArmPll => self.arm, slcr::ArmPllSource::ArmPll => self.arm,
ArmPllSource::DdrPll => self.ddr, slcr::ArmPllSource::DdrPll => self.ddr,
ArmPllSource::IoPll => self.io, slcr::ArmPllSource::IoPll => self.io,
}; };
pll / u32::from(arm_clk_ctrl.divisor()) pll / u32::from(arm_clk_ctrl.divisor())
} }
@ -92,7 +77,7 @@ impl Clocks {
} }
pub fn uart_ref_clk(&self) -> u32 { pub fn uart_ref_clk(&self) -> u32 {
let regs = slcr::RegisterBlock::slcr(); let regs = slcr::RegisterBlock::new();
let uart_clk_ctrl = regs.uart_clk_ctrl.read(); let uart_clk_ctrl = regs.uart_clk_ctrl.read();
let pll = match uart_clk_ctrl.srcsel() { let pll = match uart_clk_ctrl.srcsel() {
slcr::PllSource::ArmPll => slcr::PllSource::ArmPll =>
@ -104,18 +89,4 @@ impl Clocks {
}; };
pll / u32::from(uart_clk_ctrl.divisor()) pll / u32::from(uart_clk_ctrl.divisor())
} }
pub fn sdio_ref_clk(&self) -> u32 {
let regs = slcr::RegisterBlock::slcr();
let sdio_clk_ctrl = regs.sdio_clk_ctrl.read();
let pll = match sdio_clk_ctrl.srcsel() {
slcr::PllSource::ArmPll =>
self.arm,
slcr::PllSource::DdrPll =>
self.ddr,
slcr::PllSource::IoPll =>
self.io,
};
pll / u32::from(sdio_clk_ctrl.divisor())
}
} }

36
src/cortex_a9/asm.rs Normal file
View File

@ -0,0 +1,36 @@
/// The classic no-op
#[inline]
pub fn nop() {
unsafe { asm!("nop" :::: "volatile") }
}
/// Wait For Event
#[inline]
pub fn wfe() {
unsafe { asm!("wfe" :::: "volatile") }
}
/// Send Event
#[inline]
pub fn sev() {
unsafe { asm!("sev" :::: "volatile") }
}
/// Data Memory Barrier
#[inline]
pub fn dmb() {
unsafe { asm!("dmb" :::: "volatile") }
}
/// Data Synchronization Barrier
#[inline]
pub fn dsb() {
unsafe { asm!("dsb" :::: "volatile") }
}
/// Instruction Synchronization Barrier
#[inline]
pub fn isb() {
unsafe { asm!("isb" :::: "volatile") }
}

View File

@ -1,6 +1,6 @@
use bit_field::BitField; use bit_field::BitField;
use super::{regs::*, asm::*, cache::*}; use super::{regs::*, asm};
use libregister::RegisterW; use crate::regs::RegisterW;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
#[repr(u8)] #[repr(u8)]
@ -44,12 +44,6 @@ pub enum AccessPermissions {
} }
impl AccessPermissions { impl AccessPermissions {
fn new(ap: u8, apx: bool) -> Self {
unsafe {
core::mem::transmute(if apx { 0b100 } else { 0 } | ap)
}
}
fn ap(&self) -> u8 { fn ap(&self) -> u8 {
(*self as u8) & 0b11 (*self as u8) & 0b11
} }
@ -71,64 +65,45 @@ pub struct L1Section {
pub bufferable: bool, pub bufferable: bool,
} }
const ENTRY_TYPE_SECTION: u32 = 0b10;
pub const L1_PAGE_SIZE: usize = 0x100000;
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct L1Entry(u32); pub struct L1Entry(u32);
impl L1Entry { impl L1Entry {
#[inline(always)] #[inline(always)]
pub fn from_section(phys_base: u32, section: L1Section) -> Self { pub fn section(phys_base: u32, section: L1Section) -> Self {
// Must be aligned to 1 MB // Must be aligned to 1 MB
assert!(phys_base & 0x000f_ffff == 0); assert!(phys_base & 0x000f_ffff == 0);
let mut entry = L1Entry(phys_base); let mut entry = L1Entry(phys_base);
entry.set_section(section); entry.0.set_bits(0..=1, 0b10);
entry entry.0.set_bit(2, section.bufferable);
} entry.0.set_bit(3, section.cacheable);
entry.0.set_bit(4, !section.exec);
pub fn get_section(&mut self) -> L1Section {
assert_eq!(self.0.get_bits(0..=1), ENTRY_TYPE_SECTION);
let access = AccessPermissions::new(
self.0.get_bits(10..=11) as u8,
self.0.get_bit(15)
);
L1Section {
global: !self.0.get_bit(17),
shareable: self.0.get_bit(16),
access,
tex: self.0.get_bits(12..=14) as u8,
domain: self.0.get_bits(5..=8) as u8,
exec: !self.0.get_bit(4),
cacheable: self.0.get_bit(3),
bufferable: self.0.get_bit(2),
}
}
pub fn set_section(&mut self, section: L1Section) {
self.0.set_bits(0..=1, ENTRY_TYPE_SECTION);
self.0.set_bit(2, section.bufferable);
self.0.set_bit(3, section.cacheable);
self.0.set_bit(4, !section.exec);
assert!(section.domain < 16); assert!(section.domain < 16);
self.0.set_bits(5..=8, section.domain.into()); entry.0.set_bits(5..=8, section.domain.into());
self.0.set_bits(10..=11, section.access.ap().into()); entry.0.set_bits(10..=11, section.access.ap().into());
assert!(section.tex < 8); assert!(section.tex < 8);
self.0.set_bits(12..=14, section.tex.into()); entry.0.set_bits(12..=14, section.tex.into());
self.0.set_bit(15, section.access.apx()); entry.0.set_bit(15, section.access.apx());
self.0.set_bit(16, section.shareable); entry.0.set_bit(16, section.shareable);
self.0.set_bit(17, !section.global); entry.0.set_bit(17, !section.global);
entry
} }
} }
const L1_TABLE_SIZE: usize = 4096; const L1_TABLE_SIZE: usize = 4096;
static mut L1_TABLE: L1Table = L1Table { #[doc(hidden)]
#[link_section = ".bss.l1_table"]
#[no_mangle]
pub static mut l1_table: L1Table = L1Table {
table: [L1Entry(0); L1_TABLE_SIZE] table: [L1Entry(0); L1_TABLE_SIZE]
}; };
#[repr(C, align(16384))] /// The `#[repr(align(16384))]` is unfortunately ineffective. Hence we
/// require explicit linking to a region defined in the linker script.
#[repr(align(16384))]
pub struct L1Table { pub struct L1Table {
table: [L1Entry; L1_TABLE_SIZE] table: [L1Entry; L1_TABLE_SIZE]
} }
@ -136,7 +111,7 @@ pub struct L1Table {
impl L1Table { impl L1Table {
pub fn get() -> &'static mut Self { pub fn get() -> &'static mut Self {
unsafe { unsafe {
&mut L1_TABLE &mut l1_table
} }
} }
@ -149,22 +124,37 @@ impl L1Table {
tex: 0b101, tex: 0b101,
domain: 0b1111, domain: 0b1111,
exec: true, exec: true,
cacheable: true, // TODO: temporarily turn on cache for SMP testing;
// consider turning it off again for production
cacheable: !false,
bufferable: true, bufferable: true,
}); });
/* (DDR cacheable) */ /* (DDR cacheable) */
for ddr in 1..=0x3ff { for ddr in 1..=0x1ff {
self.direct_mapped_section(ddr, L1Section { self.direct_mapped_section(ddr, L1Section {
global: true, global: true,
shareable: true, shareable: true,
access: AccessPermissions::FullAccess, access: AccessPermissions::FullAccess,
tex: 0b0, tex: 0b101,
domain: 0b1111, domain: 0b1111,
exec: true, exec: true,
cacheable: true, cacheable: true,
bufferable: true, bufferable: true,
}); });
} }
/* (unassigned/reserved). */
for undef in 0x1ff..=0x3ff {
self.direct_mapped_section(undef, L1Section {
global: false,
shareable: false,
access: AccessPermissions::PermissionFault,
tex: 0,
domain: 0,
exec: false,
cacheable: false,
bufferable: false,
});
}
/* 0x40000000 - 0x7fffffff (FPGA slave0) */ /* 0x40000000 - 0x7fffffff (FPGA slave0) */
for fpga_slave in 0x400..=0x7ff { for fpga_slave in 0x400..=0x7ff {
self.direct_mapped_section(fpga_slave, L1Section { self.direct_mapped_section(fpga_slave, L1Section {
@ -213,7 +203,7 @@ impl L1Table {
access: AccessPermissions::FullAccess, access: AccessPermissions::FullAccess,
tex: 0, tex: 0,
domain: 0, domain: 0,
exec: false, exec: true,
cacheable: false, cacheable: false,
bufferable: true, bufferable: true,
}); });
@ -338,7 +328,7 @@ impl L1Table {
/* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */ /* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */
self.direct_mapped_section(0xfff, L1Section { self.direct_mapped_section(0xfff, L1Section {
global: true, global: true,
shareable: true, shareable: false,
access: AccessPermissions::FullAccess, access: AccessPermissions::FullAccess,
tex: 0b100, tex: 0b100,
domain: 0, domain: 0,
@ -355,34 +345,7 @@ impl L1Table {
assert!(index < L1_TABLE_SIZE); assert!(index < L1_TABLE_SIZE);
let base = (index as u32) << 20; let base = (index as u32) << 20;
self.table[index] = L1Entry::from_section(base, section); self.table[index] = L1Entry::section(base, section);
}
pub fn update<T, F, R>(&mut self, ptr: *const T, f: F) -> R
where
F: FnOnce(&'_ mut L1Section) -> R,
{
let index = (ptr as usize) >> 20;
let entry = &mut self.table[index];
let mut section = entry.get_section();
let result = f(&mut section);
entry.set_section(section);
// Flush L1Dcache
dcciall();
// // TODO: L2?
// Invalidate TLB
tlbiall();
// Invalidate all branch predictors
bpiall();
// ensure completion of the BP and TLB invalidation
dsb();
// synchronize context on this processor
isb();
result
} }
} }
@ -410,15 +373,14 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
.a(false) .a(false)
.c(true) .c(true)
.i(true) .i(true)
.z(true)
.unaligned(true) .unaligned(true)
); );
// Synchronization barriers // Synchronization barriers
// Allows MMU to start // Allows MMU to start
dsb(); asm::dsb();
// Flushes pre-fetch buffer // Flushes pre-fetch buffer
isb(); asm::isb();
f(); f();
} }

5
src/cortex_a9/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod asm;
pub mod regs;
pub mod mmu;
global_asm!(include_str!("exceptions.s"));

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