forked from M-Labs/zynq-rs
Compare commits
3 Commits
master
...
multiproce
Author | SHA1 | Date | |
---|---|---|---|
|
606fef6d5c | ||
|
1f4add397b | ||
|
60bab77a19 |
@ -1,5 +1,6 @@
|
|||||||
[target.armv7-none-eabihf]
|
[target.armv7-none-eabihf]
|
||||||
runner = "./runner.sh"
|
runner = "./runner.sh"
|
||||||
|
linker = "arm-none-eabihf-gcc"
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C", "link-arg=-Tlink.x",
|
"-C", "link-arg=-Tlink.x",
|
||||||
"-C", "target-feature=a9,armv7-a,neon",
|
"-C", "target-feature=a9,armv7-a,neon",
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/target
|
|
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -16,56 +16,9 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libboard_zc706"
|
name = "compiler_builtins"
|
||||||
version = "0.0.0"
|
version = "0.1.19"
|
||||||
dependencies = [
|
source = "git+https://github.com/rust-lang-nursery/compiler-builtins#36da64f20e96206ac279f700586817c8abe3bdf8"
|
||||||
"libboard_zynq 0.0.0",
|
|
||||||
"libcortex_a9 0.0.0",
|
|
||||||
"libregister 0.0.0",
|
|
||||||
"linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libboard_zynq"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libcortex_a9 0.0.0",
|
|
||||||
"libregister 0.0.0",
|
|
||||||
"linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3)",
|
|
||||||
"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 = "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",
|
|
||||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3)",
|
|
||||||
"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 = "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 = "linked_list_allocator"
|
|
||||||
version = "0.6.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "managed"
|
name = "managed"
|
||||||
@ -80,7 +33,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "smoltcp"
|
name = "smoltcp"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3#8eb01aca364aefe5f823d68d552d62c76c9be4a3"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.1.0 (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)",
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -89,7 +42,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcell"
|
name = "vcell"
|
||||||
version = "0.1.2"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -97,27 +50,27 @@ name = "volatile-register"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zc706-experiments"
|
name = "zc706"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libboard_zc706 0.0.0",
|
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libboard_zynq 0.0.0",
|
"compiler_builtins 0.1.19 (git+https://github.com/rust-lang-nursery/compiler-builtins)",
|
||||||
"libcortex_a9 0.0.0",
|
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libregister 0.0.0",
|
"smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3)",
|
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
|
"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
|
||||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
"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 byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||||
"checksum linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "47314ec1d29aa869ee7cb5a5be57be9b1055c56567d59c3fb6689926743e0bea"
|
"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 managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
|
||||||
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
|
||||||
"checksum smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3)" = "<none>"
|
"checksum smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fef582369edb298c6c41319a544ca9c4e83622f226055ccfcb35974fbb55ed34"
|
||||||
"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
"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"
|
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
||||||
|
25
Cargo.toml
25
Cargo.toml
@ -1,9 +1,8 @@
|
|||||||
[workspace]
|
[package]
|
||||||
members = [
|
name = "zc706"
|
||||||
"libregister", "libcortex_a9",
|
version = "0.0.0"
|
||||||
"libboard_zynq", "libboard_zc706",
|
authors = ["Astro <astro@spaceboyz.net>"]
|
||||||
"experiments",
|
edition = "2018"
|
||||||
]
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
@ -12,5 +11,15 @@ lto = false
|
|||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
debug = true
|
debug = true
|
||||||
lto = true # Link-Time Optimization
|
|
||||||
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"] }
|
||||||
|
File diff suppressed because it is too large
Load Diff
49
default.nix
49
default.nix
@ -1,54 +1,27 @@
|
|||||||
{ # Use master branch of the overlay by default
|
{ # Use master branch of the overlay by default
|
||||||
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
|
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
|
||||||
rustManifest ? ./channel-rust-nightly.toml,
|
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
|
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
|
||||||
rustcSrc = pkgs.fetchgit {
|
in
|
||||||
|
with pkgs;
|
||||||
|
let
|
||||||
|
rustcSrc = fetchgit {
|
||||||
url = https://github.com/rust-lang/rust.git;
|
url = https://github.com/rust-lang/rust.git;
|
||||||
# master of 2019-11-09
|
# master of 2019-08-18
|
||||||
rev = "ac162c6abe34cdf965afc0389f6cefa79653c63b";
|
rev = "ea52be482ab4945fda63cb65b6a198309a041e3c";
|
||||||
sha256 = "06c5gws1mrpr69z1gzs358zf7hcsg6ky8n4ha0vv2s9d9w93x1kj";
|
sha256 = "1spifrkvyyrh1gazqrby29fjqsdbwvajv9k9f6mk2ldrdghlsd21";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
targets = [];
|
targets = [
|
||||||
rustChannelOfTargets = _channel: _date: targets:
|
];
|
||||||
(pkgs.lib.rustLib.fromManifestFile rustManifest {
|
|
||||||
inherit (pkgs) stdenv fetchurl patchelf;
|
|
||||||
}).rust.override { inherit targets; };
|
|
||||||
rust =
|
rust =
|
||||||
rustChannelOfTargets "nightly" null targets;
|
rustChannelOfTargets "nightly" null targets;
|
||||||
rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
|
rustPlatform = recurseIntoAttrs (makeRustPlatform {
|
||||||
rustc = rust // { src = rustcSrc; };
|
rustc = rust // { src = rustcSrc; };
|
||||||
cargo = rust;
|
cargo = rust;
|
||||||
});
|
});
|
||||||
gcc = pkgs.pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc;
|
|
||||||
xbuildRustPackage = attrs:
|
|
||||||
let
|
|
||||||
buildPkg = rustPlatform.buildRustPackage attrs;
|
|
||||||
in
|
|
||||||
buildPkg.overrideAttrs ({ name, nativeBuildInputs, ... }: {
|
|
||||||
nativeBuildInputs =
|
|
||||||
nativeBuildInputs ++ [ pkgs.cargo-xbuild ];
|
|
||||||
buildPhase = ''
|
|
||||||
cargo xbuild --release --frozen
|
|
||||||
'';
|
|
||||||
XARGO_RUST_SRC = "${rustcSrc}/src";
|
|
||||||
installPhase = ''
|
|
||||||
mkdir $out
|
|
||||||
cp target/armv7-none-eabihf/release/${name} $out/${name}.elf
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
zc706 = xbuildRustPackage {
|
|
||||||
name = "zc706";
|
|
||||||
src = ./.;
|
|
||||||
cargoSha256 = "15icqy72dck82czpsqz41yjsdar17vpi15v22j6z0zxhzf517rf7";
|
|
||||||
nativeBuildInputs = [
|
|
||||||
gcc
|
|
||||||
];
|
|
||||||
doCheck = false;
|
|
||||||
};
|
|
||||||
in {
|
in {
|
||||||
inherit pkgs rustPlatform rustcSrc zc706 gcc;
|
inherit pkgs rustPlatform rustcSrc;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "zc706-experiments"
|
|
||||||
version = "0.0.0"
|
|
||||||
authors = ["Astro <astro@spaceboyz.net>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
target_zc706 = []
|
|
||||||
target_cora_z7_10 = []
|
|
||||||
default = ["target_zc706"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libregister = { path = "../libregister" }
|
|
||||||
libcortex_a9 = { path = "../libcortex_a9" }
|
|
||||||
libboard_zynq = { path = "../libboard_zynq" }
|
|
||||||
libboard_zc706 = { path = "../libboard_zc706" }
|
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
|
||||||
git = "https://github.com/m-labs/smoltcp.git"
|
|
||||||
rev = "8eb01aca364aefe5f823d68d552d62c76c9be4a3"
|
|
||||||
features = ["ethernet", "proto-ipv4", "socket-tcp"]
|
|
||||||
default-features = false
|
|
@ -1,208 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
use core::mem::transmute;
|
|
||||||
use libcortex_a9::mutex::Mutex;
|
|
||||||
use libboard_zynq::{print, println, self as zynq};
|
|
||||||
use libboard_zc706::{
|
|
||||||
ram, alloc::{vec, vec::Vec},
|
|
||||||
boot,
|
|
||||||
smoltcp::wire::{EthernetAddress, IpAddress, IpCidr},
|
|
||||||
smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder},
|
|
||||||
smoltcp::time::Instant,
|
|
||||||
smoltcp::socket::SocketSet,
|
|
||||||
smoltcp::socket::{TcpSocket, TcpSocketBuffer},
|
|
||||||
};
|
|
||||||
|
|
||||||
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
|
|
||||||
|
|
||||||
static mut STACK_CORE1: [u32; 512] = [0; 512];
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn main_core0() {
|
|
||||||
// zynq::clocks::CpuClocks::enable_io(1_250_000_000);
|
|
||||||
println!("\nzc706 main");
|
|
||||||
{
|
|
||||||
use libregister::RegisterR;
|
|
||||||
println!("Boot mode: {:?}", zynq::slcr::RegisterBlock::new().boot_mode.read().boot_mode_pins());
|
|
||||||
}
|
|
||||||
|
|
||||||
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 mut flash = flash.stop();
|
|
||||||
|
|
||||||
let mut ddr = zynq::ddr::DdrRam::new();
|
|
||||||
#[cfg(not(feature = "target_zc706"))]
|
|
||||||
ddr.memtest();
|
|
||||||
ram::init_alloc(&mut ddr);
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
let core1_stack = unsafe { &mut STACK_CORE1[..] };
|
|
||||||
println!("{} bytes stack for core1", core1_stack.len());
|
|
||||||
let core1 = boot::Core1::start(core1_stack);
|
|
||||||
|
|
||||||
for _ in 0..0x1000000 {
|
|
||||||
let mut l = SHARED.lock();
|
|
||||||
*l += 1;
|
|
||||||
}
|
|
||||||
while !*DONE.lock() {
|
|
||||||
let x = { *SHARED.lock() };
|
|
||||||
println!("shared: {:08X}", x);
|
|
||||||
}
|
|
||||||
let x = { *SHARED.lock() };
|
|
||||||
println!("done shared: {:08X}", x);
|
|
||||||
|
|
||||||
core1.stop();
|
|
||||||
|
|
||||||
libcortex_a9::asm::dsb();
|
|
||||||
print!("Core1 stack [{:08X}..{:08X}]:", &core1.stack[0] as *const _ as u32, &core1.stack[core1.stack.len() - 1] as *const _ as u32);
|
|
||||||
for w in core1.stack {
|
|
||||||
print!(" {:08X}", w);
|
|
||||||
}
|
|
||||||
println!(".");
|
|
||||||
|
|
||||||
let eth = zynq::eth::Eth::default(HWADDR.clone());
|
|
||||||
println!("Eth on");
|
|
||||||
|
|
||||||
const RX_LEN: usize = 8;
|
|
||||||
let mut rx_descs = (0..RX_LEN)
|
|
||||||
.map(|_| zynq::eth::rx::DescEntry::zeroed())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let mut rx_buffers = vec![[0u8; zynq::eth::MTU]; RX_LEN];
|
|
||||||
// Number of transmission buffers (minimum is two because with
|
|
||||||
// one, duplicate packet transmission occurs)
|
|
||||||
const TX_LEN: usize = 8;
|
|
||||||
let mut tx_descs = (0..TX_LEN)
|
|
||||||
.map(|_| zynq::eth::tx::DescEntry::zeroed())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let mut tx_buffers = vec![[0u8; zynq::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_slice()) },
|
|
||||||
unsafe { transmute(tx_buffers.as_mut_slice()) },
|
|
||||||
);
|
|
||||||
|
|
||||||
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 mut neighbor_storage = vec![None; 256];
|
|
||||||
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[..]);
|
|
||||||
|
|
||||||
// taken from example code for smoltcp
|
|
||||||
let mut tcp_server_rx_data = vec![0; 512 * 1024];
|
|
||||||
let mut tcp_server_tx_data = vec![0; 512 * 1024];
|
|
||||||
let tcp_rx_buffer = TcpSocketBuffer::new(&mut tcp_server_rx_data[..]);
|
|
||||||
let tcp_tx_buffer = TcpSocketBuffer::new(&mut tcp_server_tx_data[..]);
|
|
||||||
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
|
||||||
let tcp_handle = sockets.add(tcp_socket);
|
|
||||||
/// `chargen`
|
|
||||||
const TCP_PORT: u16 = 19;
|
|
||||||
|
|
||||||
let mut time = 0u32;
|
|
||||||
loop {
|
|
||||||
time += 1;
|
|
||||||
let timestamp = Instant::from_millis(time);
|
|
||||||
|
|
||||||
match iface.poll(&mut sockets, timestamp) {
|
|
||||||
Ok(_) => {},
|
|
||||||
Err(e) => {
|
|
||||||
println!("poll error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// (mostly) taken from smoltcp example: TCP echo server
|
|
||||||
let mut socket = sockets.get::<TcpSocket>(tcp_handle);
|
|
||||||
if !socket.is_open() {
|
|
||||||
socket.listen(TCP_PORT).unwrap()
|
|
||||||
}
|
|
||||||
if socket.may_recv() && socket.can_send() {
|
|
||||||
socket.recv(|buf| {
|
|
||||||
let len = buf.len().min(4096);
|
|
||||||
let buffer = buf[..len].iter().cloned().collect::<Vec<_>>();
|
|
||||||
(len, buffer)
|
|
||||||
})
|
|
||||||
.and_then(|buffer| socket.send_slice(&buffer[..]))
|
|
||||||
.map(|_| {})
|
|
||||||
.unwrap_or_else(|e| println!("tcp: {:?}", e));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[allow(unreachable_code)]
|
|
||||||
// drop(tx_descs);
|
|
||||||
// #[allow(unreachable_code)]
|
|
||||||
// drop(tx_buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SHARED: Mutex<u32> = Mutex::new(0);
|
|
||||||
static DONE: Mutex<bool> = Mutex::new(false);
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn main_core1() {
|
|
||||||
println!("Hello from core1!");
|
|
||||||
for _ in 0..0x1000000 {
|
|
||||||
let mut l = SHARED.lock();
|
|
||||||
*l += 1;
|
|
||||||
}
|
|
||||||
println!("core1 done!");
|
|
||||||
*DONE.lock() = true;
|
|
||||||
|
|
||||||
loop {}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "libboard_zc706"
|
|
||||||
version = "0.0.0"
|
|
||||||
authors = ["Astro <astro@spaceboyz.net>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
# TODO: propagate to libboard_zynq
|
|
||||||
target_zc706 = []
|
|
||||||
target_cora_z7_10 = []
|
|
||||||
default = ["target_zc706"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
r0 = "0.2"
|
|
||||||
linked_list_allocator = { version = "0.6", default-features = false }
|
|
||||||
libregister = { path = "../libregister" }
|
|
||||||
libcortex_a9 = { path = "../libcortex_a9" }
|
|
||||||
libboard_zynq = { path = "../libboard_zynq" }
|
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
|
||||||
git = "https://github.com/m-labs/smoltcp.git"
|
|
||||||
rev = "8eb01aca364aefe5f823d68d552d62c76c9be4a3"
|
|
||||||
features = ["ethernet", "proto-ipv4", "socket-tcp"]
|
|
||||||
default-features = false
|
|
@ -1,13 +0,0 @@
|
|||||||
use libboard_zynq::println;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PrefetchAbort() {
|
|
||||||
println!("PrefetchAbort");
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn DataAbort() {
|
|
||||||
println!("DataAbort");
|
|
||||||
loop {}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
use r0::zero_bss;
|
|
||||||
use libregister::{
|
|
||||||
VolatileCell,
|
|
||||||
RegisterR, RegisterW, RegisterRW,
|
|
||||||
};
|
|
||||||
use libcortex_a9::{asm, regs::*, cache, mmu};
|
|
||||||
use libboard_zynq::{slcr, mpcore};
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
static mut __bss_start: u32;
|
|
||||||
static mut __bss_end: u32;
|
|
||||||
static mut __stack_start: u32;
|
|
||||||
fn main_core0();
|
|
||||||
fn main_core1();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `0` means: wait for initialization by core0
|
|
||||||
static mut CORE1_STACK: VolatileCell<u32> = VolatileCell::new(0);
|
|
||||||
|
|
||||||
#[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 => {
|
|
||||||
SP.write(&mut __stack_start as *mut _ as u32);
|
|
||||||
boot_core0();
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
while CORE1_STACK.get() == 0 {
|
|
||||||
asm::wfe();
|
|
||||||
}
|
|
||||||
|
|
||||||
SP.write(CORE1_STACK.get());
|
|
||||||
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();
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
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<S: AsMut<[u32]>> {
|
|
||||||
pub stack: S,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: AsMut<[u32]>> Core1<S> {
|
|
||||||
pub fn stop(&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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset and start core1
|
|
||||||
///
|
|
||||||
/// The stack must not be in OCM because core1 still has to
|
|
||||||
/// initialize its MMU before it can access DDR.
|
|
||||||
pub fn start(stack: S) -> Self {
|
|
||||||
let mut core = Core1 { stack };
|
|
||||||
|
|
||||||
// reset and stop (safe to repeat)
|
|
||||||
core.stop();
|
|
||||||
|
|
||||||
let stack = core.stack.as_mut();
|
|
||||||
let stack_start = &mut stack[stack.len() - 1];
|
|
||||||
unsafe {
|
|
||||||
CORE1_STACK.set(stack_start as *mut _ as u32);
|
|
||||||
}
|
|
||||||
// Ensure stack pointer has been written to cache
|
|
||||||
asm::dmb();
|
|
||||||
// Flush cache-line
|
|
||||||
cache::dccmvac(unsafe { &CORE1_STACK } as *const _ as u32);
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
});
|
|
||||||
|
|
||||||
core
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
#![feature(naked_functions)]
|
|
||||||
#![feature(alloc_error_handler)]
|
|
||||||
#![feature(panic_info_message)]
|
|
||||||
|
|
||||||
pub extern crate alloc;
|
|
||||||
|
|
||||||
pub mod boot;
|
|
||||||
mod abort;
|
|
||||||
mod panic;
|
|
||||||
pub mod ram;
|
|
||||||
pub use smoltcp;
|
|
@ -1,19 +0,0 @@
|
|||||||
use libboard_zynq::{slcr, 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!("");
|
|
||||||
}
|
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
|
|
||||||
loop {}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
use core::alloc::GlobalAlloc;
|
|
||||||
use core::ptr::NonNull;
|
|
||||||
use alloc::alloc::Layout;
|
|
||||||
use linked_list_allocator::Heap;
|
|
||||||
use libcortex_a9::mutex::Mutex;
|
|
||||||
use libboard_zynq::ddr::DdrRam;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(Mutex::new(Heap::empty()));
|
|
||||||
|
|
||||||
/// LockedHeap doesn't locking properly
|
|
||||||
struct CortexA9Alloc(Mutex<Heap>);
|
|
||||||
|
|
||||||
unsafe impl Sync for CortexA9Alloc {}
|
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for CortexA9Alloc {
|
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
|
||||||
self.0.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) {
|
|
||||||
self.0.lock()
|
|
||||||
.deallocate(NonNull::new_unchecked(ptr), layout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_alloc(ddr: &mut DdrRam) {
|
|
||||||
unsafe {
|
|
||||||
ALLOCATOR.0.lock()
|
|
||||||
.init(ddr.ptr::<u8>() as usize, ddr.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[alloc_error_handler]
|
|
||||||
fn alloc_error(_: core::alloc::Layout) -> ! {
|
|
||||||
panic!("alloc_error")
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "libboard_zynq"
|
|
||||||
version = "0.0.0"
|
|
||||||
authors = ["Astro <astro@spaceboyz.net>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
target_zc706 = []
|
|
||||||
target_cora_z7_10 = []
|
|
||||||
default = ["target_zc706"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
r0 = "0.2"
|
|
||||||
vcell = "0.1"
|
|
||||||
volatile-register = "0.2"
|
|
||||||
bit_field = "0.10"
|
|
||||||
linked_list_allocator = { version = "0.6", default-features = false }
|
|
||||||
libregister = { path = "../libregister" }
|
|
||||||
libcortex_a9 = { path = "../libcortex_a9" }
|
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
|
||||||
git = "https://github.com/m-labs/smoltcp.git"
|
|
||||||
rev = "8eb01aca364aefe5f823d68d552d62c76c9be4a3"
|
|
||||||
features = ["ethernet", "proto-ipv4", "socket-tcp"]
|
|
||||||
default-features = false
|
|
@ -1,2 +0,0 @@
|
|||||||
pub const M_AXI_GP0: usize = 0x4000_0000;
|
|
||||||
pub const M_AXI_GP1: usize = 0x8000_0000;
|
|
@ -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);
|
|
@ -1,175 +0,0 @@
|
|||||||
use libregister::{RegisterR, RegisterW, RegisterRW};
|
|
||||||
use super::slcr;
|
|
||||||
|
|
||||||
#[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
|
|
||||||
C421,
|
|
||||||
/// Clocks run in 6:3:2:1 mode
|
|
||||||
C621,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuClockMode {
|
|
||||||
pub fn get() -> Self {
|
|
||||||
let regs = slcr::RegisterBlock::new();
|
|
||||||
if regs.clk_621_true.read().clk_621_true() {
|
|
||||||
CpuClockMode::C621
|
|
||||||
} else {
|
|
||||||
CpuClockMode::C421
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
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
|
|
||||||
pub ddr: u32,
|
|
||||||
/// I/O PLL: Recommended clock for I/O peripherals
|
|
||||||
pub io: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuClocks {
|
|
||||||
pub fn get() -> Self {
|
|
||||||
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 regs = slcr::RegisterBlock::new();
|
|
||||||
let arm_clk_ctrl = regs.arm_clk_ctrl.read();
|
|
||||||
let pll = match arm_clk_ctrl.srcsel() {
|
|
||||||
slcr::ArmPllSource::ArmPll => self.arm,
|
|
||||||
slcr::ArmPllSource::DdrPll => self.ddr,
|
|
||||||
slcr::ArmPllSource::IoPll => self.io,
|
|
||||||
};
|
|
||||||
pll / u32::from(arm_clk_ctrl.divisor())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cpu_3x2x(&self) -> u32 {
|
|
||||||
self.cpu_6x4x() / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cpu_2x(&self) -> u32 {
|
|
||||||
match CpuClockMode::get() {
|
|
||||||
CpuClockMode::C421 =>
|
|
||||||
self.cpu_6x4x() / 2,
|
|
||||||
CpuClockMode::C621 =>
|
|
||||||
self.cpu_6x4x() / 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cpu_1x(&self) -> u32 {
|
|
||||||
match CpuClockMode::get() {
|
|
||||||
CpuClockMode::C421 =>
|
|
||||||
self.cpu_6x4x() / 4,
|
|
||||||
CpuClockMode::C621 =>
|
|
||||||
self.cpu_6x4x() / 6,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uart_ref_clk(&self) -> u32 {
|
|
||||||
let regs = slcr::RegisterBlock::new();
|
|
||||||
let uart_clk_ctrl = regs.uart_clk_ctrl.read();
|
|
||||||
let pll = match uart_clk_ctrl.srcsel() {
|
|
||||||
slcr::PllSource::ArmPll =>
|
|
||||||
self.arm,
|
|
||||||
slcr::PllSource::DdrPll =>
|
|
||||||
self.ddr,
|
|
||||||
slcr::PllSource::IoPll =>
|
|
||||||
self.io,
|
|
||||||
};
|
|
||||||
pll / u32::from(uart_clk_ctrl.divisor())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Zynq-7000 AP SoC Technical Reference Manual:
|
|
||||||
/// 25.10.4 PLLs
|
|
||||||
pub fn enable_io(target_clock: u32) {
|
|
||||||
let fdiv = (target_clock / PS_CLK).min(66) as u16;
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
|
||||||
slcr.io_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_pwrdwn(false)
|
|
||||||
.pll_bypass_force(true)
|
|
||||||
.pll_fdiv(fdiv)
|
|
||||||
);
|
|
||||||
slcr.io_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_reset(true)
|
|
||||||
);
|
|
||||||
slcr.io_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_reset(false)
|
|
||||||
);
|
|
||||||
while ! slcr.pll_status.read().io_pll_lock() {}
|
|
||||||
slcr.io_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_bypass_force(false)
|
|
||||||
.pll_bypass_qual(false)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Zynq-7000 AP SoC Technical Reference Manual:
|
|
||||||
/// 25.10.4 PLLs
|
|
||||||
pub fn enable_ddr(target_clock: u32) {
|
|
||||||
let fdiv = (target_clock / 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();
|
|
||||||
slcr::RegisterBlock::unlocked(|regs| {
|
|
||||||
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_pwrdwn(false)
|
|
||||||
.pll_bypass_force(true)
|
|
||||||
.pll_fdiv(fdiv)
|
|
||||||
);
|
|
||||||
regs.ddr_pll_cfg.write(
|
|
||||||
slcr::PllCfg::zeroed()
|
|
||||||
.pll_res(pll_res)
|
|
||||||
.pll_cp(pll_cp)
|
|
||||||
.lock_cnt(lock_cnt)
|
|
||||||
);
|
|
||||||
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_reset(true)
|
|
||||||
);
|
|
||||||
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_reset(false)
|
|
||||||
);
|
|
||||||
while ! regs.pll_status.read().ddr_pll_lock() {}
|
|
||||||
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
||||||
.pll_bypass_force(false)
|
|
||||||
.pll_bypass_qual(false)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// (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)),
|
|
||||||
];
|
|
@ -1,231 +0,0 @@
|
|||||||
use libregister::{RegisterR, RegisterW, RegisterRW};
|
|
||||||
use crate::{print, println};
|
|
||||||
use super::slcr;
|
|
||||||
use super::clocks::CpuClocks;
|
|
||||||
|
|
||||||
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 = 533_333_333;
|
|
||||||
|
|
||||||
/// 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.reset_ddrc();
|
|
||||||
ddr
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Zynq-7000 AP SoC Technical Reference Manual:
|
|
||||||
/// 10.6.1 DDR Clock Initialization
|
|
||||||
fn clock_setup() -> CpuClocks {
|
|
||||||
let clocks = CpuClocks::get();
|
|
||||||
if clocks.ddr == 0 {
|
|
||||||
CpuClocks::enable_ddr(clocks.arm);
|
|
||||||
}
|
|
||||||
let clocks = CpuClocks::get();
|
|
||||||
println!("Clocks: {:?}", clocks);
|
|
||||||
|
|
||||||
let ddr3x_clk_divisor = ((DDR_FREQ - 1 + clocks.ddr) / DDR_FREQ).min(255) as u8;
|
|
||||||
let ddr2x_clk_divisor = 3 * ddr3x_clk_divisor / 2;
|
|
||||||
println!("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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Zynq-7000 AP SoC Technical Reference Manual:
|
|
||||||
/// 10.6.2 DDR IOB Impedance Calibration
|
|
||||||
fn calibrate_iob_impedance(clocks: &CpuClocks) {
|
|
||||||
let divisor0 = ((DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ)
|
|
||||||
.max(1).min(63) as u8;
|
|
||||||
let divisor1 = (clocks.ddr / DCI_FREQ / u32::from(divisor0))
|
|
||||||
.max(1).min(63) as u8;
|
|
||||||
println!("DDR DCI clock: {} Hz", clocks.ddr / u32::from(divisor0) / u32::from(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);
|
|
||||||
|
|
||||||
let data_config = slcr::DdriobConfig::zeroed()
|
|
||||||
.inp_type(slcr::DdriobInputType::VrefDifferential)
|
|
||||||
.term_en(true)
|
|
||||||
.dci_type(slcr::DdriobDciType::Termination)
|
|
||||||
.output_en(slcr::DdriobOutputEn::Obuf);
|
|
||||||
slcr.ddriob_data0.write(data_config.clone());
|
|
||||||
slcr.ddriob_data1.write(data_config);
|
|
||||||
|
|
||||||
let diff_config = slcr::DdriobConfig::zeroed()
|
|
||||||
.inp_type(slcr::DdriobInputType::Differential)
|
|
||||||
.term_en(true)
|
|
||||||
.dci_type(slcr::DdriobDciType::Termination)
|
|
||||||
.output_en(slcr::DdriobOutputEn::Obuf);
|
|
||||||
slcr.ddriob_diff0.write(diff_config.clone());
|
|
||||||
slcr.ddriob_diff1.write(diff_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
let vref_sel = slcr::DdriobVrefSel::Vref0_75V;
|
|
||||||
#[cfg(feature = "target_cora_z7_10")]
|
|
||||||
let vref_sel = slcr::DdriobVrefSel::Vref0_675V;
|
|
||||||
|
|
||||||
// // Enable internal V[REF]
|
|
||||||
// slcr.ddriob_ddr_ctrl.modify(|_, w| w
|
|
||||||
// .vref_ext_en_lower(false)
|
|
||||||
// .vref_ext_en_upper(false)
|
|
||||||
// .vref_sel(vref_sel)
|
|
||||||
// .vref_int_en(true)
|
|
||||||
// );
|
|
||||||
// Enable external V[REF]
|
|
||||||
slcr.ddriob_ddr_ctrl.modify(|_, w| w
|
|
||||||
.vref_ext_en_lower(true)
|
|
||||||
.vref_ext_en_upper(true)
|
|
||||||
.vref_sel(vref_sel)
|
|
||||||
.vref_int_en(false)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset DDR controller
|
|
||||||
fn reset_ddrc(&mut self) {
|
|
||||||
self.regs.ddrc_ctrl.modify(|_, w| w
|
|
||||||
.soft_rstb(false)
|
|
||||||
);
|
|
||||||
#[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(true)
|
|
||||||
.powerdown_en(false)
|
|
||||||
.data_bus_width(width)
|
|
||||||
);
|
|
||||||
|
|
||||||
while self.status() == regs::ControllerStatus::Init {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn status(&self) -> regs::ControllerStatus {
|
|
||||||
self.regs.mode_sts_reg.read().operating_mode()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptr<T>(&mut self) -> *mut T {
|
|
||||||
0x0010_0000 as *mut _
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
let megabytes = 511;
|
|
||||||
#[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() {
|
|
||||||
println!("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).min(slice.len());
|
|
||||||
for b in slice[start..end].iter_mut() {
|
|
||||||
expected.map(|expected| {
|
|
||||||
let read: u32 = *b;
|
|
||||||
if read != expected {
|
|
||||||
println!("{:08X}: expected {:08X}, read {:08X}", b as *mut _ as usize, expected, read);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*b = *pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
print!("\r{} MB", megabyte);
|
|
||||||
}
|
|
||||||
println!(" Ok");
|
|
||||||
|
|
||||||
expected = Some(*pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,178 +0,0 @@
|
|||||||
use volatile_register::{RO, RW};
|
|
||||||
|
|
||||||
use libregister::{register, register_bit, register_bits_typed};
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[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_reg: RW<u32>,
|
|
||||||
pub lpr_reg: RW<u32>,
|
|
||||||
pub wr_reg: RW<u32>,
|
|
||||||
pub dram_param_reg0: RW<u32>,
|
|
||||||
pub dram_param_reg1: RW<u32>,
|
|
||||||
pub dram_param_reg2: RW<u32>,
|
|
||||||
pub dram_param_reg3: RW<u32>,
|
|
||||||
pub dram_param_reg4: RW<u32>,
|
|
||||||
pub dram_init_param: RW<u32>,
|
|
||||||
pub dram_emr_reg: RW<u32>,
|
|
||||||
pub dram_emr_mr_reg: RW<u32>,
|
|
||||||
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_reg: RW<u32>,
|
|
||||||
pub phy_dbg_reg: RW<u32>,
|
|
||||||
pub phy_cmd_timeout_rddata_cpt: RW<u32>,
|
|
||||||
pub mode_sts_reg: ModeStsReg,
|
|
||||||
pub dll_calib: RW<u32>,
|
|
||||||
pub odt_delay_hold: RW<u32>,
|
|
||||||
pub ctrl_reg1: RW<u32>,
|
|
||||||
pub ctrl_reg2: RW<u32>,
|
|
||||||
pub ctrl_reg3: RW<u32>,
|
|
||||||
pub ctrl_reg4: RW<u32>,
|
|
||||||
_unused0: [RO<u32>; 2],
|
|
||||||
pub ctrl_reg5: RW<u32>,
|
|
||||||
pub ctrl_reg6: RW<u32>,
|
|
||||||
_unused1: [RO<u32>; 8],
|
|
||||||
pub che_refresh_timer01: RW<u32>,
|
|
||||||
pub che_t_zq: RW<u32>,
|
|
||||||
pub che_t_zq_short_interval_reg: RW<u32>,
|
|
||||||
pub deep_pwrdwn_reg: RW<u32>,
|
|
||||||
pub reg_2c: RW<u32>,
|
|
||||||
pub reg_2d: RW<u32>,
|
|
||||||
pub dfi_timing: RW<u32>,
|
|
||||||
_unused2: [RO<u32>; 2],
|
|
||||||
pub che_ecc_control_reg_offset: RW<u32>,
|
|
||||||
pub che_corr_ecc_log_reg_offset: RW<u32>,
|
|
||||||
pub che_corr_ecc_addr_reg_offset: RW<u32>,
|
|
||||||
pub che_corr_ecc_data_31_0_reg_offset: RW<u32>,
|
|
||||||
pub che_corr_ecc_data_63_32_reg_offset: RW<u32>,
|
|
||||||
pub che_corr_ecc_data_71_64_reg_offset: RW<u32>,
|
|
||||||
pub che_uncorr_ecc_log_reg_offset: RW<u32>,
|
|
||||||
pub che_uncorr_ecc_addr_reg_offset: RW<u32>,
|
|
||||||
pub che_uncorr_ecc_data_31_0_reg_offset: RW<u32>,
|
|
||||||
pub che_uncorr_ecc_data_63_32_reg_offset: RW<u32>,
|
|
||||||
pub che_uncorr_ecc_data_71_64_reg_offset: RW<u32>,
|
|
||||||
pub che_ecc_stats_reg_offset: RW<u32>,
|
|
||||||
pub ecc_scrub: RW<u32>,
|
|
||||||
pub che_ecc_corr_bit_mask_31_0_reg_offset: RW<u32>,
|
|
||||||
pub che_ecc_corr_bit_mask_63_32_reg_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: RW<u32>,
|
|
||||||
pub phy_init_ratio1: RW<u32>,
|
|
||||||
pub phy_init_ratio2: RW<u32>,
|
|
||||||
pub phy_init_ratio3: RW<u32>,
|
|
||||||
_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: RW<u32>,
|
|
||||||
_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_sts_reg2: 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) ...
|
|
||||||
|
|
||||||
// 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) ...
|
|
@ -1,59 +0,0 @@
|
|||||||
use bit_field::BitField;
|
|
||||||
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
/// 1000Base-T Extended Status Register
|
|
||||||
pub struct ExtendedStatus(pub u16);
|
|
||||||
|
|
||||||
impl ExtendedStatus {
|
|
||||||
pub fn cap_1000base_t_half(&self) -> bool {
|
|
||||||
self.0.get_bit(12)
|
|
||||||
}
|
|
||||||
pub fn cap_1000base_t_full(&self) -> bool {
|
|
||||||
self.0.get_bit(13)
|
|
||||||
}
|
|
||||||
pub fn cap_1000base_x_half(&self) -> bool {
|
|
||||||
self.0.get_bit(14)
|
|
||||||
}
|
|
||||||
pub fn cap_1000base_x_full(&self) -> bool {
|
|
||||||
self.0.get_bit(12)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_link(&self) -> Option<Link> {
|
|
||||||
if self.cap_1000base_t_half() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Half,
|
|
||||||
})
|
|
||||||
} else if self.cap_1000base_t_full() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Full,
|
|
||||||
})
|
|
||||||
} else if self.cap_1000base_x_half() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Half,
|
|
||||||
})
|
|
||||||
} else if self.cap_1000base_x_full() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Full,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhyRegister for ExtendedStatus {
|
|
||||||
fn addr() -> u8 {
|
|
||||||
0xF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u16> for ExtendedStatus {
|
|
||||||
fn from(value: u16) -> Self {
|
|
||||||
ExtendedStatus(value)
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,506 +0,0 @@
|
|||||||
//! Quad-SPI Flash Controller
|
|
||||||
|
|
||||||
use crate::{print, println};
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use libregister::{RegisterR, RegisterW, RegisterRW};
|
|
||||||
use super::slcr;
|
|
||||||
use super::clocks::CpuClocks;
|
|
||||||
|
|
||||||
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;
|
|
||||||
const INST_READ: u8 = 0x03;
|
|
||||||
/// 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: Sector Erase
|
|
||||||
const INST_SE: u8 = 0xD8;
|
|
||||||
/// 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 enable_interrupts(&mut self) {
|
|
||||||
self.regs.intr_en.write(
|
|
||||||
regs::IntrEn::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
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enable_clocks(clock: u32) {
|
|
||||||
let io_pll = CpuClocks::get().io;
|
|
||||||
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)
|
|
||||||
.pullup(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_11.write(
|
|
||||||
slcr::MioPin11::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_12.write(
|
|
||||||
slcr::MioPin12::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
slcr.mio_pin_13.write(
|
|
||||||
slcr::MioPin13::zeroed()
|
|
||||||
.l0_sel(true)
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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(0xEB)
|
|
||||||
.mode_bits(0xFF)
|
|
||||||
.dummy_byte(0x2)
|
|
||||||
.mode_en(true)
|
|
||||||
// 2 devices
|
|
||||||
.two_mem(true)
|
|
||||||
.u_page(false)
|
|
||||||
// Linear Addressing 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()
|
|
||||||
.mode_bits(0xFF)
|
|
||||||
.dummy_byte(0x2)
|
|
||||||
.mode_en(true)
|
|
||||||
// 2 devices
|
|
||||||
.two_mem(true)
|
|
||||||
.sep_bus(true)
|
|
||||||
.u_page(chip_index != 0)
|
|
||||||
// Manual I/O 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() {
|
|
||||||
println!("E_ERR");
|
|
||||||
} else if sr1.p_err() {
|
|
||||||
println!("P_ERR");
|
|
||||||
} else if sr1.wip() {
|
|
||||||
print!("Erase in progress");
|
|
||||||
while self.read_reg::<SR1>().wip() {
|
|
||||||
print!(".");
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
} else {
|
|
||||||
println!("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() {
|
|
||||||
println!("E_ERR");
|
|
||||||
} else if sr1.p_err() {
|
|
||||||
println!("P_ERR");
|
|
||||||
} else if sr1.wip() {
|
|
||||||
println!("Program in progress");
|
|
||||||
while self.read_reg::<SR1>().wip() {
|
|
||||||
print!(".");
|
|
||||||
}
|
|
||||||
println!("");
|
|
||||||
} else {
|
|
||||||
println!("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!("");
|
|
||||||
}
|
|
||||||
}
|
|
@ -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_byte, 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);
|
|
@ -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);
|
|
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
pub mod slcr;
|
|
||||||
pub mod clocks;
|
|
||||||
pub mod uart;
|
|
||||||
pub mod stdio;
|
|
||||||
pub mod eth;
|
|
||||||
pub mod axi_hp;
|
|
||||||
pub mod axi_gp;
|
|
||||||
pub mod ddr;
|
|
||||||
pub mod mpcore;
|
|
||||||
pub mod flash;
|
|
@ -1,61 +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 {
|
|
||||||
pub scu_control: ScuControl,
|
|
||||||
pub scu_config: RO<u32>,
|
|
||||||
pub scu_cpu_power: RW<u32>,
|
|
||||||
pub scu_invalidate: ScuInvalidate,
|
|
||||||
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);
|
|
||||||
|
|
||||||
impl ScuControl {
|
|
||||||
pub fn start(&mut self) {
|
|
||||||
self.modify(|_, w| w.enable(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +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() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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, "\r\n");
|
|
||||||
while !uart.tx_fifo_empty() {}
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "libcortex_a9"
|
|
||||||
version = "0.0.0"
|
|
||||||
authors = ["Astro <astro@spaceboyz.net>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
target_zc706 = []
|
|
||||||
target_cora_z7_10 = []
|
|
||||||
default = ["target_zc706"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
r0 = "0.2"
|
|
||||||
vcell = "0.1"
|
|
||||||
volatile-register = "0.2"
|
|
||||||
bit_field = "0.10"
|
|
||||||
libregister = { path = "../libregister" }
|
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
|
||||||
git = "https://github.com/m-labs/smoltcp.git"
|
|
||||||
rev = "8eb01aca364aefe5f823d68d552d62c76c9be4a3"
|
|
||||||
features = ["ethernet", "proto-ipv4", "socket-tcp"]
|
|
||||||
default-features = false
|
|
@ -1,214 +0,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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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.
|
|
||||||
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.
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data cache clear 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 dccimva(addr: usize) {
|
|
||||||
unsafe {
|
|
||||||
asm!("mcr p15, 0, $0, c7, c14, 1" :: "r" (addr) :: "volatile");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The DCCIVMA (data cache clear and invalidate) applied to the
|
|
||||||
/// region of memory occupied by the argument. This does not modify
|
|
||||||
/// the argument, but due to the invalidate part (only ever needed if
|
|
||||||
/// external write access is to be granted, e.g. by DMA) it only makes
|
|
||||||
/// sense if the caller has exclusive access to it as otherwise other
|
|
||||||
/// accesses might just bring it back into the data cache.
|
|
||||||
pub fn dcci<T>(object: &mut T) {
|
|
||||||
let cache_line = 0x20;
|
|
||||||
let first_addr =
|
|
||||||
(object as *mut _ as *const _ as usize) & !(cache_line - 1);
|
|
||||||
let beyond_addr = (
|
|
||||||
(object as *mut _ as *const _ as usize)
|
|
||||||
+ core::mem::size_of_val(object)
|
|
||||||
+ (cache_line - 1)
|
|
||||||
) & !(cache_line - 1);
|
|
||||||
for addr in (first_addr..beyond_addr).step_by(cache_line) {
|
|
||||||
dccimva(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dcci_slice_content<T>(slice: &mut [T]) {
|
|
||||||
if slice.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cache_line = 0x20;
|
|
||||||
let first_addr =
|
|
||||||
(&slice[0] as *const _ as usize) & !(cache_line - 1);
|
|
||||||
let beyond_addr = (
|
|
||||||
(&slice[slice.len() - 1] as *const _ as usize)
|
|
||||||
+ (cache_line - 1)
|
|
||||||
) & !(cache_line - 1);
|
|
||||||
for addr in (first_addr..beyond_addr).step_by(cache_line) {
|
|
||||||
dccimva(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dcci_slice_content_unmut<T>(slice: &[T]) {
|
|
||||||
if slice.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cache_line = 0x20;
|
|
||||||
let first_addr =
|
|
||||||
(&slice[0] as *const _ as usize) & !(cache_line - 1);
|
|
||||||
let beyond_addr = (
|
|
||||||
(&slice[slice.len() - 1] as *const _ as usize)
|
|
||||||
+ (cache_line - 1)
|
|
||||||
) & !(cache_line - 1);
|
|
||||||
for addr in (first_addr..beyond_addr).step_by(cache_line) {
|
|
||||||
dccimva(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 dcimva(addr: usize) {
|
|
||||||
asm!("mcr p15, 0, $0, c7, c6, 1" :: "r" (addr) :: "volatile");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data cache invalidate for an object. Panics if not properly
|
|
||||||
/// aligned and properly sized to be contained in an exact number of
|
|
||||||
/// cache lines.
|
|
||||||
pub fn dci<T>(object: &mut T) {
|
|
||||||
let cache_line = 0x20;
|
|
||||||
let first_addr = object as *mut _ as *const _ as usize;
|
|
||||||
let beyond_addr = (object as *mut _ as *const _ as usize) +
|
|
||||||
core::mem::size_of_val(object);
|
|
||||||
assert_eq!((first_addr & (cache_line - 1)), 0x00);
|
|
||||||
assert_eq!((beyond_addr & (cache_line - 1)), 0x00);
|
|
||||||
for addr in (first_addr..beyond_addr).step_by(cache_line) {
|
|
||||||
unsafe {
|
|
||||||
dcimva(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data cache invalidate for the contents of a slice. Panics if not
|
|
||||||
/// properly aligned and properly sized to be contained in an exact
|
|
||||||
/// number of cache lines.
|
|
||||||
pub fn dci_slice_content<T>(slice: &mut [T]) {
|
|
||||||
if slice.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cache_line = 0x20;
|
|
||||||
let first_addr = &slice[0] as *const _ as usize;
|
|
||||||
let beyond_addr = (&slice[slice.len() - 1] as *const _ as usize)
|
|
||||||
+ core::mem::size_of::<T>();
|
|
||||||
assert_eq!((first_addr & (cache_line - 1)), 0x00);
|
|
||||||
assert_eq!((beyond_addr & (cache_line - 1)), 0x00);
|
|
||||||
for addr in (first_addr..beyond_addr).step_by(cache_line) {
|
|
||||||
unsafe {
|
|
||||||
dcimva(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn dci_more_than_slice_content<T>(slice: &mut [T]) {
|
|
||||||
if slice.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cache_line = 0x20;
|
|
||||||
let first_addr =
|
|
||||||
(&slice[0] as *const _ as usize) & !(cache_line - 1);
|
|
||||||
let beyond_addr = (
|
|
||||||
(&slice[slice.len() - 1] as *const _ as usize)
|
|
||||||
+ (cache_line - 1)
|
|
||||||
) & !(cache_line - 1);
|
|
||||||
assert_eq!((first_addr & (cache_line - 1)), 0x00);
|
|
||||||
assert_eq!((beyond_addr & (cache_line - 1)), 0x00);
|
|
||||||
for addr in (first_addr..beyond_addr).step_by(cache_line) {
|
|
||||||
dcimva(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn dci_more_than_slice_content_nonmut<T>(slice: &[T]) {
|
|
||||||
if slice.len() == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cache_line = 0x20;
|
|
||||||
let first_addr =
|
|
||||||
(&slice[0] as *const _ as usize) & !(cache_line - 1);
|
|
||||||
let beyond_addr = (
|
|
||||||
(&slice[slice.len() - 1] as *const _ as usize)
|
|
||||||
+ (cache_line - 1)
|
|
||||||
) & !(cache_line - 1);
|
|
||||||
assert_eq!((first_addr & (cache_line - 1)), 0x00);
|
|
||||||
assert_eq!((beyond_addr & (cache_line - 1)), 0x00);
|
|
||||||
for addr in (first_addr..beyond_addr).step_by(cache_line) {
|
|
||||||
dcimva(addr);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(asm, global_asm)]
|
|
||||||
#![feature(never_type)]
|
|
||||||
|
|
||||||
pub mod asm;
|
|
||||||
pub mod regs;
|
|
||||||
pub mod cache;
|
|
||||||
pub mod mmu;
|
|
||||||
pub mod mutex;
|
|
||||||
|
|
||||||
global_asm!(include_str!("exceptions.s"));
|
|
@ -1,83 +0,0 @@
|
|||||||
use core::ops::{Deref, DerefMut};
|
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
use super::asm::*;
|
|
||||||
|
|
||||||
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
|
|
||||||
#[inline]
|
|
||||||
fn wait_for_update() {
|
|
||||||
wfe();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
|
|
||||||
#[inline]
|
|
||||||
fn signal_update() {
|
|
||||||
dsb();
|
|
||||||
sev();
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
|
||||||
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
|
|
||||||
wait_for_update();
|
|
||||||
}
|
|
||||||
dmb();
|
|
||||||
MutexGuard { mutex: self }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unlock(&self) {
|
|
||||||
dmb();
|
|
||||||
self.locked.store(UNLOCKED, Ordering::Release);
|
|
||||||
|
|
||||||
signal_update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returned by `Mutex.lock()`, allows access to data via
|
|
||||||
/// `Deref`/`DerefMutx`
|
|
||||||
pub struct MutexGuard<'a, T> {
|
|
||||||
mutex: &'a Mutex<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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"
|
|
3
link.x
3
link.x
@ -1,5 +1,6 @@
|
|||||||
ENTRY(_boot_cores);
|
ENTRY(_boot_cores);
|
||||||
|
|
||||||
|
/* Size of stack for core 0 in bytes */
|
||||||
STACK_SIZE = 0x8000;
|
STACK_SIZE = 0x8000;
|
||||||
|
|
||||||
/* Provide some defaults */
|
/* Provide some defaults */
|
||||||
@ -60,7 +61,7 @@ SECTIONS
|
|||||||
. += STACK_SIZE;
|
. += STACK_SIZE;
|
||||||
} > OCM
|
} > OCM
|
||||||
__stack_end = ADDR(.stack);
|
__stack_end = ADDR(.stack);
|
||||||
__stack_start = ADDR(.stack) + SIZEOF(.stack);
|
__stack_start = ADDR(.stack) + SIZEOF(.stack) - 4;
|
||||||
|
|
||||||
/DISCARD/ :
|
/DISCARD/ :
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
source [find interface/ftdi/digilent-hs1.cfg]
|
source [find interface/ftdi/digilent-hs1.cfg]
|
||||||
adapter_khz 10000
|
adapter_khz 10000
|
||||||
|
|
||||||
set PL_TAPID 0x13723093
|
set PL_TAPID 0x03722093
|
||||||
set SMP 1
|
set SMP 1
|
||||||
|
|
||||||
source ./zynq-7000.cfg
|
source ./zynq-7000.cfg
|
||||||
|
@ -15,4 +15,5 @@ ftdi_device_desc "Digilent USB Device"
|
|||||||
ftdi_vid_pid 0x0403 0x6014
|
ftdi_vid_pid 0x0403 0x6014
|
||||||
ftdi_channel 0
|
ftdi_channel 0
|
||||||
ftdi_layout_init 0x00e8 0x60eb
|
ftdi_layout_init 0x00e8 0x60eb
|
||||||
ftdi_layout_signal nSRST -data 0x2000
|
reset_config none
|
||||||
|
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
|
source ./digilent_jtag_smt2_nc.cfg
|
||||||
adapter_khz 1000
|
adapter_khz 10000
|
||||||
|
|
||||||
set PL_TAPID 0x23731093
|
set PL_TAPID 0x03731093
|
||||||
set SMP 1
|
set SMP 1
|
||||||
|
|
||||||
source ./zynq-7000.cfg
|
source ./zynq-7000.cfg
|
||||||
source ./xilinx-tcl.cfg
|
source ./xilinx-tcl.cfg
|
||||||
source ./ps7_init.tcl
|
source ./ps7_init.tcl
|
||||||
|
|
||||||
reset_config srst_only srst_open_drain
|
reset_config srst_only srst_push_pull
|
||||||
adapter_nsrst_assert_width 250
|
|
||||||
adapter_nsrst_delay 400
|
|
||||||
|
|
||||||
set XC7_JSHUTDOWN 0x0d
|
set XC7_JSHUTDOWN 0x0d
|
||||||
set XC7_JPROGRAM 0x0b
|
set XC7_JPROGRAM 0x0b
|
||||||
|
10
shell.nix
10
shell.nix
@ -8,12 +8,14 @@ let
|
|||||||
in
|
in
|
||||||
with project;
|
with project;
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "zynq-env";
|
name = "adc2tcp-env";
|
||||||
buildInputs = (with rustPlatform.rust; [
|
buildInputs = with rustPlatform.rust; [
|
||||||
rustc cargo
|
rustc cargo
|
||||||
cargo-xbuild rustcSrc
|
cargo-xbuild rustcSrc
|
||||||
gcc
|
pkgsCross.armhf-embedded.buildPackages.gcc
|
||||||
]) ++ (with pkgs; [ openocd gdb ]);
|
#pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc
|
||||||
|
#pkgsCross.armhf-embedded.buildPackages.binutils
|
||||||
|
];
|
||||||
|
|
||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
RUST_BACKTRACE = 1;
|
RUST_BACKTRACE = 1;
|
||||||
|
92
src/clocks.rs
Normal file
92
src/clocks.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use crate::slcr;
|
||||||
|
use crate::regs::RegisterR;
|
||||||
|
|
||||||
|
#[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
|
||||||
|
C421,
|
||||||
|
/// Clocks run in 6:3:2:1 mode
|
||||||
|
C621,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuClockMode {
|
||||||
|
pub fn get() -> Self {
|
||||||
|
let regs = slcr::RegisterBlock::new();
|
||||||
|
if regs.clk_621_true.read().clk_621_true() {
|
||||||
|
CpuClockMode::C621
|
||||||
|
} else {
|
||||||
|
CpuClockMode::C421
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
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
|
||||||
|
pub ddr: u32,
|
||||||
|
/// I/O PLL: Recommended clock for I/O peripherals
|
||||||
|
pub io: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuClocks {
|
||||||
|
pub fn get() -> Self {
|
||||||
|
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 regs = slcr::RegisterBlock::new();
|
||||||
|
let arm_clk_ctrl = regs.arm_clk_ctrl.read();
|
||||||
|
let pll = match arm_clk_ctrl.srcsel() {
|
||||||
|
slcr::ArmPllSource::ArmPll => self.arm,
|
||||||
|
slcr::ArmPllSource::DdrPll => self.ddr,
|
||||||
|
slcr::ArmPllSource::IoPll => self.io,
|
||||||
|
};
|
||||||
|
pll / u32::from(arm_clk_ctrl.divisor())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_3x2x(&self) -> u32 {
|
||||||
|
self.cpu_6x4x() / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_2x(&self) -> u32 {
|
||||||
|
match CpuClockMode::get() {
|
||||||
|
CpuClockMode::C421 =>
|
||||||
|
self.cpu_6x4x() / 2,
|
||||||
|
CpuClockMode::C621 =>
|
||||||
|
self.cpu_6x4x() / 3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_1x(&self) -> u32 {
|
||||||
|
match CpuClockMode::get() {
|
||||||
|
CpuClockMode::C421 =>
|
||||||
|
self.cpu_6x4x() / 4,
|
||||||
|
CpuClockMode::C621 =>
|
||||||
|
self.cpu_6x4x() / 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uart_ref_clk(&self) -> u32 {
|
||||||
|
let regs = slcr::RegisterBlock::new();
|
||||||
|
let uart_clk_ctrl = regs.uart_clk_ctrl.read();
|
||||||
|
let pll = match uart_clk_ctrl.srcsel() {
|
||||||
|
slcr::PllSource::ArmPll =>
|
||||||
|
self.arm,
|
||||||
|
slcr::PllSource::DdrPll =>
|
||||||
|
self.ddr,
|
||||||
|
slcr::PllSource::IoPll =>
|
||||||
|
self.io,
|
||||||
|
};
|
||||||
|
pll / u32::from(uart_clk_ctrl.divisor())
|
||||||
|
}
|
||||||
|
}
|
@ -33,3 +33,4 @@ pub fn dsb() {
|
|||||||
pub fn isb() {
|
pub fn isb() {
|
||||||
unsafe { asm!("isb" :::: "volatile") }
|
unsafe { asm!("isb" :::: "volatile") }
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
use bit_field::BitField;
|
use bit_field::BitField;
|
||||||
use super::{regs::*, asm};
|
use super::{regs::*, asm};
|
||||||
use libregister::RegisterW;
|
use crate::regs::RegisterW;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -124,7 +124,9 @@ impl L1Table {
|
|||||||
tex: 0b101,
|
tex: 0b101,
|
||||||
domain: 0b1111,
|
domain: 0b1111,
|
||||||
exec: true,
|
exec: true,
|
||||||
cacheable: true,
|
// TODO: temporarily turn on cache for SMP testing;
|
||||||
|
// consider turning it off again for production
|
||||||
|
cacheable: !false,
|
||||||
bufferable: true,
|
bufferable: true,
|
||||||
});
|
});
|
||||||
/* (DDR cacheable) */
|
/* (DDR cacheable) */
|
5
src/cortex_a9/mod.rs
Normal file
5
src/cortex_a9/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub mod asm;
|
||||||
|
pub mod regs;
|
||||||
|
pub mod mmu;
|
||||||
|
|
||||||
|
global_asm!(include_str!("exceptions.s"));
|
@ -1,7 +1,5 @@
|
|||||||
use libregister::{
|
use crate::{register_bit, register_bits};
|
||||||
register_bit, register_bits,
|
use crate::regs::{RegisterR, RegisterW};
|
||||||
RegisterR, RegisterW, RegisterRW,
|
|
||||||
};
|
|
||||||
|
|
||||||
macro_rules! def_reg_r {
|
macro_rules! def_reg_r {
|
||||||
($name:tt, $type: ty, $asm_instr:tt) => {
|
($name:tt, $type: ty, $asm_instr:tt) => {
|
||||||
@ -117,6 +115,19 @@ register_bit!(sctlr,
|
|||||||
/// Thumb Exception Enable
|
/// Thumb Exception Enable
|
||||||
te, 30);
|
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
|
/// Auxiliary Control Register
|
||||||
pub struct ACTLR;
|
pub struct ACTLR;
|
||||||
wrap_reg!(actlr);
|
wrap_reg!(actlr);
|
||||||
@ -132,18 +143,14 @@ register_bit!(actlr, l1_prefetch_enable, 2);
|
|||||||
// Cache/TLB maintenance broadcast
|
// Cache/TLB maintenance broadcast
|
||||||
register_bit!(actlr, fw, 0);
|
register_bit!(actlr, fw, 0);
|
||||||
|
|
||||||
impl RegisterRW for ACTLR {
|
impl crate::regs::RegisterRW for ACTLR {
|
||||||
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
||||||
let r = self.read();
|
let inner = self.read().inner;
|
||||||
let w = actlr::Write { inner: r.inner };
|
let inner_w = f(
|
||||||
let w = f(r, w);
|
actlr::Read { inner },
|
||||||
self.write(w);
|
actlr::Write { inner }
|
||||||
}
|
);
|
||||||
}
|
self.write(inner_w);
|
||||||
|
|
||||||
impl ACTLR {
|
|
||||||
pub fn enable_smp(&mut self) {
|
|
||||||
self.modify(|_, w| w.smp(true).fw(true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,3 +175,78 @@ register_bit!(ttbr,
|
|||||||
/// Translation table walk to shared memory?
|
/// Translation table walk to shared memory?
|
||||||
s, 1);
|
s, 1);
|
||||||
register_bit!(ttbr, irgn1, 0);
|
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");
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
use libregister::*;
|
use crate::regs::*;
|
||||||
|
use crate::slcr;
|
||||||
use crate::println;
|
use crate::println;
|
||||||
use super::slcr;
|
use crate::clocks::CpuClocks;
|
||||||
use super::clocks::CpuClocks;
|
|
||||||
|
|
||||||
pub mod phy;
|
pub mod phy;
|
||||||
use phy::{Phy, PhyAccess};
|
|
||||||
mod regs;
|
mod regs;
|
||||||
pub mod rx;
|
pub mod rx;
|
||||||
pub mod tx;
|
pub mod tx;
|
||||||
@ -13,16 +12,13 @@ pub mod tx;
|
|||||||
pub const MTU: usize = 1536;
|
pub const MTU: usize = 1536;
|
||||||
/// Maximum MDC clock
|
/// Maximum MDC clock
|
||||||
const MAX_MDC: u32 = 2_500_000;
|
const MAX_MDC: u32 = 2_500_000;
|
||||||
const TX_10: u32 = 10_000_000;
|
|
||||||
const TX_100: u32 = 25_000_000;
|
|
||||||
/// Clock for GbE
|
/// Clock for GbE
|
||||||
const TX_1000: u32 = 125_000_000;
|
const TX_1000: u32 = 125_000_000;
|
||||||
|
|
||||||
pub struct Eth<'r, RX, TX> {
|
pub struct Eth<'r, RX, TX> {
|
||||||
|
regs: &'r mut regs::RegisterBlock,
|
||||||
rx: RX,
|
rx: RX,
|
||||||
tx: TX,
|
tx: TX,
|
||||||
inner: EthInner<'r>,
|
|
||||||
phy: Phy,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Eth<'r, (), ()> {
|
impl<'r> Eth<'r, (), ()> {
|
||||||
@ -172,31 +168,21 @@ impl<'r> Eth<'r, (), ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
|
fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
|
||||||
let mut inner = EthInner {
|
let mut eth = Eth {
|
||||||
regs,
|
regs,
|
||||||
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 {
|
|
||||||
rx: (),
|
rx: (),
|
||||||
tx: (),
|
tx: (),
|
||||||
inner,
|
}.init();
|
||||||
phy,
|
eth.configure(macaddr);
|
||||||
}
|
eth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, RX, TX> Eth<'r, RX, TX> {
|
impl<'r, RX, TX> Eth<'r, RX, TX> {
|
||||||
pub fn setup_gem0_clock(tx_clock: u32) {
|
pub fn setup_gem0_clock(tx_clock: u32) {
|
||||||
let io_pll = CpuClocks::get().io;
|
let io_pll = CpuClocks::get().io;
|
||||||
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
|
let d0 = (io_pll / tx_clock).min(63);
|
||||||
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
|
let d1 = (io_pll / tx_clock / d0).min(63);
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
slcr.gem0_clk_ctrl.write(
|
slcr.gem0_clk_ctrl.write(
|
||||||
@ -219,8 +205,8 @@ impl<'r, RX, TX> Eth<'r, RX, TX> {
|
|||||||
|
|
||||||
pub fn setup_gem1_clock(tx_clock: u32) {
|
pub fn setup_gem1_clock(tx_clock: u32) {
|
||||||
let io_pll = CpuClocks::get().io;
|
let io_pll = CpuClocks::get().io;
|
||||||
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
|
let d0 = (io_pll / tx_clock).min(63);
|
||||||
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
|
let d1 = (io_pll / tx_clock / d0).min(63);
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
slcr.gem1_clk_ctrl.write(
|
slcr.gem1_clk_ctrl.write(
|
||||||
@ -239,155 +225,7 @@ impl<'r, RX, TX> Eth<'r, RX, TX> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_rx<'rx>(self, rx_list: &'rx mut [rx::DescEntry], rx_buffers: &'rx mut [[u8; MTU]]) -> Eth<'r, rx::DescList<'rx>, TX> {
|
fn init(self) -> Self {
|
||||||
let new_self = Eth {
|
|
||||||
rx: rx::DescList::new(rx_list, rx_buffers),
|
|
||||||
tx: self.tx,
|
|
||||||
inner: self.inner,
|
|
||||||
phy: self.phy,
|
|
||||||
};
|
|
||||||
let list_addr = new_self.rx.list_addr();
|
|
||||||
assert!(list_addr & 0b11 == 0);
|
|
||||||
new_self.inner.regs.rx_qbar.write(
|
|
||||||
regs::RxQbar::zeroed()
|
|
||||||
.rx_q_baseaddr(list_addr >> 2)
|
|
||||||
);
|
|
||||||
new_self.inner.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 {
|
|
||||||
rx: self.rx,
|
|
||||||
tx: tx::DescList::new(tx_list, tx_buffers),
|
|
||||||
inner: self.inner,
|
|
||||||
phy: self.phy,
|
|
||||||
};
|
|
||||||
let list_addr = &new_self.tx.list_addr();
|
|
||||||
assert!(list_addr & 0b11 == 0);
|
|
||||||
new_self.inner.regs.tx_qbar.write(
|
|
||||||
regs::TxQbar::zeroed()
|
|
||||||
.tx_q_baseaddr(list_addr >> 2)
|
|
||||||
);
|
|
||||||
new_self.inner.regs.net_ctrl.modify(|_, w|
|
|
||||||
w.tx_en(true)
|
|
||||||
);
|
|
||||||
new_self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.inner.regs.rx_status.read();
|
|
||||||
if status.hresp_not_ok() {
|
|
||||||
// Clear
|
|
||||||
self.inner.regs.rx_status.write(
|
|
||||||
regs::RxStatus::zeroed()
|
|
||||||
.hresp_not_ok(true)
|
|
||||||
);
|
|
||||||
return Err(rx::Error::HrespNotOk);
|
|
||||||
}
|
|
||||||
if status.rx_overrun() {
|
|
||||||
// Clear
|
|
||||||
self.inner.regs.rx_status.write(
|
|
||||||
regs::RxStatus::zeroed()
|
|
||||||
.rx_overrun(true)
|
|
||||||
);
|
|
||||||
return Err(rx::Error::RxOverrun);
|
|
||||||
}
|
|
||||||
if status.buffer_not_avail() {
|
|
||||||
// Clear
|
|
||||||
self.inner.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.inner.regs.rx_status.write(
|
|
||||||
regs::RxStatus::zeroed()
|
|
||||||
.frame_recd(true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
self.inner.check_link_change(&self.phy);
|
|
||||||
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.inner.regs, length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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.checksum = checksum_caps;
|
|
||||||
|
|
||||||
caps
|
|
||||||
}
|
|
||||||
|
|
||||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
|
||||||
match self.rx.recv_next() {
|
|
||||||
Ok(Some(pktref)) => {
|
|
||||||
let tx_token = tx::Token {
|
|
||||||
regs: self.inner.regs,
|
|
||||||
desc_list: &mut self.tx,
|
|
||||||
};
|
|
||||||
Some((pktref, tx_token))
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
self.inner.check_link_change(&self.phy);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("eth recv error: {:?}", e);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
|
||||||
Some(tx::Token {
|
|
||||||
regs: self.inner.regs,
|
|
||||||
desc_list: &mut self.tx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct EthInner<'r> {
|
|
||||||
regs: &'r mut regs::RegisterBlock,
|
|
||||||
link: Option<phy::Link>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r> EthInner<'r> {
|
|
||||||
fn init(&mut self) {
|
|
||||||
// Clear the Network Control register.
|
// Clear the Network Control register.
|
||||||
self.regs.net_ctrl.write(regs::NetCtrl::zeroed());
|
self.regs.net_ctrl.write(regs::NetCtrl::zeroed());
|
||||||
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
|
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
|
||||||
@ -449,11 +287,13 @@ impl<'r> EthInner<'r> {
|
|||||||
self.regs.tx_qbar.write(
|
self.regs.tx_qbar.write(
|
||||||
regs::TxQbar::zeroed()
|
regs::TxQbar::zeroed()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure(&mut self, macaddr: [u8; 6]) {
|
fn configure(&mut self, macaddr: [u8; 6]) {
|
||||||
let clocks = CpuClocks::get();
|
let clocks = CpuClocks::get();
|
||||||
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
|
let mut mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
|
||||||
|
|
||||||
self.regs.net_cfg.write(
|
self.regs.net_cfg.write(
|
||||||
regs::NetCfg::zeroed()
|
regs::NetCfg::zeroed()
|
||||||
@ -466,8 +306,6 @@ impl<'r> EthInner<'r> {
|
|||||||
.copy_all(true)
|
.copy_all(true)
|
||||||
// Remove 4-byte Frame CheckSum
|
// Remove 4-byte Frame CheckSum
|
||||||
.fcs_remove(true)
|
.fcs_remove(true)
|
||||||
// RX checksum offload
|
|
||||||
.rx_chksum_offld_en(true)
|
|
||||||
// One of the slower speeds
|
// One of the slower speeds
|
||||||
.mdc_clk_div((mdc_clk_div >> 4).min(0b111) as u8)
|
.mdc_clk_div((mdc_clk_div >> 4).min(0b111) as u8)
|
||||||
);
|
);
|
||||||
@ -498,7 +336,6 @@ impl<'r> EthInner<'r> {
|
|||||||
.rx_pktbuf_memsz_sel(0x3)
|
.rx_pktbuf_memsz_sel(0x3)
|
||||||
// 4 KB
|
// 4 KB
|
||||||
.tx_pktbuf_memsz_sel(true)
|
.tx_pktbuf_memsz_sel(true)
|
||||||
// TX checksum offload
|
|
||||||
.csum_gen_offload_en(true)
|
.csum_gen_offload_en(true)
|
||||||
// Little-endian
|
// Little-endian
|
||||||
.ahb_endian_swp_mgmt_en(false)
|
.ahb_endian_swp_mgmt_en(false)
|
||||||
@ -512,57 +349,126 @@ impl<'r> EthInner<'r> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 wait_phy_idle(&self) {
|
fn wait_phy_idle(&self) {
|
||||||
while !self.regs.net_status.read().phy_mgmt_idle() {}
|
while !self.regs.net_status.read().phy_mgmt_idle() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_phy(&mut self) -> bool {
|
||||||
fn check_link_change(&mut self, phy: &Phy) {
|
match phy::Phy::find(self) {
|
||||||
// As the PHY access takes some time, exit early if there was
|
Some(phy) => {
|
||||||
// already a link. TODO: check once per second.
|
println!("eth: Found PHY at {}: {}", phy.addr, phy.name());
|
||||||
if self.link.is_some() {
|
phy.modify_control(self, |control|
|
||||||
return
|
control.set_reset(true)
|
||||||
}
|
);
|
||||||
|
while phy.get_control(self).reset() {
|
||||||
let link = phy.get_link(self);
|
println!("eth: Wait for PHY reset");
|
||||||
|
|
||||||
// Check link state transition
|
|
||||||
if self.link != link {
|
|
||||||
match &link {
|
|
||||||
Some(link) => {
|
|
||||||
println!("eth: got {:?}", link);
|
|
||||||
|
|
||||||
use phy::LinkSpeed::*;
|
|
||||||
let txclock = match link.speed {
|
|
||||||
S10 => TX_10,
|
|
||||||
S100 => TX_100,
|
|
||||||
S1000 => TX_1000,
|
|
||||||
};
|
|
||||||
Eth::<(), ()>::setup_gem0_clock(txclock);
|
|
||||||
/* .full_duplex(false) doesn't work even if
|
|
||||||
half duplex has been negotiated. */
|
|
||||||
self.regs.net_cfg.modify(|_, w| w
|
|
||||||
.full_duplex(true)
|
|
||||||
.gige_en(link.speed == S1000)
|
|
||||||
.speed(link.speed != S10)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
println!("eth: link lost");
|
|
||||||
phy.modify_control(self, |control|
|
|
||||||
control.set_autoneg_enable(true)
|
|
||||||
.set_restart_autoneg(true)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
None => false
|
||||||
self.link = link;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> PhyAccess for EthInner<'r> {
|
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 {
|
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
|
||||||
self.wait_phy_idle();
|
self.wait_phy_idle();
|
||||||
self.regs.phy_maint.write(
|
self.regs.phy_maint.write(
|
||||||
@ -592,4 +498,39 @@ impl<'r> PhyAccess for EthInner<'r> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,42 +2,19 @@ pub mod id;
|
|||||||
use id::{identify_phy, PhyIdentifier};
|
use id::{identify_phy, PhyIdentifier};
|
||||||
mod status;
|
mod status;
|
||||||
pub use status::Status;
|
pub use status::Status;
|
||||||
mod extended_status;
|
|
||||||
pub use extended_status::ExtendedStatus;
|
|
||||||
mod control;
|
mod control;
|
||||||
pub use control::Control;
|
pub use control::Control;
|
||||||
|
|
||||||
#[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 {
|
pub trait PhyAccess {
|
||||||
fn read_phy(&mut self, addr: u8, reg: u8) -> u16;
|
fn read_phy(&mut self, addr: u8, reg: u8) -> u16;
|
||||||
fn write_phy(&mut self, addr: u8, reg: u8, data: u16);
|
fn write_phy(&mut self, addr: u8, reg: u8, data: u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Phy {
|
pub struct Phy {
|
||||||
pub addr: u8,
|
pub addr: u8,
|
||||||
device: PhyDevice,
|
device: PhyDevice,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PhyDevice {
|
pub enum PhyDevice {
|
||||||
Marvel88E1116R,
|
Marvel88E1116R,
|
||||||
Rtl8211E,
|
Rtl8211E,
|
||||||
@ -114,36 +91,6 @@ impl Phy {
|
|||||||
pub fn get_status<PA: PhyAccess>(&self, pa: &mut PA) -> Status {
|
pub fn get_status<PA: PhyAccess>(&self, pa: &mut PA) -> Status {
|
||||||
self.read_reg(pa)
|
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 ext_status: ExtendedStatus = self.read_reg(pa);
|
|
||||||
if let Some(link) = ext_status.get_link() {
|
|
||||||
Some(link)
|
|
||||||
} else {
|
|
||||||
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 {
|
pub trait PhyRegister {
|
@ -1,5 +1,5 @@
|
|||||||
use bit_field::BitField;
|
use bit_field::BitField;
|
||||||
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
|
use super::PhyRegister;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
/// Basic Mode Status Register
|
/// Basic Mode Status Register
|
||||||
@ -51,49 +51,6 @@ impl Status {
|
|||||||
pub fn cap_100base_t4(&self) -> bool {
|
pub fn cap_100base_t4(&self) -> bool {
|
||||||
self.0.get_bit(15)
|
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 {
|
impl PhyRegister for Status {
|
@ -1,6 +1,6 @@
|
|||||||
use volatile_register::{RO, WO, RW};
|
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)]
|
#[repr(C)]
|
||||||
pub struct RegisterBlock {
|
pub struct RegisterBlock {
|
@ -1,5 +1,5 @@
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use libregister::*;
|
use crate::{register, register_bit, register_bits, regs::*};
|
||||||
use super::MTU;
|
use super::MTU;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -11,22 +11,13 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Descriptor entry
|
/// Descriptor entry
|
||||||
#[repr(C, align(0x08))]
|
#[repr(C)]
|
||||||
pub struct DescEntry {
|
pub struct DescEntry {
|
||||||
word0: DescWord0,
|
word0: DescWord0,
|
||||||
word1: DescWord1,
|
word1: DescWord1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DescEntry {
|
register!(desc_word0, DescWord0, RW, u32);
|
||||||
pub fn zeroed() -> Self {
|
|
||||||
DescEntry {
|
|
||||||
word0: DescWord0 { inner: VolatileCell::new(0) },
|
|
||||||
word1: DescWord1 { inner: VolatileCell::new(0) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register!(desc_word0, DescWord0, VolatileCell, u32);
|
|
||||||
register_bit!(desc_word0,
|
register_bit!(desc_word0,
|
||||||
/// true if owned by software, false if owned by hardware
|
/// true if owned by software, false if owned by hardware
|
||||||
used, 0);
|
used, 0);
|
||||||
@ -35,7 +26,7 @@ register_bit!(desc_word0,
|
|||||||
wrap, 1);
|
wrap, 1);
|
||||||
register_bits!(desc_word0, address, u32, 2, 31);
|
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_bits!(desc_word1, frame_length_lsbs, u16, 0, 12);
|
||||||
register_bit!(desc_word1, bad_fcs, 13);
|
register_bit!(desc_word1, bad_fcs, 13);
|
||||||
register_bit!(desc_word1, start_of_frame, 14);
|
register_bit!(desc_word1, start_of_frame, 14);
|
||||||
@ -78,8 +69,7 @@ impl<'a> DescList<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DescList {
|
DescList {
|
||||||
// Shorten the list of descriptors to the required number.
|
list,
|
||||||
list: &mut list[0..=last],
|
|
||||||
buffers,
|
buffers,
|
||||||
next: 0,
|
next: 0,
|
||||||
}
|
}
|
||||||
@ -95,7 +85,7 @@ impl<'a> DescList<'a> {
|
|||||||
if entry.word0.read().used() {
|
if entry.word0.read().used() {
|
||||||
let word1 = entry.word1.read();
|
let word1 = entry.word1.read();
|
||||||
let len = word1.frame_length_lsbs().into();
|
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;
|
self.next += 1;
|
||||||
if self.next >= list_len {
|
if self.next >= list_len {
|
||||||
@ -117,7 +107,7 @@ impl<'a> DescList<'a> {
|
|||||||
/// Releases a buffer back to the HW upon Drop
|
/// Releases a buffer back to the HW upon Drop
|
||||||
pub struct PktRef<'a> {
|
pub struct PktRef<'a> {
|
||||||
entry: &'a mut DescEntry,
|
entry: &'a mut DescEntry,
|
||||||
buffer: &'a mut [u8],
|
buffer: &'a [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drop for PktRef<'a> {
|
impl<'a> Drop for PktRef<'a> {
|
||||||
@ -134,9 +124,9 @@ impl<'a> Deref for PktRef<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> smoltcp::phy::RxToken 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
|
where
|
||||||
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>
|
F: FnOnce(&[u8]) -> smoltcp::Result<R>
|
||||||
{
|
{
|
||||||
f(self.buffer)
|
f(self.buffer)
|
||||||
}
|
}
|
@ -1,18 +1,18 @@
|
|||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use libregister::*;
|
use crate::{register, register_bit, register_bits, regs::*};
|
||||||
|
use crate::println;
|
||||||
use super::{MTU, regs};
|
use super::{MTU, regs};
|
||||||
|
|
||||||
/// Descriptor entry
|
/// Descriptor entry
|
||||||
#[repr(C, align(0x08))]
|
|
||||||
pub struct DescEntry {
|
pub struct DescEntry {
|
||||||
word0: DescWord0,
|
word0: DescWord0,
|
||||||
word1: DescWord1,
|
word1: DescWord1,
|
||||||
}
|
}
|
||||||
|
|
||||||
register!(desc_word0, DescWord0, VolatileCell, u32);
|
register!(desc_word0, DescWord0, RW, u32);
|
||||||
register_bits!(desc_word0, address, u32, 0, 31);
|
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_bits!(desc_word1, length, u16, 0, 13);
|
||||||
register_bit!(desc_word1, last_buffer, 15);
|
register_bit!(desc_word1, last_buffer, 15);
|
||||||
register_bit!(desc_word1, no_crc_append, 16);
|
register_bit!(desc_word1, no_crc_append, 16);
|
||||||
@ -27,15 +27,6 @@ register_bit!(desc_word1,
|
|||||||
/// true if owned by software, false if owned by hardware
|
/// true if owned by software, false if owned by hardware
|
||||||
used, 31);
|
used, 31);
|
||||||
|
|
||||||
impl DescEntry {
|
|
||||||
pub fn zeroed() -> Self {
|
|
||||||
DescEntry {
|
|
||||||
word0: DescWord0 { inner: VolatileCell::new(0) },
|
|
||||||
word1: DescWord1 { inner: VolatileCell::new(0) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Number of descriptors
|
/// Number of descriptors
|
||||||
pub const DESCS: usize = 8;
|
pub const DESCS: usize = 8;
|
||||||
|
|
||||||
@ -49,12 +40,6 @@ pub struct DescList<'a> {
|
|||||||
impl<'a> DescList<'a> {
|
impl<'a> DescList<'a> {
|
||||||
pub fn new(list: &'a mut [DescEntry], buffers: &'a mut [[u8; MTU]]) -> Self {
|
pub fn new(list: &'a mut [DescEntry], buffers: &'a mut [[u8; MTU]]) -> Self {
|
||||||
let last = list.len().min(buffers.len()) - 1;
|
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() {
|
for (i, (entry, buffer)) in list.iter_mut().zip(buffers.iter_mut()).enumerate() {
|
||||||
let is_last = i == last;
|
let is_last = i == last;
|
||||||
let buffer_addr = &mut buffer[0] as *mut _ as u32;
|
let buffer_addr = &mut buffer[0] as *mut _ as u32;
|
||||||
@ -73,8 +58,7 @@ impl<'a> DescList<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DescList {
|
DescList {
|
||||||
// Shorten the list of descriptors to the required number.
|
list,
|
||||||
list: &mut list[0..=last],
|
|
||||||
buffers,
|
buffers,
|
||||||
next: 0,
|
next: 0,
|
||||||
}
|
}
|
||||||
@ -88,13 +72,8 @@ impl<'a> DescList<'a> {
|
|||||||
let list_len = self.list.len();
|
let list_len = self.list.len();
|
||||||
let entry = &mut self.list[self.next];
|
let entry = &mut self.list[self.next];
|
||||||
if entry.word1.read().used() {
|
if entry.word1.read().used() {
|
||||||
|
entry.word1.modify(|_, w| w.length(length as u16));
|
||||||
let buffer = &mut self.buffers[self.next][0..length];
|
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;
|
self.next += 1;
|
||||||
if self.next >= list_len {
|
if self.next >= list_len {
|
||||||
@ -121,6 +100,7 @@ impl<'a> Drop for PktRef<'a> {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.entry.word1.modify(|_, w| w.used(false));
|
self.entry.word1.modify(|_, w| w.used(false));
|
||||||
if ! self.regs.tx_status.read().tx_go() {
|
if ! self.regs.tx_status.read().tx_go() {
|
||||||
|
println!("tx start_tx");
|
||||||
self.regs.net_ctrl.modify(|_, w|
|
self.regs.net_ctrl.modify(|_, w|
|
||||||
w.start_tx(true)
|
w.start_tx(true)
|
||||||
);
|
);
|
132
src/mailbox.rs
Normal file
132
src/mailbox.rs
Normal 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
405
src/main.rs
Normal 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
29
src/mpcore.rs
Normal 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
71
src/mutex.rs
Normal 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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
//! Type-safe interface to peripheral registers akin to the code that
|
//! Type-safe interface to peripheral registers akin to the code that
|
||||||
//! svd2rust generates.
|
//! svd2rust generates.
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
#![no_std]
|
use volatile_register::{RO, WO, RW};
|
||||||
|
use bit_field::BitField;
|
||||||
pub use vcell::VolatileCell;
|
|
||||||
pub use volatile_register::{RO, WO, RW};
|
|
||||||
pub use bit_field::BitField;
|
|
||||||
|
|
||||||
/// A readable register
|
/// A readable register
|
||||||
pub trait RegisterR {
|
pub trait RegisterR {
|
||||||
@ -37,11 +35,9 @@ macro_rules! register_common {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod $mod_name {
|
pub mod $mod_name {
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Read {
|
pub struct Read {
|
||||||
pub inner: $inner,
|
pub inner: $inner,
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Write {
|
pub struct Write {
|
||||||
pub inner: $inner,
|
pub inner: $inner,
|
||||||
}
|
}
|
||||||
@ -52,7 +48,7 @@ macro_rules! register_common {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! register_r {
|
macro_rules! register_r {
|
||||||
($mod_name: ident, $struct_name: ident) => (
|
($mod_name: ident, $struct_name: ident) => (
|
||||||
impl libregister::RegisterR for $struct_name {
|
impl crate::regs::RegisterR for $struct_name {
|
||||||
type R = $mod_name::Read;
|
type R = $mod_name::Read;
|
||||||
|
|
||||||
fn read(&self) -> Self::R {
|
fn read(&self) -> Self::R {
|
||||||
@ -66,7 +62,7 @@ macro_rules! register_r {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! register_w {
|
macro_rules! register_w {
|
||||||
($mod_name: ident, $struct_name: ident) => (
|
($mod_name: ident, $struct_name: ident) => (
|
||||||
impl libregister::RegisterW for $struct_name {
|
impl crate::regs::RegisterW for $struct_name {
|
||||||
type W = $mod_name::Write;
|
type W = $mod_name::Write;
|
||||||
|
|
||||||
fn zeroed() -> $mod_name::Write {
|
fn zeroed() -> $mod_name::Write {
|
||||||
@ -85,7 +81,7 @@ macro_rules! register_w {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! register_rw {
|
macro_rules! register_rw {
|
||||||
($mod_name: ident, $struct_name: ident) => (
|
($mod_name: ident, $struct_name: ident) => (
|
||||||
impl libregister::RegisterRW for $struct_name {
|
impl crate::regs::RegisterRW for $struct_name {
|
||||||
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.inner.modify(|inner| {
|
self.inner.modify(|inner| {
|
||||||
@ -98,67 +94,27 @@ macro_rules! register_rw {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
fn zeroed() -> $mod_name::Write {
|
|
||||||
$mod_name::Write { inner: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, w: Self::W) {
|
|
||||||
self.inner.set(w.inner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl libregister::RegisterRW for $struct_name {
|
|
||||||
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
|
/// Main macro for register definition
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! register {
|
macro_rules! register {
|
||||||
// Define read-only register
|
// Define read-only register
|
||||||
($mod_name: ident, $struct_name: ident, RO, $inner: ty) => (
|
($mod_name: ident, $struct_name: ident, RO, $inner: ty) => (
|
||||||
libregister::register_common!($mod_name, $struct_name, libregister::RO<$inner>, $inner);
|
crate::register_common!($mod_name, $struct_name, volatile_register::RO<$inner>, $inner);
|
||||||
libregister::register_r!($mod_name, $struct_name);
|
crate::register_r!($mod_name, $struct_name);
|
||||||
);
|
);
|
||||||
|
|
||||||
// Define write-only register
|
// Define write-only register
|
||||||
($mod_name: ident, $struct_name: ident, WO, $inner: ty) => (
|
($mod_name: ident, $struct_name: ident, WO, $inner: ty) => (
|
||||||
libregister::register_common!($mod_name, $struct_name, volatile_register::WO<$inner>, $inner);
|
crate::register_common!($mod_name, $struct_name, volatile_register::WO<$inner>, $inner);
|
||||||
libregister::register_w!($mod_name, $struct_name);
|
crate::register_w!($mod_name, $struct_name);
|
||||||
);
|
);
|
||||||
|
|
||||||
// Define read-write register
|
// Define read-write register
|
||||||
($mod_name: ident, $struct_name: ident, RW, $inner: ty) => (
|
($mod_name: ident, $struct_name: ident, RW, $inner: ty) => (
|
||||||
libregister::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner);
|
crate::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner);
|
||||||
libregister::register_r!($mod_name, $struct_name);
|
crate::register_r!($mod_name, $struct_name);
|
||||||
libregister::register_w!($mod_name, $struct_name);
|
crate::register_w!($mod_name, $struct_name);
|
||||||
libregister::register_rw!($mod_name, $struct_name);
|
crate::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);
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,9 @@
|
|||||||
///! Register definitions for System Level Control
|
///! Register definitions for System Level Control
|
||||||
|
|
||||||
use volatile_register::{RO, RW};
|
use volatile_register::{RO, RW};
|
||||||
use libregister::{
|
use crate::{register, register_at,
|
||||||
register, register_at,
|
register_bit, register_bits, register_bits_typed,
|
||||||
register_bit, register_bits, register_bits_typed,
|
regs::RegisterW, regs::RegisterRW};
|
||||||
RegisterW, RegisterRW,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum PllSource {
|
pub enum PllSource {
|
||||||
@ -21,45 +19,6 @@ pub enum ArmPllSource {
|
|||||||
IoPll = 0b11,
|
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,
|
|
||||||
/// For DDR3L with 1.35V IO
|
|
||||||
Vref0_675V,
|
|
||||||
/// For DDR3 with 1.5V IO
|
|
||||||
Vref0_75V,
|
|
||||||
/// For DDR2 with 1.8V IO
|
|
||||||
Vref0_9V,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct RegisterBlock {
|
pub struct RegisterBlock {
|
||||||
pub scl: RW<u32>,
|
pub scl: RW<u32>,
|
||||||
@ -70,14 +29,14 @@ pub struct RegisterBlock {
|
|||||||
pub arm_pll_ctrl: PllCtrl,
|
pub arm_pll_ctrl: PllCtrl,
|
||||||
pub ddr_pll_ctrl: PllCtrl,
|
pub ddr_pll_ctrl: PllCtrl,
|
||||||
pub io_pll_ctrl: PllCtrl,
|
pub io_pll_ctrl: PllCtrl,
|
||||||
pub pll_status: PllStatus,
|
pub pll_status: RO<u32>,
|
||||||
pub arm_pll_cfg: PllCfg,
|
pub arm_pll_cfg: RW<u32>,
|
||||||
pub ddr_pll_cfg: PllCfg,
|
pub ddr_pll_cfg: RW<u32>,
|
||||||
pub io_pll_cfg: PllCfg,
|
pub io_pll_cfg: RW<u32>,
|
||||||
reserved1: [u32; 1],
|
reserved1: [u32; 1],
|
||||||
pub arm_clk_ctrl: ArmClkCtrl,
|
pub arm_clk_ctrl: ArmClkCtrl,
|
||||||
pub ddr_clk_ctrl: DdrClkCtrl,
|
pub ddr_clk_ctrl: RW<u32>,
|
||||||
pub dci_clk_ctrl: DciClkCtrl,
|
pub dci_clk_ctrl: RW<u32>,
|
||||||
pub aper_clk_ctrl: AperClkCtrl,
|
pub aper_clk_ctrl: AperClkCtrl,
|
||||||
pub usb0_clk_ctrl: RW<u32>,
|
pub usb0_clk_ctrl: RW<u32>,
|
||||||
pub usb1_clk_ctrl: RW<u32>,
|
pub usb1_clk_ctrl: RW<u32>,
|
||||||
@ -86,7 +45,7 @@ pub struct RegisterBlock {
|
|||||||
pub gem0_clk_ctrl: GemClkCtrl,
|
pub gem0_clk_ctrl: GemClkCtrl,
|
||||||
pub gem1_clk_ctrl: GemClkCtrl,
|
pub gem1_clk_ctrl: GemClkCtrl,
|
||||||
pub smc_clk_ctrl: RW<u32>,
|
pub smc_clk_ctrl: RW<u32>,
|
||||||
pub lqspi_clk_ctrl: LqspiClkCtrl,
|
pub lqspi_clk_ctrl: RW<u32>,
|
||||||
pub sdio_clk_ctrl: RW<u32>,
|
pub sdio_clk_ctrl: RW<u32>,
|
||||||
pub uart_clk_ctrl: UartClkCtrl,
|
pub uart_clk_ctrl: UartClkCtrl,
|
||||||
pub spi_clk_ctrl: RW<u32>,
|
pub spi_clk_ctrl: RW<u32>,
|
||||||
@ -126,7 +85,7 @@ pub struct RegisterBlock {
|
|||||||
pub i2c_rst_ctrl: RW<u32>,
|
pub i2c_rst_ctrl: RW<u32>,
|
||||||
pub uart_rst_ctrl: UartRstCtrl,
|
pub uart_rst_ctrl: UartRstCtrl,
|
||||||
pub gpio_rst_ctrl: RW<u32>,
|
pub gpio_rst_ctrl: RW<u32>,
|
||||||
pub lqspi_rst_ctrl: LqspiRstCtrl,
|
pub lqspi_rst_ctrl: RW<u32>,
|
||||||
pub smc_rst_ctrl: RW<u32>,
|
pub smc_rst_ctrl: RW<u32>,
|
||||||
pub ocm_rst_ctrl: RW<u32>,
|
pub ocm_rst_ctrl: RW<u32>,
|
||||||
reserved4: [u32; 1],
|
reserved4: [u32; 1],
|
||||||
@ -136,7 +95,7 @@ pub struct RegisterBlock {
|
|||||||
pub rs_awdt_ctrl: RW<u32>,
|
pub rs_awdt_ctrl: RW<u32>,
|
||||||
reserved6: [u32; 2],
|
reserved6: [u32; 2],
|
||||||
pub reboot_status: RW<u32>,
|
pub reboot_status: RW<u32>,
|
||||||
pub boot_mode: BootMode,
|
pub boot_mode: RW<u32>,
|
||||||
reserved7: [u32; 40],
|
reserved7: [u32; 40],
|
||||||
pub apu_ctrl: RW<u32>,
|
pub apu_ctrl: RW<u32>,
|
||||||
pub wdt_clk_sel: RW<u32>,
|
pub wdt_clk_sel: RW<u32>,
|
||||||
@ -231,25 +190,23 @@ pub struct RegisterBlock {
|
|||||||
pub gpiob_cfg_hstl: RW<u32>,
|
pub gpiob_cfg_hstl: RW<u32>,
|
||||||
pub gpiob_drvr_bias_ctrl: RW<u32>,
|
pub gpiob_drvr_bias_ctrl: RW<u32>,
|
||||||
reserved21: [u32; 9],
|
reserved21: [u32; 9],
|
||||||
pub ddriob_addr0: DdriobConfig,
|
pub ddriob_addr1: RW<u32>,
|
||||||
pub ddriob_addr1: DdriobConfig,
|
pub ddriob_data0: RW<u32>,
|
||||||
pub ddriob_data0: DdriobConfig,
|
pub ddriob_data1: RW<u32>,
|
||||||
pub ddriob_data1: DdriobConfig,
|
pub ddriob_diff0: RW<u32>,
|
||||||
pub ddriob_diff0: DdriobConfig,
|
pub ddriob_diff1: RW<u32>,
|
||||||
pub ddriob_diff1: DdriobConfig,
|
pub ddriob_clock: RW<u32>,
|
||||||
pub ddriob_clock: DdriobConfig,
|
pub w_addr: RW<u32>,
|
||||||
pub ddriob_drive_slew_addr: RW<u32>,
|
pub w_data: RW<u32>,
|
||||||
pub ddriob_drive_slew_data: RW<u32>,
|
pub w_diff: RW<u32>,
|
||||||
pub ddriob_drive_slew_diff: RW<u32>,
|
pub w_clock: RW<u32>,
|
||||||
pub ddriob_drive_slew_clock: RW<u32>,
|
pub ddriob_ddr_ctrl: RW<u32>,
|
||||||
pub ddriob_ddr_ctrl: DdriobDdrCtrl,
|
pub ddriob_dci_ctrl: RW<u32>,
|
||||||
pub ddriob_dci_ctrl: DdriobDciCtrl,
|
pub ddriob_dci_status: RW<u32>,
|
||||||
pub ddriob_dci_status: DdriobDciStatus,
|
|
||||||
}
|
}
|
||||||
register_at!(RegisterBlock, 0xF8000000, new);
|
register_at!(RegisterBlock, 0xF8000000, new);
|
||||||
|
|
||||||
impl RegisterBlock {
|
impl RegisterBlock {
|
||||||
/// Required to modify any sclr register
|
|
||||||
pub fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
|
pub fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
|
||||||
let mut self_ = Self::new();
|
let mut self_ = Self::new();
|
||||||
self_.slcr_unlock.unlock();
|
self_.slcr_unlock.unlock();
|
||||||
@ -290,38 +247,12 @@ impl SlcrUnlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
register!(pll_ctrl, PllCtrl, RW, u32);
|
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_force, 4);
|
||||||
register_bit!(pll_ctrl, pll_bypass_qual, 3);
|
register_bit!(pll_ctrl, pll_bypass_qual, 3);
|
||||||
register_bit!(pll_ctrl, pll_pwrdwn, 1);
|
register_bit!(pll_ctrl, pll_pwrdwn, 1);
|
||||||
register_bit!(pll_ctrl, pll_reset, 0);
|
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!(arm_clk_ctrl, ArmClkCtrl, RW, u32);
|
||||||
register_bit!(arm_clk_ctrl,
|
register_bit!(arm_clk_ctrl,
|
||||||
/// Clock active
|
/// Clock active
|
||||||
@ -333,17 +264,6 @@ register_bit!(arm_clk_ctrl, cpu_6or4xclkact, 24);
|
|||||||
register_bits!(arm_clk_ctrl, divisor, u8, 8, 13);
|
register_bits!(arm_clk_ctrl, divisor, u8, 8, 13);
|
||||||
register_bits_typed!(arm_clk_ctrl, srcsel, u8, ArmPllSource, 8, 13);
|
register_bits_typed!(arm_clk_ctrl, srcsel, u8, ArmPllSource, 8, 13);
|
||||||
|
|
||||||
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!(clk_621_true, Clk621True, RW, u32);
|
register!(clk_621_true, Clk621True, RW, u32);
|
||||||
register_bit!(clk_621_true, clk_621_true, 0);
|
register_bit!(clk_621_true, clk_621_true, 0);
|
||||||
|
|
||||||
@ -377,7 +297,7 @@ register_bits!(gem_clk_ctrl,
|
|||||||
divisor, u8, 8, 13);
|
divisor, u8, 8, 13);
|
||||||
register_bits_typed!(gem_clk_ctrl,
|
register_bits_typed!(gem_clk_ctrl,
|
||||||
/// Source to generate the ref clock
|
/// Source to generate the ref clock
|
||||||
srcsel, u8, PllSource, 4, 6);
|
srcsel, u8, PllSource, 4, 5);
|
||||||
register_bit!(gem_clk_ctrl,
|
register_bit!(gem_clk_ctrl,
|
||||||
/// SMC reference clock control
|
/// SMC reference clock control
|
||||||
clkact, 0);
|
clkact, 0);
|
||||||
@ -442,14 +362,8 @@ impl UartRstCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
|
register!(pss_rst_ctrl, PssRstCtrl, RW, u32);
|
||||||
register_bit!(lqspi_clk_ctrl, clkact, 0);
|
register_bit!(pss_rst_ctrl, soft_rst, 1);
|
||||||
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!(a9_cpu_rst_ctrl, A9CpuRstCtrl, RW, u32);
|
register!(a9_cpu_rst_ctrl, A9CpuRstCtrl, RW, u32);
|
||||||
register_bit!(a9_cpu_rst_ctrl, peri_rst, 8);
|
register_bit!(a9_cpu_rst_ctrl, peri_rst, 8);
|
||||||
@ -458,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_rst1, 1);
|
||||||
register_bit!(a9_cpu_rst_ctrl, a9_rst0, 0);
|
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
|
/// Used for MioPin*.io_type
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum IoBufferType {
|
pub enum IoBufferType {
|
||||||
@ -556,34 +453,3 @@ mio_pin_register!(mio_pin_53, MioPin53);
|
|||||||
|
|
||||||
register!(gpiob_ctrl, GpiobCtrl, RW, u32);
|
register!(gpiob_ctrl, GpiobCtrl, RW, u32);
|
||||||
register_bit!(gpiob_ctrl, vref_en, 0);
|
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, 1);
|
|
||||||
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, 0);
|
|
||||||
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
53
src/stdio.rs
Normal 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() {}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use libregister::*;
|
use crate::regs::*;
|
||||||
use super::regs::{RegisterBlock, BaudRateGen, BaudRateDiv};
|
use super::regs::{RegisterBlock, BaudRateGen, BaudRateDiv};
|
||||||
|
|
||||||
const BDIV_MIN: u32 = 4;
|
const BDIV_MIN: u32 = 4;
|
@ -1,8 +1,8 @@
|
|||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use libregister::*;
|
use crate::regs::*;
|
||||||
use super::slcr;
|
use crate::slcr;
|
||||||
use super::clocks::CpuClocks;
|
use crate::clocks::CpuClocks;
|
||||||
|
|
||||||
mod regs;
|
mod regs;
|
||||||
mod baud_rate_gen;
|
mod baud_rate_gen;
|
@ -1,11 +1,7 @@
|
|||||||
use volatile_register::{RO, WO, RW};
|
use volatile_register::{RO, WO, RW};
|
||||||
|
|
||||||
use libregister::{
|
use crate::{register, register_bit, register_bits, register_bits_typed, register_at};
|
||||||
register, register_at,
|
|
||||||
register_bit, register_bits, register_bits_typed,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ChannelMode {
|
pub enum ChannelMode {
|
||||||
Normal = 0b00,
|
Normal = 0b00,
|
||||||
@ -14,7 +10,6 @@ pub enum ChannelMode {
|
|||||||
RemoteLoopback = 0b11,
|
RemoteLoopback = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ParityMode {
|
pub enum ParityMode {
|
||||||
EvenParity = 0b000,
|
EvenParity = 0b000,
|
||||||
@ -24,7 +19,6 @@ pub enum ParityMode {
|
|||||||
None = 0b100,
|
None = 0b100,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum StopBits {
|
pub enum StopBits {
|
||||||
One = 0b00,
|
One = 0b00,
|
Loading…
Reference in New Issue
Block a user