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
99 changed files with 1393 additions and 28247 deletions

View File

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

1
.gitignore vendored
View File

@ -1 +0,0 @@
/target

160
Cargo.lock generated
View File

@ -7,152 +7,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "compiler_builtins"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "embedded-hal"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nb 0.1.2 (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.3 (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.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libasync"
version = "0.0.0"
dependencies = [
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libcortex_a9 0.0.0",
"nb 0.1.2 (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.0 (registry+https://github.com/rust-lang/crates.io-index)",
"embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"nb 0.1.2 (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 = "libcortex_a9"
version = "0.0.0"
dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libregister 0.0.0",
]
[[package]]
name = "libregister"
version = "0.0.0"
dependencies = [
"bit_field 0.10.0 (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.27 (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.4 (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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.8"
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)",
]
version = "0.1.19"
source = "git+https://github.com/rust-lang-nursery/compiler-builtins#36da64f20e96206ac279f700586817c8abe3bdf8"
[[package]]
name = "managed"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nb"
version = "0.1.2"
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"
[[package]]
name = "r0"
version = "1.0.0"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smoltcp"
version = "0.6.0"
version = "0.5.0"
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)",
"bitflags 1.1.0 (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.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "vcell"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -160,23 +50,27 @@ name = "volatile-register"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]
"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 byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum compiler_builtins 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "38f18416546abfbf8d801c555a0e99524453e7214f9cc9107ad49de3d5948ccc"
"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
"checksum linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum compiler_builtins 0.1.19 (git+https://github.com/rust-lang-nursery/compiler-builtins)" = "<none>"
"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
"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 r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fef582369edb298c6c41319a544ca9c4e83622f226055ccfcb35974fbb55ed34"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

View File

@ -1,10 +1,8 @@
[workspace]
members = [
"libregister", "libcortex_a9",
"libboard_zynq", "libsupport_zynq",
"libasync",
"experiments",
]
[package]
name = "zc706"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
edition = "2018"
[profile.dev]
panic = "abort"
@ -13,7 +11,15 @@ lto = false
[profile.release]
panic = "abort"
debug = true
# Link-Time Optimization:
# turn off if you get unusable debug symbols.
lto = true
opt-level = 'z' # Optimize for size.
[features]
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,11 +1,9 @@
# Build
```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
## Using the Xilinx toolchain
@ -41,7 +39,7 @@ Proceed using gdb with `load`, `c`
### Running on the ZC706
```shell
nix-shell --command "cargo xbuild --release -p experiments"
nix-shell --command "cargo xbuild --release"
cd openocd
openocd -f zc706.cfg
```
@ -49,54 +47,7 @@ openocd -f zc706.cfg
### Running on the Cora Z7-10
```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
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"
```
### Development Process
Clone this repo onto your development/build machine and the raspberry pi that controls the Xilinx 7000 board
On the dev machine, the below script builds zc706 and secure copies it to the target pi (in your pi $HOME directory):
```shell
cd ~/zynq-rs
./build.sh $your_user_or_ssh_id
```
On the pi, we need an information rich environment that includes a relatively reliable `gdb` experience (that includes `ctrl-p` and `ctrl-n` command history that persists across `cgdb` executions), run:
```shell
ssh pi4
cd zynq-rs
# For ZC706, run:
./tmux.sh 0
# For Cora Z7, run:
./tmux.sh
```
Time to run your code with:
```shell
zynq-connect
zynq-restart
c
```
or, for a more succinct experience, (identical to above)
```shell
dc
dr
c
```
After every build on your dev machine, simply run:
```shell
dr
c
```
Sometimes you might need to type `load` after `dr`.

View File

@ -12,7 +12,7 @@ fn main() {
.unwrap();
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.
println!("cargo:rerun-if-changed=link.x");
}

View File

@ -1 +0,0 @@
nix-shell --command "cargo xbuild --release -p experiments" && scp -C target/armv7-none-eabihf/release/experiments $1@rpi-4.m-labs.hk:/home/$1/zc706/zc706.elf

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +1,27 @@
{ # Use master branch of the overlay by default
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
rustManifest ? ./channel-rust-nightly.toml,
}:
let
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
rustcSrc = pkgs.fetchgit {
in
with pkgs;
let
rustcSrc = fetchgit {
url = https://github.com/rust-lang/rust.git;
# master of 2020-04-25
rev = "14b15521c52549ebbb113173b4abecd124b5a823";
sha256 = "0a6bi8g636cajpdrpcfkpza95b7ss7041m9cs6hxcd7h8bf6xhwi";
# master of 2019-08-18
rev = "ea52be482ab4945fda63cb65b6a198309a041e3c";
sha256 = "1spifrkvyyrh1gazqrby29fjqsdbwvajv9k9f6mk2ldrdghlsd21";
fetchSubmodules = true;
};
targets = [];
rustChannelOfTargets = _channel: _date: targets:
(pkgs.lib.rustLib.fromManifestFile rustManifest {
inherit (pkgs) stdenv fetchurl patchelf;
}).rust.override { inherit targets; };
targets = [
];
rust =
rustChannelOfTargets "nightly" null targets;
rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
rustPlatform = recurseIntoAttrs (makeRustPlatform {
rustc = rust // { src = rustcSrc; };
cargo = rust;
});
gcc = pkgs.pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc;
xbuildRustPackage = { cargoFeatures, crateSubdir, ... } @ attrs:
let
buildPkg = rustPlatform.buildRustPackage attrs;
in
buildPkg.overrideAttrs ({ name, nativeBuildInputs, ... }: {
nativeBuildInputs =
nativeBuildInputs ++ [ pkgs.cargo-xbuild ];
buildPhase = ''
pushd ${crateSubdir}
cargo xbuild --release --frozen \
--no-default-features \
--features=${cargoFeatures}
popd
'';
XARGO_RUST_SRC = "${rustcSrc}/src";
installPhase = ''
mkdir $out
ls -la target/armv7-none-eabihf/release/
cp target/armv7-none-eabihf/release/${name} $out/${name}.elf
'';
});
xbuildCrate = name: crate: features: xbuildRustPackage rec {
name = "${crate}";
src = ./.;
crateSubdir = crate;
cargoSha256 = "1c1qpg9by8bg93yhgllb5xs155g27qmh99pbrb681wazm8k7nwim";
cargoFeatures = features;
doCheck = false;
dontFixup = true;
};
in {
inherit pkgs rustPlatform rustcSrc gcc;
zc706 = {
experiments-zc706 = xbuildCrate "experiments-zc706" "experiments" "target_zc706";
experiments-cora = xbuildCrate "experiments-cora" "experiments" "target_cora_z7_10";
};
inherit pkgs rustPlatform rustcSrc;
}

View File

@ -1,20 +0,0 @@
[package]
name = "experiments"
description = "Developing bare-metal Rust on Zynq"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
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"]
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,347 +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,
print, println, stdio,
mpcore,
gic,
smoltcp::{
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
time::Instant,
wire::{EthernetAddress, IpAddress, IpCidr},
},
time::Milliseconds,
};
use libcortex_a9::{
mutex::Mutex,
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::new();
let mut gic = gic::InterruptController::new(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::new(mpcore::RegisterBlock::new());
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);
println!("\nzc706 main");
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
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::new()
.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;
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();
}
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 mut flash = zynq::flash::Flash::new(200_000_000).linear_addressing_mode();
let flash_ram: &[u8] = unsafe { core::slice::from_raw_parts(flash.ptr(), flash.size()) };
for i in 0..=1 {
print!("Flash {}:", i);
for b in &flash_ram[(i * 16 * 1024 * 1024)..][..128] {
print!(" {:02X}", *b);
}
println!("");
}
let _flash = flash.stop();
let timer = libboard_zynq::timer::GlobalTimer::start();
let mut ddr = zynq::ddr::DdrRam::new();
#[cfg(not(feature = "target_zc706"))]
ddr.memtest();
ram::init_alloc_ddr(&mut ddr);
#[cfg(dev)]
for i in 0..=1 {
let mut flash_io = flash.manual_mode(i);
// println!("rdcr={:02X}", flash_io.rdcr());
print!("Flash {} ID:", i);
for b in flash_io.rdid() {
print!(" {:02X}", b);
}
println!("");
print!("Flash {} I/O:", i);
for o in 0..8 {
const CHUNK: u32 = 8;
for b in flash_io.read(CHUNK * o, CHUNK as usize) {
print!(" {:02X}", b);
}
}
println!("");
flash_io.dump("Read cr1", 0x35);
flash_io.dump("Read Autoboot", 0x14);
flash_io.dump("Read Bank", 0x16);
flash_io.dump("DLP Bank", 0x16);
flash_io.dump("Read ESig", 0xAB);
flash_io.dump("OTP Read", 0x4B);
flash_io.dump("DYB Read", 0xE0);
flash_io.dump("PPB Read", 0xE2);
flash_io.dump("ASP Read", 0x2B);
flash_io.dump("Password Read", 0xE7);
flash_io.write_enabled(|flash_io| {
flash_io.erase(0);
});
flash_io.write_enabled(|flash_io| {
flash_io.program(0, [0x23054223; 0x100 >> 2].iter().cloned());
});
flash = flash_io.stop();
}
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::i2c();
i2c.init();
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]);
eeprom.write(0x08, &eeprom_buffer[6..22]);
println!("Data written to EEPROM");
let mut eeprom_buffer = [0u8; 24];
// Read from 0x00
eeprom.read(0x00, &mut eeprom_buffer);
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);
print!("Data read from EEPROM @ 0x08: (hex) ");
for i in 0..16 {
print!("{:02x} ", eeprom_buffer[i]);
}
println!("");
}
let eth = zynq::eth::Eth::default(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::new(mpcore::RegisterBlock::new());
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 = ["Astro <astro@spaceboyz.net>"]
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, collections::VecDeque as Deque};
//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<Deque<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(Deque::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 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");
}
// println!("tasks: {}", self.tasks.borrow().len());
// advance other tasks
let next_task = self.tasks.borrow_mut().pop_front();
if let Some(mut task) = next_task {
// 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_back(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_back(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,87 +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) {
Self::instance().wakers.borrow_mut()
.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,25 +0,0 @@
[package]
name = "libboard_zynq"
description = "Drivers for peripherals in the Zynq PS"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
edition = "2018"
[features]
target_zc706 = []
target_cora_z7_10 = []
[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", "proto-ipv6", "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,172 +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;
/// (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::new();
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,358 +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;
/// MT41K256M16HA-125
const DCI_FREQ: u32 = 10_000_000;
pub struct DdrRam {
regs: &'static mut regs::RegisterBlock,
}
impl DdrRam {
pub fn new() -> Self {
let clocks = Self::clock_setup();
Self::calibrate_iob_impedance(&clocks);
Self::configure_iob();
let regs = unsafe { regs::RegisterBlock::new() };
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);
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);
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)
);
});
}
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;
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;
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,237 +0,0 @@
use volatile_register::{RO, RW};
use libregister::{register, 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>,
}
impl RegisterBlock {
pub unsafe fn new() -> &'static mut Self {
&mut *(0xF8006000 as *mut _)
}
}
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,41 +0,0 @@
pub trait BytesTransferExt: Sized {
// Turn u32 into u8
fn bytes_transfer(self) -> BytesTransfer<Self>
where
Self: Iterator<Item = u32>;
}
impl<I: Iterator<Item = u32>> BytesTransferExt for I {
// Turn u32 into u8
fn bytes_transfer(self) -> BytesTransfer<Self> {
BytesTransfer {
iter: self,
shift: 0,
word: 0,
}
}
}
pub struct BytesTransfer<I: Iterator<Item = u32> + Sized> {
iter: I,
shift: u8,
word: u32,
}
impl<I: Iterator<Item = u32> + Sized> Iterator for BytesTransfer<I> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
if self.shift > 0 {
self.shift -= 8;
Some((self.word >> self.shift) as u8)
} else {
self.iter.next()
.and_then(|word| {
self.shift = 32;
self.word = word;
self.next()
})
}
}
}

View File

@ -1,503 +0,0 @@
//! Quad-SPI Flash Controller
use core::marker::PhantomData;
use log::{error, info, warn};
use libregister::{RegisterR, RegisterW, RegisterRW};
use crate::{print, println};
use super::slcr;
use super::clocks::source::{IoPll, ClockSource};
mod regs;
mod bytes;
pub use bytes::{BytesTransferExt, BytesTransfer};
mod spi_flash_register;
use spi_flash_register::*;
mod transfer;
use transfer::Transfer;
const FLASH_BAUD_RATE: u32 = 50_000_000;
/// 16 MB
pub const SINGLE_CAPACITY: u32 = 0x1000000;
pub const SECTOR_SIZE: u32 = 0x10000;
pub const PAGE_SIZE: u32 = 0x100;
/// Instruction: Read Identification
const INST_RDID: u8 = 0x9F;
/// Instruction: Read
const INST_READ: u8 = 0x03;
/// Instruction: Quad I/O Fast Read
const INST_4IO_FAST_READ: u8 = 0xEB;
/// Instruction: Write Disable
const INST_WRDI: u8 = 0x04;
/// Instruction: Write Enable
const INST_WREN: u8 = 0x06;
/// Instruction: Program page
const INST_PP: u8 = 0x02;
/// Instruction: Erase 4K Block
const INST_BE_4K: u8 = 0x20;
#[derive(Clone)]
pub enum SpiWord {
W8(u8),
W16(u16),
W24(u32),
W32(u32),
}
impl From<u8> for SpiWord {
fn from(x: u8) -> Self {
SpiWord::W8(x)
}
}
impl From<u16> for SpiWord {
fn from(x: u16) -> Self {
SpiWord::W16(x)
}
}
impl From<u32> for SpiWord {
fn from(x: u32) -> Self {
SpiWord::W32(x)
}
}
/// Memory-mapped mode
pub struct LinearAddressing;
/// Manual I/O mode
pub struct Manual;
/// Flash Interface Driver
///
/// For 2x Spansion S25FL128SAGMFIR01
pub struct Flash<MODE> {
regs: &'static mut regs::RegisterBlock,
_mode: PhantomData<MODE>,
}
impl<MODE> Flash<MODE> {
fn transition<TO>(self) -> Flash<TO> {
Flash {
regs: self.regs,
_mode: PhantomData,
}
}
fn disable_interrupts(&mut self) {
self.regs.intr_dis.write(
regs::IntrDis::zeroed()
.rx_overflow(true)
.tx_fifo_not_full(true)
.tx_fifo_full(true)
.rx_fifo_not_empty(true)
.rx_fifo_full(true)
.tx_fifo_underflow(true)
);
}
fn clear_rx_fifo(&self) {
while self.regs.intr_status.read().rx_fifo_not_empty() {
let _ = self.regs.rx_data.read();
}
}
fn clear_interrupt_status(&mut self) {
self.regs.intr_status.write(
regs::IntrStatus::zeroed()
.rx_overflow(true)
.tx_fifo_underflow(true)
);
}
fn wait_tx_fifo_flush(&mut self) {
self.regs.config.modify(|_, w| w.man_start_com(true));
while !self.regs.intr_status.read().tx_fifo_not_full() {}
}
}
impl Flash<()> {
pub fn new(clock: u32) -> Self {
Self::enable_clocks(clock);
Self::setup_signals();
Self::reset();
let regs = regs::RegisterBlock::qspi();
let mut flash = Flash { regs, _mode: PhantomData };
flash.configure((FLASH_BAUD_RATE - 1 + clock) / FLASH_BAUD_RATE);
flash
}
/// typical: `200_000_000` Hz
fn enable_clocks(clock: u32) {
let io_pll = IoPll::freq();
let divisor = ((clock - 1 + io_pll) / clock)
.max(1).min(63) as u8;
slcr::RegisterBlock::unlocked(|slcr| {
slcr.lqspi_clk_ctrl.write(
slcr::LqspiClkCtrl::zeroed()
.src_sel(slcr::PllSource::IoPll)
.divisor(divisor)
.clkact(true)
);
});
}
fn setup_signals() {
slcr::RegisterBlock::unlocked(|slcr| {
// 1. Configure MIO pin 1 for chip select 0 output.
slcr.mio_pin_01.write(
slcr::MioPin01::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// Configure MIO pins 2 through 5 for I/O.
slcr.mio_pin_02.write(
slcr::MioPin02::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
slcr.mio_pin_03.write(
slcr::MioPin03::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
slcr.mio_pin_04.write(
slcr::MioPin04::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
slcr.mio_pin_05.write(
slcr::MioPin05::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
// 3. Configure MIO pin 6 for serial clock 0 output.
slcr.mio_pin_06.write(
slcr::MioPin06::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
// Option: Add Second Device Chip Select
// 4. Configure MIO pin 0 for chip select 1 output.
slcr.mio_pin_00.write(
slcr::MioPin00::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
);
// Option: Add Second Serial Clock
// 5. Configure MIO pin 9 for serial clock 1 output.
slcr.mio_pin_09.write(
slcr::MioPin09::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// Option: Add 4-bit Data
// 6. Configure MIO pins 10 through 13 for I/O.
slcr.mio_pin_10.write(
slcr::MioPin10::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
slcr.mio_pin_11.write(
slcr::MioPin11::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
slcr.mio_pin_12.write(
slcr::MioPin12::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
slcr.mio_pin_13.write(
slcr::MioPin13::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
// Option: Add Feedback Output Clock
// 7. Configure MIO pin 8 for feedback clock.
slcr.mio_pin_08.write(
slcr::MioPin08::zeroed()
.l0_sel(true)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
);
});
}
fn reset() {
slcr::RegisterBlock::unlocked(|slcr| {
slcr.lqspi_rst_ctrl.write(
slcr::LqspiRstCtrl::zeroed()
.ref_rst(true)
.cpu1x_rst(true)
);
slcr.lqspi_rst_ctrl.write(
slcr::LqspiRstCtrl::zeroed()
);
});
}
fn configure(&mut self, divider: u32) {
// Disable
self.regs.enable.write(
regs::Enable::zeroed()
);
self.disable_interrupts();
self.regs.lqspi_cfg.write(
regs::LqspiCfg::zeroed()
);
self.clear_rx_fifo();
self.clear_interrupt_status();
// for a baud_rate_div=1 LPBK_DLY_ADJ would be required
let mut baud_rate_div = 2u32;
while baud_rate_div < 7 && 2u32.pow(1 + baud_rate_div) < divider {
baud_rate_div += 1;
}
self.regs.config.write(regs::Config::zeroed()
.baud_rate_div(baud_rate_div as u8)
.mode_sel(true)
.leg_flsh(true)
.holdb_dr(true)
// 32 bits TX FIFO width
.fifo_width(0b11)
);
// Initialize RX/TX pipes thresholds
unsafe {
self.regs.rx_thres.write(1);
self.regs.tx_thres.write(1);
}
}
pub fn linear_addressing_mode(self) -> Flash<LinearAddressing> {
// Set manual start enable to auto mode.
// Assert the chip select.
self.regs.config.modify(|_, w| w
.man_start_en(false)
.pcs(false)
.manual_cs(false)
);
self.regs.lqspi_cfg.write(regs::LqspiCfg::zeroed()
// Quad I/O Fast Read
.inst_code(INST_4IO_FAST_READ)
.dummy_mask(0x2)
.mode_en(false)
.mode_bits(0xFF)
// 2 devices
.two_mem(true)
.u_page(false)
// Quad SPI mode
.lq_mode(true)
);
self.regs.enable.write(
regs::Enable::zeroed()
.spi_en(true)
);
self.transition()
}
pub fn manual_mode(self, chip_index: usize) -> Flash<Manual> {
self.regs.config.modify(|_, w| w
.man_start_en(true)
.manual_cs(true)
.endian(true)
);
self.regs.lqspi_cfg.write(regs::LqspiCfg::zeroed()
// Quad I/O Fast Read
.inst_code(INST_READ)
.dummy_mask(0x2)
.mode_en(false)
.mode_bits(0xFF)
// 2 devices
.two_mem(true)
.u_page(chip_index != 0)
// Quad SPI mode
.lq_mode(false)
);
self.transition()
}
}
impl Flash<LinearAddressing> {
/// Stop linear addressing mode
pub fn stop(self) -> Flash<()> {
self.regs.enable.modify(|_, w| w.spi_en(false));
// De-assert chip select.
self.regs.config.modify(|_, w| w.pcs(true));
self.transition()
}
pub fn ptr<T>(&mut self) -> *mut T {
0xFC00_0000 as *mut _
}
pub fn size(&self) -> usize {
2 * (SINGLE_CAPACITY as usize)
}
}
impl Flash<Manual> {
pub fn stop(self) -> Flash<()> {
self.transition()
}
pub fn read_reg<R: SpiFlashRegister>(&mut self) -> R {
let args = Some(R::inst_code());
let transfer = self.transfer(args.into_iter(), 2)
.bytes_transfer();
R::new(transfer.skip(1).next().unwrap())
}
pub fn read_reg_until<R, F, A>(&mut self, f: F) -> A
where
R: SpiFlashRegister,
F: Fn(R) -> Option<A>,
{
let mut result = None;
while result.is_none() {
let args = Some(R::inst_code());
for b in self.transfer(args.into_iter(), 32)
.bytes_transfer().skip(1) {
result = f(R::new(b));
if result.is_none() {
break;
}
}
}
result.unwrap()
}
/// Status Register-1 remains `0x00` immediately after invoking a command.
fn wait_while_sr1_zeroed(&mut self) -> SR1 {
self.read_reg_until::<SR1, _, SR1>(|sr1|
if sr1.is_zeroed() {
None
} else {
Some(sr1)
}
)
}
/// Read Identification
pub fn rdid(&mut self) -> core::iter::Skip<BytesTransfer<Transfer<core::option::IntoIter<u32>, u32>>> {
let args = Some((INST_RDID as u32) << 24);
self.transfer(args.into_iter(), 0x44)
.bytes_transfer().skip(1)
}
/// Read flash data
pub fn read(&mut self, offset: u32, len: usize
) -> core::iter::Take<core::iter::Skip<BytesTransfer<Transfer<core::option::IntoIter<u32>, u32>>>>
{
let args = Some(((INST_READ as u32) << 24) | (offset as u32));
self.transfer(args.into_iter(), len + 6)
.bytes_transfer().skip(6).take(len)
}
pub fn erase(&mut self, offset: u32) {
let args = Some(((INST_BE_4K as u32) << 24) | (offset as u32));
self.transfer(args.into_iter(), 4);
let sr1 = self.wait_while_sr1_zeroed();
if sr1.e_err() {
error!("E_ERR");
} else if sr1.p_err() {
error!("P_ERR");
} else if sr1.wip() {
info!("Erase in progress");
while self.read_reg::<SR1>().wip() {
print!(".");
}
println!("");
} else {
warn!("erased? sr1={:02X}", sr1.inner);
}
}
pub fn program<I: Iterator<Item=u32>>(&mut self, offset: u32, data: I) {
{
let len = 4 + 4 * data.size_hint().0;
let args = Some(SpiWord::W32(((INST_PP as u32) << 24) | (offset as u32))).into_iter()
.chain(data.map(SpiWord::W32));
self.transfer(args, len);
}
// let sr1 = self.wait_while_sr1_zeroed();
let sr1 = self.read_reg::<SR1>();
if sr1.e_err() {
error!("E_ERR");
} else if sr1.p_err() {
error!("P_ERR");
} else if sr1.wip() {
info!("Program in progress");
while self.read_reg::<SR1>().wip() {
print!(".");
}
println!("");
} else {
warn!("programmed? sr1={:02X}", sr1.inner);
}
}
pub fn write_enabled<F: Fn(&mut Self) -> R, R>(&mut self, f: F) -> R {
// Write Enable
let args = Some(INST_WREN);
self.transfer(args.into_iter(), 1);
self.regs.gpio.modify(|_, w| w.wp_n(true));
let sr1 = self.wait_while_sr1_zeroed();
if !sr1.wel() {
panic!("Cannot write-enable flash");
}
let result = f(self);
// Write Disable
let args = Some(INST_WRDI);
self.transfer(args.into_iter(), 1);
self.regs.gpio.modify(|_, w| w.wp_n(false));
result
}
pub fn transfer<'s: 't, 't, Args, W>(&'s mut self, args: Args, len: usize) -> Transfer<'t, Args, W>
where
Args: Iterator<Item = W>,
W: Into<SpiWord>,
{
Transfer::new(self, args, len)
}
pub fn dump(&mut self, label: &'_ str, inst_code: u8) {
print!("{}:", label);
let args = Some(u32::from(inst_code) << 24);
for b in self.transfer(args.into_iter(), 32).bytes_transfer() {
print!(" {:02X}", b);
}
println!("");
}
}

View File

@ -1,126 +0,0 @@
use volatile_register::{RO, WO, RW};
use libregister::{register, register_bit, register_bits};
#[repr(C)]
pub struct RegisterBlock {
pub config: Config,
pub intr_status: IntrStatus,
pub intr_en: IntrEn,
pub intr_dis: IntrDis,
pub intr_mask: RO<u32>,
pub enable: Enable,
pub delay: RW<u32>,
pub txd0: WO<u32>,
pub rx_data: RO<u32>,
pub slave_idle_count: RW<u32>,
pub tx_thres: RW<u32>,
pub rx_thres: RW<u32>,
pub gpio: QspiGpio,
pub _unused1: RO<u32>,
pub lpbk_dly_adj: RW<u32>,
pub _unused2: [RO<u32>; 17],
pub txd1: WO<u32>,
pub txd2: WO<u32>,
pub txd3: WO<u32>,
pub _unused3: [RO<u32>; 5],
pub lqspi_cfg: LqspiCfg,
pub lqspi_sts: RW<u32>,
pub _unused4: [RO<u32>; 21],
pub mod_id: RW<u32>,
}
impl RegisterBlock {
const BASE_ADDRESS: *mut Self = 0xE000D000 as *mut _;
pub fn qspi() -> &'static mut Self {
unsafe { &mut *Self::BASE_ADDRESS }
}
}
register!(config, Config, RW, u32);
register_bit!(config,
/// Enables master mode
mode_sel, 0);
register_bit!(config,
/// Clock polarity low/high
clk_pol, 1);
register_bit!(config,
/// Clock phase
clk_ph, 2);
register_bits!(config,
/// divider = 2 ** (1 + baud_rate_div)
baud_rate_div, u8, 3, 5);
register_bits!(config,
/// Must be set to 0b11
fifo_width, u8, 6, 7);
register_bit!(config,
/// Must be 0
ref_clk, 8);
register_bit!(config,
/// Peripheral Chip Select Line
pcs, 10);
register_bit!(config,
/// false: auto mode, true: manual CS mode
manual_cs, 14);
register_bit!(config,
/// false: auto mode, true: enables manual start enable
man_start_en, 15);
register_bit!(config,
/// false: auto mode, true: enables manual start command
man_start_com, 16);
register_bit!(config, holdb_dr, 19);
register_bit!(config,
/// false: little, true: endian
endian, 26);
register_bit!(config,
/// false: legacy SPI mode, true: Flash memory interface mode
leg_flsh, 31);
register!(intr_status, IntrStatus, RW, u32);
register_bit!(intr_status, rx_overflow, 0);
register_bit!(intr_status,
/// < tx_thres
tx_fifo_not_full, 2);
register_bit!(intr_status, tx_fifo_full, 3);
register_bit!(intr_status,
/// >= rx_thres
rx_fifo_not_empty, 4);
register_bit!(intr_status, rx_fifo_full, 5);
register_bit!(intr_status, tx_fifo_underflow, 6);
register!(intr_en, IntrEn, WO, u32);
register_bit!(intr_en, rx_overflow, 0);
register_bit!(intr_en, tx_fifo_not_full, 2);
register_bit!(intr_en, tx_fifo_full, 3);
register_bit!(intr_en, rx_fifo_not_empty, 4);
register_bit!(intr_en, rx_fifo_full, 5);
register_bit!(intr_en, tx_fifo_underflow, 6);
register!(intr_dis, IntrDis, WO, u32);
register_bit!(intr_dis, rx_overflow, 0);
register_bit!(intr_dis, tx_fifo_not_full, 2);
register_bit!(intr_dis, tx_fifo_full, 3);
register_bit!(intr_dis, rx_fifo_not_empty, 4);
register_bit!(intr_dis, rx_fifo_full, 5);
register_bit!(intr_dis, tx_fifo_underflow, 6);
register!(enable, Enable, RW, u32);
register_bit!(enable, spi_en, 0);
// named to avoid confusion with normal gpio
register!(qspi_gpio, QspiGpio, RW, u32);
register_bit!(qspi_gpio,
/// Write protect pin (inverted)
wp_n, 0);
register!(lqspi_cfg, LqspiCfg, RW, u32);
register_bits!(lqspi_cfg, inst_code, u8, 0, 7);
register_bits!(lqspi_cfg, dummy_mask, u8, 8, 10);
register_bits!(lqspi_cfg, mode_bits, u8, 16, 23);
register_bit!(lqspi_cfg, mode_on, 24);
register_bit!(lqspi_cfg, mode_en, 25);
register_bit!(lqspi_cfg, u_page, 28);
register_bit!(lqspi_cfg, sep_bus, 29);
register_bit!(lqspi_cfg, two_mem, 30);
register_bit!(lqspi_cfg, lq_mode, 31);

View File

@ -1,62 +0,0 @@
use bit_field::BitField;
pub trait SpiFlashRegister {
fn inst_code() -> u8;
fn new(src: u8) -> Self;
}
macro_rules! u8_register {
($name: ident, $doc: tt, $inst_code: expr) => {
#[derive(Clone)]
#[doc=$doc]
pub struct $name {
pub inner: u8,
}
impl SpiFlashRegister for $name {
fn inst_code() -> u8 {
$inst_code
}
fn new(src: u8) -> Self {
$name {
inner: src,
}
}
}
impl $name {
#[allow(unused)]
pub fn is_zeroed(&self) -> bool {
self.inner == 0
}
}
};
}
u8_register!(CR, "Configuration Register", 0x35);
u8_register!(SR1, "Status Register-1", 0x05);
impl SR1 {
/// Write In Progress
pub fn wip(&self) -> bool {
self.inner.get_bit(0)
}
/// Write Enable Latch
pub fn wel(&self) -> bool {
self.inner.get_bit(1)
}
/// Erase Error Occurred
pub fn e_err(&self) -> bool {
self.inner.get_bit(5)
}
/// Programming Error Occurred
pub fn p_err(&self) -> bool {
self.inner.get_bit(6)
}
}
u8_register!(SR2, "Status Register-2", 0x07);
u8_register!(BA, "Bank Address Register", 0xB9);

View File

@ -1,125 +0,0 @@
use libregister::{RegisterR, RegisterW, RegisterRW};
use super::regs;
use super::{SpiWord, Flash, Manual};
pub struct Transfer<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> {
flash: &'a mut Flash<Manual>,
args: Args,
sent: usize,
received: usize,
len: usize,
}
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Transfer<'a, Args, W> {
pub fn new(flash: &'a mut Flash<Manual>, args: Args, len: usize) -> Self {
flash.regs.config.modify(|_, w| w.pcs(false));
flash.regs.enable.write(
regs::Enable::zeroed()
.spi_en(true)
);
let mut xfer = Transfer {
flash,
args,
sent: 0,
received: 0,
len,
};
xfer.fill_tx_fifo();
xfer.flash.regs.config.modify(|_, w| w.man_start_com(true));
xfer
}
fn fill_tx_fifo(&mut self) {
while self.sent < self.len && !self.flash.regs.intr_status.read().tx_fifo_full() {
let arg = self.args.next()
.map(|n| n.into())
.unwrap_or(SpiWord::W32(0));
match arg {
SpiWord::W32(w) => {
// println!("txd0 {:08X}", w);
unsafe {
self.flash.regs.txd0.write(w);
}
self.sent += 4;
}
// Only txd0 can be used without flushing
_ => {
if !self.flash.regs.intr_status.read().tx_fifo_not_full() {
// Flush if necessary
self.flash.wait_tx_fifo_flush();
}
match arg {
SpiWord::W8(w) => {
// println!("txd1 {:02X}", w);
unsafe {
self.flash.regs.txd1.write(u32::from(w) << 24);
}
self.sent += 1;
}
SpiWord::W16(w) => {
unsafe {
self.flash.regs.txd2.write(u32::from(w) << 16);
}
self.sent += 2;
}
SpiWord::W24(w) => {
unsafe {
self.flash.regs.txd3.write(w << 8);
}
self.sent += 3;
}
SpiWord::W32(_) => unreachable!(),
}
self.flash.wait_tx_fifo_flush();
}
}
}
}
fn can_read(&mut self) -> bool {
self.flash.regs.intr_status.read().rx_fifo_not_empty()
}
fn read(&mut self) -> u32 {
let rx = self.flash.regs.rx_data.read();
self.received += 4;
rx
}
}
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Drop for Transfer<'a, Args, W> {
fn drop(&mut self) {
// Discard remaining rx_data
while self.can_read() {
self.read();
}
// Stop
self.flash.regs.enable.write(
regs::Enable::zeroed()
.spi_en(false)
);
self.flash.regs.config.modify(|_, w| w
.pcs(true)
.man_start_com(false)
);
}
}
impl<'a, Args: Iterator<Item = W>, W: Into<SpiWord>> Iterator for Transfer<'a, Args, W> {
type Item = u32;
fn next<'s>(&'s mut self) -> Option<u32> {
if self.received >= self.len {
return None;
}
self.fill_tx_fifo();
while !self.can_read() {}
Some(self.read())
}
}

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 new(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,235 +0,0 @@
//! I2C Bit-banging Controller
mod regs;
pub mod eeprom;
use super::clocks::Clocks;
use super::slcr;
use super::time::Microseconds;
use embedded_hal::timer::CountDown;
use libregister::{RegisterR, RegisterRW, RegisterW};
const INVALID_BUS: &'static str = "Invalid I2C bus";
pub struct I2C {
regs: regs::RegisterWrapper,
count_down: super::timer::global::CountDown<Microseconds>
}
impl I2C {
#[cfg(feature = "target_zc706")]
pub fn i2c() -> 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::ctor_common(0xFFFF - 0x000C)
}
fn ctor_common(gpio_output_mask: u16) -> Self {
// Setup register block
let clocks = Clocks::get();
let self_ = Self {
regs: regs::RegisterWrapper::new(),
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,93 +0,0 @@
use volatile_register::{RO, WO, RW};
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 RegisterWrapper {
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 RegisterWrapper {
pub fn new() -> 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,26 +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 flash;
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, new);
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));
}
}
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,449 +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),
);
}
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,69 +0,0 @@
use core::ops::{Deref, DerefMut};
use libcortex_a9::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() }
}
pub fn drop_uart() {
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 => {
let uart = Uart::serial(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::new();
GlobalTimer { regs }
}
/// Get the timer with a reset
pub fn start() -> GlobalTimer {
let mut regs = mpcore::RegisterBlock::new();
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 = "libcortex_a9"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
edition = "2018"
[features]
target_zc706 = []
target_cora_z7_10 = []
power_saving = []
default = ["target_zc706"]
[dependencies]
bit_field = "0.10"
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,226 +0,0 @@
use super::asm::{dmb, dsb};
/// 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() {
// 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": flush and invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dcciall() {
// 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));
}
}
}
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) {
dmb();
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) {
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();
}
/// 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]) {
dmb();
for addr in slice_cache_line_addrs(slice) {
dccmvac(addr);
}
dsb();
}
/// 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) {
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) {
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,36 +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;
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,91 +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::{dmb, 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::Acquire) != UNLOCKED {
unsafe {
exit_critical(irq);
spin_lock_yield();
irq = enter_critical();
}
}
dmb();
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::Acquire) != UNLOCKED {
unsafe { exit_critical(irq) };
None
} else {
dmb();
Some(MutexGuard { mutex: self, irq })
}
}
fn unlock(&self) {
dmb();
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 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,188 +0,0 @@
use core::{
pin::Pin,
future::Future,
ptr::drop_in_place,
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 {
drop_in_place(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() {
drop_in_place(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,66 +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) {
L1Table::get()
.update(page_start as *const (), |l1_section| {
l1_section.tex = 0b100;
l1_section.cacheable = false;
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 = ["Astro <astro@spaceboyz.net>"]
edition = "2018"
[dependencies]
vcell = "0.1"
volatile-register = "0.2"
bit_field = "0.10"

View File

@ -1,23 +0,0 @@
[package]
name = "libsupport_zynq"
description = "Software support for running in the Zynq PS"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
edition = "2018"
[features]
target_zc706 = ["libboard_zynq/target_zc706"]
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10"]
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,169 +0,0 @@
use r0::zero_bss;
use core::ptr::write_volatile;
use libregister::{
VolatileCell,
RegisterR, RegisterW, RegisterRW,
};
use libcortex_a9::{asm, 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::new();
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();
// 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::new();
mpcore.scu_invalidate.invalidate_core1();
let mmu_table = mmu::L1Table::get();
mmu::with_mmu(mmu_table, || {
ACTLR.enable_smp();
// 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();
}
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);
}
// Ensure values have been written to cache
asm::dmb();
// Flush cache-line
cache::dccmvac(unsafe { &CORE1_ENABLED } as *const _ as usize);
if sdram {
cache::dccmvac(0);
}
// 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,97 +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")) || MPIDR.read().cpu_id() == 0 {
&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.*);
}
}

View File

@ -1,7 +1,7 @@
source [find interface/ftdi/digilent-hs1.cfg]
adapter_khz 10000
set PL_TAPID 0x13722093
set PL_TAPID 0x03722093
set SMP 1
source ./zynq-7000.cfg

View File

@ -15,4 +15,5 @@ ftdi_device_desc "Digilent USB Device"
ftdi_vid_pid 0x0403 0x6014
ftdi_channel 0
ftdi_layout_init 0x00e8 0x60eb
ftdi_layout_signal nSRST -data 0x2000
reset_config none

View File

@ -1,28 +0,0 @@
def zynq-connect
target remote :3333
end
def zynq-fsbl-restart
mon xilinx_ps7_init
end
def zynq-restart
mon xilinx_ps7_init
load
end
# easily typed shortcuts
# device connect
def dc
zynq-connect
end
# device restart
def dr
zynq-restart
end
def dfr
zynq-fsbl-restart
end

View File

@ -1,17 +1,14 @@
source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
source [find xilinx-tcl.cfg]
adapter_khz 1000
source ./digilent_jtag_smt2_nc.cfg
adapter_khz 10000
set PL_TAPID 0x23731093
set PL_TAPID 0x03731093
set SMP 1
source ./zynq-7000.cfg
source ./xilinx-tcl.cfg
source ./ps7_init.tcl
reset_config srst_only srst_open_drain
adapter_nsrst_assert_width 250
adapter_nsrst_delay 400
reset_config srst_only srst_push_pull
set XC7_JSHUTDOWN 0x0d
set XC7_JPROGRAM 0x0b
@ -41,3 +38,5 @@ 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]

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

View File

@ -8,17 +8,20 @@ let
in
with project;
stdenv.mkDerivation {
name = "zynq-env";
buildInputs = (with rustPlatform.rust; [
name = "adc2tcp-env";
buildInputs = with rustPlatform.rust; [
rustc cargo
cargo-xbuild rustcSrc
]) ++ (with pkgs; [ openocd gdb ]);
pkgsCross.armhf-embedded.buildPackages.gcc
#pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc
#pkgsCross.armhf-embedded.buildPackages.binutils
];
# Set Environment Variables
RUST_BACKTRACE = 1;
XARGO_RUST_SRC = "${rustcSrc}/src";
shellHook = ''
echo "Run 'cargo xbuild --release -p experiments' to build."
echo "Run 'cargo xbuild --release' to build."
'';
}

View File

@ -1,9 +1,10 @@
use libregister::{RegisterR, RegisterRW};
use super::slcr;
pub use slcr::ArmPllSource;
use crate::slcr;
use crate::regs::RegisterR;
pub mod source;
use source::*;
#[cfg(feature = "target_zc706")]
const PS_CLK: u32 = 33_333_333;
#[cfg(feature = "target_cora_z7_10")]
const PS_CLK: u32 = 50_000_000;
enum CpuClockMode {
/// Clocks run in 4:2:2:1 mode
@ -20,11 +21,11 @@ impl CpuClockMode {
} else {
CpuClockMode::C421
}
}
}
}
#[derive(Debug, Clone)]
pub struct Clocks {
pub struct CpuClocks {
/// ARM PLL: Recommended clock source for the CPUs and the interconnect
pub arm: u32,
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
@ -33,38 +34,22 @@ pub struct Clocks {
pub io: u32,
}
impl Clocks {
impl CpuClocks {
pub fn get() -> Self {
Clocks {
arm: ArmPll::freq(),
ddr: DdrPll::freq(),
io: IoPll::freq(),
}
}
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)
);
})
let regs = slcr::RegisterBlock::new();
let arm = u32::from(regs.arm_pll_ctrl.read().pll_fdiv()) * PS_CLK;
let ddr = u32::from(regs.ddr_pll_ctrl.read().pll_fdiv()) * PS_CLK;
let io = u32::from(regs.io_pll_ctrl.read().pll_fdiv()) * PS_CLK;
CpuClocks { arm, ddr, io }
}
pub fn cpu_6x4x(&self) -> u32 {
let slcr = slcr::RegisterBlock::new();
let arm_clk_ctrl = slcr.arm_clk_ctrl.read();
let regs = slcr::RegisterBlock::new();
let arm_clk_ctrl = regs.arm_clk_ctrl.read();
let pll = match arm_clk_ctrl.srcsel() {
ArmPllSource::ArmPll => self.arm,
ArmPllSource::DdrPll => self.ddr,
ArmPllSource::IoPll => self.io,
slcr::ArmPllSource::ArmPll => self.arm,
slcr::ArmPllSource::DdrPll => self.ddr,
slcr::ArmPllSource::IoPll => self.io,
};
pll / u32::from(arm_clk_ctrl.divisor())
}
@ -104,18 +89,4 @@ impl Clocks {
};
pll / u32::from(uart_clk_ctrl.divisor())
}
pub fn sdio_ref_clk(&self) -> u32 {
let regs = slcr::RegisterBlock::new();
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 super::{regs::*, asm::*, cache::*};
use libregister::RegisterW;
use super::{regs::*, asm};
use crate::regs::RegisterW;
#[derive(Copy, Clone)]
#[repr(u8)]
@ -44,12 +44,6 @@ pub enum AccessPermissions {
}
impl AccessPermissions {
fn new(ap: u8, apx: bool) -> Self {
unsafe {
core::mem::transmute(if apx { 0b100 } else { 0 } | ap)
}
}
fn ap(&self) -> u8 {
(*self as u8) & 0b11
}
@ -71,64 +65,45 @@ pub struct L1Section {
pub bufferable: bool,
}
const ENTRY_TYPE_SECTION: u32 = 0b10;
pub const L1_PAGE_SIZE: usize = 0x100000;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct L1Entry(u32);
impl L1Entry {
#[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
assert!(phys_base & 0x000f_ffff == 0);
let mut entry = L1Entry(phys_base);
entry.set_section(section);
entry
}
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);
entry.0.set_bits(0..=1, 0b10);
entry.0.set_bit(2, section.bufferable);
entry.0.set_bit(3, section.cacheable);
entry.0.set_bit(4, !section.exec);
assert!(section.domain < 16);
self.0.set_bits(5..=8, section.domain.into());
self.0.set_bits(10..=11, section.access.ap().into());
entry.0.set_bits(5..=8, section.domain.into());
entry.0.set_bits(10..=11, section.access.ap().into());
assert!(section.tex < 8);
self.0.set_bits(12..=14, section.tex.into());
self.0.set_bit(15, section.access.apx());
self.0.set_bit(16, section.shareable);
self.0.set_bit(17, !section.global);
entry.0.set_bits(12..=14, section.tex.into());
entry.0.set_bit(15, section.access.apx());
entry.0.set_bit(16, section.shareable);
entry.0.set_bit(17, !section.global);
entry
}
}
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]
};
#[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 {
table: [L1Entry; L1_TABLE_SIZE]
}
@ -136,7 +111,7 @@ pub struct L1Table {
impl L1Table {
pub fn get() -> &'static mut Self {
unsafe {
&mut L1_TABLE
&mut l1_table
}
}
@ -149,11 +124,13 @@ impl L1Table {
tex: 0b101,
domain: 0b1111,
exec: true,
cacheable: true,
// TODO: temporarily turn on cache for SMP testing;
// consider turning it off again for production
cacheable: !false,
bufferable: true,
});
/* (DDR cacheable) */
for ddr in 1..=0x3ff {
for ddr in 1..=0x1ff {
self.direct_mapped_section(ddr, L1Section {
global: true,
shareable: true,
@ -165,6 +142,19 @@ impl L1Table {
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) */
for fpga_slave in 0x400..=0x7ff {
self.direct_mapped_section(fpga_slave, L1Section {
@ -338,7 +328,7 @@ impl L1Table {
/* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */
self.direct_mapped_section(0xfff, L1Section {
global: true,
shareable: true,
shareable: false,
access: AccessPermissions::FullAccess,
tex: 0b100,
domain: 0,
@ -355,34 +345,7 @@ impl L1Table {
assert!(index < L1_TABLE_SIZE);
let base = (index as u32) << 20;
self.table[index] = L1Entry::from_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
self.table[index] = L1Entry::section(base, section);
}
}
@ -415,9 +378,9 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
// Synchronization barriers
// Allows MMU to start
dsb();
asm::dsb();
// Flushes pre-fetch buffer
isb();
asm::isb();
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"));

View File

@ -1,17 +1,15 @@
use libregister::{
register_bit, register_bits,
RegisterR, RegisterW, RegisterRW,
};
use crate::{register_bit, register_bits};
use crate::regs::{RegisterR, RegisterW};
macro_rules! def_reg_r {
($name:tt, $type: ty, $asm_instr:tt) => {
impl RegisterR for $name {
type R = $type;
#[inline]
#[inline(always)]
fn read(&self) -> Self::R {
let mut value: u32;
unsafe { llvm_asm!($asm_instr : "=r" (value) ::: "volatile") }
unsafe { asm!($asm_instr : "=r" (value) ::: "volatile") }
value.into()
}
}
@ -23,13 +21,12 @@ macro_rules! def_reg_w {
impl RegisterW for $name {
type W = $type;
#[inline]
#[inline(always)]
fn write(&mut self, value: Self::W) {
let value: u32 = value.into();
unsafe { llvm_asm!($asm_instr :: "r" (value) :: "volatile") }
unsafe { asm!($asm_instr :: "r" (value) :: "volatile") }
}
#[inline]
fn zeroed() -> Self::W {
0u32.into()
}
@ -44,7 +41,6 @@ macro_rules! wrap_reg {
pub inner: u32,
}
impl From<u32> for Read {
#[inline]
fn from(value: u32) -> Self {
Read { inner: value }
}
@ -54,13 +50,11 @@ macro_rules! wrap_reg {
pub inner: u32,
}
impl From<u32> for Write {
#[inline]
fn from(value: u32) -> Self {
Write { inner: value }
}
}
impl Into<u32> for Write {
#[inline]
fn into(self) -> u32 {
self.inner
}
@ -79,31 +73,8 @@ pub struct LR;
def_reg_r!(LR, u32, "mov $0, lr");
def_reg_w!(LR, u32, "mov lr, $0");
pub struct VBAR;
def_reg_r!(VBAR, u32, "mrc p15, 0, $0, c12, c0, 0");
def_reg_w!(VBAR, u32, "mcr p15, 0, $0, c12, c0, 0");
pub struct MVBAR;
def_reg_r!(MVBAR, u32, "mrc p15, 0, $0, c12, c0, 1");
def_reg_w!(MVBAR, u32, "mcr p15, 0, $0, c12, c0, 1");
pub struct HVBAR;
def_reg_r!(HVBAR, u32, "mrc p15, 4, $0, c12, c0, 0");
def_reg_w!(HVBAR, u32, "mcr p15, 4, $0, c12, c0, 0");
/// Multiprocess Affinity Register
pub struct MPIDR;
def_reg_r!(MPIDR, mpidr::Read, "mrc p15, 0, $0, c0, c0, 5");
wrap_reg!(mpidr);
register_bits!(mpidr,
/// CPU core index
cpu_id, u8, 0, 1);
register_bits!(mpidr,
/// Processor index in "multi-socket" systems
cluster_id, u8, 8, 11);
register_bit!(mpidr,
/// true if part of uniprocessor system
u, 30);
def_reg_r!(MPIDR, u32, "mrc p15, 0, $0, c0, c0, 5");
pub struct DFAR;
def_reg_r!(DFAR, u32, "mrc p15, 0, $0, c6, c0, 0");
@ -144,6 +115,19 @@ register_bit!(sctlr,
/// Thumb Exception Enable
te, 30);
impl crate::regs::RegisterRW for SCTLR {
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
// todo: this may fail for .nmfi and, in non-secure state,
// also RR (bit 14)
let inner = self.read().inner;
let inner_w = f(
sctlr::Read { inner },
sctlr::Write { inner }
);
self.write(inner_w);
}
}
/// Auxiliary Control Register
pub struct ACTLR;
wrap_reg!(actlr);
@ -159,19 +143,14 @@ register_bit!(actlr, l1_prefetch_enable, 2);
// Cache/TLB maintenance broadcast
register_bit!(actlr, fw, 0);
impl RegisterRW for ACTLR {
#[inline]
impl crate::regs::RegisterRW for ACTLR {
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
let r = self.read();
let w = actlr::Write { inner: r.inner };
let w = f(r, w);
self.write(w);
}
}
impl ACTLR {
pub fn enable_smp(&mut self) {
self.modify(|_, w| w.smp(true).fw(true));
let inner = self.read().inner;
let inner_w = f(
actlr::Read { inner },
actlr::Write { inner }
);
self.write(inner_w);
}
}
@ -196,3 +175,78 @@ register_bit!(ttbr,
/// Translation table walk to shared memory?
s, 1);
register_bit!(ttbr, irgn1, 0);
/// Invalidate TLBs
#[inline(always)]
pub fn tlbiall() {
unsafe {
asm!("mcr p15, 0, $0, c8, c7, 0" :: "r" (0) :: "volatile");
}
}
/// Invalidate I-Cache
#[inline(always)]
pub fn iciallu() {
unsafe {
asm!("mcr p15, 0, $0, c7, c5, 0" :: "r" (0) :: "volatile");
}
}
/// Invalidate Branch Predictor Array
#[inline(always)]
pub fn bpiall() {
unsafe {
asm!("mcr p15, 0, $0, c7, c5, 6" :: "r" (0) :: "volatile");
}
}
/// Invalidate D-Cache
#[inline(always)]
pub fn dcisw(setway: u32) {
// TODO: $0 is r11 at what value?
unsafe {
// steinb: the following is incorrect
//asm!("mcr p15, 0, $0, c7, c5, 6" :: "r" (0) :: "volatile");
// 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.
asm!("mcr p15, 0, $0, c7, c6, 2" :: "r" (setway) :: "volatile");
}
}
/// A made-up "instruction": invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dciall() {
// 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 for a Cortex-A9.
let sets = 256;
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
// select L1 data cache
unsafe {
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));
}
}
}
/// clear cache line by virtual address to point of coherency (DCCMVAC)
#[inline]
pub fn dccmvac(addr: u32) {
unsafe {
asm!("mcr p15, 0, $0, c7, c10, 1" :: "r" (addr) :: "volatile");
}
}

View File

@ -1,14 +1,9 @@
use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
};
use log::{debug, info, warn, error};
use libregister::*;
use super::slcr;
use super::clocks::Clocks;
use crate::regs::*;
use crate::slcr;
use crate::println;
use crate::clocks::CpuClocks;
pub mod phy;
use phy::{Phy, PhyAccess};
mod regs;
pub mod rx;
pub mod tx;
@ -17,137 +12,16 @@ pub mod tx;
pub const MTU: usize = 1536;
/// Maximum MDC clock
const MAX_MDC: u32 = 2_500_000;
const TX_10: u32 = 10_000_000;
const TX_100: u32 = 25_000_000;
/// Clock for GbE
const TX_1000: u32 = 125_000_000;
#[derive(Clone)]
#[repr(C, align(0x20))]
pub struct Buffer(pub [u8; MTU]);
impl Buffer {
pub fn new() -> Self {
Buffer([0; MTU])
}
}
impl Deref for Buffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Buffer {
fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
&mut self.0
}
}
/// Gigabit Ethernet Peripheral
pub trait Gem {
fn setup_clock(tx_clock: u32);
fn regs() -> &'static mut regs::RegisterBlock;
}
/// first Gigabit Ethernet peripheral
pub struct Gem0;
impl Gem for Gem0 {
fn setup_clock(tx_clock: u32) {
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem0_clk_ctrl.write(
// 0x0050_0801: 8, 5: 100 Mb/s
// ...: 8, 1: 1000 Mb/s
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(divisor0 as u8)
.divisor1(divisor1 as u8)
);
// Enable gem0 recv clock
slcr.gem0_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
fn regs() -> &'static mut regs::RegisterBlock {
regs::RegisterBlock::gem0()
}
}
/// second Gigabit Ethernet peripheal
pub struct Gem1;
impl Gem for Gem1 {
fn setup_clock(tx_clock: u32) {
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem1_clk_ctrl.write(
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(divisor0 as u8)
.divisor1(divisor1 as u8)
);
// Enable gem1 recv clock
slcr.gem1_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
fn regs() -> &'static mut regs::RegisterBlock {
regs::RegisterBlock::gem1()
}
}
fn calculate_tx_divisors(tx_clock: u32) -> (u8, u8) {
let io_pll = Clocks::get().io;
let target = (tx_clock - 1 + io_pll) / tx_clock;
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;
}
}
}
let result = best.unwrap();
debug!("Eth TX clock for {}: {} / {} / {} = {}",
tx_clock, io_pll,
result.0, result.1,
io_pll / result.0 as u32 / result.1 as u32
);
result
}
pub struct Eth<GEM: Gem, RX, TX> {
pub struct Eth<'r, RX, TX> {
regs: &'r mut regs::RegisterBlock,
rx: RX,
tx: TX,
inner: EthInner<GEM>,
phy: Phy,
}
impl Eth<Gem0, (), ()> {
impl<'r> Eth<'r, (), ()> {
pub fn default(macaddr: [u8; 6]) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Manual example: 0x0000_1280
@ -280,203 +154,90 @@ impl Eth<Gem0, (), ()> {
}
pub fn gem0(macaddr: [u8; 6]) -> Self {
Self::new(macaddr)
Self::setup_gem0_clock(TX_1000);
let regs = regs::RegisterBlock::gem0();
Self::from_regs(regs, macaddr)
}
}
impl Eth<Gem1, (), ()> {
pub fn gem1(macaddr: [u8; 6]) -> Self {
Self::new(macaddr)
Self::setup_gem1_clock(TX_1000);
let regs = regs::RegisterBlock::gem1();
Self::from_regs(regs, macaddr)
}
}
impl<GEM: Gem> Eth<GEM, (), ()> {
fn new(macaddr: [u8; 6]) -> Self {
GEM::setup_clock(TX_1000);
let mut inner = EthInner {
gem: PhantomData,
link: None,
};
inner.init();
inner.configure(macaddr);
let phy = Phy::find(&mut inner).expect("phy");
phy.reset(&mut inner);
phy.restart_autoneg(&mut inner);
Eth {
fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
let mut eth = Eth {
regs,
rx: (),
tx: (),
inner,
phy,
}
}.init();
eth.configure(macaddr);
eth
}
}
impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
pub fn start_rx(self, rx_size: usize) -> Eth<GEM, rx::DescList, TX> {
let new_self = Eth {
rx: rx::DescList::new(rx_size),
tx: self.tx,
inner: self.inner,
phy: self.phy,
};
let list_addr = new_self.rx.list_addr();
assert!(list_addr & 0b11 == 0);
GEM::regs().rx_qbar.write(
regs::RxQbar::zeroed()
.rx_q_baseaddr(list_addr >> 2)
);
GEM::regs().net_ctrl.modify(|_, w|
w.rx_en(true)
);
new_self
}
impl<'r, RX, TX> Eth<'r, RX, TX> {
pub fn setup_gem0_clock(tx_clock: u32) {
let io_pll = CpuClocks::get().io;
let d0 = (io_pll / tx_clock).min(63);
let d1 = (io_pll / tx_clock / d0).min(63);
pub fn start_tx(self, tx_size: usize) -> Eth<GEM, RX, tx::DescList> {
let new_self = Eth {
rx: self.rx,
tx: tx::DescList::new(tx_size),
inner: self.inner,
phy: self.phy,
};
let list_addr = &new_self.tx.list_addr();
assert!(list_addr & 0b11 == 0);
GEM::regs().tx_qbar.write(
regs::TxQbar::zeroed()
.tx_q_baseaddr(list_addr >> 2)
);
GEM::regs().net_ctrl.modify(|_, w|
w.tx_en(true)
);
new_self
}
}
impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<rx::PktRef<'p>>, rx::Error> {
let status = GEM::regs().rx_status.read();
if status.hresp_not_ok() {
// Clear
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.hresp_not_ok(true)
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem0_clk_ctrl.write(
// 0x0050_0801: 8, 5: 100 Mb/s
// ...: 8, 1: 1000 Mb/s
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(d0 as u8)
.divisor1(d1 as u8)
);
return Err(rx::Error::HrespNotOk);
}
if status.rx_overrun() {
// Clear
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.rx_overrun(true)
// Enable gem0 recv clock
slcr.gem0_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
return Err(rx::Error::RxOverrun);
}
if status.buffer_not_avail() {
// Clear
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.buffer_not_avail(true)
});
}
pub fn setup_gem1_clock(tx_clock: u32) {
let io_pll = CpuClocks::get().io;
let d0 = (io_pll / tx_clock).min(63);
let d1 = (io_pll / tx_clock / d0).min(63);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem1_clk_ctrl.write(
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(d0 as u8)
.divisor1(d1 as u8)
);
return Err(rx::Error::BufferNotAvail);
}
if status.frame_recd() {
let result = self.rx.recv_next();
match result {
Ok(None) => {
// No packet, clear status bit
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.frame_recd(true)
);
}
_ => {}
}
result
} else {
self.inner.check_link_change(&self.phy);
Ok(None)
}
}
}
impl<GEM: Gem, RX> Eth<GEM, RX, tx::DescList> {
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
self.tx.send(GEM::regs(), length)
}
}
impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::DescList> {
type RxToken = rx::PktRef<'a>;
type TxToken = tx::Token<'a>;
fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
use smoltcp::phy::{DeviceCapabilities, ChecksumCapabilities, Checksum};
let mut checksum_caps = ChecksumCapabilities::default();
checksum_caps.ipv4 = Checksum::Both;
checksum_caps.tcp = Checksum::Both;
checksum_caps.udp = Checksum::Both;
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = MTU;
caps.max_burst_size = Some(self.rx.len().min(self.tx.len()));
caps.checksum = checksum_caps;
caps
// Enable gem1 recv clock
slcr.gem1_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
match self.rx.recv_next() {
Ok(Some(pktref)) => {
let tx_token = tx::Token {
regs: GEM::regs(),
desc_list: &mut self.tx,
};
Some((pktref, tx_token))
}
Ok(None) => {
self.inner.check_link_change(&self.phy);
None
}
Err(e) => {
error!("eth recv error: {:?}", e);
None
}
}
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
Some(tx::Token {
regs: GEM::regs(),
desc_list: &mut self.tx,
})
}
}
struct EthInner<GEM: Gem> {
gem: PhantomData<GEM>,
link: Option<phy::Link>,
}
impl<GEM: Gem> EthInner<GEM> {
fn init(&mut self) {
fn init(self) -> Self {
// Clear the Network Control register.
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed());
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
self.regs.net_ctrl.write(regs::NetCtrl::zeroed());
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
// Clear the Status registers.
GEM::regs().rx_status.write(
self.regs.rx_status.write(
regs::RxStatus::zeroed()
.buffer_not_avail(true)
.frame_recd(true)
.rx_overrun(true)
.hresp_not_ok(true)
);
GEM::regs().tx_status.write(
self.regs.tx_status.write(
regs::TxStatus::zeroed()
.used_bit_read(true)
.collision(true)
@ -490,7 +251,7 @@ impl<GEM: Gem> EthInner<GEM> {
.hresp_not_ok(true)
);
// Disable all interrupts.
GEM::regs().intr_dis.write(
self.regs.intr_dis.write(
regs::IntrDis::zeroed()
.mgmt_done(true)
.rx_complete(true)
@ -520,19 +281,21 @@ impl<GEM: Gem> EthInner<GEM> {
.tsu_sec_incr(true)
);
// Clear the buffer queues.
GEM::regs().rx_qbar.write(
self.regs.rx_qbar.write(
regs::RxQbar::zeroed()
);
GEM::regs().tx_qbar.write(
self.regs.tx_qbar.write(
regs::TxQbar::zeroed()
);
self
}
fn configure(&mut self, macaddr: [u8; 6]) {
let clocks = Clocks::get();
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
let clocks = CpuClocks::get();
let mut mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
GEM::regs().net_cfg.write(
self.regs.net_cfg.write(
regs::NetCfg::zeroed()
.full_duplex(true)
.gige_en(true)
@ -543,8 +306,6 @@ impl<GEM: Gem> EthInner<GEM> {
.copy_all(true)
// Remove 4-byte Frame CheckSum
.fcs_remove(true)
// RX checksum offload
.rx_chksum_offld_en(true)
// One of the slower speeds
.mdc_clk_div((mdc_clk_div >> 4).min(0b111) as u8)
);
@ -557,17 +318,17 @@ impl<GEM: Gem> EthInner<GEM> {
(u32::from(macaddr[3]) << 16) |
(u32::from(macaddr[4]) << 8) |
u32::from(macaddr[5]);
GEM::regs().spec_addr1_top.write(
self.regs.spec_addr1_top.write(
regs::SpecAddrTop::zeroed()
.addr_msbs(macaddr_msbs)
);
GEM::regs().spec_addr1_bot.write(
self.regs.spec_addr1_bot.write(
regs::SpecAddrBot::zeroed()
.addr_lsbs(macaddr_lsbs)
);
GEM::regs().dma_cfg.write(
self.regs.dma_cfg.write(
regs::DmaCfg::zeroed()
// 1536 bytes
.ahb_mem_rx_buf_size((MTU >> 6) as u8)
@ -575,7 +336,6 @@ impl<GEM: Gem> EthInner<GEM> {
.rx_pktbuf_memsz_sel(0x3)
// 4 KB
.tx_pktbuf_memsz_sel(true)
// TX checksum offload
.csum_gen_offload_en(true)
// Little-endian
.ahb_endian_swp_mgmt_en(false)
@ -583,64 +343,135 @@ impl<GEM: Gem> EthInner<GEM> {
.ahb_fixed_burst_len(0x10)
);
GEM::regs().net_ctrl.write(
self.regs.net_ctrl.write(
regs::NetCtrl::zeroed()
.mgmt_port_en(true)
);
}
fn wait_phy_idle(&self) {
while !GEM::regs().net_status.read().phy_mgmt_idle() {}
pub fn start_rx<'rx>(self, rx_list: &'rx mut [rx::DescEntry], rx_buffers: &'rx mut [[u8; MTU]]) -> Eth<'r, rx::DescList<'rx>, TX> {
let new_self = Eth {
regs: self.regs,
rx: rx::DescList::new(rx_list, rx_buffers),
tx: self.tx,
};
let list_addr = new_self.rx.list_addr();
assert!(list_addr & 0b11 == 0);
new_self.regs.rx_qbar.write(
regs::RxQbar::zeroed()
.rx_q_baseaddr(list_addr >> 2)
);
new_self.regs.net_ctrl.modify(|_, w|
w.rx_en(true)
);
new_self
}
pub fn start_tx<'tx>(self, tx_list: &'tx mut [tx::DescEntry], tx_buffers: &'tx mut [[u8; MTU]]) -> Eth<'r, RX, tx::DescList<'tx>> {
let new_self = Eth {
regs: self.regs,
rx: self.rx,
tx: tx::DescList::new(tx_list, tx_buffers),
};
let list_addr = &new_self.tx.list_addr();
assert!(list_addr & 0b11 == 0);
new_self.regs.tx_qbar.write(
regs::TxQbar::zeroed()
.tx_q_baseaddr(list_addr >> 2)
);
new_self.regs.net_ctrl.modify(|_, w|
w.tx_en(true)
);
new_self
}
fn check_link_change(&mut self, phy: &Phy) {
// As the PHY access takes some time, exit early if there was
// already a link. TODO: check once per second.
if self.link.is_some() {
return
}
fn wait_phy_idle(&self) {
while !self.regs.net_status.read().phy_mgmt_idle() {}
}
let link = phy.get_link(self);
// Check link state transition
if self.link != link {
match &link {
Some(link) => {
info!("eth: got {:?}", link);
use phy::{LinkDuplex::Full, LinkSpeed::*};
let txclock = match link.speed {
S10 => TX_10,
S100 => TX_100,
S1000 => TX_1000,
};
GEM::setup_clock(txclock);
GEM::regs().net_cfg.modify(|_, w| w
.full_duplex(link.duplex == Full)
.gige_en(link.speed == S1000)
.speed(link.speed != S10)
);
}
None => {
warn!("eth: link lost");
phy.modify_control(self, |control|
control.set_autoneg_enable(true)
.set_restart_autoneg(true)
);
pub fn reset_phy(&mut self) -> bool {
match phy::Phy::find(self) {
Some(phy) => {
println!("eth: Found PHY at {}: {}", phy.addr, phy.name());
phy.modify_control(self, |control|
control.set_reset(true)
);
while phy.get_control(self).reset() {
println!("eth: Wait for PHY reset");
}
phy.modify_control(self, |control|
control.set_autoneg_enable(true)
.set_restart_autoneg(true)
);
println!("eth: Wait for link");
while !phy.get_status(self).link_status() {}
println!("eth: Got link, setting clock for gigabit");
Self::setup_gem0_clock(TX_1000);
true
}
self.link = link;
None => false
}
}
}
impl<GEM: Gem> PhyAccess for EthInner<GEM> {
impl<'r, 'rx, TX> Eth<'r, rx::DescList<'rx>, TX> {
pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<rx::PktRef<'p>>, rx::Error> {
let status = self.regs.rx_status.read();
if status.hresp_not_ok() {
// Clear
self.regs.rx_status.write(
regs::RxStatus::zeroed()
.hresp_not_ok(true)
);
return Err(rx::Error::HrespNotOk);
}
if status.rx_overrun() {
// Clear
self.regs.rx_status.write(
regs::RxStatus::zeroed()
.rx_overrun(true)
);
return Err(rx::Error::RxOverrun);
}
if status.buffer_not_avail() {
// Clear
self.regs.rx_status.write(
regs::RxStatus::zeroed()
.buffer_not_avail(true)
);
return Err(rx::Error::BufferNotAvail);
}
if status.frame_recd() {
let result = self.rx.recv_next();
match result {
Ok(None) => {
// No packet, clear status bit
self.regs.rx_status.write(
regs::RxStatus::zeroed()
.frame_recd(true)
);
}
_ => {}
}
result
} else {
Ok(None)
}
}
}
impl<'r, 'tx, RX> Eth<'r, RX, tx::DescList<'tx>> {
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
self.tx.send(self.regs, length)
}
}
impl<'r, RX, TX> phy::PhyAccess for Eth<'r, RX, TX> {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
self.wait_phy_idle();
GEM::regs().phy_maint.write(
self.regs.phy_maint.write(
regs::PhyMaint::zeroed()
.clause_22(true)
.operation(regs::PhyOperation::Read)
@ -649,12 +480,12 @@ impl<GEM: Gem> PhyAccess for EthInner<GEM> {
.must_10(0b10)
);
self.wait_phy_idle();
GEM::regs().phy_maint.read().data()
self.regs.phy_maint.read().data()
}
fn write_phy(&mut self, addr: u8, reg: u8, data: u16) {
self.wait_phy_idle();
GEM::regs().phy_maint.write(
self.regs.phy_maint.write(
regs::PhyMaint::zeroed()
.clause_22(true)
.operation(regs::PhyOperation::Write)
@ -667,4 +498,39 @@ impl<GEM: Gem> PhyAccess for EthInner<GEM> {
}
}
impl<'r, 'rx, 'tx: 'a, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList<'rx>, tx::DescList<'tx>> {
type RxToken = rx::PktRef<'a>;
type TxToken = tx::Token<'a, 'tx>;
fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
let mut caps = smoltcp::phy::DeviceCapabilities::default();
caps.max_transmission_unit = MTU;
caps
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
match self.rx.recv_next() {
Ok(Some(mut pktref)) => {
let tx_token = tx::Token {
regs: self.regs,
desc_list: &mut self.tx,
};
Some((pktref, tx_token))
}
Ok(None) =>
None,
Err(e) => {
println!("eth recv error: {:?}", e);
None
}
}
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
Some(tx::Token {
regs: self.regs,
desc_list: &mut self.tx,
})
}
}

View File

@ -4,71 +4,55 @@ mod status;
pub use status::Status;
mod control;
pub use control::Control;
mod pssr;
pub use pssr::PSSR;
#[derive(Clone, Debug, PartialEq)]
pub struct Link {
pub speed: LinkSpeed,
pub duplex: LinkDuplex,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum LinkSpeed {
S10,
S100,
S1000,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum LinkDuplex {
Half,
Full,
}
pub trait PhyAccess {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16;
fn write_phy(&mut self, addr: u8, reg: u8, data: u16);
}
#[derive(Clone)]
pub struct Phy {
pub addr: u8,
device: PhyDevice,
}
#[derive(Clone, Copy)]
pub enum PhyDevice {
Marvell88E1116R,
Marvel88E1116R,
Rtl8211E,
}
const OUI_MARVELL: u32 = 0x005043;
const OUI_MARVEL: u32 = 0x005043;
const OUI_REALTEK: u32 = 0x000732;
impl Phy {
/// Probe all addresses on MDIO for a known PHY
pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> {
(1..32).filter_map(|addr| {
match identify_phy(pa, addr) {
for addr in 1..32 {
let device = match identify_phy(pa, addr) {
Some(PhyIdentifier {
oui: OUI_MARVELL,
oui: OUI_MARVEL,
model: 36,
..
}) => Some(PhyDevice::Marvell88E1116R),
}) => Some(PhyDevice::Marvel88E1116R),
Some(PhyIdentifier {
oui: OUI_REALTEK,
model: 0b010001,
rev: 0b0101,
}) => Some(PhyDevice::Rtl8211E),
_ => None,
}.map(|device| Phy { addr, device })
}).next()
};
match device {
Some(device) =>
return Some(Phy { addr, device }),
None => {}
}
}
None
}
pub fn name(&self) -> &'static str {
match self.device {
PhyDevice::Marvell88E1116R => &"Marvell 88E1116R",
PhyDevice::Marvel88E1116R => &"Marvel 88E1116R",
PhyDevice::Rtl8211E => &"RTL8211E",
}
}
@ -107,32 +91,6 @@ impl Phy {
pub fn get_status<PA: PhyAccess>(&self, pa: &mut PA) -> Status {
self.read_reg(pa)
}
pub fn get_link<PA: PhyAccess>(&self, pa: &mut PA) -> Option<Link> {
let status = self.get_status(pa);
if !status.link_status() {
None
} else if status.cap_1000base_t_extended_status() {
let phy_status: PSSR = self.read_reg(pa);
phy_status.get_link()
} else {
status.get_link()
}
}
pub fn reset<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_control(pa, |control|
control.set_reset(true)
);
while self.get_control(pa).reset() {}
}
pub fn restart_autoneg<PA: PhyAccess>(&self, pa: &mut PA) {
self.modify_control(pa, |control|
control.set_autoneg_enable(true)
.set_restart_autoneg(true)
);
}
}
pub trait PhyRegister {

View File

@ -1,5 +1,5 @@
use bit_field::BitField;
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
use super::PhyRegister;
#[derive(Clone, Copy, Debug)]
/// Basic Mode Status Register
@ -51,49 +51,6 @@ impl Status {
pub fn cap_100base_t4(&self) -> bool {
self.0.get_bit(15)
}
pub fn get_link(&self) -> Option<Link> {
if ! self.link_status() {
None
} else if self.cap_10base_t_half() {
Some(Link {
speed: LinkSpeed::S10,
duplex: LinkDuplex::Half,
})
} else if self.cap_10base_t_full() {
Some(Link {
speed: LinkSpeed::S10,
duplex: LinkDuplex::Full,
})
} else if self.cap_10base_t2_half() {
Some(Link {
speed: LinkSpeed::S10,
duplex: LinkDuplex::Half,
})
} else if self.cap_10base_t2_full() {
Some(Link {
speed: LinkSpeed::S10,
duplex: LinkDuplex::Full,
})
} else if self.cap_100base_t4() {
Some(Link {
speed: LinkSpeed::S100,
duplex: LinkDuplex::Half,
})
} else if self.cap_100base_tx_half() {
Some(Link {
speed: LinkSpeed::S100,
duplex: LinkDuplex::Half,
})
} else if self.cap_100base_tx_full() {
Some(Link {
speed: LinkSpeed::S100,
duplex: LinkDuplex::Full,
})
} else {
None
}
}
}
impl PhyRegister for Status {

View File

@ -1,6 +1,6 @@
use volatile_register::{RO, WO, RW};
use libregister::{register, register_bit, register_bits, register_bits_typed};
use crate::{register, register_bit, register_bits, register_bits_typed};
#[repr(C)]
pub struct RegisterBlock {

View File

@ -1,8 +1,6 @@
use core::ops::Deref;
use alloc::{vec, vec::Vec};
use libcortex_a9::{asm::*, cache::*, UncachedSlice};
use libregister::*;
use super::Buffer;
use crate::{register, register_bit, register_bits, regs::*};
use super::MTU;
#[derive(Debug)]
pub enum Error {
@ -13,22 +11,13 @@ pub enum Error {
}
/// Descriptor entry
#[repr(C, align(0x08))]
#[repr(C)]
pub struct DescEntry {
word0: DescWord0,
word1: DescWord1,
}
impl DescEntry {
pub fn zeroed() -> Self {
DescEntry {
word0: DescWord0 { inner: VolatileCell::new(0) },
word1: DescWord1 { inner: VolatileCell::new(0) },
}
}
}
register!(desc_word0, DescWord0, VolatileCell, u32);
register!(desc_word0, DescWord0, RW, u32);
register_bit!(desc_word0,
/// true if owned by software, false if owned by hardware
used, 0);
@ -37,7 +26,7 @@ register_bit!(desc_word0,
wrap, 1);
register_bits!(desc_word0, address, u32, 2, 31);
register!(desc_word1, DescWord1, VolatileCell, u32);
register!(desc_word1, DescWord1, RW, u32);
register_bits!(desc_word1, frame_length_lsbs, u16, 0, 12);
register_bit!(desc_word1, bad_fcs, 13);
register_bit!(desc_word1, start_of_frame, 14);
@ -55,22 +44,18 @@ register_bit!(desc_word1, multi_hash_match, 30);
register_bit!(desc_word1, global_broadcast, 31);
#[repr(C)]
pub struct DescList {
list: UncachedSlice<DescEntry>,
buffers: Vec<Buffer>,
pub struct DescList<'a> {
list: &'a mut [DescEntry],
buffers: &'a mut [[u8; MTU]],
next: usize,
}
impl DescList {
pub fn new(size: usize) -> Self {
let mut list = UncachedSlice::new(size, || DescEntry::zeroed())
.unwrap();
let mut buffers = vec![Buffer::new(); size];
impl<'a> DescList<'a> {
pub fn new(list: &'a mut [DescEntry], buffers: &'a mut [[u8; MTU]]) -> Self {
let last = list.len().min(buffers.len()) - 1;
for (i, (entry, buffer)) in list.iter_mut().zip(buffers.iter_mut()).enumerate() {
let is_last = i == last;
let buffer_addr = &mut buffer.0[0] as *mut _ as u32;
let buffer_addr = &mut buffer[0] as *mut _ as u32;
assert!(buffer_addr & 0b11 == 0);
entry.word0.write(
DescWord0::zeroed()
@ -81,9 +66,6 @@ impl DescList {
entry.word1.write(
DescWord1::zeroed()
);
// Flush buffer from cache, to be filled by the peripheral
// before next read
dcci_slice(&buffer[..]);
}
DescList {
@ -93,10 +75,6 @@ impl DescList {
}
}
pub fn len(&self) -> usize {
self.list.len().min(self.buffers.len())
}
pub fn list_addr(&self) -> u32 {
&self.list[0] as *const _ as u32
}
@ -104,11 +82,10 @@ impl DescList {
pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<PktRef<'p>>, Error> {
let list_len = self.list.len();
let entry = &mut self.list[self.next];
dmb();
if entry.word0.read().used() {
let word1 = entry.word1.read();
let len = word1.frame_length_lsbs().into();
let buffer = &mut self.buffers[self.next][0..len];
let buffer = &self.buffers[self.next][0..len];
self.next += 1;
if self.next >= list_len {
@ -130,17 +107,12 @@ impl DescList {
/// Releases a buffer back to the HW upon Drop
pub struct PktRef<'a> {
entry: &'a mut DescEntry,
buffer: &'a mut [u8],
buffer: &'a [u8],
}
impl<'a> Drop for PktRef<'a> {
fn drop(&mut self) {
// Flush buffer from cache, to be filled by the peripheral
// before next read
dcci_slice(self.buffer);
self.entry.word0.modify(|_, w| w.used(false));
dmb();
}
}
@ -152,9 +124,9 @@ impl<'a> Deref for PktRef<'a> {
}
impl<'a> smoltcp::phy::RxToken for PktRef<'a> {
fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result<R>
fn consume<R, F>(mut self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result<R>
where
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>
F: FnOnce(&[u8]) -> smoltcp::Result<R>
{
f(self.buffer)
}

View File

@ -1,20 +1,18 @@
use core::ops::{Deref, DerefMut};
use alloc::{vec, vec::Vec};
use libcortex_a9::{cache::dcc_slice, UncachedSlice};
use libregister::*;
use super::{Buffer, regs};
use crate::{register, register_bit, register_bits, regs::*};
use crate::println;
use super::{MTU, regs};
/// Descriptor entry
#[repr(C, align(0x08))]
pub struct DescEntry {
word0: DescWord0,
word1: DescWord1,
}
register!(desc_word0, DescWord0, VolatileCell, u32);
register!(desc_word0, DescWord0, RW, u32);
register_bits!(desc_word0, address, u32, 0, 31);
register!(desc_word1, DescWord1, VolatileCell, u32);
register!(desc_word1, DescWord1, RW, u32);
register_bits!(desc_word1, length, u16, 0, 13);
register_bit!(desc_word1, last_buffer, 15);
register_bit!(desc_word1, no_crc_append, 16);
@ -29,41 +27,22 @@ register_bit!(desc_word1,
/// true if owned by software, false if owned by hardware
used, 31);
impl DescEntry {
pub fn zeroed() -> Self {
DescEntry {
word0: DescWord0 { inner: VolatileCell::new(0) },
word1: DescWord1 { inner: VolatileCell::new(0) },
}
}
}
/// Number of descriptors
pub const DESCS: usize = 8;
#[repr(C)]
pub struct DescList {
list: UncachedSlice<DescEntry>,
buffers: Vec<Buffer>,
pub struct DescList<'a> {
list: &'a mut [DescEntry],
buffers: &'a mut [[u8; MTU]],
next: usize,
}
impl DescList {
pub fn new(size: usize) -> Self {
let mut list = UncachedSlice::new(size, || DescEntry::zeroed())
.unwrap();
let mut buffers = vec![Buffer::new(); size];
impl<'a> DescList<'a> {
pub fn new(list: &'a mut [DescEntry], buffers: &'a mut [[u8; MTU]]) -> Self {
let last = list.len().min(buffers.len()) - 1;
// Sending seems to not work properly with only one packet
// buffer (two duplicates get send with every packet), so
// check that at least 2 are allocated, i.e. that the index of
// the last one is at least one.
assert!(last > 0);
for (i, (entry, buffer)) in list.iter_mut().zip(buffers.iter_mut()).enumerate() {
let is_last = i == last;
let buffer_addr = &mut buffer.0[0] as *mut _ as u32;
let buffer_addr = &mut buffer[0] as *mut _ as u32;
assert!(buffer_addr & 0b11 == 0);
entry.word0.write(
DescWord0::zeroed()
@ -85,10 +64,6 @@ impl DescList {
}
}
pub fn len(&self) -> usize {
self.list.len().min(self.buffers.len())
}
pub fn list_addr(&self) -> u32 {
&self.list[0] as *const _ as u32
}
@ -97,13 +72,8 @@ impl DescList {
let list_len = self.list.len();
let entry = &mut self.list[self.next];
if entry.word1.read().used() {
entry.word1.modify(|_, w| w.length(length as u16));
let buffer = &mut self.buffers[self.next][0..length];
entry.word1.write(DescWord1::zeroed()
.length(length as u16)
.last_buffer(true)
.wrap(self.next >= list_len - 1)
.used(true)
);
self.next += 1;
if self.next >= list_len {
@ -128,13 +98,12 @@ pub struct PktRef<'a> {
impl<'a> Drop for PktRef<'a> {
fn drop(&mut self) {
// Write back all dirty cachelines of this buffer
dcc_slice(self.buffer);
self.entry.word1.modify(|_, w| w.used(false));
if ! self.regs.tx_status.read().tx_go() {
// Start TX if not already running
self.regs.net_ctrl.modify(|_, w| w.start_tx(true));
println!("tx start_tx");
self.regs.net_ctrl.modify(|_, w|
w.start_tx(true)
);
}
}
}
@ -153,12 +122,12 @@ impl<'a> DerefMut for PktRef<'a> {
}
/// TxToken for smoltcp support
pub struct Token<'a> {
pub struct Token<'a, 'tx: 'a> {
pub regs: &'a mut regs::RegisterBlock,
pub desc_list: &'a mut DescList,
pub desc_list: &'a mut DescList<'tx>,
}
impl<'a> smoltcp::phy::TxToken for Token<'a> {
impl<'a, 'tx: 'a> smoltcp::phy::TxToken for Token<'a, 'tx> {
fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result<R>
where F: FnOnce(&mut [u8]) -> smoltcp::Result<R>
{

132
src/mailbox.rs Normal file
View File

@ -0,0 +1,132 @@
use crate::cortex_a9::asm;
use core::ptr::{read_volatile, write_volatile};
/*
One-way mailbox:
All transmissions must originate from one core only,
and all receives from the other core only.
Example transmission (to be executed on core 0):
{
while (!MAILBOX_FROM_CORE0.acknowledged()) {}
println!("ready to send");
MAILBOX_FROM_CORE0.send(&data);
println!("sent");
while (!MAILBOX_FROM_CORE0.acknowledged()) {}
println!("got receipt (acknowledgement)");
}
Example reception (to be executed on core 1):
{
println("wait for data");
while (!MAILBOX_FROM_CORE0.available()) {}
let data = MAILBOX_FROM_CORE0.receive();
println("data received");
MAILBOX_FROM_CORE0.acknowledge(data);
}
Note that unsafe { ... } blocks must be used around most functions;
these have been omitted from the examples for clarity.
*/
pub struct OneWayMailbox {
// pointer (data to be transferred): write-only for sending core,
// readable and clearable (to 0) for receiving core
pointer: usize,
// helper variable (last pointer value received) for receiving
// core
echo: usize,
}
pub static mut MAILBOX_FROM_CORE0: OneWayMailbox = OneWayMailbox::new();
pub static mut MAILBOX_FROM_CORE1: OneWayMailbox = OneWayMailbox::new();
impl OneWayMailbox {
// instantiate a one-way mailbox with no undelivered message
pub const fn new() -> OneWayMailbox {
OneWayMailbox { pointer: 0, echo: 0 }
}
// recreate pristine condition; may only be called when producers
// and consumers are stopped (e.g. when starting core 1 from core
// 0).
pub fn reset_discard(&mut self) {
unsafe {
write_volatile(&mut self.pointer, 0);
write_volatile(&mut self.echo, 0);
}
asm::dmb();
}
// send a pointer from one core to be received by the other core
pub fn send(&mut self, ptr: usize) -> usize {
assert!(ptr != 0); // ptr may not be the NULL-like flag
unsafe {
write_volatile(&mut self.pointer, ptr);
}
asm::dmb(); // ensure data at (ptr) has been fully written
ptr
}
// receive a pointer from the other core, or 0 if none is present
pub fn receive(&self) -> usize {
let ptr = unsafe {
read_volatile(&self.pointer)
};
// necessary memory barrier to guarantee that the data at
// (ptr) has been fully written before it may be accessed
// by the caller of this function
asm::dmb();
ptr
}
// return true if it is guaranteed that the next self.receive()
// will return actual data rather than 0
pub fn available(&self) -> bool {
let ptr = unsafe {
read_volatile(&self.pointer)
};
asm::dmb();
ptr != 0
}
// acknowledge receipt of data to the sender (i.e. release it)
pub fn acknowledge(&mut self, ptr: usize) {
// ensure that the data we release is the data last sent
assert_eq!(ptr, unsafe {
read_volatile(&self.pointer)
});
// first possibility for "release" flag:
// pointer and echo are equal
unsafe {
write_volatile(&mut self.echo, ptr);
}
asm::dmb(); // write to self.echo before self.pointer
// second possibility for "release" flag:
// NULL-like pointer
unsafe {
write_volatile(&mut self.pointer, 0);
}
asm::dmb();
// reset echo
unsafe {
write_volatile(&mut self.echo, 0);
}
}
// has data been acknowledged?
pub fn acknowledged(&self) -> bool {
let ptr = unsafe {
read_volatile(&self.pointer)
};
// read self.pointer before self.echo, not after
asm::dmb();
let echo = unsafe {
read_volatile(&self.echo)
};
(ptr == 0) || (ptr == echo)
}
}

405
src/main.rs Normal file
View File

@ -0,0 +1,405 @@
#![no_std]
#![no_main]
#![feature(asm)]
#![feature(global_asm)]
#![feature(naked_functions)]
#![feature(compiler_builtins_lib)]
#![feature(never_type)]
// TODO: disallow unused/dead_code when code moves into a lib crate
#![allow(dead_code)]
use core::mem::{uninitialized, transmute};
use core::ptr::write_volatile;
use r0::zero_bss;
use compiler_builtins as _;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
use smoltcp::time::Instant;
use smoltcp::socket::SocketSet;
use mailbox::{MAILBOX_FROM_CORE0, MAILBOX_FROM_CORE1};
mod regs;
mod cortex_a9;
mod clocks;
mod mailbox;
mod mpcore;
mod mutex;
mod slcr;
mod uart;
mod stdio;
mod eth;
use crate::regs::{RegisterR, RegisterW, RegisterRW};
use crate::cortex_a9::{asm, regs::*, mmu};
extern "C" {
static mut __bss_start: u32;
static mut __bss_end: u32;
static mut __stack_start: u32; // refers to the stack for core 0
static mut __stack1_start: u32; // refers to the stack for core 1
}
// program address as u32, for execution after setting up core 1
static mut START_ADDR_CORE1: u32 = 0;
// initial stack pointer for starting core 1
static mut INITIAL_SP_CORE1: u32 = 0; // must be zero (as a flag)
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u32 = 0x3;
match MPIDR.read() & CORE_MASK {
0 => {
// executing on core 0
SP.write(&mut __stack_start as *mut _ as u32);
boot_core0();
}
_ => {
// executing on core 1 (as there are only cores 0 and 1)
while INITIAL_SP_CORE1 == 0 {
// NOTE: This wfe and its loop can be removed as long
// as the regular boot loader remains in place
// (i.e. this program is not written into ROM).
asm::wfe();
}
// the following requires a stack (at least later, for the
// function for setting up the MMU)
SP.write(INITIAL_SP_CORE1);
boot_core1();
}
}
}
#[naked]
#[inline(never)]
unsafe fn boot_core0() -> ! {
l1_cache_init();
// Invalidate SCU, for all cores
mpcore::RegisterBlock::new().scu_invalidate.write(0xffff);
zero_bss(&mut __bss_start, &mut __bss_end);
let mmu_table = mmu::L1Table::get()
.setup_flat_layout();
mmu::with_mmu(mmu_table, || {
// start SCU
mpcore::RegisterBlock::new().scu_control.modify(
|_, w| w.enable(true)
);
// enable SMP (for starting correct SCU operation)
ACTLR.modify(|_, w|
w.smp(true) // SMP mode
.fw(true) // cache and TLB maintenance broadcast on
);
asm::dmb();
asm::dsb();
main();
panic!("return from main");
});
}
#[naked]
#[inline(never)]
unsafe fn boot_core1() -> ! {
l1_cache_init();
// Invalidate SCU, for core1 only
mpcore::RegisterBlock::new().scu_invalidate.write(0x00f0);
// use the MMU L1 Table already set up by core 0
let mmu_table = mmu::L1Table::get();
mmu::with_mmu(mmu_table, || {
// enable SMP (for correct SCU operation)
ACTLR.modify(|_, w|
w.smp(true) // SMP mode
.fw(true) // cache and TLB maintenance broadcast
);
asm::dmb();
asm::dsb();
// now that the MMU is active using the same table as active
// on the other core, one can branch to any normal memory
// location in which the code may reside
asm!("bx r1" :: "{r1}"(START_ADDR_CORE1) :: "volatile");
unreachable!();
});
}
fn l1_cache_init() {
// Invalidate TLBs
tlbiall();
// Invalidate I-Cache
iciallu();
// Invalidate Branch Predictor Array
bpiall();
// Invalidate D-Cache
//
// Note: Do use dcisw rather than dccisw to only invalidate rather
// than also clear (which may write values back into the
// underlying L2 cache or memory!)
//
// use the "made-up instruction" (see definition) dciall()
dciall();
asm::dsb();
asm::isb();
}
fn stop_core1() {
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)
});
});
}
// Execute f on core 1 using the given stack. Note that these
// semantics are inherently unsafe as the stack needs to live longer
// than Rust semantics dictate...hence this method is marked as unsafe
// to remind the caller to take special care (but also many operations
// performed would otherwise require `unsafe` blocks).
unsafe fn run_on_core1(f: fn() -> !, stack: &mut [u32]) {
// reset and stop core 1 (this is safe to repeat, if the caller
// has already performed this)
stop_core1();
// ensure any mailbox access finishes before the mailbox reset
asm::dmb();
// reset the mailbox for sending messages
MAILBOX_FROM_CORE0.reset_discard();
MAILBOX_FROM_CORE1.reset_discard();
// determine address of f and save it as start address for core 1
write_volatile(
&mut START_ADDR_CORE1,
f as *const () as u32
);
write_volatile(
&mut INITIAL_SP_CORE1,
&mut stack[stack.len() - 1] as *const _ as u32
);
// ensure the above is written to cache before it is cleaned
asm::dmb();
// TODO: Is the following necessary, considering that the SCU
// should take care of coherency of all (normal) memory?
//
// clean cache lines containing START_ADDR_CORE1 and
// INITIAL_SP_CORE1
dccmvac(&START_ADDR_CORE1 as *const _ as u32);
dccmvac(&INITIAL_SP_CORE1 as *const _ as u32);
// clean cache lines containing mailboxes
dccmvac(&MAILBOX_FROM_CORE0 as *const _ as u32);
dccmvac(&MAILBOX_FROM_CORE1 as *const _ as u32);
// restart core 1
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)
});
});
}
fn main_core1() -> ! {
let mut data: [u32; 2] = [42, 42];
println!("Core 1 SP: 0x{:X}", SP.read());
loop {
// effectively perform something similar to `println!("from
// core 1");` by passing a message to core 0 and having core 0
// output it via the println! macro
unsafe {
println!("sending from core 1");
MAILBOX_FROM_CORE1.send(&data as *const _ as usize);
while !MAILBOX_FROM_CORE1.acknowledged() {
println!("core 1 waiting for acknowledgement from core 0");
}
}
// change data to make it more interesting
data[1] += 1;
}
}
fn main_core1_program2() -> ! {
let mut data: [u32; 2] = [4200, 4200];
println!("Core 1 SP: 0x{:X}", SP.read());
loop {
unsafe {
MAILBOX_FROM_CORE1.send(&data as *const _ as usize);
while !MAILBOX_FROM_CORE1.acknowledged() {}
}
// change data to make it more interesting
data[0] -= 1;
data[1] += 1;
}
}
// reserve some memory as stack for core1
static mut STACK_CORE1: [u32; 256] = [0; 256];
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
fn main() {
println!("Main.");
println!("Core 0 SP: 0x{:X}", SP.read());
let clocks = clocks::CpuClocks::get();
println!("Clocks: {:?}", clocks);
println!("CPU speeds: {}/{}/{}/{} MHz",
clocks.cpu_6x4x() / 1_000_000,
clocks.cpu_3x2x() / 1_000_000,
clocks.cpu_2x() / 1_000_000,
clocks.cpu_1x() / 1_000_000);
let mut eth = eth::Eth::default(HWADDR.clone());
println!("Eth on");
eth.reset_phy();
// start executing main_core1() on core 1
unsafe {
run_on_core1(main_core1, &mut STACK_CORE1[..]);
}
println!("Started main_core1() on core 1");
for _ in 0..5 {
// wait for data
while unsafe { !MAILBOX_FROM_CORE1.available() } {}
// receive data
let data_ptr = unsafe { MAILBOX_FROM_CORE1.receive() };
println!(
"Received via mailbox from core 1: data {} and {} at address 0x{:X}",
unsafe { (*(data_ptr as *const [u32; 2]))[0] },
unsafe { (*(data_ptr as *const [u32; 2]))[1] },
data_ptr
);
unsafe {
MAILBOX_FROM_CORE1.acknowledge(data_ptr);
}
}
stop_core1();
println!("Stopped core 1.");
// start executing main_core1_program2() on core 1
unsafe {
run_on_core1(main_core1_program2, &mut STACK_CORE1[..]);
}
println!("Started main_core1_program2() on core 1");
for _ in 0..5 {
// wait for data
while unsafe { !MAILBOX_FROM_CORE1.available() } {}
// receive data
let data_ptr = unsafe { MAILBOX_FROM_CORE1.receive() };
println!(
"Received via mailbox from core 1: data {} and {} at address 0x{:X}",
unsafe { (*(data_ptr as *const [u32; 2]))[0] },
unsafe { (*(data_ptr as *const [u32; 2]))[1] },
data_ptr
);
unsafe {
MAILBOX_FROM_CORE1.acknowledge(data_ptr);
}
}
stop_core1();
println!("Stopped core 1.");
const RX_LEN: usize = 1;
let mut rx_descs: [eth::rx::DescEntry; RX_LEN] = unsafe { uninitialized() };
let mut rx_buffers = [[0u8; eth::MTU]; RX_LEN];
const TX_LEN: usize = 1;
let mut tx_descs: [eth::tx::DescEntry; TX_LEN] = unsafe { uninitialized() };
let mut tx_buffers = [[0u8; eth::MTU]; TX_LEN];
let eth = eth.start_rx(&mut rx_descs, &mut rx_buffers);
//let mut eth = eth.start_tx(&mut tx_descs, &mut tx_buffers);
let mut eth = eth.start_tx(
// HACK
unsafe { transmute(tx_descs.as_mut()) },
unsafe { transmute(tx_buffers.as_mut()) },
);
let ethernet_addr = EthernetAddress(HWADDR);
// IP stack
let local_addr = IpAddress::v4(10, 0, 0, 1);
let mut ip_addrs = [IpCidr::new(local_addr, 24)];
let mut neighbor_storage = [None; 16];
let neighbor_cache = NeighborCache::new(&mut neighbor_storage[..]);
let mut iface = EthernetInterfaceBuilder::new(&mut eth)
.ethernet_addr(ethernet_addr)
.ip_addrs(&mut ip_addrs[..])
.neighbor_cache(neighbor_cache)
.finalize();
let mut sockets_storage = [
None, None, None, None,
None, None, None, None
];
let mut sockets = SocketSet::new(&mut sockets_storage[..]);
let mut time = 0u32;
loop {
time += 1;
let timestamp = Instant::from_millis(time.into());
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Err(e) => {
println!("poll error: {}", e);
}
}
// match eth.recv_next() {
// Ok(Some(pkt)) => {
// print!("eth: rx {} bytes", pkt.len());
// for b in pkt.iter() {
// print!(" {:02X}", b);
// }
// println!("");
// }
// Ok(None) => {}
// Err(e) => {
// println!("eth rx error: {:?}", e);
// }
// }
// match eth.send(512) {
// Some(mut pkt) => {
// let mut x = 0;
// for b in pkt.iter_mut() {
// *b = x;
// x += 1;
// }
// println!("eth tx {} bytes", pkt.len());
// }
// None => println!("eth tx shortage"),
// }
}
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
println!("\nPanic: {}", info);
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
loop {}
}
#[no_mangle]
pub unsafe extern "C" fn PrefetchAbort() {
println!("PrefetchAbort");
loop {}
}
#[no_mangle]
pub unsafe extern "C" fn DataAbort() {
println!("DataAbort");
loop {}
}

