forked from M-Labs/zynq-rs
Compare commits
136 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2e8d291ee7 | ||
|
8b025b8644 | ||
9199bb7a16 | |||
da60be38b1 | |||
6c4b07e0cf | |||
4439a64974 | |||
cf1983e543 | |||
1036ecc0f7 | |||
a8cb085a25 | |||
1ba587ccf9 | |||
62ecb5095b | |||
887627b137 | |||
0adb0d5c51 | |||
dd0fe054d7 | |||
1dbb358a4c | |||
b94afa1581 | |||
0d1cf04a34 | |||
8a9dde6119 | |||
5268839467 | |||
0b9a150255 | |||
2d1c8e1f4f | |||
e1068af948 | |||
3b3b5dc7c1 | |||
70d56d2b28 | |||
b346ea8297 | |||
e9b80eaef9 | |||
0823a74164 | |||
aab82f6843 | |||
f3676c945a | |||
1e465250f5 | |||
e37659e4b3 | |||
45cc271735 | |||
cfaa1213e2 | |||
7107244a6e | |||
dd3ad3be67 | |||
a8a7f11990 | |||
78caca1f04 | |||
5642feb824 | |||
a199a5dc7d | |||
3180f1c3f7 | |||
8037042040 | |||
6ffcf7d4a4 | |||
4f8a76e29b | |||
ff41f4dd2d | |||
d89f594ba4 | |||
4e4ff512d9 | |||
85f29ace6b | |||
ef6d0ff3f1 | |||
0bc941d789 | |||
a416f48af1 | |||
b6596d930d | |||
49901d1b8a | |||
|
4a1d0fc0c3 | ||
50481b3a80 | |||
b76dc4037d | |||
caa69fda2e | |||
3279aab961 | |||
92c274348f | |||
3eb7fce572 | |||
b1472096ba | |||
cb1b5776cd | |||
3496755406 | |||
959bf8a245 | |||
4d3b2ac7e5 | |||
cae02947bc | |||
afd96bd887 | |||
261455877d | |||
ff96bf903b | |||
d2df5652d0 | |||
eb56dda44f | |||
6e50b32e80 | |||
74c43b3477 | |||
99a00e019b | |||
961e2e1dd0 | |||
04e816d99e | |||
6bee1f44f4 | |||
54e4b9281f | |||
f688eb83ab | |||
5c62716a99 | |||
1f728686ff | |||
e248d3d3b1 | |||
91bab76ab6 | |||
43501003f9 | |||
ceeaa6427e | |||
7cdf6c0918 | |||
fc39885d3b | |||
f199ac68b4 | |||
637bb35f43 | |||
85bd506132 | |||
27114aec62 | |||
9b4f07f37c | |||
e61d1268ac | |||
a4d3360a70 | |||
838434cdec | |||
4cf5283ba8 | |||
a8886de067 | |||
afda48e3fe | |||
c046bbf8a2 | |||
9d725bcf0f | |||
58cf9833cc | |||
83b8bb096a | |||
b541160f38 | |||
|
1804c4c6e8 | ||
|
d87b874b21 | ||
|
9053166acc | ||
4e9c38527e | |||
a76214cb9d | |||
0f6bc68d1f | |||
378755a0ce | |||
644cc64524 | |||
f9cc561144 | |||
06bc7ba809 | |||
d5334cc083 | |||
20249cf2da | |||
ace24112fa | |||
109a203e02 | |||
5ef0213b97 | |||
d210b2b13f | |||
c38b2e418e | |||
6771531255 | |||
4c62ce0dad | |||
9c73cf130d | |||
c5a7f059c2 | |||
cc0c4e521e | |||
45ed5f6c5b | |||
d11e581862 | |||
3a5ed0aac6 | |||
5603766c5d | |||
43c3f3e4a6 | |||
4bc1d21ae9 | |||
bfb3a00a4e | |||
022da2a7a7 | |||
b8818863c4 | |||
1f9ad5ff62 | |||
e135b27c13 | |||
80e808208e |
@ -1,6 +1,5 @@
|
||||
[target.armv7-none-eabihf]
|
||||
runner = "./runner.sh"
|
||||
linker = "arm-none-eabihf-gcc"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
"-C", "target-feature=a9,armv7-a,neon",
|
||||
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -16,9 +16,56 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.19"
|
||||
source = "git+https://github.com/rust-lang-nursery/compiler-builtins#36da64f20e96206ac279f700586817c8abe3bdf8"
|
||||
name = "libboard_zc706"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"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]]
|
||||
name = "managed"
|
||||
@ -33,7 +80,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
source = "git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3#8eb01aca364aefe5f823d68d552d62c76c9be4a3"
|
||||
dependencies = [
|
||||
"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)",
|
||||
@ -42,7 +89,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -50,27 +97,27 @@ name = "volatile-register"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zc706"
|
||||
name = "zc706-experiments"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"compiler_builtins 0.1.19 (git+https://github.com/rust-lang-nursery/compiler-builtins)",
|
||||
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libboard_zc706 0.0.0",
|
||||
"libboard_zynq 0.0.0",
|
||||
"libcortex_a9 0.0.0",
|
||||
"libregister 0.0.0",
|
||||
"smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"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 byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum compiler_builtins 0.1.19 (git+https://github.com/rust-lang-nursery/compiler-builtins)" = "<none>"
|
||||
"checksum linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "47314ec1d29aa869ee7cb5a5be57be9b1055c56567d59c3fb6689926743e0bea"
|
||||
"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 smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fef582369edb298c6c41319a544ca9c4e83622f226055ccfcb35974fbb55ed34"
|
||||
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
|
||||
"checksum smoltcp 0.5.0 (git+https://github.com/m-labs/smoltcp.git?rev=8eb01aca364aefe5f823d68d552d62c76c9be4a3)" = "<none>"
|
||||
"checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
||||
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
||||
|
24
Cargo.toml
24
Cargo.toml
@ -1,8 +1,9 @@
|
||||
[package]
|
||||
name = "zc706"
|
||||
version = "0.0.0"
|
||||
authors = ["Astro <astro@spaceboyz.net>"]
|
||||
edition = "2018"
|
||||
[workspace]
|
||||
members = [
|
||||
"libregister", "libcortex_a9",
|
||||
"libboard_zynq", "libboard_zc706",
|
||||
"experiments",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
@ -11,14 +12,5 @@ lto = false
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
debug = true
|
||||
|
||||
[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"]}
|
||||
lto = true # Link-Time Optimization
|
||||
opt-level = 'z' # Optimize for size.
|
||||
|
12
README.md
12
README.md
@ -1,7 +1,7 @@
|
||||
# Build
|
||||
|
||||
```shell
|
||||
nix-shell --command "cargo build --release"
|
||||
nix-shell --command "cargo xbuild --release"
|
||||
```
|
||||
|
||||
# Debug
|
||||
@ -39,13 +39,15 @@ Proceed using gdb with `load`, `c`
|
||||
### Running on the ZC706
|
||||
|
||||
```shell
|
||||
nix-shell --command "cargo build --release"
|
||||
openocd -f openocd/zc706.cfg
|
||||
nix-shell --command "cargo xbuild --release"
|
||||
cd openocd
|
||||
openocd -f zc706.cfg
|
||||
```
|
||||
|
||||
### Running on the Cora Z7-10
|
||||
|
||||
```shell
|
||||
nix-shell --command "cargo build --release --no-default-features --features=target_cora_z7_10"
|
||||
openocd -f openocd/cora-z7-10.cfg
|
||||
nix-shell --command "cargo xbuild --release --no-default-features --features=target_cora_z7_10"
|
||||
cd openocd
|
||||
openocd -f cora-z7-10.cfg
|
||||
```
|
||||
|
15156
channel-rust-nightly.toml
Normal file
15156
channel-rust-nightly.toml
Normal file
File diff suppressed because it is too large
Load Diff
49
default.nix
49
default.nix
@ -1,27 +1,54 @@
|
||||
{ # Use master branch of the overlay by default
|
||||
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
|
||||
rustManifest ? ./channel-rust-nightly.toml,
|
||||
}:
|
||||
|
||||
let
|
||||
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
|
||||
in
|
||||
with pkgs;
|
||||
let
|
||||
rustcSrc = fetchgit {
|
||||
rustcSrc = pkgs.fetchgit {
|
||||
url = https://github.com/rust-lang/rust.git;
|
||||
# master of 2019-08-06
|
||||
rev = "8996328ebf34aa73e83a1db326767c11041f811d";
|
||||
sha256 = "1daz0y97dm35nfy7ip0wqvyax0g36szm25n77rcg20k6wab4fqi7";
|
||||
# master of 2019-11-09
|
||||
rev = "ac162c6abe34cdf965afc0389f6cefa79653c63b";
|
||||
sha256 = "06c5gws1mrpr69z1gzs358zf7hcsg6ky8n4ha0vv2s9d9w93x1kj";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
targets = [
|
||||
];
|
||||
targets = [];
|
||||
rustChannelOfTargets = _channel: _date: targets:
|
||||
(pkgs.lib.rustLib.fromManifestFile rustManifest {
|
||||
inherit (pkgs) stdenv fetchurl patchelf;
|
||||
}).rust.override { inherit targets; };
|
||||
rust =
|
||||
rustChannelOfTargets "nightly" null targets;
|
||||
rustPlatform = recurseIntoAttrs (makeRustPlatform {
|
||||
rustPlatform = pkgs.recurseIntoAttrs (pkgs.makeRustPlatform {
|
||||
rustc = rust // { src = rustcSrc; };
|
||||
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 {
|
||||
inherit pkgs rustPlatform rustcSrc;
|
||||
inherit pkgs rustPlatform rustcSrc zc706 gcc;
|
||||
}
|
||||
|
22
experiments/Cargo.toml
Normal file
22
experiments/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[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
|
208
experiments/src/main.rs
Normal file
208
experiments/src/main.rs
Normal file
@ -0,0 +1,208 @@
|
||||
#![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 {}
|
||||
}
|
24
libboard_zc706/Cargo.toml
Normal file
24
libboard_zc706/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[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
|
13
libboard_zc706/src/abort.rs
Normal file
13
libboard_zc706/src/abort.rs
Normal file
@ -0,0 +1,13 @@
|
||||
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 {}
|
||||
}
|
148
libboard_zc706/src/boot.rs
Normal file
148
libboard_zc706/src/boot.rs
Normal file
@ -0,0 +1,148 @@
|
||||
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
|
||||
}
|
||||
}
|
13
libboard_zc706/src/lib.rs
Normal file
13
libboard_zc706/src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
||||
#![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;
|
19
libboard_zc706/src/panic.rs
Normal file
19
libboard_zc706/src/panic.rs
Normal file
@ -0,0 +1,19 @@
|
||||
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 {}
|
||||
}
|
41
libboard_zc706/src/ram.rs
Normal file
41
libboard_zc706/src/ram.rs
Normal file
@ -0,0 +1,41 @@
|
||||
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")
|
||||
}
|
25
libboard_zynq/Cargo.toml
Normal file
25
libboard_zynq/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[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
|
2
libboard_zynq/src/axi_gp.rs
Normal file
2
libboard_zynq/src/axi_gp.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub const M_AXI_GP0: usize = 0x4000_0000;
|
||||
pub const M_AXI_GP1: usize = 0x8000_0000;
|
59
libboard_zynq/src/axi_hp.rs
Normal file
59
libboard_zynq/src/axi_hp.rs
Normal file
@ -0,0 +1,59 @@
|
||||
//! 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);
|
175
libboard_zynq/src/clocks.rs
Normal file
175
libboard_zynq/src/clocks.rs
Normal file
@ -0,0 +1,175 @@
|
||||
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)),
|
||||
];
|
231
libboard_zynq/src/ddr/mod.rs
Normal file
231
libboard_zynq/src/ddr/mod.rs
Normal file
@ -0,0 +1,231 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
178
libboard_zynq/src/ddr/regs.rs
Normal file
178
libboard_zynq/src/ddr/regs.rs
Normal file
@ -0,0 +1,178 @@
|
||||
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,23 +1,31 @@
|
||||
use crate::regs::*;
|
||||
use crate::slcr;
|
||||
use libregister::*;
|
||||
use crate::println;
|
||||
use super::slcr;
|
||||
use super::clocks::CpuClocks;
|
||||
|
||||
pub mod phy;
|
||||
use phy::{Phy, PhyAccess};
|
||||
mod regs;
|
||||
pub mod rx;
|
||||
pub mod tx;
|
||||
|
||||
/// Size of all the buffers
|
||||
pub const MTU: usize = 1536;
|
||||
pub const IO_PLL: u32 = 1_000;
|
||||
/// Maximum MDC clock
|
||||
const MAX_MDC: u32 = 2_500_000;
|
||||
const TX_10: u32 = 10_000_000;
|
||||
const TX_100: u32 = 25_000_000;
|
||||
/// Clock for GbE
|
||||
const TX_1000: u32 = 125_000_000;
|
||||
|
||||
pub struct Eth<RX, TX> {
|
||||
regs: &'static mut regs::RegisterBlock,
|
||||
pub struct Eth<'r, RX, TX> {
|
||||
rx: RX,
|
||||
tx: TX,
|
||||
inner: EthInner<'r>,
|
||||
phy: Phy,
|
||||
}
|
||||
|
||||
impl Eth<(), ()> {
|
||||
impl<'r> Eth<'r, (), ()> {
|
||||
pub fn default(macaddr: [u8; 6]) -> Self {
|
||||
slcr::RegisterBlock::unlocked(|slcr| {
|
||||
// Manual example: 0x0000_1280
|
||||
@ -150,40 +158,51 @@ impl Eth<(), ()> {
|
||||
}
|
||||
|
||||
pub fn gem0(macaddr: [u8; 6]) -> Self {
|
||||
Self::setup_gem0_clock(125);
|
||||
Self::setup_gem0_clock(TX_1000);
|
||||
|
||||
let regs = regs::RegisterBlock::gem0();
|
||||
Self::from_regs(regs, macaddr)
|
||||
}
|
||||
|
||||
pub fn gem1(macaddr: [u8; 6]) -> Self {
|
||||
Self::setup_gem1_clock(125);
|
||||
Self::setup_gem1_clock(TX_1000);
|
||||
|
||||
let regs = regs::RegisterBlock::gem1();
|
||||
Self::from_regs(regs, macaddr)
|
||||
}
|
||||
|
||||
fn from_regs(regs: &'static mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
|
||||
let mut eth = Eth {
|
||||
fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
|
||||
let mut inner = EthInner {
|
||||
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: (),
|
||||
tx: (),
|
||||
}.init();
|
||||
eth.configure(macaddr);
|
||||
eth
|
||||
inner,
|
||||
phy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<RX, TX> Eth<RX, TX> {
|
||||
impl<'r, RX, TX> Eth<'r, RX, TX> {
|
||||
pub fn setup_gem0_clock(tx_clock: u32) {
|
||||
let d0 = (IO_PLL / tx_clock).min(63);
|
||||
let d1 = (IO_PLL / tx_clock / d0).min(63);
|
||||
let io_pll = CpuClocks::get().io;
|
||||
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
|
||||
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
|
||||
|
||||
slcr::RegisterBlock::unlocked(|slcr| {
|
||||
slcr.gem0_clk_ctrl.write(
|
||||
// 0x0050_0801: 8, 5: 100 Mb/s
|
||||
// ...: 8, 1: 1000 Mb/s
|
||||
slcr::ClkCtrl::zeroed()
|
||||
slcr::GemClkCtrl::zeroed()
|
||||
.clkact(true)
|
||||
.srcsel(slcr::PllSource::IoPll)
|
||||
.divisor(d0 as u8)
|
||||
@ -199,12 +218,13 @@ impl<RX, TX> Eth<RX, TX> {
|
||||
}
|
||||
|
||||
pub fn setup_gem1_clock(tx_clock: u32) {
|
||||
let d0 = (IO_PLL / tx_clock).min(63);
|
||||
let d1 = (IO_PLL / tx_clock / d0).min(63);
|
||||
let io_pll = CpuClocks::get().io;
|
||||
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
|
||||
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
|
||||
|
||||
slcr::RegisterBlock::unlocked(|slcr| {
|
||||
slcr.gem1_clk_ctrl.write(
|
||||
slcr::ClkCtrl::zeroed()
|
||||
slcr::GemClkCtrl::zeroed()
|
||||
.clkact(true)
|
||||
.srcsel(slcr::PllSource::IoPll)
|
||||
.divisor(d0 as u8)
|
||||
@ -219,7 +239,155 @@ impl<RX, TX> Eth<RX, TX> {
|
||||
});
|
||||
}
|
||||
|
||||
fn init(mut self) -> Self {
|
||||
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 {
|
||||
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.
|
||||
self.regs.net_ctrl.write(regs::NetCtrl::zeroed());
|
||||
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
|
||||
@ -281,11 +449,12 @@ impl<RX, TX> Eth<RX, TX> {
|
||||
self.regs.tx_qbar.write(
|
||||
regs::TxQbar::zeroed()
|
||||
);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn configure(&mut self, macaddr: [u8; 6]) {
|
||||
let clocks = CpuClocks::get();
|
||||
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
|
||||
|
||||
self.regs.net_cfg.write(
|
||||
regs::NetCfg::zeroed()
|
||||
.full_duplex(true)
|
||||
@ -297,9 +466,10 @@ impl<RX, TX> Eth<RX, TX> {
|
||||
.copy_all(true)
|
||||
// Remove 4-byte Frame CheckSum
|
||||
.fcs_remove(true)
|
||||
// RX checksum offload
|
||||
.rx_chksum_offld_en(true)
|
||||
// One of the slower speeds
|
||||
// TODO: calculate properly
|
||||
.mdc_clk_div(0b110)
|
||||
.mdc_clk_div((mdc_clk_div >> 4).min(0b111) as u8)
|
||||
);
|
||||
|
||||
let macaddr_msbs =
|
||||
@ -328,7 +498,8 @@ impl<RX, TX> Eth<RX, TX> {
|
||||
.rx_pktbuf_memsz_sel(0x3)
|
||||
// 4 KB
|
||||
.tx_pktbuf_memsz_sel(true)
|
||||
// .csum_gen_offload_en(true)
|
||||
// TX checksum offload
|
||||
.csum_gen_offload_en(true)
|
||||
// Little-endian
|
||||
.ahb_endian_swp_mgmt_en(false)
|
||||
// INCR16 AHB burst
|
||||
@ -341,123 +512,57 @@ impl<RX, TX> Eth<RX, TX> {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn start_rx<'rx>(self, rx_list: &'rx mut [rx::DescEntry], rx_buffers: &'rx mut [[u8; MTU]]) -> Eth<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<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) {
|
||||
while !self.regs.net_status.read().phy_mgmt_idle() {}
|
||||
}
|
||||
|
||||
pub fn reset_phy(&mut self) -> bool {
|
||||
match phy::Phy::find(self) {
|
||||
Some(phy) => {
|
||||
phy.modify_control(self, |control|
|
||||
control.set_reset(true)
|
||||
);
|
||||
while phy.get_control(self).reset() {
|
||||
println!("Wait for PHY reset");
|
||||
}
|
||||
phy.modify_control(self, |control|
|
||||
control.set_autoneg_enable(true)
|
||||
.set_restart_autoneg(true)
|
||||
);
|
||||
// 125 MHz for 1000base-TX
|
||||
Self::setup_gem0_clock(125);
|
||||
|
||||
true
|
||||
}
|
||||
None => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'rx, TX> Eth<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);
|
||||
fn check_link_change(&mut self, phy: &Phy) {
|
||||
// As the PHY access takes some time, exit early if there was
|
||||
// already a link. TODO: check once per second.
|
||||
if self.link.is_some() {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
let link = phy.get_link(self);
|
||||
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
Ok(None)
|
||||
|
||||
self.link = link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tx, RX> Eth<RX, tx::DescList<'tx>> {
|
||||
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
|
||||
self.tx.send(&mut self.regs, length)
|
||||
}
|
||||
}
|
||||
|
||||
impl<RX, TX> phy::PhyAccess for Eth<RX, TX> {
|
||||
impl<'r> PhyAccess for EthInner<'r> {
|
||||
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
|
||||
self.wait_phy_idle();
|
||||
self.regs.phy_maint.write(
|
||||
@ -486,3 +591,5 @@ impl<RX, TX> phy::PhyAccess for Eth<RX, TX> {
|
||||
self.wait_phy_idle();
|
||||
}
|
||||
}
|
||||
|
||||
|
59
libboard_zynq/src/eth/phy/extended_status.rs
Normal file
59
libboard_zynq/src/eth/phy/extended_status.rs
Normal file
@ -0,0 +1,59 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -2,19 +2,42 @@ pub mod id;
|
||||
use id::{identify_phy, PhyIdentifier};
|
||||
mod status;
|
||||
pub use status::Status;
|
||||
mod extended_status;
|
||||
pub use extended_status::ExtendedStatus;
|
||||
mod 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 {
|
||||
fn read_phy(&mut self, addr: u8, reg: u8) -> u16;
|
||||
fn write_phy(&mut self, addr: u8, reg: u8, data: u16);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Phy {
|
||||
pub addr: u8,
|
||||
device: PhyDevice,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PhyDevice {
|
||||
Marvel88E1116R,
|
||||
Rtl8211E,
|
||||
@ -91,6 +114,36 @@ impl Phy {
|
||||
pub fn get_status<PA: PhyAccess>(&self, pa: &mut PA) -> Status {
|
||||
self.read_reg(pa)
|
||||
}
|
||||
|
||||
pub fn get_link<PA: PhyAccess>(&self, pa: &mut PA) -> Option<Link> {
|
||||
let status = self.get_status(pa);
|
||||
if !status.link_status() {
|
||||
None
|
||||
} else if status.cap_1000base_t_extended_status() {
|
||||
let 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 {
|
@ -1,5 +1,5 @@
|
||||
use bit_field::BitField;
|
||||
use super::PhyRegister;
|
||||
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// Basic Mode Status Register
|
||||
@ -51,6 +51,49 @@ impl Status {
|
||||
pub fn cap_100base_t4(&self) -> bool {
|
||||
self.0.get_bit(15)
|
||||
}
|
||||
|
||||
pub fn get_link(&self) -> Option<Link> {
|
||||
if ! self.link_status() {
|
||||
None
|
||||
} else if self.cap_10base_t_half() {
|
||||
Some(Link {
|
||||
speed: LinkSpeed::S10,
|
||||
duplex: LinkDuplex::Half,
|
||||
})
|
||||
} else if self.cap_10base_t_full() {
|
||||
Some(Link {
|
||||
speed: LinkSpeed::S10,
|
||||
duplex: LinkDuplex::Full,
|
||||
})
|
||||
} else if self.cap_10base_t2_half() {
|
||||
Some(Link {
|
||||
speed: LinkSpeed::S10,
|
||||
duplex: LinkDuplex::Half,
|
||||
})
|
||||
} else if self.cap_10base_t2_full() {
|
||||
Some(Link {
|
||||
speed: LinkSpeed::S10,
|
||||
duplex: LinkDuplex::Full,
|
||||
})
|
||||
} else if self.cap_100base_t4() {
|
||||
Some(Link {
|
||||
speed: LinkSpeed::S100,
|
||||
duplex: LinkDuplex::Half,
|
||||
})
|
||||
} else if self.cap_100base_tx_half() {
|
||||
Some(Link {
|
||||
speed: LinkSpeed::S100,
|
||||
duplex: LinkDuplex::Half,
|
||||
})
|
||||
} else if self.cap_100base_tx_full() {
|
||||
Some(Link {
|
||||
speed: LinkSpeed::S100,
|
||||
duplex: LinkDuplex::Full,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PhyRegister for Status {
|
@ -1,6 +1,6 @@
|
||||
use volatile_register::{RO, WO, RW};
|
||||
|
||||
use crate::{register, register_bit, register_bits, register_bits_typed, regs::*};
|
||||
use libregister::{register, register_bit, register_bits, register_bits_typed};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
@ -1,5 +1,5 @@
|
||||
use core::ops::Deref;
|
||||
use crate::{register, register_bit, register_bits, register_bits_typed, regs::*};
|
||||
use libregister::*;
|
||||
use super::MTU;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -11,20 +11,31 @@ pub enum Error {
|
||||
}
|
||||
|
||||
/// Descriptor entry
|
||||
#[repr(C)]
|
||||
#[repr(C, align(0x08))]
|
||||
pub struct DescEntry {
|
||||
word0: DescWord0,
|
||||
word1: DescWord1,
|
||||
}
|
||||
|
||||
register!(desc_word0, DescWord0, RW, u32);
|
||||
/// true if owned by software, false if owned by hardware
|
||||
register_bit!(desc_word0, used, 0);
|
||||
/// mark last desc in list
|
||||
register_bit!(desc_word0, wrap, 1);
|
||||
impl DescEntry {
|
||||
pub fn zeroed() -> Self {
|
||||
DescEntry {
|
||||
word0: DescWord0 { inner: VolatileCell::new(0) },
|
||||
word1: DescWord1 { inner: VolatileCell::new(0) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register!(desc_word0, DescWord0, VolatileCell, u32);
|
||||
register_bit!(desc_word0,
|
||||
/// true if owned by software, false if owned by hardware
|
||||
used, 0);
|
||||
register_bit!(desc_word0,
|
||||
/// mark last desc in list
|
||||
wrap, 1);
|
||||
register_bits!(desc_word0, address, u32, 2, 31);
|
||||
|
||||
register!(desc_word1, DescWord1, RW, u32);
|
||||
register!(desc_word1, DescWord1, VolatileCell, u32);
|
||||
register_bits!(desc_word1, frame_length_lsbs, u16, 0, 12);
|
||||
register_bit!(desc_word1, bad_fcs, 13);
|
||||
register_bit!(desc_word1, start_of_frame, 14);
|
||||
@ -67,7 +78,8 @@ impl<'a> DescList<'a> {
|
||||
}
|
||||
|
||||
DescList {
|
||||
list,
|
||||
// Shorten the list of descriptors to the required number.
|
||||
list: &mut list[0..=last],
|
||||
buffers,
|
||||
next: 0,
|
||||
}
|
||||
@ -83,7 +95,7 @@ impl<'a> DescList<'a> {
|
||||
if entry.word0.read().used() {
|
||||
let word1 = entry.word1.read();
|
||||
let len = word1.frame_length_lsbs().into();
|
||||
let buffer = &self.buffers[self.next][0..len];
|
||||
let buffer = &mut self.buffers[self.next][0..len];
|
||||
|
||||
self.next += 1;
|
||||
if self.next >= list_len {
|
||||
@ -105,7 +117,7 @@ impl<'a> DescList<'a> {
|
||||
/// Releases a buffer back to the HW upon Drop
|
||||
pub struct PktRef<'a> {
|
||||
entry: &'a mut DescEntry,
|
||||
buffer: &'a [u8],
|
||||
buffer: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl<'a> Drop for PktRef<'a> {
|
||||
@ -120,3 +132,12 @@ impl<'a> Deref for PktRef<'a> {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> smoltcp::phy::RxToken for PktRef<'a> {
|
||||
fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>
|
||||
{
|
||||
f(self.buffer)
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use crate::{register, register_bit, register_bits, register_bits_typed, regs::*};
|
||||
use crate::println;
|
||||
use libregister::*;
|
||||
use super::{MTU, regs};
|
||||
|
||||
/// Descriptor entry
|
||||
#[repr(C, align(0x08))]
|
||||
pub struct DescEntry {
|
||||
word0: DescWord0,
|
||||
word1: DescWord1,
|
||||
}
|
||||
|
||||
register!(desc_word0, DescWord0, RW, u32);
|
||||
register!(desc_word0, DescWord0, VolatileCell, u32);
|
||||
register_bits!(desc_word0, address, u32, 0, 31);
|
||||
|
||||
register!(desc_word1, DescWord1, RW, u32);
|
||||
register!(desc_word1, DescWord1, VolatileCell, u32);
|
||||
register_bits!(desc_word1, length, u16, 0, 13);
|
||||
register_bit!(desc_word1, last_buffer, 15);
|
||||
register_bit!(desc_word1, no_crc_append, 16);
|
||||
@ -20,10 +20,21 @@ register_bits!(desc_word1, csum_offload_errors, u8, 20, 22);
|
||||
register_bit!(desc_word1, late_collision_tx_error, 26);
|
||||
register_bit!(desc_word1, ahb_frame_corruption, 27);
|
||||
register_bit!(desc_word1, retry_limit_exceeded, 29);
|
||||
/// marks last descriptor in list
|
||||
register_bit!(desc_word1, wrap, 30);
|
||||
/// true if owned by software, false if owned by hardware
|
||||
register_bit!(desc_word1, used, 31);
|
||||
register_bit!(desc_word1,
|
||||
/// marks last descriptor in list
|
||||
wrap, 30);
|
||||
register_bit!(desc_word1,
|
||||
/// true if owned by software, false if owned by hardware
|
||||
used, 31);
|
||||
|
||||
impl DescEntry {
|
||||
pub fn zeroed() -> Self {
|
||||
DescEntry {
|
||||
word0: DescWord0 { inner: VolatileCell::new(0) },
|
||||
word1: DescWord1 { inner: VolatileCell::new(0) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of descriptors
|
||||
pub const DESCS: usize = 8;
|
||||
@ -38,6 +49,12 @@ pub struct DescList<'a> {
|
||||
impl<'a> DescList<'a> {
|
||||
pub fn new(list: &'a mut [DescEntry], buffers: &'a mut [[u8; MTU]]) -> Self {
|
||||
let last = list.len().min(buffers.len()) - 1;
|
||||
// Sending seems to not work properly with only one packet
|
||||
// buffer (two duplicates get send with every packet), so
|
||||
// check that at least 2 are allocated, i.e. that the index of
|
||||
// the last one is at least one.
|
||||
assert!(last > 0);
|
||||
|
||||
for (i, (entry, buffer)) in list.iter_mut().zip(buffers.iter_mut()).enumerate() {
|
||||
let is_last = i == last;
|
||||
let buffer_addr = &mut buffer[0] as *mut _ as u32;
|
||||
@ -56,7 +73,8 @@ impl<'a> DescList<'a> {
|
||||
}
|
||||
|
||||
DescList {
|
||||
list,
|
||||
// Shorten the list of descriptors to the required number.
|
||||
list: &mut list[0..=last],
|
||||
buffers,
|
||||
next: 0,
|
||||
}
|
||||
@ -66,12 +84,17 @@ impl<'a> DescList<'a> {
|
||||
&self.list[0] as *const _ as u32
|
||||
}
|
||||
|
||||
pub fn send<'s: 'p, 'p>(&'s mut self, regs: &'p mut regs::RegisterBlock, length: usize) -> Option<PktRef<'p>> {
|
||||
pub fn send<'s: 'p, 'p>(&'s mut self, regs: &'s mut regs::RegisterBlock, length: usize) -> Option<PktRef<'p>> {
|
||||
let list_len = self.list.len();
|
||||
let entry = &mut self.list[self.next];
|
||||
if entry.word1.read().used() {
|
||||
entry.word1.modify(|_, w| w.length(length as u16));
|
||||
let buffer = &mut self.buffers[self.next][0..length];
|
||||
entry.word1.write(DescWord1::zeroed()
|
||||
.length(length as u16)
|
||||
.last_buffer(true)
|
||||
.wrap(self.next >= list_len - 1)
|
||||
.used(true)
|
||||
);
|
||||
|
||||
self.next += 1;
|
||||
if self.next >= list_len {
|
||||
@ -98,7 +121,6 @@ impl<'a> Drop for PktRef<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.entry.word1.modify(|_, w| w.used(false));
|
||||
if ! self.regs.tx_status.read().tx_go() {
|
||||
println!("tx start_tx");
|
||||
self.regs.net_ctrl.modify(|_, w|
|
||||
w.start_tx(true)
|
||||
);
|
||||
@ -118,3 +140,26 @@ impl<'a> DerefMut for PktRef<'a> {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
/// TxToken for smoltcp support
|
||||
pub struct Token<'a, 'tx: 'a> {
|
||||
pub regs: &'a mut regs::RegisterBlock,
|
||||
pub desc_list: &'a mut DescList<'tx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tx: 'a> smoltcp::phy::TxToken for Token<'a, 'tx> {
|
||||
fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> smoltcp::Result<R>
|
||||
{
|
||||
match self.desc_list.send(self.regs, len) {
|
||||
None =>
|
||||
Err(smoltcp::Error::Exhausted),
|
||||
Some(mut pktref) => {
|
||||
let result = f(pktref.deref_mut());
|
||||
// TODO: on result.is_err() don;t send
|
||||
drop(pktref);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
libboard_zynq/src/flash/bytes.rs
Normal file
41
libboard_zynq/src/flash/bytes.rs
Normal file
@ -0,0 +1,41 @@
|
||||
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()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
506
libboard_zynq/src/flash/mod.rs
Normal file
506
libboard_zynq/src/flash/mod.rs
Normal file
@ -0,0 +1,506 @@
|
||||
//! 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!("");
|
||||
}
|
||||
}
|
126
libboard_zynq/src/flash/regs.rs
Normal file
126
libboard_zynq/src/flash/regs.rs
Normal file
@ -0,0 +1,126 @@
|
||||
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);
|
62
libboard_zynq/src/flash/spi_flash_register.rs
Normal file
62
libboard_zynq/src/flash/spi_flash_register.rs
Normal file
@ -0,0 +1,62 @@
|
||||
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);
|
125
libboard_zynq/src/flash/transfer.rs
Normal file
125
libboard_zynq/src/flash/transfer.rs
Normal file
@ -0,0 +1,125 @@
|
||||
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())
|
||||
}
|
||||
}
|
12
libboard_zynq/src/lib.rs
Normal file
12
libboard_zynq/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
||||
#![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;
|
61
libboard_zynq/src/mpcore.rs
Normal file
61
libboard_zynq/src/mpcore.rs
Normal file
@ -0,0 +1,61 @@
|
||||
///! 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,9 +1,11 @@
|
||||
///! Register definitions for System Level Control
|
||||
|
||||
use volatile_register::{RO, WO, RW};
|
||||
use crate::{register, register_at,
|
||||
register_bit, register_bits, register_bits_typed,
|
||||
regs::RegisterW, regs::RegisterRW};
|
||||
use volatile_register::{RO, RW};
|
||||
use libregister::{
|
||||
register, register_at,
|
||||
register_bit, register_bits, register_bits_typed,
|
||||
RegisterW, RegisterRW,
|
||||
};
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum PllSource {
|
||||
@ -12,6 +14,52 @@ pub enum PllSource {
|
||||
DdrPll = 0b11,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum ArmPllSource {
|
||||
ArmPll = 0b00,
|
||||
DdrPll = 0b10,
|
||||
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)]
|
||||
pub struct RegisterBlock {
|
||||
pub scl: RW<u32>,
|
||||
@ -19,26 +67,26 @@ pub struct RegisterBlock {
|
||||
pub slcr_unlock: SlcrUnlock,
|
||||
pub slcr_locksta: RO<u32>,
|
||||
reserved0: [u32; 60],
|
||||
pub arm_pll_ctrl: RW<u32>,
|
||||
pub ddr_pll_ctrl: RW<u32>,
|
||||
pub io_pll_ctrl: RW<u32>,
|
||||
pub pll_status: RO<u32>,
|
||||
pub arm_pll_cfg: RW<u32>,
|
||||
pub ddr_pll_cfg: RW<u32>,
|
||||
pub io_pll_cfg: RW<u32>,
|
||||
pub arm_pll_ctrl: PllCtrl,
|
||||
pub ddr_pll_ctrl: PllCtrl,
|
||||
pub io_pll_ctrl: PllCtrl,
|
||||
pub pll_status: PllStatus,
|
||||
pub arm_pll_cfg: PllCfg,
|
||||
pub ddr_pll_cfg: PllCfg,
|
||||
pub io_pll_cfg: PllCfg,
|
||||
reserved1: [u32; 1],
|
||||
pub arm_clk_ctrl: RW<u32>,
|
||||
pub ddr_clk_ctrl: RW<u32>,
|
||||
pub dci_clk_ctrl: RW<u32>,
|
||||
pub arm_clk_ctrl: ArmClkCtrl,
|
||||
pub ddr_clk_ctrl: DdrClkCtrl,
|
||||
pub dci_clk_ctrl: DciClkCtrl,
|
||||
pub aper_clk_ctrl: AperClkCtrl,
|
||||
pub usb0_clk_ctrl: RW<u32>,
|
||||
pub usb1_clk_ctrl: RW<u32>,
|
||||
pub gem0_rclk_ctrl: RclkCtrl,
|
||||
pub gem1_rclk_ctrl: RclkCtrl,
|
||||
pub gem0_clk_ctrl: ClkCtrl,
|
||||
pub gem1_clk_ctrl: ClkCtrl,
|
||||
pub gem0_clk_ctrl: GemClkCtrl,
|
||||
pub gem1_clk_ctrl: GemClkCtrl,
|
||||
pub smc_clk_ctrl: RW<u32>,
|
||||
pub lqspi_clk_ctrl: RW<u32>,
|
||||
pub lqspi_clk_ctrl: LqspiClkCtrl,
|
||||
pub sdio_clk_ctrl: RW<u32>,
|
||||
pub uart_clk_ctrl: UartClkCtrl,
|
||||
pub spi_clk_ctrl: RW<u32>,
|
||||
@ -64,7 +112,7 @@ pub struct RegisterBlock {
|
||||
pub fpga3_thr_cnt: RW<u32>,
|
||||
pub fpga3_thr_sta: RO<u32>,
|
||||
reserved2: [u32; 5],
|
||||
pub clk_621_true: RW<u32>,
|
||||
pub clk_621_true: Clk621True,
|
||||
reserved3: [u32; 14],
|
||||
pub pss_rst_ctrl: PssRstCtrl,
|
||||
pub ddr_rst_ctrl: RW<u32>,
|
||||
@ -78,17 +126,17 @@ pub struct RegisterBlock {
|
||||
pub i2c_rst_ctrl: RW<u32>,
|
||||
pub uart_rst_ctrl: UartRstCtrl,
|
||||
pub gpio_rst_ctrl: RW<u32>,
|
||||
pub lqspi_rst_ctrl: RW<u32>,
|
||||
pub lqspi_rst_ctrl: LqspiRstCtrl,
|
||||
pub smc_rst_ctrl: RW<u32>,
|
||||
pub ocm_rst_ctrl: RW<u32>,
|
||||
reserved4: [u32; 1],
|
||||
pub fpga_rst_ctrl: RW<u32>,
|
||||
pub a9_cpu_rst_ctrl: RW<u32>,
|
||||
pub a9_cpu_rst_ctrl: A9CpuRstCtrl,
|
||||
reserved5: [u32; 1],
|
||||
pub rs_awdt_ctrl: RW<u32>,
|
||||
reserved6: [u32; 2],
|
||||
pub reboot_status: RW<u32>,
|
||||
pub boot_mode: RW<u32>,
|
||||
pub boot_mode: BootMode,
|
||||
reserved7: [u32; 40],
|
||||
pub apu_ctrl: RW<u32>,
|
||||
pub wdt_clk_sel: RW<u32>,
|
||||
@ -183,23 +231,25 @@ pub struct RegisterBlock {
|
||||
pub gpiob_cfg_hstl: RW<u32>,
|
||||
pub gpiob_drvr_bias_ctrl: RW<u32>,
|
||||
reserved21: [u32; 9],
|
||||
pub ddriob_addr1: RW<u32>,
|
||||
pub ddriob_data0: RW<u32>,
|
||||
pub ddriob_data1: RW<u32>,
|
||||
pub ddriob_diff0: RW<u32>,
|
||||
pub ddriob_diff1: RW<u32>,
|
||||
pub ddriob_clock: RW<u32>,
|
||||
pub w_addr: RW<u32>,
|
||||
pub w_data: RW<u32>,
|
||||
pub w_diff: RW<u32>,
|
||||
pub w_clock: RW<u32>,
|
||||
pub ddriob_ddr_ctrl: RW<u32>,
|
||||
pub ddriob_dci_ctrl: RW<u32>,
|
||||
pub ddriob_dci_status: RW<u32>,
|
||||
pub ddriob_addr0: DdriobConfig,
|
||||
pub ddriob_addr1: DdriobConfig,
|
||||
pub ddriob_data0: DdriobConfig,
|
||||
pub ddriob_data1: DdriobConfig,
|
||||
pub ddriob_diff0: DdriobConfig,
|
||||
pub ddriob_diff1: DdriobConfig,
|
||||
pub ddriob_clock: DdriobConfig,
|
||||
pub ddriob_drive_slew_addr: RW<u32>,
|
||||
pub ddriob_drive_slew_data: RW<u32>,
|
||||
pub ddriob_drive_slew_diff: RW<u32>,
|
||||
pub ddriob_drive_slew_clock: RW<u32>,
|
||||
pub ddriob_ddr_ctrl: DdriobDdrCtrl,
|
||||
pub ddriob_dci_ctrl: DdriobDciCtrl,
|
||||
pub ddriob_dci_status: DdriobDciStatus,
|
||||
}
|
||||
register_at!(RegisterBlock, 0xF8000000, new);
|
||||
|
||||
impl RegisterBlock {
|
||||
/// Required to modify any sclr register
|
||||
pub fn unlocked<F: FnMut(&mut Self) -> R, R>(mut f: F) -> R {
|
||||
let mut self_ = Self::new();
|
||||
self_.slcr_unlock.unlock();
|
||||
@ -239,6 +289,64 @@ impl SlcrUnlock {
|
||||
}
|
||||
}
|
||||
|
||||
register!(pll_ctrl, PllCtrl, RW, u32);
|
||||
register_bits!(pll_ctrl, pll_fdiv, u16, 12, 18);
|
||||
register_bit!(pll_ctrl, pll_bypass_force, 4);
|
||||
register_bit!(pll_ctrl, pll_bypass_qual, 3);
|
||||
register_bit!(pll_ctrl, pll_pwrdwn, 1);
|
||||
register_bit!(pll_ctrl, pll_reset, 0);
|
||||
|
||||
register!(pll_status, PllStatus, RO, u32);
|
||||
register_bit!(pll_status, arm_pll_lock, 0);
|
||||
register_bit!(pll_status, ddr_pll_lock, 1);
|
||||
register_bit!(pll_status, io_pll_lock, 2);
|
||||
register_bit!(pll_status, arm_pll_stable, 3);
|
||||
register_bit!(pll_status, ddr_pll_stable, 4);
|
||||
register_bit!(pll_status, io_pll_stable, 5);
|
||||
|
||||
impl core::fmt::Display for pll_status::Read {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
|
||||
write!(fmt, "ARM: {}/{} DDR: {}/{} IO: {}/{}",
|
||||
if self.arm_pll_lock() { "locked" } else { "NOT locked" },
|
||||
if self.arm_pll_stable() { "stable" } else { "UNSTABLE" },
|
||||
if self.ddr_pll_lock() { "locked" } else { "NOT locked" },
|
||||
if self.ddr_pll_stable() { "stable" } else { "UNSTABLE" },
|
||||
if self.io_pll_lock() { "locked" } else { "NOT locked" },
|
||||
if self.io_pll_stable() { "stable" } else { "UNSTABLE" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
register!(pll_cfg, PllCfg, RW, u32);
|
||||
register_bits!(pll_cfg, pll_res, u8, 4, 7);
|
||||
register_bits!(pll_cfg, pll_cp, u8, 8, 11);
|
||||
register_bits!(pll_cfg, lock_cnt, u16, 12, 21);
|
||||
|
||||
register!(arm_clk_ctrl, ArmClkCtrl, RW, u32);
|
||||
register_bit!(arm_clk_ctrl,
|
||||
/// Clock active
|
||||
cpu_peri_clkact, 28);
|
||||
register_bit!(arm_clk_ctrl, cpu_1xclkact, 27);
|
||||
register_bit!(arm_clk_ctrl, cpu_2xclkact, 26);
|
||||
register_bit!(arm_clk_ctrl, cpu_3or2xclkact, 25);
|
||||
register_bit!(arm_clk_ctrl, cpu_6or4xclkact, 24);
|
||||
register_bits!(arm_clk_ctrl, divisor, u8, 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_bit!(clk_621_true, clk_621_true, 0);
|
||||
|
||||
register!(aper_clk_ctrl, AperClkCtrl, RW, u32);
|
||||
register_bit!(aper_clk_ctrl, uart1_cpu_1xclkact, 21);
|
||||
register_bit!(aper_clk_ctrl, uart0_cpu_1xclkact, 20);
|
||||
@ -260,17 +368,17 @@ register_bit!(rclk_ctrl,
|
||||
/// false: MIO, true: EMIO
|
||||
srcsel, 4);
|
||||
|
||||
register!(clk_ctrl, ClkCtrl, RW, u32);
|
||||
register_bits!(clk_ctrl,
|
||||
register!(gem_clk_ctrl, GemClkCtrl, RW, u32);
|
||||
register_bits!(gem_clk_ctrl,
|
||||
/// 2nd divisor for source clock
|
||||
divisor1, u8, 20, 25);
|
||||
register_bits!(clk_ctrl,
|
||||
register_bits!(gem_clk_ctrl,
|
||||
/// 1st divisor for source clock
|
||||
divisor, u8, 8, 13);
|
||||
register_bits_typed!(clk_ctrl,
|
||||
register_bits_typed!(gem_clk_ctrl,
|
||||
/// Source to generate the ref clock
|
||||
srcsel, u8, PllSource, 4, 5);
|
||||
register_bit!(clk_ctrl,
|
||||
srcsel, u8, PllSource, 4, 6);
|
||||
register_bit!(gem_clk_ctrl,
|
||||
/// SMC reference clock control
|
||||
clkact, 0);
|
||||
|
||||
@ -334,6 +442,36 @@ impl UartRstCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
|
||||
register_bit!(lqspi_clk_ctrl, clkact, 0);
|
||||
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
||||
register_bits!(lqspi_clk_ctrl, divisor, u8, 8, 13);
|
||||
|
||||
register!(lqspi_rst_ctrl, LqspiRstCtrl, RW, u32);
|
||||
register_bit!(lqspi_rst_ctrl, ref_rst, 1);
|
||||
register_bit!(lqspi_rst_ctrl, cpu1x_rst, 0);
|
||||
|
||||
register!(a9_cpu_rst_ctrl, A9CpuRstCtrl, RW, u32);
|
||||
register_bit!(a9_cpu_rst_ctrl, peri_rst, 8);
|
||||
register_bit!(a9_cpu_rst_ctrl, a9_clkstop1, 5);
|
||||
register_bit!(a9_cpu_rst_ctrl, a9_clkstop0, 4);
|
||||
register_bit!(a9_cpu_rst_ctrl, a9_rst1, 1);
|
||||
register_bit!(a9_cpu_rst_ctrl, a9_rst0, 0);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum BootModePins {
|
||||
Jtag = 0b000,
|
||||
Nor = 0b001,
|
||||
Nand = 0b010,
|
||||
QuadSpi = 0b100,
|
||||
SdCard = 0b110,
|
||||
}
|
||||
|
||||
register!(boot_mode, BootMode, RO, u32);
|
||||
register_bit!(boot_mode, pll_bypass, 4);
|
||||
register_bits_typed!(boot_mode, boot_mode_pins, u8, BootModePins, 0, 3);
|
||||
|
||||
register!(pss_rst_ctrl, PssRstCtrl, RW, u32);
|
||||
register_bit!(pss_rst_ctrl, soft_rst, 1);
|
||||
|
||||
@ -418,3 +556,34 @@ mio_pin_register!(mio_pin_53, MioPin53);
|
||||
|
||||
register!(gpiob_ctrl, GpiobCtrl, RW, u32);
|
||||
register_bit!(gpiob_ctrl, vref_en, 0);
|
||||
|
||||
register!(ddriob_config, DdriobConfig, RW, u32);
|
||||
register_bits_typed!(ddriob_config, inp_type, u8, DdriobInputType, 1, 2);
|
||||
register_bit!(ddriob_config, dci_update_b, 3);
|
||||
register_bit!(ddriob_config, term_en, 4);
|
||||
register_bits_typed!(ddriob_config, dci_type, u8, DdriobDciType, 5, 6);
|
||||
register_bit!(ddriob_config, ibuf_disable_mode, 7);
|
||||
register_bit!(ddriob_config, term_disable_mode, 8);
|
||||
register_bits_typed!(ddriob_config, output_en, u8, DdriobOutputEn, 9, 10);
|
||||
register_bit!(ddriob_config, pullup_en, 11);
|
||||
|
||||
register!(ddriob_ddr_ctrl, DdriobDdrCtrl, RW, u32);
|
||||
register_bit!(ddriob_ddr_ctrl, vref_int_en, 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);
|
64
libboard_zynq/src/stdio.rs
Normal file
64
libboard_zynq/src/stdio.rs
Normal file
@ -0,0 +1,64 @@
|
||||
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,4 +1,4 @@
|
||||
use crate::regs::*;
|
||||
use libregister::*;
|
||||
use super::regs::{RegisterBlock, BaudRateGen, BaudRateDiv};
|
||||
|
||||
const BDIV_MIN: u32 = 4;
|
||||
@ -37,7 +37,7 @@ pub fn configure(regs: &mut RegisterBlock, mut clk: u32, baud: u32) {
|
||||
}
|
||||
|
||||
match best {
|
||||
Some((cd, bdiv, error)) => {
|
||||
Some((cd, bdiv, _error)) => {
|
||||
regs.baud_rate_gen.write(BaudRateGen::zeroed().cd(cd));
|
||||
regs.baud_rate_divider.write(BaudRateDiv::zeroed().bdiv(bdiv));
|
||||
}
|
@ -1,19 +1,12 @@
|
||||
use core::fmt;
|
||||
use volatile_register::RW;
|
||||
|
||||
use crate::regs::*;
|
||||
use crate::slcr;
|
||||
use libregister::*;
|
||||
use super::slcr;
|
||||
use super::clocks::CpuClocks;
|
||||
|
||||
mod regs;
|
||||
mod baud_rate_gen;
|
||||
|
||||
/// Determined through experimentation. Actually supposed to be
|
||||
/// 1 GHz (IO PLL) / 0x14 (slcr.UART_CLK_CTRL[DIVISOR]) = 50 MHz.
|
||||
#[cfg(feature = "target_zc706")]
|
||||
const UART_REF_CLK: u32 = 50_000_000;
|
||||
#[cfg(feature = "target_cora_z7_10")]
|
||||
const UART_REF_CLK: u32 = 72_000_000;
|
||||
|
||||
pub struct Uart {
|
||||
regs: &'static mut regs::RegisterBlock,
|
||||
}
|
||||
@ -117,7 +110,8 @@ impl Uart {
|
||||
self.disable_rx();
|
||||
self.disable_tx();
|
||||
|
||||
baud_rate_gen::configure(self.regs, UART_REF_CLK, baudrate);
|
||||
let clocks = CpuClocks::get();
|
||||
baud_rate_gen::configure(self.regs, clocks.uart_ref_clk(), baudrate);
|
||||
|
||||
// Enable controller
|
||||
self.reset_rx();
|
@ -1,7 +1,11 @@
|
||||
use volatile_register::{RO, WO, RW};
|
||||
|
||||
use crate::{register, register_bit, register_bits, register_bits_typed, register_at, regs::*};
|
||||
use libregister::{
|
||||
register, register_at,
|
||||
register_bit, register_bits, register_bits_typed,
|
||||
};
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(u8)]
|
||||
pub enum ChannelMode {
|
||||
Normal = 0b00,
|
||||
@ -10,6 +14,7 @@ pub enum ChannelMode {
|
||||
RemoteLoopback = 0b11,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(u8)]
|
||||
pub enum ParityMode {
|
||||
EvenParity = 0b000,
|
||||
@ -19,6 +24,7 @@ pub enum ParityMode {
|
||||
None = 0b100,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[repr(u8)]
|
||||
pub enum StopBits {
|
||||
One = 0b00,
|
23
libcortex_a9/Cargo.toml
Normal file
23
libcortex_a9/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[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
|
@ -10,6 +10,12 @@ pub fn wfe() {
|
||||
unsafe { asm!("wfe" :::: "volatile") }
|
||||
}
|
||||
|
||||
/// Send Event
|
||||
#[inline]
|
||||
pub fn sev() {
|
||||
unsafe { asm!("sev" :::: "volatile") }
|
||||
}
|
||||
|
||||
/// Data Memory Barrier
|
||||
#[inline]
|
||||
pub fn dmb() {
|
214
libcortex_a9/src/cache.rs
Normal file
214
libcortex_a9/src/cache.rs
Normal file
@ -0,0 +1,214 @@
|
||||
/// 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);
|
||||
}
|
||||
}
|
11
libcortex_a9/src/lib.rs
Normal file
11
libcortex_a9/src/lib.rs
Normal file
@ -0,0 +1,11 @@
|
||||
#![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,7 +1,6 @@
|
||||
use core::mem::uninitialized;
|
||||
use bit_field::BitField;
|
||||
use super::{regs::*, asm};
|
||||
use crate::regs::RegisterW;
|
||||
use libregister::RegisterW;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
@ -73,7 +72,7 @@ pub struct L1Entry(u32);
|
||||
impl L1Entry {
|
||||
#[inline(always)]
|
||||
pub fn section(phys_base: u32, section: L1Section) -> Self {
|
||||
/// Must be aligned to 1 MB
|
||||
// Must be aligned to 1 MB
|
||||
assert!(phys_base & 0x000f_ffff == 0);
|
||||
let mut entry = L1Entry(phys_base);
|
||||
|
||||
@ -125,7 +124,7 @@ impl L1Table {
|
||||
tex: 0b101,
|
||||
domain: 0b1111,
|
||||
exec: true,
|
||||
cacheable: false,
|
||||
cacheable: true,
|
||||
bufferable: true,
|
||||
});
|
||||
/* (DDR cacheable) */
|
||||
@ -382,8 +381,4 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
|
||||
asm::isb();
|
||||
|
||||
f();
|
||||
|
||||
// table must live until here
|
||||
drop(l1table.table);
|
||||
unreachable!();
|
||||
}
|
83
libcortex_a9/src/mutex.rs
Normal file
83
libcortex_a9/src/mutex.rs
Normal file
@ -0,0 +1,83 @@
|
||||
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,5 +1,7 @@
|
||||
use crate::{register_bit, register_bits};
|
||||
use crate::regs::{RegisterR, RegisterW, RegisterRW};
|
||||
use libregister::{
|
||||
register_bit, register_bits,
|
||||
RegisterR, RegisterW, RegisterRW,
|
||||
};
|
||||
|
||||
macro_rules! def_reg_r {
|
||||
($name:tt, $type: ty, $asm_instr:tt) => {
|
||||
@ -115,6 +117,36 @@ register_bit!(sctlr,
|
||||
/// Thumb Exception Enable
|
||||
te, 30);
|
||||
|
||||
/// Auxiliary Control Register
|
||||
pub struct ACTLR;
|
||||
wrap_reg!(actlr);
|
||||
def_reg_r!(ACTLR, actlr::Read, "mrc p15, 0, $0, c1, c0, 1");
|
||||
def_reg_w!(ACTLR, actlr::Write, "mcr p15, 0, $0, c1, c0, 1");
|
||||
// SMP bit
|
||||
register_bit!(actlr, parity_on, 9);
|
||||
register_bit!(actlr, alloc_one_way, 8);
|
||||
register_bit!(actlr, excl, 7);
|
||||
register_bit!(actlr, smp, 6);
|
||||
register_bit!(actlr, write_full_line_of_zeros, 3);
|
||||
register_bit!(actlr, l1_prefetch_enable, 2);
|
||||
// Cache/TLB maintenance broadcast
|
||||
register_bit!(actlr, fw, 0);
|
||||
|
||||
impl RegisterRW for ACTLR {
|
||||
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
||||
let r = self.read();
|
||||
let w = actlr::Write { inner: r.inner };
|
||||
let w = f(r, w);
|
||||
self.write(w);
|
||||
}
|
||||
}
|
||||
|
||||
impl ACTLR {
|
||||
pub fn enable_smp(&mut self) {
|
||||
self.modify(|_, w| w.smp(true).fw(true));
|
||||
}
|
||||
}
|
||||
|
||||
/// Domain Access Control Register
|
||||
pub struct DACR;
|
||||
def_reg_r!(DACR, u32, "mrc p15, 0, $0, c3, c0, 0");
|
||||
@ -136,36 +168,3 @@ register_bit!(ttbr,
|
||||
/// Translation table walk to shared memory?
|
||||
s, 1);
|
||||
register_bit!(ttbr, irgn1, 0);
|
||||
|
||||
/// Invalidate TLBs
|
||||
#[inline(always)]
|
||||
pub fn tlbiall() {
|
||||
unsafe {
|
||||
asm!("mcr p15, 0, $0, c8, c7, 0" :: "r" (0) :: "volatile");
|
||||
}
|
||||
}
|
||||
|
||||
/// Invalidate I-Cache
|
||||
#[inline(always)]
|
||||
pub fn iciallu() {
|
||||
unsafe {
|
||||
asm!("mcr p15, 0, $0, c7, c5, 0" :: "r" (0) :: "volatile");
|
||||
}
|
||||
}
|
||||
|
||||
/// Invalidate Branch Predictor Array
|
||||
#[inline(always)]
|
||||
pub fn bpiall() {
|
||||
unsafe {
|
||||
asm!("mcr p15, 0, $0, c7, c5, 6" :: "r" (0) :: "volatile");
|
||||
}
|
||||
}
|
||||
|
||||
/// Invalidate D-Cache
|
||||
#[inline(always)]
|
||||
pub fn dccisw() {
|
||||
// TODO: $0 is r11 at what value?
|
||||
unsafe {
|
||||
asm!("mcr p15, 0, $0, c7, c5, 6" :: "r" (0) :: "volatile");
|
||||
}
|
||||
}
|
10
libregister/Cargo.toml
Normal file
10
libregister/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[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"
|
@ -1,9 +1,11 @@
|
||||
//! Type-safe interface to peripheral registers akin to the code that
|
||||
//! svd2rust generates.
|
||||
#![allow(unused)]
|
||||
|
||||
use volatile_register::{RO, WO, RW};
|
||||
use bit_field::BitField;
|
||||
#![no_std]
|
||||
|
||||
pub use vcell::VolatileCell;
|
||||
pub use volatile_register::{RO, WO, RW};
|
||||
pub use bit_field::BitField;
|
||||
|
||||
/// A readable register
|
||||
pub trait RegisterR {
|
||||
@ -35,9 +37,11 @@ macro_rules! register_common {
|
||||
}
|
||||
|
||||
pub mod $mod_name {
|
||||
#[derive(Clone)]
|
||||
pub struct Read {
|
||||
pub inner: $inner,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Write {
|
||||
pub inner: $inner,
|
||||
}
|
||||
@ -48,7 +52,7 @@ macro_rules! register_common {
|
||||
#[macro_export]
|
||||
macro_rules! register_r {
|
||||
($mod_name: ident, $struct_name: ident) => (
|
||||
impl crate::regs::RegisterR for $struct_name {
|
||||
impl libregister::RegisterR for $struct_name {
|
||||
type R = $mod_name::Read;
|
||||
|
||||
fn read(&self) -> Self::R {
|
||||
@ -62,7 +66,7 @@ macro_rules! register_r {
|
||||
#[macro_export]
|
||||
macro_rules! register_w {
|
||||
($mod_name: ident, $struct_name: ident) => (
|
||||
impl crate::regs::RegisterW for $struct_name {
|
||||
impl libregister::RegisterW for $struct_name {
|
||||
type W = $mod_name::Write;
|
||||
|
||||
fn zeroed() -> $mod_name::Write {
|
||||
@ -81,7 +85,7 @@ macro_rules! register_w {
|
||||
#[macro_export]
|
||||
macro_rules! register_rw {
|
||||
($mod_name: ident, $struct_name: ident) => (
|
||||
impl crate::regs::RegisterRW for $struct_name {
|
||||
impl libregister::RegisterRW for $struct_name {
|
||||
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
||||
unsafe {
|
||||
self.inner.modify(|inner| {
|
||||
@ -94,27 +98,67 @@ 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
|
||||
#[macro_export]
|
||||
macro_rules! register {
|
||||
// Define read-only register
|
||||
($mod_name: ident, $struct_name: ident, RO, $inner: ty) => (
|
||||
crate::register_common!($mod_name, $struct_name, volatile_register::RO<$inner>, $inner);
|
||||
crate::register_r!($mod_name, $struct_name);
|
||||
libregister::register_common!($mod_name, $struct_name, libregister::RO<$inner>, $inner);
|
||||
libregister::register_r!($mod_name, $struct_name);
|
||||
);
|
||||
|
||||
// Define write-only register
|
||||
($mod_name: ident, $struct_name: ident, WO, $inner: ty) => (
|
||||
crate::register_common!($mod_name, $struct_name, volatile_register::WO<$inner>, $inner);
|
||||
crate::register_w!($mod_name, $struct_name);
|
||||
libregister::register_common!($mod_name, $struct_name, volatile_register::WO<$inner>, $inner);
|
||||
libregister::register_w!($mod_name, $struct_name);
|
||||
);
|
||||
|
||||
// Define read-write register
|
||||
($mod_name: ident, $struct_name: ident, RW, $inner: ty) => (
|
||||
crate::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner);
|
||||
crate::register_r!($mod_name, $struct_name);
|
||||
crate::register_w!($mod_name, $struct_name);
|
||||
crate::register_rw!($mod_name, $struct_name);
|
||||
libregister::register_common!($mod_name, $struct_name, volatile_register::RW<$inner>, $inner);
|
||||
libregister::register_r!($mod_name, $struct_name);
|
||||
libregister::register_w!($mod_name, $struct_name);
|
||||
libregister::register_rw!($mod_name, $struct_name);
|
||||
);
|
||||
|
||||
// Define read-write register
|
||||
($mod_name: ident, $struct_name: ident, VolatileCell, $inner: ty) => (
|
||||
libregister::register_common!($mod_name, $struct_name, VolatileCell<$inner>, $inner);
|
||||
libregister::register_vcell!($mod_name, $struct_name);
|
||||
);
|
||||
}
|
||||
|
5
link.x
5
link.x
@ -1,6 +1,6 @@
|
||||
ENTRY(_boot_cores);
|
||||
|
||||
STACK_SIZE = 0x10000 - 8;
|
||||
STACK_SIZE = 0x8000;
|
||||
|
||||
/* Provide some defaults */
|
||||
PROVIDE(Reset = _boot_cores);
|
||||
@ -15,7 +15,8 @@ PROVIDE(FIQ = Reset);
|
||||
MEMORY
|
||||
{
|
||||
/* 256 kB On-Chip Memory */
|
||||
OCM : ORIGIN = 0, LENGTH = 0x40000
|
||||
OCM : ORIGIN = 0, LENGTH = 0x30000
|
||||
OCM3 : ORIGIN = 0xFFFF0000, LENGTH = 0x10000
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
@ -1,7 +1,7 @@
|
||||
source [find interface/ftdi/digilent-hs1.cfg]
|
||||
adapter_khz 10000
|
||||
|
||||
set PL_TAPID 0x03727093
|
||||
set PL_TAPID 0x13723093
|
||||
set SMP 1
|
||||
|
||||
source ./zynq-7000.cfg
|
||||
|
@ -15,5 +15,4 @@ ftdi_device_desc "Digilent USB Device"
|
||||
ftdi_vid_pid 0x0403 0x6014
|
||||
ftdi_channel 0
|
||||
ftdi_layout_init 0x00e8 0x60eb
|
||||
reset_config none
|
||||
|
||||
ftdi_layout_signal nSRST -data 0x2000
|
||||
|
@ -1,14 +1,16 @@
|
||||
source ./digilent_jtag_smt2_nc.cfg
|
||||
adapter_khz 10000
|
||||
source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
|
||||
adapter_khz 1000
|
||||
|
||||
set PL_TAPID 0x03727093
|
||||
set PL_TAPID 0x23731093
|
||||
set SMP 1
|
||||
|
||||
source ./zynq-7000.cfg
|
||||
source ./xilinx-tcl.cfg
|
||||
source ./ps7_init.tcl
|
||||
|
||||
reset_config srst_only srst_push_pull
|
||||
reset_config srst_only srst_open_drain
|
||||
adapter_nsrst_assert_width 250
|
||||
adapter_nsrst_delay 400
|
||||
|
||||
set XC7_JSHUTDOWN 0x0d
|
||||
set XC7_JPROGRAM 0x0b
|
||||
|
10
shell.nix
10
shell.nix
@ -8,14 +8,12 @@ let
|
||||
in
|
||||
with project;
|
||||
stdenv.mkDerivation {
|
||||
name = "adc2tcp-env";
|
||||
buildInputs = with rustPlatform.rust; [
|
||||
name = "zynq-env";
|
||||
buildInputs = (with rustPlatform.rust; [
|
||||
rustc cargo
|
||||
cargo-xbuild rustcSrc
|
||||
pkgsCross.armhf-embedded.buildPackages.gcc
|
||||
#pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc
|
||||
#pkgsCross.armhf-embedded.buildPackages.binutils
|
||||
];
|
||||
gcc
|
||||
]) ++ (with pkgs; [ openocd gdb ]);
|
||||
|
||||
# Set Environment Variables
|
||||
RUST_BACKTRACE = 1;
|
||||
|
@ -1,5 +0,0 @@
|
||||
pub mod asm;
|
||||
pub mod regs;
|
||||
pub mod mmu;
|
||||
|
||||
global_asm!(include_str!("exceptions.s"));
|
133
src/main.rs
133
src/main.rs
@ -1,133 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(asm)]
|
||||
#![feature(global_asm)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(compiler_builtins_lib)]
|
||||
#![feature(never_type)]
|
||||
|
||||
use core::mem::uninitialized;
|
||||
|
||||
use r0::zero_bss;
|
||||
use compiler_builtins as _;
|
||||
|
||||
mod regs;
|
||||
mod cortex_a9;
|
||||
mod slcr;
|
||||
mod uart;
|
||||
mod stdio;
|
||||
mod eth;
|
||||
|
||||
use crate::regs::{RegisterR, RegisterW};
|
||||
use crate::cortex_a9::{asm, regs::*, mmu};
|
||||
|
||||
extern "C" {
|
||||
static mut __bss_start: u32;
|
||||
static mut __bss_end: u32;
|
||||
static mut __stack_start: u32;
|
||||
}
|
||||
|
||||
#[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();
|
||||
}
|
||||
_ => loop {
|
||||
// if not core0, infinitely wait for events
|
||||
asm::wfe();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[naked]
|
||||
#[inline(never)]
|
||||
unsafe fn boot_core0() -> ! {
|
||||
l1_cache_init();
|
||||
zero_bss(&mut __bss_start, &mut __bss_end);
|
||||
|
||||
let mmu_table = mmu::L1Table::get()
|
||||
.setup_flat_layout();
|
||||
mmu::with_mmu(mmu_table, || {
|
||||
main();
|
||||
panic!("return from main");
|
||||
});
|
||||
}
|
||||
|
||||
fn l1_cache_init() {
|
||||
// Invalidate TLBs
|
||||
tlbiall();
|
||||
// Invalidate I-Cache
|
||||
iciallu();
|
||||
// Invalidate Branch Predictor Array
|
||||
bpiall();
|
||||
// Invalidate D-Cache
|
||||
dccisw();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Main.");
|
||||
|
||||
let mut eth = eth::Eth::default([0x0, 0x17, 0xde, 0xea, 0xbe, 0xef]);
|
||||
println!("Eth on");
|
||||
eth.reset_phy();
|
||||
|
||||
let mut rx_descs: [eth::rx::DescEntry; 8] = unsafe { uninitialized() };
|
||||
let mut rx_buffers = [[0u8; 1536]; 8];
|
||||
let mut eth = eth.start_rx(&mut rx_descs, &mut rx_buffers);
|
||||
let mut tx_descs: [eth::tx::DescEntry; 8] = unsafe { uninitialized() };
|
||||
let mut tx_buffers = [[0u8; 1536]; 8];
|
||||
let mut eth = eth.start_tx(&mut tx_descs, &mut tx_buffers);
|
||||
|
||||
loop {
|
||||
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!("End");
|
||||
}
|
||||
|
||||
#[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() {
|
||||
panic!("PrefetchAbort");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn DataAbort() {
|
||||
panic!("DataAbort");
|
||||
}
|
39
src/stdio.rs
39
src/stdio.rs
@ -1,39 +0,0 @@
|
||||
use crate::uart::Uart;
|
||||
|
||||
const UART_RATE: u32 = 115_200;
|
||||
static mut UART: Option<Uart> = None;
|
||||
|
||||
// TODO: locking for SMP
|
||||
#[doc(hidden)]
|
||||
pub fn get_uart() -> &'static mut Uart {
|
||||
unsafe {
|
||||
match &mut UART {
|
||||
None => {
|
||||
let mut uart = Uart::serial(UART_RATE);
|
||||
UART = Some(uart);
|
||||
UART.as_mut().unwrap()
|
||||
}
|
||||
Some(uart) => uart,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ({
|
||||
use core::fmt::Write;
|
||||
let uart = crate::stdio::get_uart();
|
||||
write!(uart, $($arg)*);
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($($arg:tt)*) => ({
|
||||
use core::fmt::Write;
|
||||
let uart = crate::stdio::get_uart();
|
||||
write!(uart, $($arg)*);
|
||||
write!(uart, "\r\n");
|
||||
while !uart.tx_fifo_empty() {}
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user