29
src/mpcore.rs Normal file
View File

@ -0,0 +1,29 @@
///! Register definitions for Application Processing Unit (mpcore)
use volatile_register::{RO, RW, WO};
use crate::{register, register_at, register_bit};
#[repr(C)]
pub struct RegisterBlock {
pub scu_control: ScuControl,
pub scu_config: RO<u32>,
pub scu_cpu_power: RW<u32>,
pub scu_invalidate: WO<u32>,
reserved0: [u32; 12],
pub filter_start: RW<u32>,
pub filter_end: RW<u32>,
reserved1: [u32; 2],
pub scu_access_control: RW<u32>,
pub scu_non_secure_access_control: RW<u32>,
// there is plenty more (unimplemented)
}
register_at!(RegisterBlock, 0xF8F00000, new);
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);

71
src/mutex.rs Normal file
View File

@ -0,0 +1,71 @@
/// Mutex for SMP-safe locking
use crate::cortex_a9::asm;
pub struct Mutex {
state: u32,
}
const UNLOCKED_MUTEX: u32 = 0;
const LOCKED_MUTEX: u32 = 1;
impl Mutex {
pub const fn new_unlocked() -> Mutex {
Mutex { state: UNLOCKED_MUTEX }
}
pub const fn new_locked() -> Mutex {
Mutex { state: LOCKED_MUTEX }
}
// inlining causes problems with the labels
#[inline(never)]
pub fn acquire(&mut self) {
unsafe {
// code adapted from an example by ARM at
// http://infocenter.arm.com (Home > ARM Synchronization
// Primitives > Practical uses > Implementing a mutex)
asm!("
mutex_acquire_label1:
ldrex r2, [$0];
cmp r2, $1;
beq mutex_acquire_label2;
strexne r2, $1, [$0];
cmpne r2, 1;
beq mutex_acquire_label1;
dmb;
b mutex_acquire_label3;
mutex_acquire_label2:
wfe;
b mutex_acquire_label1;
mutex_acquire_label3: ;
"
::
// inputs
"r" (&mut self.state as *mut _ as u32), "r" (LOCKED_MUTEX)
:
// clobbers
"r2"
:
"volatile"
);
}
}
pub fn release(&mut self) {
unsafe {
asm!("
dmb;
str $1, [$0];
dsb;
sev;
"
::
// inputs
"r" (&mut self.state as *mut _ as u32), "r" (UNLOCKED_MUTEX)
::
"volatile"
);
}
}
}

View File

@ -1,11 +1,9 @@
//! Type-safe interface to peripheral registers akin to the code that
//! svd2rust generates.
#![allow(unused)]
#![no_std]
pub use vcell::VolatileCell;
pub use volatile_register::{RO, WO, RW};
pub use bit_field::BitField;
use volatile_register::{RO, WO, RW};
use bit_field::BitField;
/// A readable register
pub trait RegisterR {
@ -37,11 +35,9 @@ macro_rules! register_common {
}
pub mod $mod_name {
#[derive(Clone)]
pub struct Read {
pub inner: $inner,
}
#[derive(Clone)]
pub struct Write {
pub inner: $inner,
}
@ -52,10 +48,9 @@ macro_rules! register_common {
#[macro_export]
macro_rules! register_r {
($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterR for $struct_name {
impl crate::regs::RegisterR for $struct_name {
type R = $mod_name::Read;
#[inline]
fn read(&self) -> Self::R {
let inner = self.inner.read();
$mod_name::Read { inner }
@ -67,15 +62,13 @@ macro_rules! register_r {
#[macro_export]
macro_rules! register_w {
($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterW for $struct_name {
impl crate::regs::RegisterW for $struct_name {
type W = $mod_name::Write;
#[inline]
fn zeroed() -> $mod_name::Write {
$mod_name::Write { inner: 0 }
}
#[inline]
fn write(&mut self, w: Self::W) {
unsafe {
self.inner.write(w.inner);
@ -88,8 +81,7 @@ macro_rules! register_w {
#[macro_export]
macro_rules! register_rw {
($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterRW for $struct_name {
#[inline]
impl crate::regs::RegisterRW for $struct_name {
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
unsafe {
self.inner.modify(|inner| {
@ -100,57 +92,6 @@ macro_rules! register_rw {
}
}
);
($mod_name: ident, $struct_name: ident, $mask: expr) => (
impl libregister::RegisterRW for $struct_name {
#[inline]
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
unsafe {
self.inner.modify(|inner| {
f($mod_name::Read { inner }, $mod_name::Write { inner: inner & ($mask) })
.inner
});
}
}
}
);
}
#[doc(hidden)]
#[macro_export]
macro_rules! register_vcell {
($mod_name: ident, $struct_name: ident) => (
impl libregister::RegisterR for $struct_name {
type R = $mod_name::Read;
#[inline]
fn read(&self) -> Self::R {
let inner = self.inner.get();
$mod_name::Read { inner }
}
}
impl libregister::RegisterW for $struct_name {
type W = $mod_name::Write;
#[inline]
fn zeroed() -> $mod_name::Write {
$mod_name::Write { inner: 0 }
}
#[inline]
fn write(&mut self, w: Self::W) {
self.inner.set(w.inner);
}
}
impl libregister::RegisterRW for $struct_name {
#[inline]
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
let r = self.read();
let w = $mod_name::Write { inner: r.inner };
let w = f(r, w);
self.write(w);
}
}
);
}
/// Main macro for register definition
@ -158,36 +99,22 @@ macro_rules! register_vcell {
macro_rules! register {
// Define read-only register
($mod_name: ident, $struct_name: ident, RO, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, libregister::RO<$inner>, $inner);
libregister::register_r!($mod_name, $struct_name);
crate::register_common!($mod_name, $struct_name, volatile_register::RO<$inner>, $inner);
crate::register_r!($mod_name, $struct_name);
);
// Define write-only register
($mod_name: ident, $struct_name: ident, WO, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, volatile_register::WO<$inner>, $inner);
libregister::register_w!($mod_name, $struct_name);
crate::register_common!($mod_name, $struct_name, volatile_register::WO<$inner>, $inner);
crate::register_w!($mod_name, $struct_name);
);
// Define read-write register
($mod_name: ident, $struct_name: ident, RW, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner);
libregister::register_r!($mod_name, $struct_name);
libregister::register_w!($mod_name, $struct_name);
libregister::register_rw!($mod_name, $struct_name);
);
// Define read-write register
($mod_name: ident, $struct_name: ident, VolatileCell, $inner: ty) => (
libregister::register_common!($mod_name, $struct_name, VolatileCell<$inner>, $inner);
libregister::register_vcell!($mod_name, $struct_name);
);
// Define read-write register with mask on write (for WTC mixed access.)
($mod_name: ident, $struct_name: ident, RW, $inner: ty, $mask: expr) => (
libregister::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner);
libregister::register_r!($mod_name, $struct_name);
libregister::register_w!($mod_name, $struct_name);
libregister::register_rw!($mod_name, $struct_name, $mask);
crate::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner);
crate::register_r!($mod_name, $struct_name);
crate::register_w!($mod_name, $struct_name);
crate::register_rw!($mod_name, $struct_name);
);
}
@ -198,7 +125,6 @@ macro_rules! register_bit {
$(#[$outer])*
impl $mod_name::Read {
#[allow(unused)]
#[inline]
pub fn $name(&self) -> bool {
use bit_field::BitField;
@ -209,7 +135,6 @@ macro_rules! register_bit {
$(#[$outer])*
impl $mod_name::Write {
#[allow(unused)]
#[inline]
pub fn $name(mut self, value: bool) -> Self {
use bit_field::BitField;
@ -218,47 +143,6 @@ macro_rules! register_bit {
}
}
);
// Single bit read-only
($mod_name: ident, $(#[$outer:meta])* $name: ident, $bit: expr, RO) => (
$(#[$outer])*
impl $mod_name::Read {
#[allow(unused)]
#[inline]
pub fn $name(&self) -> bool {
use bit_field::BitField;
self.inner.get_bit($bit)
}
}
);
// Single bit write to clear. Note that this must be used with WTC register.
($mod_name: ident, $(#[$outer:meta])* $name: ident, $bit: expr, WTC) => (
$(#[$outer])*
impl $mod_name::Read {
#[allow(unused)]
#[inline]
pub fn $name(&self) -> bool {
use bit_field::BitField;
self.inner.get_bit($bit)
}
}
$(#[$outer])*
impl $mod_name::Write {
/// Clear bit field. (WTC)
#[allow(unused)]
#[inline]
pub fn $name(mut self) -> Self {
use bit_field::BitField;
self.inner.set_bit($bit, true);
self
}
}
);
}
/// Define a multi-bit field of a register
@ -267,7 +151,6 @@ macro_rules! register_bits {
($mod_name: ident, $(#[$outer:meta])* $name: ident, $type: ty, $bit_begin: expr, $bit_end: expr) => (
impl $mod_name::Read {
#[allow(unused)]
#[inline]
$(#[$outer])*
pub fn $name(&self) -> $type {
use bit_field::BitField;
@ -280,7 +163,6 @@ macro_rules! register_bits {
$(#[$outer])*
impl $mod_name::Write {
#[allow(unused)]
#[inline]
pub fn $name(mut self, value: $type) -> Self {
use bit_field::BitField;
@ -300,7 +182,6 @@ macro_rules! register_bits_typed {
($mod_name: ident, $(#[$outer:meta])* $name: ident, $bit_type: ty, $type: ty, $bit_begin: expr, $bit_end: expr) => (
impl $mod_name::Read {
#[allow(unused)]
#[inline]
$(#[$outer])*
pub fn $name(&self) -> $type {
use bit_field::BitField;
@ -312,7 +193,6 @@ macro_rules! register_bits_typed {
impl $mod_name::Write {
#[allow(unused)]
#[inline]
$(#[$outer])*
pub fn $name(mut self, value: $type) -> Self {
use bit_field::BitField;
@ -330,7 +210,6 @@ macro_rules! register_at {
($name: ident, $addr: expr, $ctor: ident) => (
impl $name {
#[allow(unused)]
#[inline]
pub fn $ctor() -> &'static mut Self {
let addr = $addr as *mut Self;
unsafe { &mut *addr }

View File

@ -1,11 +1,9 @@
///! Register definitions for System Level Control
use volatile_register::{RO, RW};
use libregister::{
register, register_at,
register_bit, register_bits, register_bits_typed,
RegisterW, RegisterRW,
};
use crate::{register, register_at,
register_bit, register_bits, register_bits_typed,
regs::RegisterW, regs::RegisterRW};
#[repr(u8)]
pub enum PllSource {
@ -21,52 +19,6 @@ pub enum ArmPllSource {
IoPll = 0b11,
}
#[repr(u8)]
pub enum DdriobInputType {
Off = 0b00,
/// For SSTL, HSTL
VrefDifferential = 0b01,
Differential = 0b10,
Lvcmos = 0b11,
}
#[repr(u8)]
pub enum DdriobDciType {
/// DDR2/3L Addr and Clock
Disabled = 0b00,
/// LPDDR2
Drive = 0b01,
/// DDR2/3/3L Data and Diff
Termination = 0b11,
}
#[repr(u8)]
pub enum DdriobOutputEn {
Ibuf = 0b00,
Obuf = 0b11,
}
#[repr(u8)]
pub enum DdriobVrefSel {
/// For LPDDR2 with 1.2V IO
Vref0_6V = 0b0001,
/// For DDR3L with 1.35V IO
Vref0_675V = 0b0010,
/// For DDR3 with 1.5V IO
Vref0_75V = 0b0100,
/// For DDR2 with 1.8V IO
Vref0_9V = 0b1000,
}
#[repr(u8)]
pub enum LevelShifterEnable {
DisableAll = 0x0,
EnablePsToPl = 0xA,
EnableAll = 0xF,
}
#[repr(C)]
pub struct RegisterBlock {
pub scl: RW<u32>,
@ -77,14 +29,14 @@ pub struct RegisterBlock {
pub arm_pll_ctrl: PllCtrl,
pub ddr_pll_ctrl: PllCtrl,
pub io_pll_ctrl: PllCtrl,
pub pll_status: PllStatus,
pub arm_pll_cfg: PllCfg,
pub ddr_pll_cfg: PllCfg,
pub io_pll_cfg: PllCfg,
pub pll_status: RO<u32>,
pub arm_pll_cfg: RW<u32>,
pub ddr_pll_cfg: RW<u32>,
pub io_pll_cfg: RW<u32>,
reserved1: [u32; 1],
pub arm_clk_ctrl: ArmClkCtrl,
pub ddr_clk_ctrl: DdrClkCtrl,
pub dci_clk_ctrl: DciClkCtrl,
pub ddr_clk_ctrl: RW<u32>,
pub dci_clk_ctrl: RW<u32>,
pub aper_clk_ctrl: AperClkCtrl,
pub usb0_clk_ctrl: RW<u32>,
pub usb1_clk_ctrl: RW<u32>,
@ -93,8 +45,8 @@ pub struct RegisterBlock {
pub gem0_clk_ctrl: GemClkCtrl,
pub gem1_clk_ctrl: GemClkCtrl,
pub smc_clk_ctrl: RW<u32>,
pub lqspi_clk_ctrl: LqspiClkCtrl,
pub sdio_clk_ctrl: SdioClkCtrl,
pub lqspi_clk_ctrl: RW<u32>,
pub sdio_clk_ctrl: RW<u32>,
pub uart_clk_ctrl: UartClkCtrl,
pub spi_clk_ctrl: RW<u32>,
pub can_clk_ctrl: RW<u32>,
@ -102,19 +54,19 @@ pub struct RegisterBlock {
pub dbg_clk_ctrl: RW<u32>,
pub pcap_clk_ctrl: RW<u32>,
pub topsw_clk_ctrl: RW<u32>,
pub fpga0_clk_ctrl: Fpga0ClkCtrl,
pub fpga0_clk_ctrl: RW<u32>,
pub fpga0_thr_ctrl: RW<u32>,
pub fpga0_thr_cnt: RW<u32>,
pub fpga0_thr_sta: RO<u32>,
pub fpga1_clk_ctrl: Fpga1ClkCtrl,
pub fpga1_clk_ctrl: RW<u32>,
pub fpga1_thr_ctrl: RW<u32>,
pub fpga1_thr_cnt: RW<u32>,
pub fpga1_thr_sta: RO<u32>,
pub fpga2_clk_ctrl: Fpga2ClkCtrl,
pub fpga2_clk_ctrl: RW<u32>,
pub fpga2_thr_ctrl: RW<u32>,
pub fpga2_thr_cnt: RW<u32>,
pub fpga2_thr_sta: RO<u32>,
pub fpga3_clk_ctrl: Fpga3ClkCtrl,
pub fpga3_clk_ctrl: RW<u32>,
pub fpga3_thr_ctrl: RW<u32>,
pub fpga3_thr_cnt: RW<u32>,
pub fpga3_thr_sta: RO<u32>,
@ -127,23 +79,23 @@ pub struct RegisterBlock {
pub dmac_rst_ctrl: RW<u32>,
pub usb_rst_ctrl: RW<u32>,
pub gem_rst_ctrl: RW<u32>,
pub sdio_rst_ctrl: SdioRstCtrl,
pub sdio_rst_ctrl: RW<u32>,
pub spi_rst_ctrl: RW<u32>,
pub can_rst_ctrl: RW<u32>,
pub i2c_rst_ctrl: RW<u32>,
pub uart_rst_ctrl: UartRstCtrl,
pub gpio_rst_ctrl: GpioRstCtrl,
pub lqspi_rst_ctrl: LqspiRstCtrl,
pub gpio_rst_ctrl: RW<u32>,
pub lqspi_rst_ctrl: RW<u32>,
pub smc_rst_ctrl: RW<u32>,
pub ocm_rst_ctrl: RW<u32>,
reserved4: [u32; 1],
pub fpga_rst_ctrl: FpgaRstCtrl,
pub fpga_rst_ctrl: RW<u32>,
pub a9_cpu_rst_ctrl: A9CpuRstCtrl,
reserved5: [u32; 1],
pub rs_awdt_ctrl: RW<u32>,
reserved6: [u32; 2],
pub reboot_status: RW<u32>,
pub boot_mode: BootMode,
pub boot_mode: RW<u32>,
reserved7: [u32; 40],
pub apu_ctrl: RW<u32>,
pub wdt_clk_sel: RW<u32>,
@ -226,7 +178,7 @@ pub struct RegisterBlock {
pub sd0_wp_cd_sel: RW<u32>,
pub sd1_wp_cd_sel: RW<u32>,
reserved17: [u32; 50],
pub lvl_shftr_en: LvlShftr,
pub lvl_shftr_en: RW<u32>,
reserved18: [u32; 3],
pub ocm_cfg: RW<u32>,
reserved19: [u32; 123],
@ -238,25 +190,23 @@ pub struct RegisterBlock {
pub gpiob_cfg_hstl: RW<u32>,
pub gpiob_drvr_bias_ctrl: RW<u32>,
reserved21: [u32; 9],
pub ddriob_addr0: DdriobConfig,
pub ddriob_addr1: DdriobConfig,
pub ddriob_data0: DdriobConfig,
pub ddriob_data1: DdriobConfig,
pub ddriob_diff0: DdriobConfig,
pub ddriob_diff1: DdriobConfig,
pub ddriob_clock: DdriobConfig,
pub ddriob_drive_slew_addr: RW<u32>,
pub ddriob_drive_slew_data: RW<u32>,
pub ddriob_drive_slew_diff: RW<u32>,
pub ddriob_drive_slew_clock: RW<u32>,
pub ddriob_ddr_ctrl: DdriobDdrCtrl,
pub ddriob_dci_ctrl: DdriobDciCtrl,
pub ddriob_dci_status: DdriobDciStatus,
pub ddriob_addr1: RW<u32>,
pub ddriob_data0: RW<u32>,
pub ddriob_data1: RW<u32>,
pub ddriob_diff0: RW<u32>,
pub ddriob_diff1: RW<u32>,
pub ddriob_clock: RW<u32>,
pub w_addr: RW<u32>,
pub w_data: RW<u32>,
pub w_diff: RW<u32>,
pub w_clock: RW<u32>,
pub ddriob_ddr_ctrl: RW<u32>,
pub ddriob_dci_ctrl: RW<u32>,
pub ddriob_dci_status: RW<u32>,
}
register_at!(RegisterBlock, 0xF8000000, new);
impl RegisterBlock {
/// Required to modify any sclr register
pub fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
let mut self_ = Self::new();
self_.slcr_unlock.unlock();
@ -265,35 +215,11 @@ impl RegisterBlock {
r
}
pub fn init_preload_fpga(&mut self) {
// Assert FPGA top level output resets
self.fpga_rst_ctrl.write(
FpgaRstCtrl::zeroed()
.fpga0_out_rst(true)
.fpga1_out_rst(true)
.fpga2_out_rst(true)
.fpga3_out_rst(true)
);
// Disable level shifters
self.lvl_shftr_en.write(
LvlShftr::zeroed()
);
// Enable output level shifters
self.lvl_shftr_en.write(
LvlShftr::zeroed()
.user_lvl_shftr_en(LevelShifterEnable::EnablePsToPl)
);
}
pub fn init_postload_fpga(&mut self) {
// Enable level shifters
self.lvl_shftr_en.write(
LvlShftr::zeroed()
.user_lvl_shftr_en(LevelShifterEnable::EnableAll)
);
// Deassert AXI interface resets
self.fpga_rst_ctrl.write(
FpgaRstCtrl::zeroed()
/// Perform a soft reset
pub fn soft_reset(&mut self) {
self.pss_rst_ctrl.write(
PssRstCtrl::zeroed()
.soft_rst(true)
);
}
}
@ -321,38 +247,12 @@ impl SlcrUnlock {
}
register!(pll_ctrl, PllCtrl, RW, u32);
register_bits!(pll_ctrl, pll_fdiv, u16, 12, 18);
register_bits!(pll_ctrl, pll_fdiv, u8, 12, 18);
register_bit!(pll_ctrl, pll_bypass_force, 4);
register_bit!(pll_ctrl, pll_bypass_qual, 3);
register_bit!(pll_ctrl, pll_pwrdwn, 1);
register_bit!(pll_ctrl, pll_reset, 0);
register!(pll_status, PllStatus, RO, u32);
register_bit!(pll_status, arm_pll_lock, 0);
register_bit!(pll_status, ddr_pll_lock, 1);
register_bit!(pll_status, io_pll_lock, 2);
register_bit!(pll_status, arm_pll_stable, 3);
register_bit!(pll_status, ddr_pll_stable, 4);
register_bit!(pll_status, io_pll_stable, 5);
impl core::fmt::Display for pll_status::Read {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
write!(fmt, "ARM: {}/{} DDR: {}/{} IO: {}/{}",
if self.arm_pll_lock() { "locked" } else { "NOT locked" },
if self.arm_pll_stable() { "stable" } else { "UNSTABLE" },
if self.ddr_pll_lock() { "locked" } else { "NOT locked" },
if self.ddr_pll_stable() { "stable" } else { "UNSTABLE" },
if self.io_pll_lock() { "locked" } else { "NOT locked" },
if self.io_pll_stable() { "stable" } else { "UNSTABLE" },
)
}
}
register!(pll_cfg, PllCfg, RW, u32);
register_bits!(pll_cfg, pll_res, u8, 4, 7);
register_bits!(pll_cfg, pll_cp, u8, 8, 11);
register_bits!(pll_cfg, lock_cnt, u16, 12, 21);
register!(arm_clk_ctrl, ArmClkCtrl, RW, u32);
register_bit!(arm_clk_ctrl,
/// Clock active
@ -361,21 +261,8 @@ register_bit!(arm_clk_ctrl, cpu_1xclkact, 27);
register_bit!(arm_clk_ctrl, cpu_2xclkact, 26);
register_bit!(arm_clk_ctrl, cpu_3or2xclkact, 25);
register_bit!(arm_clk_ctrl, cpu_6or4xclkact, 24);
register_bits!(arm_clk_ctrl,
/// should be divisible by 2 (see TRM: 25.2 CPU Clock)
divisor, u8, 8, 13);
register_bits_typed!(arm_clk_ctrl, srcsel, u8, ArmPllSource, 4, 5);
register!(ddr_clk_ctrl, DdrClkCtrl, RW, u32);
register_bit!(ddr_clk_ctrl, ddr_3xclkact, 0);
register_bit!(ddr_clk_ctrl, ddr_2xclkact, 1);
register_bits!(ddr_clk_ctrl, ddr_3xclk_divisor, u8, 20, 25);
register_bits!(ddr_clk_ctrl, ddr_2xclk_divisor, u8, 26, 31);
register!(dci_clk_ctrl, DciClkCtrl, RW, u32);
register_bit!(dci_clk_ctrl, clkact, 0);
register_bits!(dci_clk_ctrl, divisor0, u8, 8, 13);
register_bits!(dci_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(arm_clk_ctrl, divisor, u8, 8, 13);
register_bits_typed!(arm_clk_ctrl, srcsel, u8, ArmPllSource, 8, 13);
register!(clk_621_true, Clk621True, RW, u32);
register_bit!(clk_621_true, clk_621_true, 0);
@ -383,8 +270,6 @@ register_bit!(clk_621_true, clk_621_true, 0);
register!(aper_clk_ctrl, AperClkCtrl, RW, u32);
register_bit!(aper_clk_ctrl, uart1_cpu_1xclkact, 21);
register_bit!(aper_clk_ctrl, uart0_cpu_1xclkact, 20);
register_bit!(aper_clk_ctrl, sdio1_cpu_1xclkact, 11);
register_bit!(aper_clk_ctrl, sdio0_cpu_1xclkact, 10);
impl AperClkCtrl {
pub fn enable_uart0(&mut self) {
self.modify(|_, w| w.uart0_cpu_1xclkact(true));
@ -393,14 +278,6 @@ impl AperClkCtrl {
pub fn enable_uart1(&mut self) {
self.modify(|_, w| w.uart1_cpu_1xclkact(true));
}
pub fn enable_sdio0(&mut self) {
self.modify(|_, w| w.sdio0_cpu_1xclkact(true));
}
pub fn enable_sdio1(&mut self) {
self.modify(|_, w| w.sdio1_cpu_1xclkact(true));
}
}
register!(rclk_ctrl, RclkCtrl, RW, u32);
@ -420,29 +297,11 @@ register_bits!(gem_clk_ctrl,
divisor, u8, 8, 13);
register_bits_typed!(gem_clk_ctrl,
/// Source to generate the ref clock
srcsel, u8, PllSource, 4, 6);
srcsel, u8, PllSource, 4, 5);
register_bit!(gem_clk_ctrl,
/// SMC reference clock control
clkact, 0);
register!(sdio_clk_ctrl, SdioClkCtrl, RW, u32);
register_bit!(sdio_clk_ctrl, clkact0, 0);
register_bit!(sdio_clk_ctrl, clkact1, 1);
register_bits!(sdio_clk_ctrl, divisor, u8, 8, 13);
register_bits_typed!(sdio_clk_ctrl, srcsel, u8, PllSource, 4, 5);
impl SdioClkCtrl {
pub fn enable_sdio0(&mut self) {
self.modify(|_, w| {
w.divisor(0x14).srcsel(PllSource::IoPll).clkact0(true)
})
}
pub fn enable_sdio1(&mut self) {
self.modify(|_, w| {
w.divisor(0x14).srcsel(PllSource::IoPll).clkact1(true)
})
}
}
register!(uart_clk_ctrl, UartClkCtrl, RW, u32);
register_bit!(uart_clk_ctrl, clkact0, 0);
register_bit!(uart_clk_ctrl, clkact1, 1);
@ -473,34 +332,6 @@ impl UartClkCtrl {
}
}
register!(sdio_rst_ctrl, SdioRstCtrl, RW, u32);
register_bit!(sdio_rst_ctrl, sdio1_ref_rst, 5);
register_bit!(sdio_rst_ctrl, sdio0_ref_rst, 4);
register_bit!(sdio_rst_ctrl, sdio1_cpu1x_rst, 1);
register_bit!(sdio_rst_ctrl, sdio0_cpu1x_rst, 0);
impl SdioRstCtrl {
pub fn reset_sdio0(&mut self) {
self.modify(|_, w|
w.sdio0_ref_rst(true)
.sdio0_cpu1x_rst(true)
);
self.modify(|_, w|
w.sdio0_ref_rst(false)
.sdio0_cpu1x_rst(false)
);
}
pub fn reset_sdio1(&mut self) {
self.modify(|_, w|
w.sdio1_ref_rst(true)
.sdio1_cpu1x_rst(true)
);
self.modify(|_, w|
w.sdio1_ref_rst(false)
.sdio1_cpu1x_rst(false)
);
}
}
register!(uart_rst_ctrl, UartRstCtrl, RW, u32);
register_bit!(uart_rst_ctrl, uart0_ref_rst, 3);
register_bit!(uart_rst_ctrl, uart1_ref_rst, 2);
@ -531,54 +362,8 @@ impl UartRstCtrl {
}
}
register!(gpio_rst_ctrl, GpioRstCtrl, RW, u32);
register_bit!(gpio_rst_ctrl, gpio_cpu1x_rst, 0);
register_at!(GpioRstCtrl, 0xF800022C, new);
impl GpioRstCtrl {
pub fn reset_gpio(&mut self) {
self.modify(|_, w|
w.gpio_cpu1x_rst(true)
);
self.modify(|_, w|
w.gpio_cpu1x_rst(false)
);
}
}
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
register_bit!(lqspi_clk_ctrl, clkact, 0);
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register_bits!(lqspi_clk_ctrl, divisor, u8, 8, 13);
register!(lqspi_rst_ctrl, LqspiRstCtrl, RW, u32);
register_bit!(lqspi_rst_ctrl, ref_rst, 1);
register_bit!(lqspi_rst_ctrl, cpu1x_rst, 0);
register!(fpga0_clk_ctrl, Fpga0ClkCtrl, RW, u32);
register_bits!(fpga0_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga0_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga0_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga1_clk_ctrl, Fpga1ClkCtrl, RW, u32);
register_bits!(fpga1_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga1_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga1_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga2_clk_ctrl, Fpga2ClkCtrl, RW, u32);
register_bits!(fpga2_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga2_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga2_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga3_clk_ctrl, Fpga3ClkCtrl, RW, u32);
register_bits!(fpga3_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga3_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga3_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga_rst_ctrl, FpgaRstCtrl, RW, u32);
register_bit!(fpga_rst_ctrl, fpga0_out_rst, 0);
register_bit!(fpga_rst_ctrl, fpga1_out_rst, 1);
register_bit!(fpga_rst_ctrl, fpga2_out_rst, 2);
register_bit!(fpga_rst_ctrl, fpga3_out_rst, 3);
register!(pss_rst_ctrl, PssRstCtrl, RW, u32);
register_bit!(pss_rst_ctrl, soft_rst, 1);
register!(a9_cpu_rst_ctrl, A9CpuRstCtrl, RW, u32);
register_bit!(a9_cpu_rst_ctrl, peri_rst, 8);
@ -587,23 +372,6 @@ register_bit!(a9_cpu_rst_ctrl, a9_clkstop0, 4);
register_bit!(a9_cpu_rst_ctrl, a9_rst1, 1);
register_bit!(a9_cpu_rst_ctrl, a9_rst0, 0);
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
pub enum BootModePins {
Jtag = 0b000,
Nor = 0b001,
Nand = 0b010,
QuadSpi = 0b100,
SdCard = 0b110,
}
register!(boot_mode, BootMode, RO, u32);
register_bit!(boot_mode, pll_bypass, 4);
register_bits_typed!(boot_mode, boot_mode_pins, u8, BootModePins, 0, 3);
register!(pss_rst_ctrl, PssRstCtrl, RW, u32);
register_bit!(pss_rst_ctrl, soft_rst, 1);
/// Used for MioPin*.io_type
#[repr(u8)]
pub enum IoBufferType {
@ -683,39 +451,5 @@ mio_pin_register!(mio_pin_51, MioPin51);
mio_pin_register!(mio_pin_52, MioPin52);
mio_pin_register!(mio_pin_53, MioPin53);
register!(lvl_shftr, LvlShftr, RW, u32);
register_bits_typed!(lvl_shftr, user_lvl_shftr_en, u8, LevelShifterEnable, 0, 3);
register!(gpiob_ctrl, GpiobCtrl, RW, u32);
register_bit!(gpiob_ctrl, vref_en, 0);
register!(ddriob_config, DdriobConfig, RW, u32);
register_bits_typed!(ddriob_config, inp_type, u8, DdriobInputType, 1, 2);
register_bit!(ddriob_config, dci_update_b, 3);
register_bit!(ddriob_config, term_en, 4);
register_bits_typed!(ddriob_config, dci_type, u8, DdriobDciType, 5, 6);
register_bit!(ddriob_config, ibuf_disable_mode, 7);
register_bit!(ddriob_config, term_disable_mode, 8);
register_bits_typed!(ddriob_config, output_en, u8, DdriobOutputEn, 9, 10);
register_bit!(ddriob_config, pullup_en, 11);
register!(ddriob_ddr_ctrl, DdriobDdrCtrl, RW, u32);
register_bit!(ddriob_ddr_ctrl, vref_int_en, 0);
register_bits_typed!(ddriob_ddr_ctrl, vref_sel, u8, DdriobVrefSel, 1, 4);
register_bit!(ddriob_ddr_ctrl, vref_ext_en_lower, 5);
register_bit!(ddriob_ddr_ctrl, vref_ext_en_upper, 6);
register_bit!(ddriob_ddr_ctrl, refio_en, 9);
register!(ddriob_dci_ctrl, DdriobDciCtrl, RW, u32);
register_bit!(ddriob_dci_ctrl, reset, 0);
register_bit!(ddriob_dci_ctrl, enable, 1);
register_bits!(ddriob_dci_ctrl, nref_opt1, u8, 6, 7);
register_bits!(ddriob_dci_ctrl, nref_opt2, u8, 8, 10);
register_bits!(ddriob_dci_ctrl, nref_opt4, u8, 11, 13);
register_bits!(ddriob_dci_ctrl, pref_opt1, u8, 14, 15);
register_bits!(ddriob_dci_ctrl, pref_opt2, u8, 17, 19);
register_bit!(ddriob_dci_ctrl, update_control, 20);
register!(ddriob_dci_status, DdriobDciStatus, RW, u32);
register_bit!(ddriob_dci_status, done, 0);
register_bit!(ddriob_dci_status, lock, 13);

53
src/stdio.rs Normal file
View File

@ -0,0 +1,53 @@
use crate::uart::Uart;
use crate::mutex::Mutex;
const UART_RATE: u32 = 115_200;
static mut UART: Option<Uart> = None;
static mut UART_MUTEX: Mutex = Mutex::new_unlocked();
#[doc(hidden)]
fn get_uart() -> &'static mut Uart {
unsafe {
match &mut UART {
None => {
let uart = Uart::serial(UART_RATE);
UART = Some(uart);
UART.as_mut().unwrap()
}
Some(uart) => uart,
}
}
}
// call f(UART) with UART locked via UART_MUTEX
pub fn with_uart<F>(f: F) where F: Fn(&mut Uart) -> () {
unsafe {
UART_MUTEX.acquire();
}
f(get_uart());
unsafe {
UART_MUTEX.release();
}
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
crate::stdio::with_uart(|uart| {
use core::fmt::Write;
let _ = write!(uart, $($arg)*);
});
})
}
#[macro_export]
macro_rules! println {
($($arg:tt)*) => ({
crate::stdio::with_uart(|uart| {
use core::fmt::Write;
let _ = write!(uart, $($arg)*);
let _ = write!(uart, "\r\n");
while !uart.tx_fifo_empty() {}
});
})
}

View File

@ -1,4 +1,4 @@
use libregister::*;
use crate::regs::*;
use super::regs::{RegisterBlock, BaudRateGen, BaudRateDiv};
const BDIV_MIN: u32 = 4;

View File

@ -1,9 +1,8 @@
use core::fmt;
use void::Void;
use libregister::*;
use super::slcr;
use super::clocks::Clocks;
use crate::regs::*;
use crate::slcr;
use crate::clocks::CpuClocks;
mod regs;
mod baud_rate_gen;
@ -111,7 +110,7 @@ impl Uart {
self.disable_rx();
self.disable_tx();
let clocks = Clocks::get();
let clocks = CpuClocks::get();
baud_rate_gen::configure(self.regs, clocks.uart_ref_clk(), baudrate);
// Enable controller
@ -192,42 +191,18 @@ impl Uart {
self.regs.channel_sts.read().txfull()
}
pub fn tx_idle(&self) -> bool {
let status = self.regs.channel_sts.read();
status.txempty() && !status.tactive()
pub fn tx_fifo_empty(&self) -> bool {
self.regs.channel_sts.read().txempty()
}
}
impl fmt::Write for Uart {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
while !self.tx_fifo_empty() {}
for b in s.bytes() {
self.write_byte(b);
}
Ok(())
}
}
/// embedded_hal async API
impl embedded_hal::serial::Write<u8> for Uart {
type Error = Void;
fn write(&mut self, b: u8) -> nb::Result<(), Void> {
if self.tx_fifo_full() {
Err(nb::Error::WouldBlock)
} else {
self.write_byte(b);
Ok(())
}
}
fn flush(&mut self) -> nb::Result<(), Void> {
if self.tx_idle() {
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
/// embedded_hal sync API
impl embedded_hal::blocking::serial::write::Default<u8> for Uart {}

View File

@ -1,11 +1,7 @@
use volatile_register::{RO, WO, RW};
use libregister::{
register, register_at,
register_bit, register_bits, register_bits_typed,
};
use crate::{register, register_bit, register_bits, register_bits_typed, register_at};
#[allow(unused)]
#[repr(u8)]
pub enum ChannelMode {
Normal = 0b00,
@ -14,7 +10,6 @@ pub enum ChannelMode {
RemoteLoopback = 0b11,
}
#[allow(unused)]
#[repr(u8)]
pub enum ParityMode {
EvenParity = 0b000,
@ -24,7 +19,6 @@ pub enum ParityMode {
None = 0b100,
}
#[allow(unused)]
#[repr(u8)]
pub enum StopBits {
One = 0b00,

36
tmux.sh
View File

@ -1,36 +0,0 @@
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p gdb openocd cgdb tmux
SESSION=$USER
if [ $1 -eq 0 ]
then
tmux -2 new-session -d -s $SESSION
tmux new-window -t $SESSION:1 -n 'ZC706'
tmux split-window -h
tmux select-pane -t 0
tmux send-keys "stty 115200 < /dev/ttyUSB1 && cat /dev/ttyUSB1" C-m
tmux select-pane -t 1
tmux send-keys "sleep 10 && cgdb zc706.elf -x openocd/gdb-zynq-commands" C-m
tmux split-window -v
tmux resize-pane -D 20
tmux send-keys "cd openocd && openocd -f zc706.cfg -c reset init" C-m
else
tmux -2 new-session -d -s $SESSION
tmux new-window -t $SESSION:1 -n 'CORA Z7'
tmux split-window -h
tmux select-pane -t 0
tmux send-keys "stty 115200 < /dev/ttyUSB1 && cat /dev/ttyUSB1" C-m
tmux select-pane -t 1
tmux send-keys "sleep 10 && cgdb target/armv7-none-eabihf/release/zc706-experiments -x openocd/gdb-zynq-commands" C-m
tmux split-window -v
tmux resize-pane -D 20
tmux send-keys "cd openocd && openocd -f cora-z7-10.cfg -c reset init" C-m
fi
# Set default window
tmux select-window -t $SESSION:1
# Set focus on gdb
tmux select-pane -t $SESSION:.-
# Attach to session
tmux -2 attach-session -t $SESSION