forked from M-Labs/zynq-rs
Compare commits
73 Commits
Author | SHA1 | Date |
---|---|---|
harry | c69cd9951e | |
Harry Ho | 76a4cac873 | |
Harry Ho | 4614ed1371 | |
pca006132 | fa07bdb681 | |
Harry Ho | 4565a75766 | |
Harry Ho | 16b2df91ca | |
Harry Ho | f7d3135ec7 | |
Harry Ho | c60230af25 | |
pca006132 | e8ba73a8c7 | |
pca006132 | 3958953ceb | |
Sebastien Bourdeauducq | a36a82d86d | |
pca006132 | 25c6d5eeaa | |
pca006132 | 9e97102e12 | |
pca006132 | b65606f2d0 | |
pca006132 | ee4089c52e | |
pca006132 | 36c3fbdacd | |
pca006132 | 8328ffc66b | |
pca006132 | 84041a3154 | |
pca006132 | 5850401d72 | |
pca006132 | ccce37dffd | |
pca006132 | 3bbd1513fb | |
pca006132 | 7d38c53c18 | |
pca006132 | 02a2c4d1e3 | |
pca006132 | 12669124a4 | |
pca006132 | 8f0a6bd5ea | |
pca006132 | c1f61b5673 | |
pca006132 | 2927c43309 | |
Astro | 187801c4a7 | |
pca006132 | 91ece367f2 | |
Astro | 1f05e6977e | |
Astro | e408a8b22d | |
Astro | 27effb6257 | |
Astro | de5f605d60 | |
pca006132 | ad47521e4b | |
pca006132 | c50e72f91e | |
pca006132 | b099c56569 | |
Astro | ef4fb598fb | |
Astro | 0aa75d3544 | |
Astro | f36b1a610e | |
Astro | 7f45d10af3 | |
Astro | 855d94c48e | |
Sebastien Bourdeauducq | 84f1380f48 | |
Sebastien Bourdeauducq | f8785c3f07 | |
Sebastien Bourdeauducq | 7b78bc0494 | |
Sebastien Bourdeauducq | ef88a1313a | |
Astro | 484e385160 | |
pca006132 | 074438c3c7 | |
Astro | 191abf6b8f | |
Sebastien Bourdeauducq | 371e59cef5 | |
pca006132 | e67efe439b | |
Astro | e4e7141bf3 | |
Sebastien Bourdeauducq | f68b5896ce | |
Sebastien Bourdeauducq | e430600683 | |
Sebastien Bourdeauducq | 0c60d684e4 | |
Sebastien Bourdeauducq | 6fa3a6bbd9 | |
Sebastien Bourdeauducq | 7082e07a18 | |
Sebastien Bourdeauducq | 21c0c5cbc8 | |
pca006132 | 90904634cd | |
Sebastien Bourdeauducq | ae4d3e2455 | |
Sebastien Bourdeauducq | 9fcf9243f2 | |
pca006132 | 90e33f688a | |
Astro | f0697c3ec3 | |
Astro | b2c707d543 | |
pca006132 | 6195ad40c3 | |
Sebastien Bourdeauducq | dd288912af | |
Astro | ec252b099c | |
Astro | a16c639eaf | |
Astro | c6fa18344e | |
Astro | 5c69bbdad6 | |
Astro | c0e66a632c | |
Astro | b129d3e0df | |
Astro | 1e4be13869 | |
Astro | eea042e2ee |
|
@ -98,13 +98,13 @@ dependencies = [
|
||||||
"libboard_zynq 0.0.0",
|
"libboard_zynq 0.0.0",
|
||||||
"libcortex_a9 0.0.0",
|
"libcortex_a9 0.0.0",
|
||||||
"libregister 0.0.0",
|
"libregister 0.0.0",
|
||||||
"linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked_list_allocator"
|
name = "linked_list_allocator"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -170,7 +170,7 @@ dependencies = [
|
||||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
"checksum compiler_builtins 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "38f18416546abfbf8d801c555a0e99524453e7214f9cc9107ad49de3d5948ccc"
|
"checksum compiler_builtins 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "38f18416546abfbf8d801c555a0e99524453e7214f9cc9107ad49de3d5948ccc"
|
||||||
"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
|
"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
|
||||||
"checksum linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d6b60501dd4c850950bb43f970d544f6ce04e0ca021da2db2538fbe9d923f19e"
|
"checksum linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
|
||||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||||
"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
|
"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
|
||||||
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
|
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
|
||||||
|
|
17
README.md
17
README.md
|
@ -1,7 +1,7 @@
|
||||||
# Build
|
# Build
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
nix-shell --command "cargo xbuild --release"
|
nix-shell --command "cargo xbuild --release -p experiments"
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently the ELF output is placed at `target/armv7-none-eabihf/release/experiments`
|
Currently the ELF output is placed at `target/armv7-none-eabihf/release/experiments`
|
||||||
|
@ -41,7 +41,7 @@ Proceed using gdb with `load`, `c`
|
||||||
### Running on the ZC706
|
### Running on the ZC706
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
nix-shell --command "cargo xbuild --release"
|
nix-shell --command "cargo xbuild --release -p experiments"
|
||||||
cd openocd
|
cd openocd
|
||||||
openocd -f zc706.cfg
|
openocd -f zc706.cfg
|
||||||
```
|
```
|
||||||
|
@ -57,23 +57,26 @@ openocd -f cora-z7-10.cfg
|
||||||
### Loading a bitstream into volatile memory
|
### Loading a bitstream into volatile memory
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit"
|
openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Development Process
|
### Development Process
|
||||||
|
|
||||||
Clone this repo onto your development/build machine and the raspberry pi that controls the Xilinx 7000 board
|
Clone this repo onto your development/build machine and the raspberry pi that controls the Xilinx 7000 board
|
||||||
|
|
||||||
On the dev machine, the below script builds zc706 and secure copies it to the target pi (in your pi $HOME directory)
|
On the dev machine, the below script builds zc706 and secure copies it to the target pi (in your pi $HOME directory):
|
||||||
```shell
|
```shell
|
||||||
cd ~/zc706
|
cd ~/zynq-rs
|
||||||
./build.sh $your_user/ssh_id
|
./build.sh $your_user_or_ssh_id
|
||||||
```
|
```
|
||||||
|
|
||||||
On the pi, we need an information rich environment that includes a relatively reliable `gdb` experience (that includes `ctrl-p` and `ctrl-n` command history that persists across `cgdb` executions), run:
|
On the pi, we need an information rich environment that includes a relatively reliable `gdb` experience (that includes `ctrl-p` and `ctrl-n` command history that persists across `cgdb` executions), run:
|
||||||
```shell
|
```shell
|
||||||
ssh pi4
|
ssh pi4
|
||||||
cd zc706
|
cd zynq-rs
|
||||||
|
# For ZC706, run:
|
||||||
|
./tmux.sh 0
|
||||||
|
# For Cora Z7, run:
|
||||||
./tmux.sh
|
./tmux.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
2
build.sh
2
build.sh
|
@ -1 +1 @@
|
||||||
nix-shell --command "cargo xbuild --release" && scp -P 2204 -C target/armv7-none-eabihf/release/zc706-experiments $1@nixbld.m-labs.hk:/home/$1/zc706/zc706.elf
|
nix-shell --command "cargo xbuild --release -p experiments" && scp -C target/armv7-none-eabihf/release/experiments $1@rpi-4.m-labs.hk:/home/$1/zc706/zc706.elf
|
||||||
|
|
|
@ -49,7 +49,7 @@ let
|
||||||
name = "${crate}";
|
name = "${crate}";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
crateSubdir = crate;
|
crateSubdir = crate;
|
||||||
cargoSha256 = "0xlynsr94dyv0g41qwk5490w3wnzd5g70msaih6mcbgr3v4s2q34";
|
cargoSha256 = "1c1qpg9by8bg93yhgllb5xs155g27qmh99pbrb681wazm8k7nwim";
|
||||||
cargoFeatures = features;
|
cargoFeatures = features;
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
dontFixup = true;
|
dontFixup = true;
|
||||||
|
|
|
@ -16,5 +16,5 @@ embedded-hal = "0.2"
|
||||||
libregister = { path = "../libregister" }
|
libregister = { path = "../libregister" }
|
||||||
libcortex_a9 = { path = "../libcortex_a9" }
|
libcortex_a9 = { path = "../libcortex_a9" }
|
||||||
libboard_zynq = { path = "../libboard_zynq" }
|
libboard_zynq = { path = "../libboard_zynq" }
|
||||||
libsupport_zynq = { path = "../libsupport_zynq" }
|
libsupport_zynq = { path = "../libsupport_zynq", default-features = false, features = ["panic_handler"]}
|
||||||
libasync = { path = "../libasync" }
|
libasync = { path = "../libasync" }
|
||||||
|
|
|
@ -1,14 +1,4 @@
|
||||||
ENTRY(_boot_cores);
|
ENTRY(Reset);
|
||||||
|
|
||||||
/* Provide some defaults */
|
|
||||||
PROVIDE(Reset = _boot_cores);
|
|
||||||
PROVIDE(UndefinedInstruction = Reset);
|
|
||||||
PROVIDE(SoftwareInterrupt = Reset);
|
|
||||||
PROVIDE(PrefetchAbort = Reset);
|
|
||||||
PROVIDE(DataAbort = Reset);
|
|
||||||
PROVIDE(ReservedException = Reset);
|
|
||||||
PROVIDE(IRQ = Reset);
|
|
||||||
PROVIDE(FIQ = Reset);
|
|
||||||
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
|
@ -42,19 +32,23 @@ SECTIONS
|
||||||
*(.bss .bss.*);
|
*(.bss .bss.*);
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
__bss_end = .;
|
__bss_end = .;
|
||||||
} > OCM
|
} > OCM3
|
||||||
|
|
||||||
.stack1 (NOLOAD) : ALIGN(8) {
|
.stack1 (NOLOAD) : ALIGN(8) {
|
||||||
__stack1_end = .;
|
__stack1_end = .;
|
||||||
. += 0x200;
|
. += 0x200;
|
||||||
__stack1_start = .;
|
__stack1_start = .;
|
||||||
} > OCM
|
} > OCM3
|
||||||
|
|
||||||
.stack0 (NOLOAD) : ALIGN(8) {
|
.stack0 (NOLOAD) : ALIGN(8) {
|
||||||
__stack0_end = .;
|
__stack0_end = .;
|
||||||
. = ORIGIN(OCM) + LENGTH(OCM) - 8;
|
. = ORIGIN(OCM3) + LENGTH(OCM3) - 8;
|
||||||
__stack0_start = .;
|
__stack0_start = .;
|
||||||
} > OCM
|
|
||||||
|
/* unused heap0 to prevent the linker from complaining*/
|
||||||
|
__heap0_start = .;
|
||||||
|
__heap0_end = .;
|
||||||
|
} > OCM3
|
||||||
|
|
||||||
/DISCARD/ :
|
/DISCARD/ :
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![feature(const_in_array_repeat_expressions)]
|
||||||
|
#![feature(naked_functions)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use alloc::{borrow::ToOwned, collections::BTreeMap, format};
|
use alloc::collections::BTreeMap;
|
||||||
use core::task::Poll;
|
|
||||||
use libasync::{
|
use libasync::{
|
||||||
delay,
|
delay,
|
||||||
smoltcp::{Sockets, TcpStream},
|
smoltcp::{Sockets, TcpStream},
|
||||||
|
@ -14,10 +15,10 @@ use libboard_zynq::{
|
||||||
self as zynq,
|
self as zynq,
|
||||||
clocks::source::{ArmPll, ClockSource, IoPll},
|
clocks::source::{ArmPll, ClockSource, IoPll},
|
||||||
clocks::Clocks,
|
clocks::Clocks,
|
||||||
print, println,
|
print, println, stdio,
|
||||||
sdio::sd_card::SdCard,
|
mpcore,
|
||||||
|
gic,
|
||||||
smoltcp::{
|
smoltcp::{
|
||||||
self,
|
|
||||||
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
|
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
wire::{EthernetAddress, IpAddress, IpCidr},
|
wire::{EthernetAddress, IpAddress, IpCidr},
|
||||||
|
@ -26,22 +27,70 @@ use libboard_zynq::{
|
||||||
};
|
};
|
||||||
use libcortex_a9::{
|
use libcortex_a9::{
|
||||||
mutex::Mutex,
|
mutex::Mutex,
|
||||||
sync_channel::{self, sync_channel},
|
sync_channel::{Sender, Receiver},
|
||||||
|
sync_channel,
|
||||||
|
regs::{MPIDR, SP},
|
||||||
|
spin_lock_yield, notify_spin_lock,
|
||||||
|
asm
|
||||||
};
|
};
|
||||||
use libregister::RegisterR;
|
use libregister::{RegisterR, RegisterW};
|
||||||
use libsupport_zynq::{
|
use libsupport_zynq::{
|
||||||
boot, ram,
|
boot, ram,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::{info, warn};
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
mod ps7_init;
|
|
||||||
|
|
||||||
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
|
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
|
||||||
|
|
||||||
|
static mut CORE1_REQ: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
|
||||||
|
static mut CORE1_RES: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
static mut __stack1_start: u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
|
pub unsafe extern "C" fn IRQ() {
|
||||||
|
if MPIDR.read().cpu_id() == 1{
|
||||||
|
let mpcore = mpcore::RegisterBlock::new();
|
||||||
|
let mut gic = gic::InterruptController::new(mpcore);
|
||||||
|
let id = gic.get_interrupt_id();
|
||||||
|
if id.0 == 0 {
|
||||||
|
gic.end_interrupt(id);
|
||||||
|
asm::exit_irq();
|
||||||
|
SP.write(&mut __stack1_start as *mut _ as u32);
|
||||||
|
asm::enable_irq();
|
||||||
|
CORE1_RESTART.store(false, Ordering::Relaxed);
|
||||||
|
notify_spin_lock();
|
||||||
|
main_core1();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("IRQ");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restart_core1() {
|
||||||
|
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
|
||||||
|
CORE1_RESTART.store(true, Ordering::Relaxed);
|
||||||
|
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
|
||||||
|
while CORE1_RESTART.load(Ordering::Relaxed) {
|
||||||
|
spin_lock_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn main_core0() {
|
pub fn main_core0() {
|
||||||
// zynq::clocks::CpuClocks::enable_io(1_250_000_000);
|
// zynq::clocks::CpuClocks::enable_io(1_250_000_000);
|
||||||
println!("\nzc706 main");
|
println!("\nzc706 main");
|
||||||
|
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
|
||||||
|
interrupt_controller.enable_interrupts();
|
||||||
|
// ps7_init::apply();
|
||||||
|
libboard_zynq::stdio::drop_uart();
|
||||||
|
|
||||||
libboard_zynq::logger::init().unwrap();
|
libboard_zynq::logger::init().unwrap();
|
||||||
log::set_max_level(log::LevelFilter::Trace);
|
log::set_max_level(log::LevelFilter::Trace);
|
||||||
|
@ -82,28 +131,6 @@ pub fn main_core0() {
|
||||||
clocks.cpu_1x()
|
clocks.cpu_1x()
|
||||||
);
|
);
|
||||||
|
|
||||||
let sd = libboard_zynq::sdio::SDIO::sdio0(true);
|
|
||||||
// only test SD card if it is inserted
|
|
||||||
if sd.is_card_inserted() {
|
|
||||||
let result = SdCard::from_sdio(sd);
|
|
||||||
match &result {
|
|
||||||
Ok(_) => info!("OK!"),
|
|
||||||
Err(a) => info!("{}", a),
|
|
||||||
};
|
|
||||||
const SIZE: usize = 512 * 2 + 1;
|
|
||||||
let mut sd_card = result.unwrap();
|
|
||||||
{
|
|
||||||
let buffer: [u8; SIZE] = [5; SIZE];
|
|
||||||
sd_card.write_block(0x0, 2, &buffer).unwrap();
|
|
||||||
}
|
|
||||||
let mut buffer: [u8; SIZE] = [0; SIZE];
|
|
||||||
sd_card.read_block(0x1, 2, &mut buffer[1..]).unwrap();
|
|
||||||
for i in 0..buffer.len() {
|
|
||||||
info!("buffer[{}] = {}", i, buffer[i]);
|
|
||||||
}
|
|
||||||
info!("End");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut flash = zynq::flash::Flash::new(200_000_000).linear_addressing_mode();
|
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()) };
|
let flash_ram: &[u8] = unsafe { core::slice::from_raw_parts(flash.ptr(), flash.size()) };
|
||||||
for i in 0..=1 {
|
for i in 0..=1 {
|
||||||
|
@ -113,7 +140,7 @@ pub fn main_core0() {
|
||||||
}
|
}
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
let mut flash = flash.stop();
|
let _flash = flash.stop();
|
||||||
|
|
||||||
let timer = libboard_zynq::timer::GlobalTimer::start();
|
let timer = libboard_zynq::timer::GlobalTimer::start();
|
||||||
|
|
||||||
|
@ -161,28 +188,62 @@ pub fn main_core0() {
|
||||||
flash = flash_io.stop();
|
flash = flash_io.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let core1 = boot::Core1::start(false);
|
boot::Core1::start(false);
|
||||||
|
|
||||||
let (mut core1_req, rx) = sync_channel(10);
|
let core1_req = unsafe { &mut CORE1_REQ.0 };
|
||||||
*CORE1_REQ.lock() = Some(rx);
|
let core1_res = unsafe { &mut CORE1_RES.1 };
|
||||||
let (tx, mut core1_res) = sync_channel(10);
|
|
||||||
*CORE1_RES.lock() = Some(tx);
|
|
||||||
task::block_on(async {
|
task::block_on(async {
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
|
restart_core1();
|
||||||
core1_req.async_send(i).await;
|
core1_req.async_send(i).await;
|
||||||
let j = core1_res.async_recv().await;
|
let j = core1_res.async_recv().await;
|
||||||
println!("{} -> {}", i, j);
|
println!("{} -> {}", i, j);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
core1.disable();
|
unsafe {
|
||||||
|
core1_req.drop_elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test I2C
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
{
|
||||||
|
let mut i2c = zynq::i2c::I2C::i2c();
|
||||||
|
i2c.init();
|
||||||
|
println!("I2C bit-banging enabled");
|
||||||
|
let mut eeprom = zynq::i2c::eeprom::EEPROM::new(&mut i2c, 16);
|
||||||
|
// Write to 0x00 and 0x08
|
||||||
|
let eeprom_buffer: [u8; 22] = [
|
||||||
|
0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
|
||||||
|
0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
|
||||||
|
0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,
|
||||||
|
];
|
||||||
|
eeprom.write(0x00, &eeprom_buffer[0..6]);
|
||||||
|
eeprom.write(0x08, &eeprom_buffer[6..22]);
|
||||||
|
println!("Data written to EEPROM");
|
||||||
|
let mut eeprom_buffer = [0u8; 24];
|
||||||
|
// Read from 0x00
|
||||||
|
eeprom.read(0x00, &mut eeprom_buffer);
|
||||||
|
print!("Data read from EEPROM @ 0x00: (hex) ");
|
||||||
|
for i in 0..6 {
|
||||||
|
print!("{:02x} ", eeprom_buffer[i]);
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
// Read from 0x08
|
||||||
|
eeprom.read(0x08, &mut eeprom_buffer);
|
||||||
|
print!("Data read from EEPROM @ 0x08: (hex) ");
|
||||||
|
for i in 0..16 {
|
||||||
|
print!("{:02x} ", eeprom_buffer[i]);
|
||||||
|
}
|
||||||
|
println!("");
|
||||||
|
}
|
||||||
|
|
||||||
let eth = zynq::eth::Eth::default(HWADDR.clone());
|
let eth = zynq::eth::Eth::default(HWADDR.clone());
|
||||||
println!("Eth on");
|
println!("Eth on");
|
||||||
|
|
||||||
const RX_LEN: usize = 8;
|
const RX_LEN: usize = 4096;
|
||||||
// Number of transmission buffers (minimum is two because with
|
// Number of transmission buffers (minimum is two because with
|
||||||
// one, duplicate packet transmission occurs)
|
// one, duplicate packet transmission occurs)
|
||||||
const TX_LEN: usize = 8;
|
const TX_LEN: usize = 4096;
|
||||||
let eth = eth.start_rx(RX_LEN);
|
let eth = eth.start_rx(RX_LEN);
|
||||||
let mut eth = eth.start_tx(TX_LEN);
|
let mut eth = eth.start_tx(TX_LEN);
|
||||||
|
|
||||||
|
@ -199,56 +260,45 @@ pub fn main_core0() {
|
||||||
.neighbor_cache(neighbor_cache)
|
.neighbor_cache(neighbor_cache)
|
||||||
.finalize();
|
.finalize();
|
||||||
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
ps7_init::report_differences();
|
|
||||||
|
|
||||||
Sockets::init(32);
|
Sockets::init(32);
|
||||||
/// `chargen`
|
|
||||||
const TCP_PORT: u16 = 19;
|
const TCP_PORT: u16 = 19;
|
||||||
async fn handle_connection(stream: TcpStream) -> smoltcp::Result<()> {
|
// (rx, tx)
|
||||||
stream.send("Enter your name: ".bytes()).await?;
|
let stats = alloc::rc::Rc::new(core::cell::RefCell::new((0, 0)));
|
||||||
let name = stream
|
let stats_tx = stats.clone();
|
||||||
.recv(|buf| {
|
task::spawn(async move {
|
||||||
for (i, b) in buf.iter().enumerate() {
|
while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
|
||||||
if *b == '\n' as u8 {
|
let stats_tx = stats_tx.clone();
|
||||||
return match core::str::from_utf8(&buf[0..i]) {
|
task::spawn(async move {
|
||||||
Ok(name) => Poll::Ready((i + 1, Some(name.to_owned()))),
|
let tx_data = (0..=255).take(4096).collect::<alloc::vec::Vec<u8>>();
|
||||||
Err(_) => Poll::Ready((i + 1, None)),
|
loop {
|
||||||
};
|
// const CHUNK_SIZE: usize = 65536;
|
||||||
|
// match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
|
||||||
|
match stream.send_slice(&tx_data[..]).await {
|
||||||
|
Ok(len) => stats_tx.borrow_mut().1 += tx_data.len(), //CHUNK_SIZE,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("tx: {:?}", e);
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if buf.len() > 100 {
|
});
|
||||||
// Too much input, consume all
|
|
||||||
Poll::Ready((buf.len(), None))
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
match name {
|
|
||||||
Some(name) => stream.send(format!("Hello {}!\n", name).bytes()).await?,
|
|
||||||
None => {
|
|
||||||
stream
|
|
||||||
.send("I had trouble reading your name.\n".bytes())
|
|
||||||
.await?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let _ = stream.close().await;
|
});
|
||||||
Ok(())
|
let stats_rx = stats.clone();
|
||||||
}
|
|
||||||
|
|
||||||
let counter = alloc::rc::Rc::new(core::cell::RefCell::new(0));
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
while let Ok(stream) = TcpStream::accept(TCP_PORT, 2048, 2408).await {
|
while let Ok(stream) = TcpStream::accept(TCP_PORT+1, 0x10_0000, 0x10_0000).await {
|
||||||
let counter = counter.clone();
|
let stats_rx = stats_rx.clone();
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
*counter.borrow_mut() += 1;
|
loop {
|
||||||
println!("Serving {} connections", *counter.borrow());
|
match stream.recv(|buf| (buf.len(), buf.len())).await {
|
||||||
handle_connection(stream)
|
Ok(len) => stats_rx.borrow_mut().0 += len,
|
||||||
.await
|
Err(e) => {
|
||||||
.unwrap_or_else(|e| println!("Connection: {:?}", e));
|
warn!("rx: {:?}", e);
|
||||||
*counter.borrow_mut() -= 1;
|
break
|
||||||
println!("Now serving {} connections", *counter.borrow());
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -258,10 +308,16 @@ pub fn main_core0() {
|
||||||
loop {
|
loop {
|
||||||
delay(&mut countdown, Milliseconds(1000)).await;
|
delay(&mut countdown, Milliseconds(1000)).await;
|
||||||
|
|
||||||
let timestamp = timer.get_us();
|
let timestamp = timer.get_us().0;
|
||||||
let seconds = timestamp / 1_000_000;
|
let seconds = timestamp / 1_000_000;
|
||||||
let micros = timestamp % 1_000_000;
|
let micros = timestamp % 1_000_000;
|
||||||
info!("time: {:6}.{:06}s", seconds, micros);
|
let (rx, tx) = {
|
||||||
|
let mut stats = stats.borrow_mut();
|
||||||
|
let result = *stats;
|
||||||
|
*stats = (0, 0);
|
||||||
|
result
|
||||||
|
};
|
||||||
|
info!("time: {:6}.{:06}s, rx: {}k/s, tx: {}k/s", seconds, micros, rx / 1024, tx / 1024);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -270,27 +326,18 @@ pub fn main_core0() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static CORE1_REQ: Mutex<Option<sync_channel::Receiver<usize>>> = Mutex::new(None);
|
|
||||||
static CORE1_RES: Mutex<Option<sync_channel::Sender<usize>>> = Mutex::new(None);
|
|
||||||
static DONE: Mutex<bool> = Mutex::new(false);
|
static DONE: Mutex<bool> = Mutex::new(false);
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn main_core1() {
|
pub fn main_core1() {
|
||||||
println!("Hello from core1!");
|
println!("Hello from core1!");
|
||||||
|
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
|
||||||
let mut req = None;
|
interrupt_controller.enable_interrupts();
|
||||||
while req.is_none() {
|
let req = unsafe { &mut CORE1_REQ.1 };
|
||||||
req = CORE1_REQ.lock().take();
|
let res = unsafe { &mut CORE1_RES.0 };
|
||||||
}
|
|
||||||
let req = req.unwrap();
|
|
||||||
let mut res = None;
|
|
||||||
while res.is_none() {
|
|
||||||
res = CORE1_RES.lock().take();
|
|
||||||
}
|
|
||||||
let mut res = res.unwrap();
|
|
||||||
|
|
||||||
for i in req {
|
for i in req {
|
||||||
res.send(*i * *i);
|
res.send(i * i);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("core1 done!");
|
println!("core1 done!");
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
#![cfg(feature = "target_zc706")]
|
|
||||||
|
|
||||||
use libboard_zynq::println;
|
|
||||||
|
|
||||||
mod zc706;
|
|
||||||
// mod cora_z7_10;
|
|
||||||
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
use zc706 as target;
|
|
||||||
// #[cfg(feature = "target_cora_z7_10")]
|
|
||||||
// use cora_z7_10 as target;
|
|
||||||
|
|
||||||
pub fn report_differences() {
|
|
||||||
for (i, op) in target::INIT_DATA.iter().enumerate() {
|
|
||||||
let address = op.address();
|
|
||||||
let overwritten_later = target::INIT_DATA[(i + 1)..].iter()
|
|
||||||
.any(|later_op| later_op.address() == address);
|
|
||||||
|
|
||||||
if !overwritten_later {
|
|
||||||
op.report_difference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum InitOp {
|
|
||||||
MaskWrite(usize, usize, usize),
|
|
||||||
MaskPoll(usize, usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InitOp {
|
|
||||||
fn address(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
InitOp::MaskWrite(address, _, _) => *address,
|
|
||||||
InitOp::MaskPoll(address, _) => *address,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(&self) -> usize {
|
|
||||||
unsafe { *(self.address() as *const usize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn difference(&self) -> Option<(usize, usize)> {
|
|
||||||
let (mask, expected) = match self {
|
|
||||||
InitOp::MaskWrite(_, mask, expected) =>
|
|
||||||
(*mask, *expected),
|
|
||||||
InitOp::MaskPoll(_, mask) =>
|
|
||||||
(*mask, *mask),
|
|
||||||
};
|
|
||||||
let actual = self.read();
|
|
||||||
if actual & mask == expected {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((actual & mask, expected))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn report_difference(&self) {
|
|
||||||
if let Some((actual, expected)) = self.difference() {
|
|
||||||
println!(
|
|
||||||
"Register {:08X} is {:08X}&={:08X} != {:08X} expected",
|
|
||||||
self.address(),
|
|
||||||
self.read(),
|
|
||||||
actual,
|
|
||||||
expected
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,203 +0,0 @@
|
||||||
use super::InitOp::{self, *};
|
|
||||||
|
|
||||||
pub const INIT_DATA: &'static [InitOp] = &[
|
|
||||||
// ps7_mio_init_data_1_0
|
|
||||||
MaskWrite(0xF8000B40, 0x00000FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000B44, 0x00000FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000B48, 0x00000FFF, 0x00000672),
|
|
||||||
MaskWrite(0xF8000B4C, 0x00000FFF, 0x00000672),
|
|
||||||
MaskWrite(0xF8000B50, 0x00000FFF, 0x00000674),
|
|
||||||
MaskWrite(0xF8000B54, 0x00000FFF, 0x00000674),
|
|
||||||
MaskWrite(0xF8000B58, 0x00000FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000B5C, 0xFFFFFFFF, 0x0018C61C),
|
|
||||||
MaskWrite(0xF8000B60, 0xFFFFFFFF, 0x00F9861C),
|
|
||||||
MaskWrite(0xF8000B64, 0xFFFFFFFF, 0x00F9861C),
|
|
||||||
MaskWrite(0xF8000B68, 0xFFFFFFFF, 0x00F9861C),
|
|
||||||
MaskWrite(0xF8000B6C, 0x000073FF, 0x00000209),
|
|
||||||
MaskWrite(0xF8000B70, 0x00000021, 0x00000021),
|
|
||||||
MaskWrite(0xF8000B70, 0x00000021, 0x00000020),
|
|
||||||
MaskWrite(0xF8000B70, 0x07FFFFFF, 0x00000823),
|
|
||||||
MaskWrite(0xF8000700, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000704, 0x00003FFF, 0x00000702),
|
|
||||||
MaskWrite(0xF8000708, 0x00003FFF, 0x00000702),
|
|
||||||
MaskWrite(0xF800070C, 0x00003FFF, 0x00000702),
|
|
||||||
MaskWrite(0xF8000710, 0x00003FFF, 0x00000702),
|
|
||||||
MaskWrite(0xF8000714, 0x00003FFF, 0x00000702),
|
|
||||||
MaskWrite(0xF8000718, 0x00003FFF, 0x00000702),
|
|
||||||
MaskWrite(0xF800071C, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000720, 0x00003FFF, 0x00000700),
|
|
||||||
MaskWrite(0xF8000724, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000728, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF800072C, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000730, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000734, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000738, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF800073C, 0x00003FFF, 0x00000600),
|
|
||||||
MaskWrite(0xF8000740, 0x00003FFF, 0x00000302),
|
|
||||||
MaskWrite(0xF8000744, 0x00003FFF, 0x00000302),
|
|
||||||
MaskWrite(0xF8000748, 0x00003FFF, 0x00000302),
|
|
||||||
MaskWrite(0xF800074C, 0x00003FFF, 0x00000302),
|
|
||||||
MaskWrite(0xF8000750, 0x00003FFF, 0x00000302),
|
|
||||||
MaskWrite(0xF8000754, 0x00003FFF, 0x00000302),
|
|
||||||
MaskWrite(0xF8000758, 0x00003FFF, 0x00000303),
|
|
||||||
MaskWrite(0xF800075C, 0x00003FFF, 0x00000303),
|
|
||||||
MaskWrite(0xF8000760, 0x00003FFF, 0x00000303),
|
|
||||||
MaskWrite(0xF8000764, 0x00003FFF, 0x00000303),
|
|
||||||
MaskWrite(0xF8000768, 0x00003FFF, 0x00000303),
|
|
||||||
MaskWrite(0xF800076C, 0x00003FFF, 0x00000303),
|
|
||||||
MaskWrite(0xF8000770, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF8000774, 0x00003FFF, 0x00000305),
|
|
||||||
MaskWrite(0xF8000778, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF800077C, 0x00003FFF, 0x00000305),
|
|
||||||
MaskWrite(0xF8000780, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF8000784, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF8000788, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF800078C, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF8000790, 0x00003FFF, 0x00000305),
|
|
||||||
MaskWrite(0xF8000794, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF8000798, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF800079C, 0x00003FFF, 0x00000304),
|
|
||||||
MaskWrite(0xF80007A0, 0x00003FFF, 0x00000380),
|
|
||||||
MaskWrite(0xF80007A4, 0x00003FFF, 0x00000380),
|
|
||||||
MaskWrite(0xF80007A8, 0x00003FFF, 0x00000380),
|
|
||||||
MaskWrite(0xF80007AC, 0x00003FFF, 0x00000380),
|
|
||||||
MaskWrite(0xF80007B0, 0x00003FFF, 0x00000380),
|
|
||||||
MaskWrite(0xF80007B4, 0x00003FFF, 0x00000380),
|
|
||||||
MaskWrite(0xF80007B8, 0x00003F01, 0x00000201),
|
|
||||||
MaskWrite(0xF80007BC, 0x00003F01, 0x00000201),
|
|
||||||
MaskWrite(0xF80007C0, 0x00003FFF, 0x000002E0),
|
|
||||||
MaskWrite(0xF80007C4, 0x00003FFF, 0x000002E1),
|
|
||||||
MaskWrite(0xF80007C8, 0x00003FFF, 0x00000200),
|
|
||||||
MaskWrite(0xF80007CC, 0x00003FFF, 0x00000200),
|
|
||||||
MaskWrite(0xF80007D0, 0x00003FFF, 0x00000280),
|
|
||||||
MaskWrite(0xF80007D4, 0x00003FFF, 0x00000280),
|
|
||||||
MaskWrite(0xF8000830, 0x003F003F, 0x002F002E),
|
|
||||||
// ps7_pll_init_data_1_0
|
|
||||||
MaskWrite(0xF8000110, 0x003FFFF0, 0x000FA220),
|
|
||||||
MaskWrite(0xF8000100, 0x0007F000, 0x00028000),
|
|
||||||
MaskWrite(0xF8000100, 0x00000010, 0x00000010),
|
|
||||||
MaskWrite(0xF8000100, 0x00000001, 0x00000001),
|
|
||||||
MaskWrite(0xF8000100, 0x00000001, 0x00000000),
|
|
||||||
MaskPoll(0xF800010C, 0x00000001),
|
|
||||||
MaskWrite(0xF8000100, 0x00000010, 0x00000000),
|
|
||||||
MaskWrite(0xF8000120, 0x1F003F30, 0x1F000200),
|
|
||||||
MaskWrite(0xF8000114, 0x003FFFF0, 0x0012C220),
|
|
||||||
MaskWrite(0xF8000104, 0x0007F000, 0x00020000),
|
|
||||||
MaskWrite(0xF8000104, 0x00000010, 0x00000010),
|
|
||||||
MaskWrite(0xF8000104, 0x00000001, 0x00000001),
|
|
||||||
MaskWrite(0xF8000104, 0x00000001, 0x00000000),
|
|
||||||
MaskPoll(0xF800010C, 0x00000002),
|
|
||||||
MaskWrite(0xF8000104, 0x00000010, 0x00000000),
|
|
||||||
MaskWrite(0xF8000124, 0xFFF00003, 0x0C200003),
|
|
||||||
MaskWrite(0xF8000118, 0x003FFFF0, 0x001452C0),
|
|
||||||
MaskWrite(0xF8000108, 0x0007F000, 0x0001E000),
|
|
||||||
MaskWrite(0xF8000108, 0x00000010, 0x00000010),
|
|
||||||
MaskWrite(0xF8000108, 0x00000001, 0x00000001),
|
|
||||||
MaskWrite(0xF8000108, 0x00000001, 0x00000000),
|
|
||||||
MaskPoll(0xF800010C, 0x00000004),
|
|
||||||
MaskWrite(0xF8000108, 0x00000010, 0x00000000),
|
|
||||||
// ps7_clock_init_data_1_0
|
|
||||||
MaskWrite(0xF8000128, 0x03F03F01, 0x00700F01),
|
|
||||||
MaskWrite(0xF8000138, 0x00000011, 0x00000001),
|
|
||||||
MaskWrite(0xF8000140, 0x03F03F71, 0x00100801),
|
|
||||||
MaskWrite(0xF800014C, 0x00003F31, 0x00000501),
|
|
||||||
MaskWrite(0xF8000150, 0x00003F33, 0x00001401),
|
|
||||||
MaskWrite(0xF8000154, 0x00003F33, 0x00001402),
|
|
||||||
MaskWrite(0xF8000168, 0x00003F31, 0x00000501),
|
|
||||||
MaskWrite(0xF8000170, 0x03F03F30, 0x00200400),
|
|
||||||
MaskWrite(0xF80001C4, 0x00000001, 0x00000001),
|
|
||||||
MaskWrite(0xF800012C, 0x01FFCCCD, 0x01EC044D),
|
|
||||||
// ps7_ddr_init_data_1_0
|
|
||||||
MaskWrite(0xF8006000, 0x0001FFFF, 0x00000080),
|
|
||||||
MaskWrite(0xF8006004, 0x1FFFFFFF, 0x00081081),
|
|
||||||
MaskWrite(0xF8006008, 0x03FFFFFF, 0x03C0780F),
|
|
||||||
MaskWrite(0xF800600C, 0x03FFFFFF, 0x02001001),
|
|
||||||
MaskWrite(0xF8006010, 0x03FFFFFF, 0x00014001),
|
|
||||||
MaskWrite(0xF8006014, 0x001FFFFF, 0x0004159B),
|
|
||||||
MaskWrite(0xF8006018, 0xF7FFFFFF, 0x452460D2),
|
|
||||||
MaskWrite(0xF800601C, 0xFFFFFFFF, 0x720238E5),
|
|
||||||
MaskWrite(0xF8006020, 0xFFFFFFFC, 0x272872D0),
|
|
||||||
MaskWrite(0xF8006024, 0x0FFFFFFF, 0x0000003C),
|
|
||||||
MaskWrite(0xF8006028, 0x00003FFF, 0x00002007),
|
|
||||||
MaskWrite(0xF800602C, 0xFFFFFFFF, 0x00000008),
|
|
||||||
MaskWrite(0xF8006030, 0xFFFFFFFF, 0x00040930),
|
|
||||||
MaskWrite(0xF8006034, 0x13FF3FFF, 0x000116D4),
|
|
||||||
MaskWrite(0xF8006038, 0x00001FC3, 0x00000000),
|
|
||||||
MaskWrite(0xF800603C, 0x000FFFFF, 0x00000777),
|
|
||||||
MaskWrite(0xF8006040, 0xFFFFFFFF, 0xFFF00000),
|
|
||||||
MaskWrite(0xF8006044, 0x0FFFFFFF, 0x0FF66666),
|
|
||||||
MaskWrite(0xF8006048, 0x3FFFFFFF, 0x0003C248),
|
|
||||||
MaskWrite(0xF8006050, 0xFF0F8FFF, 0x77010800),
|
|
||||||
MaskWrite(0xF8006058, 0x0001FFFF, 0x00000101),
|
|
||||||
MaskWrite(0xF800605C, 0x0000FFFF, 0x00005003),
|
|
||||||
MaskWrite(0xF8006060, 0x000017FF, 0x0000003E),
|
|
||||||
MaskWrite(0xF8006064, 0x00021FE0, 0x00020000),
|
|
||||||
MaskWrite(0xF8006068, 0x03FFFFFF, 0x00284141),
|
|
||||||
MaskWrite(0xF800606C, 0x0000FFFF, 0x00001610),
|
|
||||||
MaskWrite(0xF80060A0, 0x00FFFFFF, 0x00008000),
|
|
||||||
MaskWrite(0xF80060A4, 0xFFFFFFFF, 0x10200802),
|
|
||||||
MaskWrite(0xF80060A8, 0x0FFFFFFF, 0x0690CB73),
|
|
||||||
MaskWrite(0xF80060AC, 0x000001FF, 0x000001FE),
|
|
||||||
MaskWrite(0xF80060B0, 0x1FFFFFFF, 0x1CFFFFFF),
|
|
||||||
MaskWrite(0xF80060B4, 0x000007FF, 0x00000200),
|
|
||||||
MaskWrite(0xF80060B8, 0x01FFFFFF, 0x00200066),
|
|
||||||
MaskWrite(0xF80060C4, 0x00000003, 0x00000000),
|
|
||||||
MaskWrite(0xF80060C8, 0x000000FF, 0x00000000),
|
|
||||||
MaskWrite(0xF80060DC, 0x00000001, 0x00000000),
|
|
||||||
MaskWrite(0xF80060F0, 0x0000FFFF, 0x00000000),
|
|
||||||
MaskWrite(0xF80060F4, 0x0000000F, 0x00000008),
|
|
||||||
MaskWrite(0xF8006114, 0x000000FF, 0x00000000),
|
|
||||||
MaskWrite(0xF8006118, 0x7FFFFFFF, 0x40000001),
|
|
||||||
MaskWrite(0xF800611C, 0x7FFFFFFF, 0x40000001),
|
|
||||||
MaskWrite(0xF8006120, 0x7FFFFFFF, 0x40000001),
|
|
||||||
MaskWrite(0xF8006124, 0x7FFFFFFF, 0x40000001),
|
|
||||||
MaskWrite(0xF800612C, 0x000FFFFF, 0x00033C03),
|
|
||||||
MaskWrite(0xF8006130, 0x000FFFFF, 0x00034003),
|
|
||||||
MaskWrite(0xF8006134, 0x000FFFFF, 0x0002F400),
|
|
||||||
MaskWrite(0xF8006138, 0x000FFFFF, 0x00030400),
|
|
||||||
MaskWrite(0xF8006140, 0x000FFFFF, 0x00000035),
|
|
||||||
MaskWrite(0xF8006144, 0x000FFFFF, 0x00000035),
|
|
||||||
MaskWrite(0xF8006148, 0x000FFFFF, 0x00000035),
|
|
||||||
MaskWrite(0xF800614C, 0x000FFFFF, 0x00000035),
|
|
||||||
MaskWrite(0xF8006154, 0x000FFFFF, 0x00000083),
|
|
||||||
MaskWrite(0xF8006158, 0x000FFFFF, 0x00000083),
|
|
||||||
MaskWrite(0xF800615C, 0x000FFFFF, 0x00000080),
|
|
||||||
MaskWrite(0xF8006160, 0x000FFFFF, 0x00000080),
|
|
||||||
MaskWrite(0xF8006168, 0x001FFFFF, 0x00000124),
|
|
||||||
MaskWrite(0xF800616C, 0x001FFFFF, 0x00000125),
|
|
||||||
MaskWrite(0xF8006170, 0x001FFFFF, 0x00000112),
|
|
||||||
MaskWrite(0xF8006174, 0x001FFFFF, 0x00000116),
|
|
||||||
MaskWrite(0xF800617C, 0x000FFFFF, 0x000000C3),
|
|
||||||
MaskWrite(0xF8006180, 0x000FFFFF, 0x000000C3),
|
|
||||||
MaskWrite(0xF8006184, 0x000FFFFF, 0x000000C0),
|
|
||||||
MaskWrite(0xF8006188, 0x000FFFFF, 0x000000C0),
|
|
||||||
MaskWrite(0xF8006190, 0xFFFFFFFF, 0x10040080),
|
|
||||||
MaskWrite(0xF8006194, 0x000FFFFF, 0x0001FC82),
|
|
||||||
MaskWrite(0xF8006204, 0xFFFFFFFF, 0x00000000),
|
|
||||||
MaskWrite(0xF8006208, 0x000F03FF, 0x000803FF),
|
|
||||||
MaskWrite(0xF800620C, 0x000F03FF, 0x000803FF),
|
|
||||||
MaskWrite(0xF8006210, 0x000F03FF, 0x000803FF),
|
|
||||||
MaskWrite(0xF8006214, 0x000F03FF, 0x000803FF),
|
|
||||||
MaskWrite(0xF8006218, 0x000F03FF, 0x000003FF),
|
|
||||||
MaskWrite(0xF800621C, 0x000F03FF, 0x000003FF),
|
|
||||||
MaskWrite(0xF8006220, 0x000F03FF, 0x000003FF),
|
|
||||||
MaskWrite(0xF8006224, 0x000F03FF, 0x000003FF),
|
|
||||||
MaskWrite(0xF80062A8, 0x00000FF7, 0x00000000),
|
|
||||||
MaskWrite(0xF80062AC, 0xFFFFFFFF, 0x00000000),
|
|
||||||
MaskWrite(0xF80062B0, 0x003FFFFF, 0x00005125),
|
|
||||||
MaskWrite(0xF80062B4, 0x0003FFFF, 0x000012A8),
|
|
||||||
MaskPoll(0xF8000B74, 0x00002000),
|
|
||||||
MaskWrite(0xF8006000, 0x0001FFFF, 0x00000081),
|
|
||||||
MaskPoll(0xF8006054, 0x00000007),
|
|
||||||
// ps7_peripherals_init_data_1_0
|
|
||||||
MaskWrite(0xF8000B48, 0x00000180, 0x00000180),
|
|
||||||
MaskWrite(0xF8000B4C, 0x00000180, 0x00000180),
|
|
||||||
MaskWrite(0xF8000B50, 0x00000180, 0x00000180),
|
|
||||||
MaskWrite(0xF8000B54, 0x00000180, 0x00000180),
|
|
||||||
MaskWrite(0xE0001034, 0x000000FF, 0x00000006),
|
|
||||||
MaskWrite(0xE0001018, 0x0000FFFF, 0x0000003E),
|
|
||||||
MaskWrite(0xE0001000, 0x000001FF, 0x00000017),
|
|
||||||
MaskWrite(0xE0001004, 0x00000FFF, 0x00000020),
|
|
||||||
MaskWrite(0xE000D000, 0x00080000, 0x00080000),
|
|
||||||
MaskWrite(0xF8007000, 0x20000000, 0x00000000),
|
|
||||||
];
|
|
|
@ -103,21 +103,19 @@ impl TcpStream {
|
||||||
|
|
||||||
/// Probe the receive buffer
|
/// Probe the receive buffer
|
||||||
///
|
///
|
||||||
/// Instead of handing you the data on the heap all at once,
|
/// Your callback will only be called when there is some data available,
|
||||||
/// smoltcp's read interface is wrapped so that your callback can
|
/// and it must consume at least one byte. It returns a tuple with the
|
||||||
/// just return `Poll::Pending` if there is not enough data
|
/// number of bytes it consumed, and a user-defined return value of type R.
|
||||||
/// yet. Likewise, return the amount of bytes consumed from the
|
|
||||||
/// buffer in the `Poll::Ready` result.
|
|
||||||
pub async fn recv<F, R>(&self, f: F) -> Result<R>
|
pub async fn recv<F, R>(&self, f: F) -> Result<R>
|
||||||
where
|
where
|
||||||
F: Fn(&[u8]) -> Poll<(usize, R)>,
|
F: Fn(&[u8]) -> (usize, R),
|
||||||
{
|
{
|
||||||
struct Recv<'a, F: FnOnce(&[u8]) -> Poll<(usize, R)>, R> {
|
struct Recv<'a, F: FnOnce(&[u8]) -> (usize, R), R> {
|
||||||
stream: &'a TcpStream,
|
stream: &'a TcpStream,
|
||||||
f: F,
|
f: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, F: Fn(&[u8]) -> Poll<(usize, R)>, R> Future for Recv<'a, F, R> {
|
impl<'a, F: Fn(&[u8]) -> (usize, R), R> Future for Recv<'a, F, R> {
|
||||||
type Output = Result<R>;
|
type Output = Result<R>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
@ -128,13 +126,9 @@ impl TcpStream {
|
||||||
|
|
||||||
socket.recv(|buf| {
|
socket.recv(|buf| {
|
||||||
if buf.len() > 0 {
|
if buf.len() > 0 {
|
||||||
match (self.f)(buf) {
|
let (amount, result) = (self.f)(buf);
|
||||||
Poll::Ready((amount, result)) =>
|
assert!(amount > 0);
|
||||||
(amount, Poll::Ready(Ok(result))),
|
(amount, Poll::Ready(Ok(result)))
|
||||||
Poll::Pending =>
|
|
||||||
// 0 bytes consumed
|
|
||||||
(0, Poll::Pending),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
(0, Poll::Pending)
|
(0, Poll::Pending)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,5 +21,5 @@ libcortex_a9 = { path = "../libcortex_a9" }
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.6"
|
version = "0.6"
|
||||||
features = ["ethernet", "proto-ipv4", "socket-tcp"]
|
features = ["ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"]
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
|
@ -29,6 +29,7 @@ impl DdrRam {
|
||||||
|
|
||||||
let regs = unsafe { regs::RegisterBlock::new() };
|
let regs = unsafe { regs::RegisterBlock::new() };
|
||||||
let mut ddr = DdrRam { regs };
|
let mut ddr = DdrRam { regs };
|
||||||
|
ddr.configure();
|
||||||
ddr.reset_ddrc();
|
ddr.reset_ddrc();
|
||||||
ddr
|
ddr
|
||||||
}
|
}
|
||||||
|
@ -55,14 +56,35 @@ impl DdrRam {
|
||||||
clocks
|
clocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_dci_divisors(clocks: &Clocks) -> (u8, u8) {
|
||||||
|
let target = (DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ;
|
||||||
|
|
||||||
|
let mut best = None;
|
||||||
|
let mut best_error = 0;
|
||||||
|
for divisor0 in 1..63 {
|
||||||
|
for divisor1 in 1..63 {
|
||||||
|
let current = (divisor0 as u32) * (divisor1 as u32);
|
||||||
|
let error = if current > target {
|
||||||
|
current - target
|
||||||
|
} else {
|
||||||
|
target - current
|
||||||
|
};
|
||||||
|
if best.is_none() || best_error > error {
|
||||||
|
best = Some((divisor0, divisor1));
|
||||||
|
best_error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
best.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Zynq-7000 AP SoC Technical Reference Manual:
|
/// Zynq-7000 AP SoC Technical Reference Manual:
|
||||||
/// 10.6.2 DDR IOB Impedance Calibration
|
/// 10.6.2 DDR IOB Impedance Calibration
|
||||||
fn calibrate_iob_impedance(clocks: &Clocks) {
|
fn calibrate_iob_impedance(clocks: &Clocks) {
|
||||||
let divisor0 = ((DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ)
|
let (divisor0, divisor1) = Self::calculate_dci_divisors(clocks);
|
||||||
.max(1).min(63) as u8;
|
debug!("DDR DCI clock: {} Hz (divisors={}*{})",
|
||||||
let divisor1 = ((DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ / u32::from(divisor0))
|
clocks.ddr / u32::from(divisor0) / u32::from(divisor1),
|
||||||
.max(1).min(63) as u8;
|
divisor0, divisor1);
|
||||||
debug!("DDR DCI clock: {} Hz", clocks.ddr / u32::from(divisor0) / u32::from(divisor1));
|
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
// Step 1.
|
// Step 1.
|
||||||
|
@ -181,6 +203,82 @@ impl DdrRam {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn configure(&mut self) {
|
||||||
|
self.regs.dram_param0.write(
|
||||||
|
regs::DramParam0::zeroed()
|
||||||
|
.t_rc(0x1b)
|
||||||
|
.t_rfc_min(0x56)
|
||||||
|
.post_selfref_gap_x32(0x10)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.regs.dram_param2.write(
|
||||||
|
regs::DramParam2::zeroed()
|
||||||
|
.write_latency(0x5)
|
||||||
|
.rd2wr(0x7)
|
||||||
|
.wr2rd(0xe)
|
||||||
|
.t_xp(0x4)
|
||||||
|
.pad_pd(0x0)
|
||||||
|
.rd2pre(0x4)
|
||||||
|
.t_rcd(0x7)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.regs.dram_emr_mr.write(
|
||||||
|
regs::DramEmrMr::zeroed()
|
||||||
|
.mr(0x930)
|
||||||
|
.emr(0x4)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.regs.phy_cmd_timeout_rddata_cpt.modify(
|
||||||
|
|_, w| w
|
||||||
|
.rd_cmd_to_data(0x0)
|
||||||
|
.wr_cmd_to_data(0x0)
|
||||||
|
.we_to_re_delay(0x8)
|
||||||
|
.rdc_fifo_rst_disable(false)
|
||||||
|
.use_fixed_re(true)
|
||||||
|
.rdc_fifo_rst_err_cnt_clr(false)
|
||||||
|
.dis_phy_ctrl_rstn(false)
|
||||||
|
.clk_stall_level(false)
|
||||||
|
.gatelvl_num_of_dq0(0x7)
|
||||||
|
.wrlvl_num_of_dq0(0x7)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.regs.reg_2c.write(
|
||||||
|
regs::Reg2C::zeroed()
|
||||||
|
.wrlvl_max_x1024(0xfff)
|
||||||
|
.rdlvl_max_x1024(0xfff)
|
||||||
|
.twrlvl_max_error(false)
|
||||||
|
.trdlvl_max_error(false)
|
||||||
|
.dfi_wr_level_en(true)
|
||||||
|
.dfi_rd_dqs_gate_level(true)
|
||||||
|
.dfi_rd_data_eye_train(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.regs.dfi_timing.write(
|
||||||
|
regs::DfiTiming::zeroed()
|
||||||
|
.rddata_en(0x6)
|
||||||
|
.ctrlup_min(0x3)
|
||||||
|
.ctrlup_max(0x40)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.regs.phy_init_ratio3.write(
|
||||||
|
regs::PhyInitRatio::zeroed()
|
||||||
|
.wrlvl_init_ratio(0x21)
|
||||||
|
.gatelvl_init_ratio(0xee)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.regs.reg_65.write(
|
||||||
|
regs::Reg65::zeroed()
|
||||||
|
.wr_rl_delay(0x2)
|
||||||
|
.rd_rl_delay(0x4)
|
||||||
|
.dll_lock_diff(0xf)
|
||||||
|
.use_wr_level(true)
|
||||||
|
.use_rd_dqs_gate_level(true)
|
||||||
|
.use_rd_data_eye_level(true)
|
||||||
|
.dis_calib_rst(false)
|
||||||
|
.ctrl_slave_delay(0x0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Reset DDR controller
|
/// Reset DDR controller
|
||||||
fn reset_ddrc(&mut self) {
|
fn reset_ddrc(&mut self) {
|
||||||
#[cfg(feature = "target_zc706")]
|
#[cfg(feature = "target_zc706")]
|
||||||
|
@ -210,7 +308,7 @@ impl DdrRam {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status(&self) -> regs::ControllerStatus {
|
pub fn status(&self) -> regs::ControllerStatus {
|
||||||
self.regs.mode_sts_reg.read().operating_mode()
|
self.regs.mode_sts.read().operating_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ptr<T>(&mut self) -> *mut T {
|
pub fn ptr<T>(&mut self) -> *mut T {
|
||||||
|
@ -239,7 +337,7 @@ impl DdrRam {
|
||||||
|
|
||||||
for megabyte in 0..slice.len() / (1024 * 1024) {
|
for megabyte in 0..slice.len() / (1024 * 1024) {
|
||||||
let start = megabyte * 1024 * 1024 / 4;
|
let start = megabyte * 1024 * 1024 / 4;
|
||||||
let end = ((megabyte + 1) * 1024 * 1024 / 4);
|
let end = (megabyte + 1) * 1024 * 1024 / 4;
|
||||||
for b in slice[start..end].iter_mut() {
|
for b in slice[start..end].iter_mut() {
|
||||||
expected.map(|expected| {
|
expected.map(|expected| {
|
||||||
let read: u32 = *b;
|
let read: u32 = *b;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use volatile_register::{RO, RW};
|
use volatile_register::{RO, RW};
|
||||||
|
|
||||||
use libregister::{register, register_bit, register_bits_typed};
|
use libregister::{register, register_bit, register_bits, register_bits_typed};
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -29,59 +29,59 @@ pub enum ControllerStatus {
|
||||||
pub struct RegisterBlock {
|
pub struct RegisterBlock {
|
||||||
pub ddrc_ctrl: DdrcCtrl,
|
pub ddrc_ctrl: DdrcCtrl,
|
||||||
pub two_rank_cfg: RW<u32>,
|
pub two_rank_cfg: RW<u32>,
|
||||||
pub hpr_reg: RW<u32>,
|
pub hpr: RW<u32>,
|
||||||
pub lpr_reg: RW<u32>,
|
pub lpr: RW<u32>,
|
||||||
pub wr_reg: RW<u32>,
|
pub wr: RW<u32>,
|
||||||
pub dram_param_reg0: RW<u32>,
|
pub dram_param0: DramParam0,
|
||||||
pub dram_param_reg1: RW<u32>,
|
pub dram_param1: RW<u32>,
|
||||||
pub dram_param_reg2: RW<u32>,
|
pub dram_param2: DramParam2,
|
||||||
pub dram_param_reg3: RW<u32>,
|
pub dram_param3: RW<u32>,
|
||||||
pub dram_param_reg4: RW<u32>,
|
pub dram_param4: RW<u32>,
|
||||||
pub dram_init_param: RW<u32>,
|
pub dram_init_param: RW<u32>,
|
||||||
pub dram_emr_reg: RW<u32>,
|
pub dram_emr: RW<u32>,
|
||||||
pub dram_emr_mr_reg: RW<u32>,
|
pub dram_emr_mr: DramEmrMr,
|
||||||
pub dram_burst8_rdwr: RW<u32>,
|
pub dram_burst8_rdwr: RW<u32>,
|
||||||
pub dram_disable_dq: RW<u32>,
|
pub dram_disable_dq: RW<u32>,
|
||||||
pub dram_addr_map_bank: RW<u32>,
|
pub dram_addr_map_bank: RW<u32>,
|
||||||
pub dram_addr_map_col: RW<u32>,
|
pub dram_addr_map_col: RW<u32>,
|
||||||
pub dram_addr_map_row: RW<u32>,
|
pub dram_addr_map_row: RW<u32>,
|
||||||
pub dram_odt_reg: RW<u32>,
|
pub dram_odt: RW<u32>,
|
||||||
pub phy_dbg_reg: RW<u32>,
|
pub phy_dbg: RW<u32>,
|
||||||
pub phy_cmd_timeout_rddata_cpt: RW<u32>,
|
pub phy_cmd_timeout_rddata_cpt: PhyCmdTimeoutRddataCpt,
|
||||||
pub mode_sts_reg: ModeStsReg,
|
pub mode_sts: ModeStsReg,
|
||||||
pub dll_calib: RW<u32>,
|
pub dll_calib: RW<u32>,
|
||||||
pub odt_delay_hold: RW<u32>,
|
pub odt_delay_hold: RW<u32>,
|
||||||
pub ctrl_reg1: RW<u32>,
|
pub ctrl1: RW<u32>,
|
||||||
pub ctrl_reg2: RW<u32>,
|
pub ctrl2: RW<u32>,
|
||||||
pub ctrl_reg3: RW<u32>,
|
pub ctrl3: RW<u32>,
|
||||||
pub ctrl_reg4: RW<u32>,
|
pub ctrl4: RW<u32>,
|
||||||
_unused0: [RO<u32>; 2],
|
_unused0: [RO<u32>; 2],
|
||||||
pub ctrl_reg5: RW<u32>,
|
pub ctrl5: RW<u32>,
|
||||||
pub ctrl_reg6: RW<u32>,
|
pub ctrl6: RW<u32>,
|
||||||
_unused1: [RO<u32>; 8],
|
_unused1: [RO<u32>; 8],
|
||||||
pub che_refresh_timer01: RW<u32>,
|
pub che_refresh_timer01: RW<u32>,
|
||||||
pub che_t_zq: RW<u32>,
|
pub che_t_zq: RW<u32>,
|
||||||
pub che_t_zq_short_interval_reg: RW<u32>,
|
pub che_t_zq_short_interval: RW<u32>,
|
||||||
pub deep_pwrdwn_reg: RW<u32>,
|
pub deep_pwrdwn: RW<u32>,
|
||||||
pub reg_2c: RW<u32>,
|
pub reg_2c: Reg2C,
|
||||||
pub reg_2d: RW<u32>,
|
pub reg_2d: RW<u32>,
|
||||||
pub dfi_timing: RW<u32>,
|
pub dfi_timing: DfiTiming,
|
||||||
_unused2: [RO<u32>; 2],
|
_unused2: [RO<u32>; 2],
|
||||||
pub che_ecc_control_reg_offset: RW<u32>,
|
pub che_ecc_control_offset: RW<u32>,
|
||||||
pub che_corr_ecc_log_reg_offset: RW<u32>,
|
pub che_corr_ecc_log_offset: RW<u32>,
|
||||||
pub che_corr_ecc_addr_reg_offset: RW<u32>,
|
pub che_corr_ecc_addr_offset: RW<u32>,
|
||||||
pub che_corr_ecc_data_31_0_reg_offset: RW<u32>,
|
pub che_corr_ecc_data_31_0_offset: RW<u32>,
|
||||||
pub che_corr_ecc_data_63_32_reg_offset: RW<u32>,
|
pub che_corr_ecc_data_63_32_offset: RW<u32>,
|
||||||
pub che_corr_ecc_data_71_64_reg_offset: RW<u32>,
|
pub che_corr_ecc_data_71_64_offset: RW<u32>,
|
||||||
pub che_uncorr_ecc_log_reg_offset: RW<u32>,
|
pub che_uncorr_ecc_log_offset: RW<u32>,
|
||||||
pub che_uncorr_ecc_addr_reg_offset: RW<u32>,
|
pub che_uncorr_ecc_addr_offset: RW<u32>,
|
||||||
pub che_uncorr_ecc_data_31_0_reg_offset: RW<u32>,
|
pub che_uncorr_ecc_data_31_0_offset: RW<u32>,
|
||||||
pub che_uncorr_ecc_data_63_32_reg_offset: RW<u32>,
|
pub che_uncorr_ecc_data_63_32_offset: RW<u32>,
|
||||||
pub che_uncorr_ecc_data_71_64_reg_offset: RW<u32>,
|
pub che_uncorr_ecc_data_71_64_offset: RW<u32>,
|
||||||
pub che_ecc_stats_reg_offset: RW<u32>,
|
pub che_ecc_stats_offset: RW<u32>,
|
||||||
pub ecc_scrub: 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_31_0_offset: RW<u32>,
|
||||||
pub che_ecc_corr_bit_mask_63_32_reg_offset: RW<u32>,
|
pub che_ecc_corr_bit_mask_63_32_offset: RW<u32>,
|
||||||
_unused3: [RO<u32>; 5],
|
_unused3: [RO<u32>; 5],
|
||||||
pub phy_rcvr_enable: RW<u32>,
|
pub phy_rcvr_enable: RW<u32>,
|
||||||
pub phy_config0: RW<u32>,
|
pub phy_config0: RW<u32>,
|
||||||
|
@ -89,10 +89,10 @@ pub struct RegisterBlock {
|
||||||
pub phy_config2: RW<u32>,
|
pub phy_config2: RW<u32>,
|
||||||
pub phy_config3: RW<u32>,
|
pub phy_config3: RW<u32>,
|
||||||
_unused4: RO<u32>,
|
_unused4: RO<u32>,
|
||||||
pub phy_init_ratio0: RW<u32>,
|
pub phy_init_ratio0: PhyInitRatio,
|
||||||
pub phy_init_ratio1: RW<u32>,
|
pub phy_init_ratio1: PhyInitRatio,
|
||||||
pub phy_init_ratio2: RW<u32>,
|
pub phy_init_ratio2: PhyInitRatio,
|
||||||
pub phy_init_ratio3: RW<u32>,
|
pub phy_init_ratio3: PhyInitRatio,
|
||||||
_unused5: RO<u32>,
|
_unused5: RO<u32>,
|
||||||
pub phy_rd_dqs_cfg0: RW<u32>,
|
pub phy_rd_dqs_cfg0: RW<u32>,
|
||||||
pub phy_rd_dqs_cfg1: RW<u32>,
|
pub phy_rd_dqs_cfg1: RW<u32>,
|
||||||
|
@ -115,7 +115,7 @@ pub struct RegisterBlock {
|
||||||
pub wr_data_slv3: RW<u32>,
|
pub wr_data_slv3: RW<u32>,
|
||||||
_unused9: RO<u32>,
|
_unused9: RO<u32>,
|
||||||
pub reg_64: RW<u32>,
|
pub reg_64: RW<u32>,
|
||||||
pub reg_65: RW<u32>,
|
pub reg_65: Reg65,
|
||||||
_unused10: [RO<u32>; 3],
|
_unused10: [RO<u32>; 3],
|
||||||
pub reg69_6a0: RW<u32>,
|
pub reg69_6a0: RW<u32>,
|
||||||
pub reg69_6a1: RW<u32>,
|
pub reg69_6a1: RW<u32>,
|
||||||
|
@ -134,7 +134,7 @@ pub struct RegisterBlock {
|
||||||
_unused13: RO<u32>,
|
_unused13: RO<u32>,
|
||||||
pub dll_lock_sts: RW<u32>,
|
pub dll_lock_sts: RW<u32>,
|
||||||
pub phy_ctrl_sts: RW<u32>,
|
pub phy_ctrl_sts: RW<u32>,
|
||||||
pub phy_ctrl_sts_reg2: RW<u32>,
|
pub phy_ctrl_sts2: RW<u32>,
|
||||||
_unused14: [RO<u32>; 5],
|
_unused14: [RO<u32>; 5],
|
||||||
pub axi_id: RW<u32>,
|
pub axi_id: RW<u32>,
|
||||||
pub page_mask: RW<u32>,
|
pub page_mask: RW<u32>,
|
||||||
|
@ -172,6 +172,64 @@ register_bit!(ddrc_ctrl, powerdown_en, 1);
|
||||||
register_bits_typed!(ddrc_ctrl, data_bus_width, u8, DataBusWidth, 2, 3);
|
register_bits_typed!(ddrc_ctrl, data_bus_width, u8, DataBusWidth, 2, 3);
|
||||||
// (ddrc_ctrl) ...
|
// (ddrc_ctrl) ...
|
||||||
|
|
||||||
|
register!(dram_param0, DramParam0, RW, u32);
|
||||||
|
register_bits!(dram_param0, t_rc, u8, 0, 5);
|
||||||
|
register_bits!(dram_param0, t_rfc_min, u8, 6, 13);
|
||||||
|
register_bits!(dram_param0, post_selfref_gap_x32, u8, 14, 20);
|
||||||
|
|
||||||
|
register!(dram_param2, DramParam2, RW, u32);
|
||||||
|
register_bits!(dram_param2, write_latency, u8, 0, 4);
|
||||||
|
register_bits!(dram_param2, rd2wr, u8, 5, 9);
|
||||||
|
register_bits!(dram_param2, wr2rd, u8, 10, 14);
|
||||||
|
register_bits!(dram_param2, t_xp, u8, 15, 19);
|
||||||
|
register_bits!(dram_param2, pad_pd, u8, 20, 22);
|
||||||
|
register_bits!(dram_param2, rd2pre, u8, 23, 27);
|
||||||
|
register_bits!(dram_param2, t_rcd, u8, 28, 31);
|
||||||
|
|
||||||
|
register!(dram_emr_mr, DramEmrMr, RW, u32);
|
||||||
|
register_bits!(dram_emr_mr, mr, u16, 0, 15);
|
||||||
|
register_bits!(dram_emr_mr, emr, u16, 16, 31);
|
||||||
|
|
||||||
|
register!(phy_cmd_timeout_rddata_cpt, PhyCmdTimeoutRddataCpt, RW, u32);
|
||||||
|
register_bits!(phy_cmd_timeout_rddata_cpt, rd_cmd_to_data, u8, 0, 3);
|
||||||
|
register_bits!(phy_cmd_timeout_rddata_cpt, wr_cmd_to_data, u8, 4, 7);
|
||||||
|
register_bits!(phy_cmd_timeout_rddata_cpt, we_to_re_delay, u8, 8, 11);
|
||||||
|
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_disable, 15);
|
||||||
|
register_bit!(phy_cmd_timeout_rddata_cpt, use_fixed_re, 16);
|
||||||
|
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_err_cnt_clr, 17);
|
||||||
|
register_bit!(phy_cmd_timeout_rddata_cpt, dis_phy_ctrl_rstn, 18);
|
||||||
|
register_bit!(phy_cmd_timeout_rddata_cpt, clk_stall_level, 19);
|
||||||
|
register_bits!(phy_cmd_timeout_rddata_cpt, gatelvl_num_of_dq0, u8, 24, 27);
|
||||||
|
register_bits!(phy_cmd_timeout_rddata_cpt, wrlvl_num_of_dq0, u8, 28, 31);
|
||||||
|
|
||||||
|
register!(reg_2c, Reg2C, RW, u32);
|
||||||
|
register_bits!(reg_2c, wrlvl_max_x1024, u16, 0, 11);
|
||||||
|
register_bits!(reg_2c, rdlvl_max_x1024, u16, 12, 23);
|
||||||
|
register_bit!(reg_2c, twrlvl_max_error, 24);
|
||||||
|
register_bit!(reg_2c, trdlvl_max_error, 25);
|
||||||
|
register_bit!(reg_2c, dfi_wr_level_en, 26);
|
||||||
|
register_bit!(reg_2c, dfi_rd_dqs_gate_level, 27);
|
||||||
|
register_bit!(reg_2c, dfi_rd_data_eye_train, 28);
|
||||||
|
|
||||||
|
register!(dfi_timing, DfiTiming, RW, u32);
|
||||||
|
register_bits!(dfi_timing, rddata_en, u8, 0, 4);
|
||||||
|
register_bits!(dfi_timing, ctrlup_min, u16, 5, 14);
|
||||||
|
register_bits!(dfi_timing, ctrlup_max, u16, 15, 24);
|
||||||
|
|
||||||
|
register!(phy_init_ratio, PhyInitRatio, RW, u32);
|
||||||
|
register_bits!(phy_init_ratio, wrlvl_init_ratio, u16, 0, 9);
|
||||||
|
register_bits!(phy_init_ratio, gatelvl_init_ratio, u16, 10, 19);
|
||||||
|
|
||||||
|
register!(reg_65, Reg65, RW, u32);
|
||||||
|
register_bits!(reg_65, wr_rl_delay, u8, 0, 4);
|
||||||
|
register_bits!(reg_65, rd_rl_delay, u8, 5, 9);
|
||||||
|
register_bits!(reg_65, dll_lock_diff, u8, 10, 13);
|
||||||
|
register_bit!(reg_65, use_wr_level, 14);
|
||||||
|
register_bit!(reg_65, use_rd_dqs_gate_level, 15);
|
||||||
|
register_bit!(reg_65, use_rd_data_eye_level, 16);
|
||||||
|
register_bit!(reg_65, dis_calib_rst, 17);
|
||||||
|
register_bits!(reg_65, ctrl_slave_delay, u8, 18, 19);
|
||||||
|
|
||||||
// Controller operation mode status
|
// Controller operation mode status
|
||||||
register!(mode_sts_reg,
|
register!(mode_sts_reg,
|
||||||
ModeStsReg, RO, u32);
|
ModeStsReg, RO, u32);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use super::clocks::Clocks;
|
|
||||||
use super::time::Milliseconds;
|
use super::time::Milliseconds;
|
||||||
use crate::slcr;
|
use crate::slcr;
|
||||||
use embedded_hal::timer::CountDown;
|
use embedded_hal::timer::CountDown;
|
||||||
|
@ -11,7 +10,7 @@ mod regs;
|
||||||
pub struct DevC {
|
pub struct DevC {
|
||||||
regs: &'static mut regs::RegisterBlock,
|
regs: &'static mut regs::RegisterBlock,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
count_down: super::timer::global::CountDown,
|
count_down: super::timer::global::CountDown<Milliseconds>,
|
||||||
timeout_ms: Milliseconds,
|
timeout_ms: Milliseconds,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
//! PrimeCell DMA Controller (PL330)
|
|
||||||
|
|
||||||
mod regs;
|
|
|
@ -1,386 +0,0 @@
|
||||||
use libregister::{
|
|
||||||
register, register_at,
|
|
||||||
register_bit, register_bits, register_bits_typed,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct RegisterBlock {
|
|
||||||
pub ds: Ds,
|
|
||||||
pub dpc: DPc,
|
|
||||||
pub inten: Inten,
|
|
||||||
pub es: Es,
|
|
||||||
pub intstatus: IntStatus,
|
|
||||||
pub intclr: IntClr,
|
|
||||||
pub fsm: Fsm,
|
|
||||||
pub fsc: Fsc,
|
|
||||||
pub ftm: Ftm,
|
|
||||||
pub ftc: [Ftc; 8],
|
|
||||||
pub cs0: Cs,
|
|
||||||
pub cpc0: Cpc,
|
|
||||||
pub cs1: Cs,
|
|
||||||
pub cpc1: Cpc,
|
|
||||||
pub cs2: Cs,
|
|
||||||
pub cpc2: Cpc,
|
|
||||||
pub cs3: Cs,
|
|
||||||
pub cpc3: Cpc,
|
|
||||||
pub cs4: Cs,
|
|
||||||
pub cpc4: Cpc,
|
|
||||||
pub cs5: Cs,
|
|
||||||
pub cpc5: Cpc,
|
|
||||||
pub cs6: Cs,
|
|
||||||
pub cpc6: Cpc,
|
|
||||||
pub cs7: Cs,
|
|
||||||
pub cpc7: Cpc,
|
|
||||||
pub sa0: Sa,
|
|
||||||
pub da0: Da,
|
|
||||||
pub cc0: Cc,
|
|
||||||
pub lc0_0: Lc,
|
|
||||||
pub lc0_1: Lc,
|
|
||||||
pub sa1: Sa,
|
|
||||||
pub da1: Da,
|
|
||||||
pub cc1: Cc,
|
|
||||||
pub lc1_0: Lc,
|
|
||||||
pub lc1_1: Lc,
|
|
||||||
pub sa2: Sa,
|
|
||||||
pub da2: Da,
|
|
||||||
pub cc2: Cc,
|
|
||||||
pub lc2_0: Lc,
|
|
||||||
pub lc2_1: Lc,
|
|
||||||
pub sa3: Sa,
|
|
||||||
pub da3: Da,
|
|
||||||
pub cc3: Cc,
|
|
||||||
pub lc3_0: Lc,
|
|
||||||
pub lc3_1: Lc,
|
|
||||||
pub sa4: Sa,
|
|
||||||
pub da4: Da,
|
|
||||||
pub cc4: Cc,
|
|
||||||
pub lc4_0: Lc,
|
|
||||||
pub lc4_1: Lc,
|
|
||||||
pub sa5: Sa,
|
|
||||||
pub da5: Da,
|
|
||||||
pub cc5: Cc,
|
|
||||||
pub lc5_0: Lc,
|
|
||||||
pub lc5_1: Lc,
|
|
||||||
pub sa6: Sa,
|
|
||||||
pub da6: Da,
|
|
||||||
pub cc6: Cc,
|
|
||||||
pub lc6_0: Lc,
|
|
||||||
pub lc6_1: Lc,
|
|
||||||
pub sa7: Sa,
|
|
||||||
pub da7: Da,
|
|
||||||
pub cc7: Cc,
|
|
||||||
pub lc7_0: Lc,
|
|
||||||
pub lc7_1: Lc,
|
|
||||||
pub dbgstatus: DbgStatus,
|
|
||||||
pub dbgcmd: DbgCmd,
|
|
||||||
pub dbginst0: DbgInst0,
|
|
||||||
pub dbginst1: DbgInst1,
|
|
||||||
pub cr0: Cr0,
|
|
||||||
pub cr1: Cr1,
|
|
||||||
pub cr2: Cr2,
|
|
||||||
pub cr3: Cr3,
|
|
||||||
pub cr4: Cr4,
|
|
||||||
pub crdn: Crdn,
|
|
||||||
pub wd: Wd,
|
|
||||||
pub periph_id_0: PeriphId0,
|
|
||||||
pub periph_id_1: PeriphId1,
|
|
||||||
pub periph_id_2: PeriphId2,
|
|
||||||
pub periph_id_3: PeriphId3,
|
|
||||||
pub pcell_id_0: PCellId0,
|
|
||||||
pub pcell_id_1: PCellId1,
|
|
||||||
pub pcell_id_2: PCellId2,
|
|
||||||
pub pcell_id_3: PCellId3,
|
|
||||||
}
|
|
||||||
|
|
||||||
register_at!(RegisterBlock, 0xF8004000, dmac0_ns);
|
|
||||||
register_at!(RegisterBlock, 0xF8003000, dmac0_s);
|
|
||||||
|
|
||||||
impl RegisterBlock {
|
|
||||||
pub fn channel_regs(&mut self, channel: usize) -> Option<ChannelRegisters>
|
|
||||||
{
|
|
||||||
match channel {
|
|
||||||
0 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[0],
|
|
||||||
cs: &mut self.cs0,
|
|
||||||
cpc: &mut self.cpc0,
|
|
||||||
sa: &mut self.sa0,
|
|
||||||
da: &mut self.da0,
|
|
||||||
cc: &mut self.cc0,
|
|
||||||
lc: [&mut self.lc0_0, &mut self.lc0_1],
|
|
||||||
}),
|
|
||||||
1 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[1],
|
|
||||||
cs: &mut self.cs1,
|
|
||||||
cpc: &mut self.cpc1,
|
|
||||||
sa: &mut self.sa1,
|
|
||||||
da: &mut self.da1,
|
|
||||||
cc: &mut self.cc1,
|
|
||||||
lc: [&mut self.lc1_0, &mut self.lc1_1],
|
|
||||||
}),
|
|
||||||
2 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[2],
|
|
||||||
cs: &mut self.cs2,
|
|
||||||
cpc: &mut self.cpc2,
|
|
||||||
sa: &mut self.sa2,
|
|
||||||
da: &mut self.da2,
|
|
||||||
cc: &mut self.cc2,
|
|
||||||
lc: [&mut self.lc2_0, &mut self.lc2_1],
|
|
||||||
}),
|
|
||||||
3 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[3],
|
|
||||||
cs: &mut self.cs3,
|
|
||||||
cpc: &mut self.cpc3,
|
|
||||||
sa: &mut self.sa3,
|
|
||||||
da: &mut self.da3,
|
|
||||||
cc: &mut self.cc3,
|
|
||||||
lc: [&mut self.lc3_0, &mut self.lc3_1],
|
|
||||||
}),
|
|
||||||
4 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[4],
|
|
||||||
cs: &mut self.cs4,
|
|
||||||
cpc: &mut self.cpc4,
|
|
||||||
sa: &mut self.sa4,
|
|
||||||
da: &mut self.da4,
|
|
||||||
cc: &mut self.cc4,
|
|
||||||
lc: [&mut self.lc4_0, &mut self.lc4_1],
|
|
||||||
}),
|
|
||||||
5 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[5],
|
|
||||||
cs: &mut self.cs5,
|
|
||||||
cpc: &mut self.cpc5,
|
|
||||||
sa: &mut self.sa5,
|
|
||||||
da: &mut self.da5,
|
|
||||||
cc: &mut self.cc5,
|
|
||||||
lc: [&mut self.lc5_0, &mut self.lc5_1],
|
|
||||||
}),
|
|
||||||
6 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[6],
|
|
||||||
cs: &mut self.cs6,
|
|
||||||
cpc: &mut self.cpc6,
|
|
||||||
sa: &mut self.sa6,
|
|
||||||
da: &mut self.da6,
|
|
||||||
cc: &mut self.cc6,
|
|
||||||
lc: [&mut self.lc6_0, &mut self.lc6_1],
|
|
||||||
}),
|
|
||||||
7 => Some(ChannelRegisters {
|
|
||||||
ftc: &mut self.ftc[7],
|
|
||||||
cs: &mut self.cs7,
|
|
||||||
cpc: &mut self.cpc7,
|
|
||||||
sa: &mut self.sa7,
|
|
||||||
da: &mut self.da7,
|
|
||||||
cc: &mut self.cc7,
|
|
||||||
lc: [&mut self.lc7_0, &mut self.lc7_1],
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ChannelRegisters<'a> {
|
|
||||||
ftc: &'a mut Ftc,
|
|
||||||
cs: &'a mut Cs,
|
|
||||||
cpc: &'a mut Cpc,
|
|
||||||
sa: &'a mut Sa,
|
|
||||||
da: &'a mut Da,
|
|
||||||
cc: &'a mut Cc,
|
|
||||||
lc: [&'a mut Lc; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum WakeUpEvent{
|
|
||||||
// @missing: there's a binary prefix ahead of this as per TRM 1173 Wakeup_event
|
|
||||||
Event0 = 0b0000,
|
|
||||||
Event1 = 0b0001,
|
|
||||||
Event2 = 0b0010,
|
|
||||||
Event3 = 0b0011,
|
|
||||||
Event4 = 0b0100,
|
|
||||||
Event5 = 0b0101,
|
|
||||||
Event6 = 0b0110,
|
|
||||||
Event7 = 0b0111,
|
|
||||||
Event8 = 0b1000,
|
|
||||||
Event9 = 0b1001,
|
|
||||||
Event10 = 0b1010,
|
|
||||||
Event11 = 0b1011,
|
|
||||||
Event12 = 0b1100,
|
|
||||||
Event13 = 0b1101,
|
|
||||||
Event14 = 0b1110,
|
|
||||||
Event15 = 0b1111,
|
|
||||||
}
|
|
||||||
#[allow(unused)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum DMAStatus{
|
|
||||||
Stopped = 0b0000,
|
|
||||||
Executing = 0b0001,
|
|
||||||
CacheMiss = 0b0010,
|
|
||||||
UpdatingPc = 0b0011,
|
|
||||||
WaitingForEvent = 0b0100,
|
|
||||||
Reserved0 = 0b0101,
|
|
||||||
Reserved1 = 0b0110,
|
|
||||||
Reserved2 = 0b0111,
|
|
||||||
Reserved3 = 0b1000,
|
|
||||||
Reserved4 = 0b1001,
|
|
||||||
Reserved5 = 0b1010,
|
|
||||||
Reserved6 = 0b1011,
|
|
||||||
Reserved7 = 0b1100,
|
|
||||||
Reserved8 = 0b1101,
|
|
||||||
Reserved9 = 0b1110,
|
|
||||||
Faulting = 0b1111,
|
|
||||||
}
|
|
||||||
|
|
||||||
register!(ds, Ds, RW, u32);
|
|
||||||
register_bit!(ds, dns, 9);
|
|
||||||
register_bits_typed!(ds, wakeup_event, u8, WakeUpEvent, 4, 8);
|
|
||||||
register_bits_typed!(ds, dma_status, u8, DMAStatus, 0, 3);
|
|
||||||
|
|
||||||
register!(dpc, DPc, RW, u32);
|
|
||||||
register_bits!(dpc, pc_mgr, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(inten, Inten, RW, u32);
|
|
||||||
register_bits!(inten, event_irq_select, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(es, Es, RW, u32);
|
|
||||||
register_bits!(es, dmasev_active, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(intstatus, IntStatus, RW, u32);
|
|
||||||
register_bits!(intstatus, irq_status, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(intclr, IntClr, RW, u32);
|
|
||||||
register_bits!(intstatus, irq_clr, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(fsm, Fsm, RW, u32);
|
|
||||||
register_bit!(fsm, fs_mgr, 0);
|
|
||||||
|
|
||||||
register!(fsc, Fsc, RW, u32);
|
|
||||||
register_bits!(fsc, fault_status, u8, 0, 7);
|
|
||||||
|
|
||||||
register!(ftm, Ftm, RW, u32);
|
|
||||||
register_bit!(ftm, dbg_instr, 30);
|
|
||||||
register_bit!(ftm, instr_fetch_err, 16);
|
|
||||||
register_bit!(ftm, mgr_evnt_err, 5);
|
|
||||||
register_bit!(ftm, dmago_err, 4);
|
|
||||||
register_bit!(ftm, operand_invalid, 1);
|
|
||||||
register_bit!(ftm, undef_instr, 0);
|
|
||||||
|
|
||||||
register!(ftc, Ftc, RW, u32);
|
|
||||||
register_bit!(ftc, lockup_err, 31);
|
|
||||||
register_bit!(ftc, dbg_instr, 30);
|
|
||||||
register_bit!(ftc, data_read_err, 18);
|
|
||||||
register_bit!(ftc, data_write_err, 17);
|
|
||||||
register_bit!(ftc, instr_fetch_err, 16);
|
|
||||||
register_bit!(ftc, st_data_unavailable, 13);
|
|
||||||
register_bit!(ftc, mfifo_err, 12);
|
|
||||||
register_bit!(ftc, ch_rdwr_err, 7);
|
|
||||||
register_bit!(ftc, ch_periph_err, 6);
|
|
||||||
register_bit!(ftc, ch_evnt_err, 5);
|
|
||||||
register_bit!(ftc, operand_invalid, 1);
|
|
||||||
register_bit!(ftc, undef_instr, 0);
|
|
||||||
|
|
||||||
register!(cs, Cs, RW, u32);
|
|
||||||
register_bit!(cs, cns, 21);
|
|
||||||
register_bit!(cs, dmawfp_periph, 15);
|
|
||||||
register_bit!(cs, dmawfp_b_ns, 14);
|
|
||||||
register_bits!(cs, wakeup_num, u8, 4, 8);
|
|
||||||
register_bits!(cs, channel_status, u8, 0, 3);
|
|
||||||
|
|
||||||
register!(cpc, Cpc, RW, u32);
|
|
||||||
register_bits!(cpc, pc_chnl, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(sa, Sa, RW, u32);
|
|
||||||
register_bits!(sa, src_addr, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(da, Da, RW, u32);
|
|
||||||
register_bits!(da, dest_addr, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(cc, Cc, RW, u32);
|
|
||||||
register_bits!(cc, endian_swap_size, u8, 28, 30);
|
|
||||||
register_bits!(cc, dst_cache_ctrl, u8, 25, 27);
|
|
||||||
register_bits!(cc, dst_prot_ctrl, u8, 22, 24);
|
|
||||||
register_bits!(cc, dst_burst_len, u8, 18, 21);
|
|
||||||
register_bits!(cc, dst_burst_size, u8, 15, 17);
|
|
||||||
register_bit!(cc, dst_inc, 14);
|
|
||||||
register_bits!(cc, src_cache_ctrl, u8, 11, 13);
|
|
||||||
register_bits!(cc, src_prot_ctrl, u8, 8, 10);
|
|
||||||
register_bits!(cc, src_burst_len, u8, 4, 7);
|
|
||||||
register_bits!(cc, src_burst_size, u8, 1, 3);
|
|
||||||
register_bit!(cc, src_inc, 0);
|
|
||||||
|
|
||||||
register!(lc0, Lc, RW, u32);
|
|
||||||
register_bits!(lc0, loop_counter_iteration, u8, 0, 7);
|
|
||||||
|
|
||||||
register!(dbgstatus, DbgStatus, RW, u32);
|
|
||||||
register_bit!(dbgstatus, dbgstatus, 0);
|
|
||||||
|
|
||||||
register!(dbgcmd, DbgCmd, RW, u32);
|
|
||||||
register_bits!(dbgcmd, dbgcmd, u8, 0, 1);
|
|
||||||
|
|
||||||
register!(dbginst0, DbgInst0, RW, u32);
|
|
||||||
register_bits!(dbginst0, instruction_byte1, u8, 24, 31);
|
|
||||||
register_bits!(dbginst0, instruction_byte0, u8, 16, 23);
|
|
||||||
register_bits!(dbginst0, channel_num, u8, 8, 10);
|
|
||||||
register_bit!(dbginst0, debug_thread, 0);
|
|
||||||
|
|
||||||
register!(dbginst1, DbgInst1, RW, u32);
|
|
||||||
register_bits!(dbginst1, instruction_byte5, u8, 24, 31);
|
|
||||||
register_bits!(dbginst1, instruction_byte4, u8, 16, 23);
|
|
||||||
register_bits!(dbginst1, instruction_byte3, u8, 8, 10);
|
|
||||||
register_bits!(dbginst1, instruction_byte2, u8, 0, 7);
|
|
||||||
|
|
||||||
register!(cr0, Cr0, RW, u32);
|
|
||||||
register_bits!(cr0, num_events, u8, 17, 21);
|
|
||||||
register_bits!(cr0, num_periph_req, u8, 12, 16);
|
|
||||||
register_bits!(cr0, num_chnls, u8, 4, 6);
|
|
||||||
register_bit!(cr0, mgr_ns_at_rst, 2);
|
|
||||||
register_bit!(cr0, boot_en, 1);
|
|
||||||
register_bit!(cr0, periph_req, 0);
|
|
||||||
|
|
||||||
register!(cr1, Cr1, RW, u32);
|
|
||||||
register_bits!(cr1, num_icache_lines, u8, 4, 7);
|
|
||||||
register_bits!(cr1, icache_len, u8, 0, 2);
|
|
||||||
|
|
||||||
register!(cr2, Cr2, RW, u32);
|
|
||||||
register_bits!(cr2, boot_addr, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(cr3, Cr3, RW, u32);
|
|
||||||
register_bits!(cr3, ins, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(cr4, Cr4, RW, u32);
|
|
||||||
register_bits!(cr4, ins, u8, 0, 31);
|
|
||||||
|
|
||||||
register!(crdn, Crdn, RW, u32);
|
|
||||||
register_bits!(crdn, data_buffer_dep, u8, 20, 29);
|
|
||||||
register_bits!(crdn, rd_q_dep, u8, 16, 19);
|
|
||||||
register_bits!(crdn, rd_cap, u8, 12, 14);
|
|
||||||
register_bits!(crdn, wr_q_dep, u8, 8, 11);
|
|
||||||
register_bits!(crdn, wr_cap, u8, 4, 6);
|
|
||||||
register_bits!(crdn, data_width, u8, 0, 2);
|
|
||||||
|
|
||||||
register!(wd, Wd, RW, u32);
|
|
||||||
register_bit!(wd, wd_irq_only, 0);
|
|
||||||
|
|
||||||
register!(periph_id_0, PeriphId0, RW, u32);
|
|
||||||
register_bits!(periph_id_0, part_number_0, u8, 0, 7);
|
|
||||||
|
|
||||||
register!(periph_id_1, PeriphId1, RW, u32);
|
|
||||||
register_bits!(periph_id_1, designer_0, u8, 4, 7);
|
|
||||||
register_bits!(periph_id_1, part_number_1, u8, 0, 3);
|
|
||||||
|
|
||||||
register!(periph_id_2, PeriphId2, RW, u32);
|
|
||||||
register_bits!(periph_id_2, revision, u8, 4, 7);
|
|
||||||
register_bits!(periph_id_2, designer_1, u8, 0, 3);
|
|
||||||
|
|
||||||
register!(periph_id_3, PeriphId3, RW, u32);
|
|
||||||
register_bit!(periph_id_3, integration_cfg, 0);
|
|
||||||
|
|
||||||
register!(pcell_id_0, PCellId0, RW, u32);
|
|
||||||
register_bits!(pcell_id_0, pcell_id_0, u8, 0, 7);
|
|
||||||
|
|
||||||
register!(pcell_id_1, PCellId1, RW, u32);
|
|
||||||
register_bits!(pcell_id_1, pcell_id_1, u8, 0, 7);
|
|
||||||
|
|
||||||
register!(pcell_id_2, PCellId2, RW, u32);
|
|
||||||
register_bits!(pcell_id_2, pcell_id_2, u8, 0, 7);
|
|
||||||
|
|
||||||
register!(pcell_id_3, PCellId3, RW, u32);
|
|
||||||
register_bits!(pcell_id_3, pcell_id_3, u8, 0, 7);
|
|
|
@ -1,5 +1,8 @@
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::{
|
||||||
use log::{error, info, warn};
|
marker::PhantomData,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
use log::{debug, info, warn, error};
|
||||||
use libregister::*;
|
use libregister::*;
|
||||||
use super::slcr;
|
use super::slcr;
|
||||||
use super::clocks::Clocks;
|
use super::clocks::Clocks;
|
||||||
|
@ -42,14 +45,109 @@ impl DerefMut for Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Eth<'r, RX, TX> {
|
/// Gigabit Ethernet Peripheral
|
||||||
|
pub trait Gem {
|
||||||
|
fn setup_clock(tx_clock: u32);
|
||||||
|
fn regs() -> &'static mut regs::RegisterBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// first Gigabit Ethernet peripheral
|
||||||
|
pub struct Gem0;
|
||||||
|
|
||||||
|
impl Gem for Gem0 {
|
||||||
|
fn setup_clock(tx_clock: u32) {
|
||||||
|
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
|
||||||
|
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
slcr.gem0_clk_ctrl.write(
|
||||||
|
// 0x0050_0801: 8, 5: 100 Mb/s
|
||||||
|
// ...: 8, 1: 1000 Mb/s
|
||||||
|
slcr::GemClkCtrl::zeroed()
|
||||||
|
.clkact(true)
|
||||||
|
.srcsel(slcr::PllSource::IoPll)
|
||||||
|
.divisor(divisor0 as u8)
|
||||||
|
.divisor1(divisor1 as u8)
|
||||||
|
);
|
||||||
|
// Enable gem0 recv clock
|
||||||
|
slcr.gem0_rclk_ctrl.write(
|
||||||
|
// 0x0000_0801
|
||||||
|
slcr::RclkCtrl::zeroed()
|
||||||
|
.clkact(true)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn regs() -> &'static mut regs::RegisterBlock {
|
||||||
|
regs::RegisterBlock::gem0()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// second Gigabit Ethernet peripheal
|
||||||
|
pub struct Gem1;
|
||||||
|
|
||||||
|
impl Gem for Gem1 {
|
||||||
|
fn setup_clock(tx_clock: u32) {
|
||||||
|
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
|
||||||
|
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
slcr.gem1_clk_ctrl.write(
|
||||||
|
slcr::GemClkCtrl::zeroed()
|
||||||
|
.clkact(true)
|
||||||
|
.srcsel(slcr::PllSource::IoPll)
|
||||||
|
.divisor(divisor0 as u8)
|
||||||
|
.divisor1(divisor1 as u8)
|
||||||
|
);
|
||||||
|
// Enable gem1 recv clock
|
||||||
|
slcr.gem1_rclk_ctrl.write(
|
||||||
|
// 0x0000_0801
|
||||||
|
slcr::RclkCtrl::zeroed()
|
||||||
|
.clkact(true)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn regs() -> &'static mut regs::RegisterBlock {
|
||||||
|
regs::RegisterBlock::gem1()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_tx_divisors(tx_clock: u32) -> (u8, u8) {
|
||||||
|
let io_pll = Clocks::get().io;
|
||||||
|
let target = (tx_clock - 1 + io_pll) / tx_clock;
|
||||||
|
|
||||||
|
let mut best = None;
|
||||||
|
let mut best_error = 0;
|
||||||
|
for divisor0 in 1..63 {
|
||||||
|
for divisor1 in 1..63 {
|
||||||
|
let current = (divisor0 as u32) * (divisor1 as u32);
|
||||||
|
let error = if current > target {
|
||||||
|
current - target
|
||||||
|
} else {
|
||||||
|
target - current
|
||||||
|
};
|
||||||
|
if best.is_none() || best_error > error {
|
||||||
|
best = Some((divisor0, divisor1));
|
||||||
|
best_error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = best.unwrap();
|
||||||
|
debug!("Eth TX clock for {}: {} / {} / {} = {}",
|
||||||
|
tx_clock, io_pll,
|
||||||
|
result.0, result.1,
|
||||||
|
io_pll / result.0 as u32 / result.1 as u32
|
||||||
|
);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Eth<GEM: Gem, RX, TX> {
|
||||||
rx: RX,
|
rx: RX,
|
||||||
tx: TX,
|
tx: TX,
|
||||||
inner: EthInner<'r>,
|
inner: EthInner<GEM>,
|
||||||
phy: Phy,
|
phy: Phy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Eth<'r, (), ()> {
|
impl Eth<Gem0, (), ()> {
|
||||||
pub fn default(macaddr: [u8; 6]) -> Self {
|
pub fn default(macaddr: [u8; 6]) -> Self {
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
// Manual example: 0x0000_1280
|
// Manual example: 0x0000_1280
|
||||||
|
@ -182,22 +280,23 @@ impl<'r> Eth<'r, (), ()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gem0(macaddr: [u8; 6]) -> Self {
|
pub fn gem0(macaddr: [u8; 6]) -> Self {
|
||||||
Self::setup_gem0_clock(TX_1000);
|
Self::new(macaddr)
|
||||||
|
|
||||||
let regs = regs::RegisterBlock::gem0();
|
|
||||||
Self::from_regs(regs, macaddr)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Eth<Gem1, (), ()> {
|
||||||
pub fn gem1(macaddr: [u8; 6]) -> Self {
|
pub fn gem1(macaddr: [u8; 6]) -> Self {
|
||||||
Self::setup_gem1_clock(TX_1000);
|
Self::new(macaddr)
|
||||||
|
|
||||||
let regs = regs::RegisterBlock::gem1();
|
|
||||||
Self::from_regs(regs, macaddr)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<GEM: Gem> Eth<GEM, (), ()> {
|
||||||
|
fn new(macaddr: [u8; 6]) -> Self {
|
||||||
|
GEM::setup_clock(TX_1000);
|
||||||
|
|
||||||
fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
|
|
||||||
let mut inner = EthInner {
|
let mut inner = EthInner {
|
||||||
regs,
|
gem: PhantomData,
|
||||||
link: None,
|
link: None,
|
||||||
};
|
};
|
||||||
inner.init();
|
inner.init();
|
||||||
|
@ -216,54 +315,8 @@ impl<'r> Eth<'r, (), ()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, RX, TX> Eth<'r, RX, TX> {
|
impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
|
||||||
pub fn setup_gem0_clock(tx_clock: u32) {
|
pub fn start_rx(self, rx_size: usize) -> Eth<GEM, rx::DescList, TX> {
|
||||||
let io_pll = Clocks::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::GemClkCtrl::zeroed()
|
|
||||||
.clkact(true)
|
|
||||||
.srcsel(slcr::PllSource::IoPll)
|
|
||||||
.divisor(d0 as u8)
|
|
||||||
.divisor1(d1 as u8)
|
|
||||||
);
|
|
||||||
// Enable gem0 recv clock
|
|
||||||
slcr.gem0_rclk_ctrl.write(
|
|
||||||
// 0x0000_0801
|
|
||||||
slcr::RclkCtrl::zeroed()
|
|
||||||
.clkact(true)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setup_gem1_clock(tx_clock: u32) {
|
|
||||||
let io_pll = Clocks::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::GemClkCtrl::zeroed()
|
|
||||||
.clkact(true)
|
|
||||||
.srcsel(slcr::PllSource::IoPll)
|
|
||||||
.divisor(d0 as u8)
|
|
||||||
.divisor1(d1 as u8)
|
|
||||||
);
|
|
||||||
// Enable gem1 recv clock
|
|
||||||
slcr.gem1_rclk_ctrl.write(
|
|
||||||
// 0x0000_0801
|
|
||||||
slcr::RclkCtrl::zeroed()
|
|
||||||
.clkact(true)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_rx(self, rx_size: usize) -> Eth<'r, rx::DescList, TX> {
|
|
||||||
let new_self = Eth {
|
let new_self = Eth {
|
||||||
rx: rx::DescList::new(rx_size),
|
rx: rx::DescList::new(rx_size),
|
||||||
tx: self.tx,
|
tx: self.tx,
|
||||||
|
@ -272,17 +325,17 @@ impl<'r, RX, TX> Eth<'r, RX, TX> {
|
||||||
};
|
};
|
||||||
let list_addr = new_self.rx.list_addr();
|
let list_addr = new_self.rx.list_addr();
|
||||||
assert!(list_addr & 0b11 == 0);
|
assert!(list_addr & 0b11 == 0);
|
||||||
new_self.inner.regs.rx_qbar.write(
|
GEM::regs().rx_qbar.write(
|
||||||
regs::RxQbar::zeroed()
|
regs::RxQbar::zeroed()
|
||||||
.rx_q_baseaddr(list_addr >> 2)
|
.rx_q_baseaddr(list_addr >> 2)
|
||||||
);
|
);
|
||||||
new_self.inner.regs.net_ctrl.modify(|_, w|
|
GEM::regs().net_ctrl.modify(|_, w|
|
||||||
w.rx_en(true)
|
w.rx_en(true)
|
||||||
);
|
);
|
||||||
new_self
|
new_self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_tx(self, tx_size: usize) -> Eth<'r, RX, tx::DescList> {
|
pub fn start_tx(self, tx_size: usize) -> Eth<GEM, RX, tx::DescList> {
|
||||||
let new_self = Eth {
|
let new_self = Eth {
|
||||||
rx: self.rx,
|
rx: self.rx,
|
||||||
tx: tx::DescList::new(tx_size),
|
tx: tx::DescList::new(tx_size),
|
||||||
|
@ -291,23 +344,23 @@ impl<'r, RX, TX> Eth<'r, RX, TX> {
|
||||||
};
|
};
|
||||||
let list_addr = &new_self.tx.list_addr();
|
let list_addr = &new_self.tx.list_addr();
|
||||||
assert!(list_addr & 0b11 == 0);
|
assert!(list_addr & 0b11 == 0);
|
||||||
new_self.inner.regs.tx_qbar.write(
|
GEM::regs().tx_qbar.write(
|
||||||
regs::TxQbar::zeroed()
|
regs::TxQbar::zeroed()
|
||||||
.tx_q_baseaddr(list_addr >> 2)
|
.tx_q_baseaddr(list_addr >> 2)
|
||||||
);
|
);
|
||||||
new_self.inner.regs.net_ctrl.modify(|_, w|
|
GEM::regs().net_ctrl.modify(|_, w|
|
||||||
w.tx_en(true)
|
w.tx_en(true)
|
||||||
);
|
);
|
||||||
new_self
|
new_self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, TX> Eth<'r, rx::DescList, TX> {
|
impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
|
||||||
pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<rx::PktRef<'p>>, rx::Error> {
|
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();
|
let status = GEM::regs().rx_status.read();
|
||||||
if status.hresp_not_ok() {
|
if status.hresp_not_ok() {
|
||||||
// Clear
|
// Clear
|
||||||
self.inner.regs.rx_status.write(
|
GEM::regs().rx_status.write(
|
||||||
regs::RxStatus::zeroed()
|
regs::RxStatus::zeroed()
|
||||||
.hresp_not_ok(true)
|
.hresp_not_ok(true)
|
||||||
);
|
);
|
||||||
|
@ -315,7 +368,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
|
||||||
}
|
}
|
||||||
if status.rx_overrun() {
|
if status.rx_overrun() {
|
||||||
// Clear
|
// Clear
|
||||||
self.inner.regs.rx_status.write(
|
GEM::regs().rx_status.write(
|
||||||
regs::RxStatus::zeroed()
|
regs::RxStatus::zeroed()
|
||||||
.rx_overrun(true)
|
.rx_overrun(true)
|
||||||
);
|
);
|
||||||
|
@ -323,7 +376,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
|
||||||
}
|
}
|
||||||
if status.buffer_not_avail() {
|
if status.buffer_not_avail() {
|
||||||
// Clear
|
// Clear
|
||||||
self.inner.regs.rx_status.write(
|
GEM::regs().rx_status.write(
|
||||||
regs::RxStatus::zeroed()
|
regs::RxStatus::zeroed()
|
||||||
.buffer_not_avail(true)
|
.buffer_not_avail(true)
|
||||||
);
|
);
|
||||||
|
@ -335,7 +388,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
|
||||||
match result {
|
match result {
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
// No packet, clear status bit
|
// No packet, clear status bit
|
||||||
self.inner.regs.rx_status.write(
|
GEM::regs().rx_status.write(
|
||||||
regs::RxStatus::zeroed()
|
regs::RxStatus::zeroed()
|
||||||
.frame_recd(true)
|
.frame_recd(true)
|
||||||
);
|
);
|
||||||
|
@ -350,13 +403,13 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, RX> Eth<'r, RX, tx::DescList> {
|
impl<GEM: Gem, RX> Eth<GEM, RX, tx::DescList> {
|
||||||
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
|
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
|
||||||
self.tx.send(self.inner.regs, length)
|
self.tx.send(GEM::regs(), length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescList> {
|
impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::DescList> {
|
||||||
type RxToken = rx::PktRef<'a>;
|
type RxToken = rx::PktRef<'a>;
|
||||||
type TxToken = tx::Token<'a>;
|
type TxToken = tx::Token<'a>;
|
||||||
|
|
||||||
|
@ -370,6 +423,7 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
|
||||||
|
|
||||||
let mut caps = DeviceCapabilities::default();
|
let mut caps = DeviceCapabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
|
caps.max_burst_size = Some(self.rx.len().min(self.tx.len()));
|
||||||
caps.checksum = checksum_caps;
|
caps.checksum = checksum_caps;
|
||||||
|
|
||||||
caps
|
caps
|
||||||
|
@ -379,7 +433,7 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
|
||||||
match self.rx.recv_next() {
|
match self.rx.recv_next() {
|
||||||
Ok(Some(pktref)) => {
|
Ok(Some(pktref)) => {
|
||||||
let tx_token = tx::Token {
|
let tx_token = tx::Token {
|
||||||
regs: self.inner.regs,
|
regs: GEM::regs(),
|
||||||
desc_list: &mut self.tx,
|
desc_list: &mut self.tx,
|
||||||
};
|
};
|
||||||
Some((pktref, tx_token))
|
Some((pktref, tx_token))
|
||||||
|
@ -397,33 +451,32 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
|
||||||
|
|
||||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
||||||
Some(tx::Token {
|
Some(tx::Token {
|
||||||
regs: self.inner.regs,
|
regs: GEM::regs(),
|
||||||
desc_list: &mut self.tx,
|
desc_list: &mut self.tx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct EthInner<'r> {
|
struct EthInner<GEM: Gem> {
|
||||||
regs: &'r mut regs::RegisterBlock,
|
gem: PhantomData<GEM>,
|
||||||
link: Option<phy::Link>,
|
link: Option<phy::Link>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> EthInner<'r> {
|
impl<GEM: Gem> EthInner<GEM> {
|
||||||
fn init(&mut self) {
|
fn init(&mut self) {
|
||||||
// Clear the Network Control register.
|
// Clear the Network Control register.
|
||||||
self.regs.net_ctrl.write(regs::NetCtrl::zeroed());
|
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed());
|
||||||
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
|
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
|
||||||
// Clear the Status registers.
|
// Clear the Status registers.
|
||||||
self.regs.rx_status.write(
|
GEM::regs().rx_status.write(
|
||||||
regs::RxStatus::zeroed()
|
regs::RxStatus::zeroed()
|
||||||
.buffer_not_avail(true)
|
.buffer_not_avail(true)
|
||||||
.frame_recd(true)
|
.frame_recd(true)
|
||||||
.rx_overrun(true)
|
.rx_overrun(true)
|
||||||
.hresp_not_ok(true)
|
.hresp_not_ok(true)
|
||||||
);
|
);
|
||||||
self.regs.tx_status.write(
|
GEM::regs().tx_status.write(
|
||||||
regs::TxStatus::zeroed()
|
regs::TxStatus::zeroed()
|
||||||
.used_bit_read(true)
|
.used_bit_read(true)
|
||||||
.collision(true)
|
.collision(true)
|
||||||
|
@ -437,7 +490,7 @@ impl<'r> EthInner<'r> {
|
||||||
.hresp_not_ok(true)
|
.hresp_not_ok(true)
|
||||||
);
|
);
|
||||||
// Disable all interrupts.
|
// Disable all interrupts.
|
||||||
self.regs.intr_dis.write(
|
GEM::regs().intr_dis.write(
|
||||||
regs::IntrDis::zeroed()
|
regs::IntrDis::zeroed()
|
||||||
.mgmt_done(true)
|
.mgmt_done(true)
|
||||||
.rx_complete(true)
|
.rx_complete(true)
|
||||||
|
@ -467,10 +520,10 @@ impl<'r> EthInner<'r> {
|
||||||
.tsu_sec_incr(true)
|
.tsu_sec_incr(true)
|
||||||
);
|
);
|
||||||
// Clear the buffer queues.
|
// Clear the buffer queues.
|
||||||
self.regs.rx_qbar.write(
|
GEM::regs().rx_qbar.write(
|
||||||
regs::RxQbar::zeroed()
|
regs::RxQbar::zeroed()
|
||||||
);
|
);
|
||||||
self.regs.tx_qbar.write(
|
GEM::regs().tx_qbar.write(
|
||||||
regs::TxQbar::zeroed()
|
regs::TxQbar::zeroed()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -479,7 +532,7 @@ impl<'r> EthInner<'r> {
|
||||||
let clocks = Clocks::get();
|
let clocks = Clocks::get();
|
||||||
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
|
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
|
||||||
|
|
||||||
self.regs.net_cfg.write(
|
GEM::regs().net_cfg.write(
|
||||||
regs::NetCfg::zeroed()
|
regs::NetCfg::zeroed()
|
||||||
.full_duplex(true)
|
.full_duplex(true)
|
||||||
.gige_en(true)
|
.gige_en(true)
|
||||||
|
@ -504,17 +557,17 @@ impl<'r> EthInner<'r> {
|
||||||
(u32::from(macaddr[3]) << 16) |
|
(u32::from(macaddr[3]) << 16) |
|
||||||
(u32::from(macaddr[4]) << 8) |
|
(u32::from(macaddr[4]) << 8) |
|
||||||
u32::from(macaddr[5]);
|
u32::from(macaddr[5]);
|
||||||
self.regs.spec_addr1_top.write(
|
GEM::regs().spec_addr1_top.write(
|
||||||
regs::SpecAddrTop::zeroed()
|
regs::SpecAddrTop::zeroed()
|
||||||
.addr_msbs(macaddr_msbs)
|
.addr_msbs(macaddr_msbs)
|
||||||
);
|
);
|
||||||
self.regs.spec_addr1_bot.write(
|
GEM::regs().spec_addr1_bot.write(
|
||||||
regs::SpecAddrBot::zeroed()
|
regs::SpecAddrBot::zeroed()
|
||||||
.addr_lsbs(macaddr_lsbs)
|
.addr_lsbs(macaddr_lsbs)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
self.regs.dma_cfg.write(
|
GEM::regs().dma_cfg.write(
|
||||||
regs::DmaCfg::zeroed()
|
regs::DmaCfg::zeroed()
|
||||||
// 1536 bytes
|
// 1536 bytes
|
||||||
.ahb_mem_rx_buf_size((MTU >> 6) as u8)
|
.ahb_mem_rx_buf_size((MTU >> 6) as u8)
|
||||||
|
@ -530,7 +583,7 @@ impl<'r> EthInner<'r> {
|
||||||
.ahb_fixed_burst_len(0x10)
|
.ahb_fixed_burst_len(0x10)
|
||||||
);
|
);
|
||||||
|
|
||||||
self.regs.net_ctrl.write(
|
GEM::regs().net_ctrl.write(
|
||||||
regs::NetCtrl::zeroed()
|
regs::NetCtrl::zeroed()
|
||||||
.mgmt_port_en(true)
|
.mgmt_port_en(true)
|
||||||
);
|
);
|
||||||
|
@ -538,7 +591,7 @@ impl<'r> EthInner<'r> {
|
||||||
|
|
||||||
|
|
||||||
fn wait_phy_idle(&self) {
|
fn wait_phy_idle(&self) {
|
||||||
while !self.regs.net_status.read().phy_mgmt_idle() {}
|
while !GEM::regs().net_status.read().phy_mgmt_idle() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -557,17 +610,15 @@ impl<'r> EthInner<'r> {
|
||||||
Some(link) => {
|
Some(link) => {
|
||||||
info!("eth: got {:?}", link);
|
info!("eth: got {:?}", link);
|
||||||
|
|
||||||
use phy::LinkSpeed::*;
|
use phy::{LinkDuplex::Full, LinkSpeed::*};
|
||||||
let txclock = match link.speed {
|
let txclock = match link.speed {
|
||||||
S10 => TX_10,
|
S10 => TX_10,
|
||||||
S100 => TX_100,
|
S100 => TX_100,
|
||||||
S1000 => TX_1000,
|
S1000 => TX_1000,
|
||||||
};
|
};
|
||||||
Eth::<(), ()>::setup_gem0_clock(txclock);
|
GEM::setup_clock(txclock);
|
||||||
/* .full_duplex(false) doesn't work even if
|
GEM::regs().net_cfg.modify(|_, w| w
|
||||||
half duplex has been negotiated. */
|
.full_duplex(link.duplex == Full)
|
||||||
self.regs.net_cfg.modify(|_, w| w
|
|
||||||
.full_duplex(true)
|
|
||||||
.gige_en(link.speed == S1000)
|
.gige_en(link.speed == S1000)
|
||||||
.speed(link.speed != S10)
|
.speed(link.speed != S10)
|
||||||
);
|
);
|
||||||
|
@ -586,10 +637,10 @@ impl<'r> EthInner<'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> PhyAccess for EthInner<'r> {
|
impl<GEM: Gem> PhyAccess for EthInner<GEM> {
|
||||||
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
|
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
|
||||||
self.wait_phy_idle();
|
self.wait_phy_idle();
|
||||||
self.regs.phy_maint.write(
|
GEM::regs().phy_maint.write(
|
||||||
regs::PhyMaint::zeroed()
|
regs::PhyMaint::zeroed()
|
||||||
.clause_22(true)
|
.clause_22(true)
|
||||||
.operation(regs::PhyOperation::Read)
|
.operation(regs::PhyOperation::Read)
|
||||||
|
@ -598,12 +649,12 @@ impl<'r> PhyAccess for EthInner<'r> {
|
||||||
.must_10(0b10)
|
.must_10(0b10)
|
||||||
);
|
);
|
||||||
self.wait_phy_idle();
|
self.wait_phy_idle();
|
||||||
self.regs.phy_maint.read().data()
|
GEM::regs().phy_maint.read().data()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_phy(&mut self, addr: u8, reg: u8, data: u16) {
|
fn write_phy(&mut self, addr: u8, reg: u8, data: u16) {
|
||||||
self.wait_phy_idle();
|
self.wait_phy_idle();
|
||||||
self.regs.phy_maint.write(
|
GEM::regs().phy_maint.write(
|
||||||
regs::PhyMaint::zeroed()
|
regs::PhyMaint::zeroed()
|
||||||
.clause_22(true)
|
.clause_22(true)
|
||||||
.operation(regs::PhyOperation::Write)
|
.operation(regs::PhyOperation::Write)
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
use bit_field::BitField;
|
|
||||||
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
/// 1000Base-T Extended Status Register
|
|
||||||
pub struct ExtendedStatus(pub u16);
|
|
||||||
|
|
||||||
impl ExtendedStatus {
|
|
||||||
pub fn cap_1000base_t_half(&self) -> bool {
|
|
||||||
self.0.get_bit(12)
|
|
||||||
}
|
|
||||||
pub fn cap_1000base_t_full(&self) -> bool {
|
|
||||||
self.0.get_bit(13)
|
|
||||||
}
|
|
||||||
pub fn cap_1000base_x_half(&self) -> bool {
|
|
||||||
self.0.get_bit(14)
|
|
||||||
}
|
|
||||||
pub fn cap_1000base_x_full(&self) -> bool {
|
|
||||||
self.0.get_bit(12)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_link(&self) -> Option<Link> {
|
|
||||||
if self.cap_1000base_t_half() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Half,
|
|
||||||
})
|
|
||||||
} else if self.cap_1000base_t_full() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Full,
|
|
||||||
})
|
|
||||||
} else if self.cap_1000base_x_half() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Half,
|
|
||||||
})
|
|
||||||
} else if self.cap_1000base_x_full() {
|
|
||||||
Some(Link {
|
|
||||||
speed: LinkSpeed::S1000,
|
|
||||||
duplex: LinkDuplex::Full,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhyRegister for ExtendedStatus {
|
|
||||||
fn addr() -> u8 {
|
|
||||||
0xF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u16> for ExtendedStatus {
|
|
||||||
fn from(value: u16) -> Self {
|
|
||||||
ExtendedStatus(value)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,10 +2,10 @@ pub mod id;
|
||||||
use id::{identify_phy, PhyIdentifier};
|
use id::{identify_phy, PhyIdentifier};
|
||||||
mod status;
|
mod status;
|
||||||
pub use status::Status;
|
pub use status::Status;
|
||||||
mod extended_status;
|
|
||||||
pub use extended_status::ExtendedStatus;
|
|
||||||
mod control;
|
mod control;
|
||||||
pub use control::Control;
|
pub use control::Control;
|
||||||
|
mod pssr;
|
||||||
|
pub use pssr::PSSR;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
|
@ -39,43 +39,36 @@ pub struct Phy {
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum PhyDevice {
|
pub enum PhyDevice {
|
||||||
Marvel88E1116R,
|
Marvell88E1116R,
|
||||||
Rtl8211E,
|
Rtl8211E,
|
||||||
}
|
}
|
||||||
|
|
||||||
const OUI_MARVEL: u32 = 0x005043;
|
const OUI_MARVELL: u32 = 0x005043;
|
||||||
const OUI_REALTEK: u32 = 0x000732;
|
const OUI_REALTEK: u32 = 0x000732;
|
||||||
|
|
||||||
impl Phy {
|
impl Phy {
|
||||||
/// Probe all addresses on MDIO for a known PHY
|
/// Probe all addresses on MDIO for a known PHY
|
||||||
pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> {
|
pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> {
|
||||||
for addr in 1..32 {
|
(1..32).filter_map(|addr| {
|
||||||
let device = match identify_phy(pa, addr) {
|
match identify_phy(pa, addr) {
|
||||||
Some(PhyIdentifier {
|
Some(PhyIdentifier {
|
||||||
oui: OUI_MARVEL,
|
oui: OUI_MARVELL,
|
||||||
model: 36,
|
model: 36,
|
||||||
..
|
..
|
||||||
}) => Some(PhyDevice::Marvel88E1116R),
|
}) => Some(PhyDevice::Marvell88E1116R),
|
||||||
Some(PhyIdentifier {
|
Some(PhyIdentifier {
|
||||||
oui: OUI_REALTEK,
|
oui: OUI_REALTEK,
|
||||||
model: 0b010001,
|
model: 0b010001,
|
||||||
rev: 0b0101,
|
rev: 0b0101,
|
||||||
}) => Some(PhyDevice::Rtl8211E),
|
}) => Some(PhyDevice::Rtl8211E),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
}.map(|device| Phy { addr, device })
|
||||||
match device {
|
}).next()
|
||||||
Some(device) =>
|
|
||||||
return Some(Phy { addr, device }),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match self.device {
|
match self.device {
|
||||||
PhyDevice::Marvel88E1116R => &"Marvel 88E1116R",
|
PhyDevice::Marvell88E1116R => &"Marvell 88E1116R",
|
||||||
PhyDevice::Rtl8211E => &"RTL8211E",
|
PhyDevice::Rtl8211E => &"RTL8211E",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,12 +113,8 @@ impl Phy {
|
||||||
if !status.link_status() {
|
if !status.link_status() {
|
||||||
None
|
None
|
||||||
} else if status.cap_1000base_t_extended_status() {
|
} else if status.cap_1000base_t_extended_status() {
|
||||||
let ext_status: ExtendedStatus = self.read_reg(pa);
|
let phy_status: PSSR = self.read_reg(pa);
|
||||||
if let Some(link) = ext_status.get_link() {
|
phy_status.get_link()
|
||||||
Some(link)
|
|
||||||
} else {
|
|
||||||
status.get_link()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
status.get_link()
|
status.get_link()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
use bit_field::BitField;
|
||||||
|
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
/// PHY-Specific Status Register
|
||||||
|
pub struct PSSR(pub u16);
|
||||||
|
|
||||||
|
impl PSSR {
|
||||||
|
pub fn link(&self) -> bool {
|
||||||
|
self.0.get_bit(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn duplex(&self) -> LinkDuplex {
|
||||||
|
if self.0.get_bit(13) {
|
||||||
|
LinkDuplex::Full
|
||||||
|
} else {
|
||||||
|
LinkDuplex::Half
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn speed(&self) -> Option<LinkSpeed> {
|
||||||
|
match self.0.get_bits(14..=15) {
|
||||||
|
0b00 => Some(LinkSpeed::S10),
|
||||||
|
0b01 => Some(LinkSpeed::S100),
|
||||||
|
0b10 => Some(LinkSpeed::S1000),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_link(&self) -> Option<Link> {
|
||||||
|
if self.link() {
|
||||||
|
Some(Link {
|
||||||
|
speed: self.speed()?,
|
||||||
|
duplex: self.duplex(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhyRegister for PSSR {
|
||||||
|
fn addr() -> u8 {
|
||||||
|
0x11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for PSSR {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
PSSR(value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -93,6 +93,10 @@ impl DescList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.list.len().min(self.buffers.len())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_addr(&self) -> u32 {
|
pub fn list_addr(&self) -> u32 {
|
||||||
&self.list[0] as *const _ as u32
|
&self.list[0] as *const _ as u32
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,10 @@ impl DescList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.list.len().min(self.buffers.len())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_addr(&self) -> u32 {
|
pub fn list_addr(&self) -> u32 {
|
||||||
&self.list[0] as *const _ as u32
|
&self.list[0] as *const _ as u32
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
//! ARM Generic Interrupt Controller
|
||||||
|
|
||||||
|
use bit_field::BitField;
|
||||||
|
use libregister::{RegisterW, RegisterRW, RegisterR};
|
||||||
|
use super::mpcore;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct InterruptId(pub u8);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum CPUCore {
|
||||||
|
Core0 = 0b01,
|
||||||
|
Core1 = 0b10
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct TargetCPU(u8);
|
||||||
|
|
||||||
|
impl TargetCPU {
|
||||||
|
pub const fn none() -> TargetCPU {
|
||||||
|
TargetCPU(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn and(self, other: TargetCPU) -> TargetCPU {
|
||||||
|
TargetCPU(self.0 | other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CPUCore> for TargetCPU {
|
||||||
|
fn from(core: CPUCore) -> Self {
|
||||||
|
TargetCPU(core as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TargetList {
|
||||||
|
CPUList(TargetCPU),
|
||||||
|
Others,
|
||||||
|
This
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CPUCore> for TargetList {
|
||||||
|
fn from(core: CPUCore) -> Self {
|
||||||
|
TargetList::CPUList(TargetCPU(core as u8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TargetCPU> for TargetList {
|
||||||
|
fn from(cpu: TargetCPU) -> Self {
|
||||||
|
TargetList::CPUList(cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum InterruptSensitivity {
|
||||||
|
Level,
|
||||||
|
Edge,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InterruptController {
|
||||||
|
mpcore: &'static mut mpcore::RegisterBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptController {
|
||||||
|
pub fn new(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
|
||||||
|
InterruptController { mpcore }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_interrupts(&mut self) {
|
||||||
|
self.mpcore.iccicr.modify(|_, w| w.enable_ns(false)
|
||||||
|
.enable_s(false));
|
||||||
|
// FIXME: Should we disable the distributor globally when we disable interrupt (for a single
|
||||||
|
// core)?
|
||||||
|
// self.mpcore.icddcr.modify(|_, w| w.enable_secure(false)
|
||||||
|
// .enable_non_secure(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// enable interrupt signaling
|
||||||
|
pub fn enable_interrupts(&mut self) {
|
||||||
|
self.mpcore.iccicr.modify(|_, w| w.enable_ns(true)
|
||||||
|
.enable_s(true));
|
||||||
|
self.mpcore.icddcr.modify(|_, w| w.enable_secure(true));
|
||||||
|
|
||||||
|
// Enable all interrupts except those of the lowest priority.
|
||||||
|
self.mpcore.iccpmr.write(mpcore::ICCPMR::zeroed().priority(0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// send software generated interrupt
|
||||||
|
pub fn send_sgi(&mut self, id: InterruptId, targets: TargetList) {
|
||||||
|
assert!(id.0 < 16);
|
||||||
|
self.mpcore.icdsgir.modify(|_, w| match targets {
|
||||||
|
TargetList::CPUList(list) => w.target_list_filter(0).cpu_target_list(list.0),
|
||||||
|
TargetList::Others => w.target_list_filter(0b01),
|
||||||
|
TargetList::This => w.target_list_filter(0b10)
|
||||||
|
}.sgiintid(id.0).satt(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// enable the interrupt *for this core*.
|
||||||
|
/// Not needed for SGI.
|
||||||
|
pub fn enable(&mut self, id: InterruptId, target_cpu: CPUCore, sensitivity: InterruptSensitivity, priority: u8) {
|
||||||
|
// only 5 bits of the priority is useful
|
||||||
|
assert!(priority < 32);
|
||||||
|
|
||||||
|
self.disable_interrupts();
|
||||||
|
|
||||||
|
// enable
|
||||||
|
let m = (id.0 >> 5) as usize;
|
||||||
|
let n = (id.0 & 0x1F) as usize;
|
||||||
|
assert!(m < 3);
|
||||||
|
unsafe {
|
||||||
|
self.mpcore.icdiser[m].modify(|mut icdiser| *icdiser.set_bit(n, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// target cpu
|
||||||
|
let m = (id.0 >> 2) as usize;
|
||||||
|
let n = (8 * (id.0 & 3)) as usize;
|
||||||
|
unsafe {
|
||||||
|
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32 + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// sensitivity
|
||||||
|
let m = (id.0 >> 4) as usize;
|
||||||
|
let n = (2 * (id.0 & 0xF)) as usize;
|
||||||
|
unsafe {
|
||||||
|
self.mpcore.icdicfr[m].modify(|mut icdicfr| *icdicfr.set_bits(n..=n+1, match sensitivity {
|
||||||
|
InterruptSensitivity::Level => 0b00,
|
||||||
|
InterruptSensitivity::Edge => 0b10,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// priority
|
||||||
|
let offset = (id.0 % 4) * 8;
|
||||||
|
let priority: u32 = (priority as u32) << (offset + 3);
|
||||||
|
let mask: u32 = 0xFFFFFFFF ^ (0xFF << offset);
|
||||||
|
unsafe {
|
||||||
|
self.mpcore.icdipr[id.0 as usize / 4].modify(|v| (v & mask) | priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_interrupt(&mut self, id: InterruptId) {
|
||||||
|
self.mpcore.icceoir.modify(|_, w| w.eoiintid(id.0 as u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_interrupt_id(&self) -> InterruptId {
|
||||||
|
InterruptId(self.mpcore.icciar.read().ackintid() as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
use super::I2C;
|
||||||
|
use crate::time::Milliseconds;
|
||||||
|
use embedded_hal::timer::CountDown;
|
||||||
|
|
||||||
|
pub struct EEPROM<'a> {
|
||||||
|
i2c: &'a mut I2C,
|
||||||
|
port: u8,
|
||||||
|
address: u8,
|
||||||
|
page_size: u8,
|
||||||
|
count_down: crate::timer::global::CountDown<Milliseconds>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> EEPROM<'a> {
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
|
||||||
|
EEPROM {
|
||||||
|
i2c: i2c,
|
||||||
|
port: 2,
|
||||||
|
address: 0b1010100,
|
||||||
|
page_size: page_size,
|
||||||
|
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
fn select(&mut self) -> Result<(), &'static str> {
|
||||||
|
let mask: u16 = 1 << self.port;
|
||||||
|
self.i2c.pca9548_select(0b1110100, mask as u8)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Random read
|
||||||
|
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
|
||||||
|
self.select()?;
|
||||||
|
|
||||||
|
self.i2c.start()?;
|
||||||
|
self.i2c.write(self.address << 1)?;
|
||||||
|
self.i2c.write(addr)?;
|
||||||
|
|
||||||
|
self.i2c.restart()?;
|
||||||
|
self.i2c.write((self.address << 1) | 1)?;
|
||||||
|
let buf_len = buf.len();
|
||||||
|
for (i, byte) in buf.iter_mut().enumerate() {
|
||||||
|
*byte = self.i2c.read(i < buf_len - 1)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.i2c.stop()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Smart multi-page writing
|
||||||
|
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
|
||||||
|
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
|
||||||
|
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
|
||||||
|
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
|
||||||
|
/// any roll-over when writing ambiguous memory regions.
|
||||||
|
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
|
||||||
|
self.select()?;
|
||||||
|
|
||||||
|
let buf_len = buf.len();
|
||||||
|
let mut pb: u8 = addr % self.page_size;
|
||||||
|
for (i, byte) in buf.iter().enumerate() {
|
||||||
|
if (i == 0) || (pb == 0) {
|
||||||
|
self.i2c.start()?;
|
||||||
|
self.i2c.write(self.address << 1)?;
|
||||||
|
self.i2c.write(addr + (i as u8))?;
|
||||||
|
}
|
||||||
|
self.i2c.write(*byte)?;
|
||||||
|
pb += 1;
|
||||||
|
|
||||||
|
if (i == buf_len-1) || (pb == self.page_size) {
|
||||||
|
self.i2c.stop()?;
|
||||||
|
self.poll(1_000)?;
|
||||||
|
pb = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Poll
|
||||||
|
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
|
||||||
|
self.select()?;
|
||||||
|
|
||||||
|
self.count_down.start(Milliseconds(timeout_ms));
|
||||||
|
loop {
|
||||||
|
self.i2c.start()?;
|
||||||
|
let ack = self.i2c.write(self.address << 1)?;
|
||||||
|
self.i2c.stop()?;
|
||||||
|
if ack {
|
||||||
|
break
|
||||||
|
};
|
||||||
|
if !self.count_down.waiting() {
|
||||||
|
return Err("I2C polling timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_eui48<'r>(&mut self) -> Result<[u8; 6], &'static str> {
|
||||||
|
let mut buffer = [0u8; 6];
|
||||||
|
self.read(0xFA, &mut buffer)?;
|
||||||
|
Ok(buffer)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
//! I2C Bit-banging Controller
|
||||||
|
|
||||||
|
mod regs;
|
||||||
|
pub mod eeprom;
|
||||||
|
use super::clocks::Clocks;
|
||||||
|
use super::slcr;
|
||||||
|
use super::time::Microseconds;
|
||||||
|
use embedded_hal::timer::CountDown;
|
||||||
|
use libregister::{RegisterR, RegisterRW, RegisterW};
|
||||||
|
|
||||||
|
const INVALID_BUS: &'static str = "Invalid I2C bus";
|
||||||
|
|
||||||
|
pub struct I2C {
|
||||||
|
regs: regs::RegisterWrapper,
|
||||||
|
count_down: super::timer::global::CountDown<Microseconds>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I2C {
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
pub fn i2c() -> Self {
|
||||||
|
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
// SCL
|
||||||
|
slcr.mio_pin_50.write(
|
||||||
|
slcr::MioPin50::zeroed()
|
||||||
|
.l3_sel(0b000) // as GPIO 50
|
||||||
|
.io_type(slcr::IoBufferType::Lvcmos18)
|
||||||
|
.pullup(true)
|
||||||
|
.disable_rcvr(true)
|
||||||
|
);
|
||||||
|
// SDA
|
||||||
|
slcr.mio_pin_51.write(
|
||||||
|
slcr::MioPin51::zeroed()
|
||||||
|
.l3_sel(0b000) // as GPIO 51
|
||||||
|
.io_type(slcr::IoBufferType::Lvcmos18)
|
||||||
|
.pullup(true)
|
||||||
|
.disable_rcvr(true)
|
||||||
|
);
|
||||||
|
// Reset
|
||||||
|
slcr.gpio_rst_ctrl.reset_gpio();
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::ctor_common(0xFFFF - 0x000C)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ctor_common(gpio_output_mask: u16) -> Self {
|
||||||
|
// Setup register block
|
||||||
|
let clocks = Clocks::get();
|
||||||
|
let self_ = Self {
|
||||||
|
regs: regs::RegisterWrapper::new(),
|
||||||
|
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setup GPIO output mask
|
||||||
|
self_.regs.gpio_output_mask.modify(|_, w| {
|
||||||
|
w.mask(gpio_output_mask)
|
||||||
|
});
|
||||||
|
// Setup GPIO driver direction
|
||||||
|
self_.regs.gpio_direction.modify(|_, w| {
|
||||||
|
w.scl(true).sda(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
self_
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delay for I2C operations, simple wrapper for nb.
|
||||||
|
fn delay_us(&mut self, us: u64) {
|
||||||
|
self.count_down.start(Microseconds(us));
|
||||||
|
nb::block!(self.count_down.wait()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn half_period(&mut self) { self.delay_us(100) }
|
||||||
|
|
||||||
|
fn sda_i(&mut self) -> bool {
|
||||||
|
self.regs.gpio_input.read().sda()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scl_i(&mut self) -> bool {
|
||||||
|
self.regs.gpio_input.read().scl()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sda_oe(&mut self, oe: bool) {
|
||||||
|
self.regs.gpio_output_enable.modify(|_, w| {
|
||||||
|
w.sda(oe)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sda_o(&mut self, o: bool) {
|
||||||
|
self.regs.gpio_output_mask.modify(|_, w| {
|
||||||
|
w.sda_o(o)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scl_oe(&mut self, oe: bool) {
|
||||||
|
self.regs.gpio_output_enable.modify(|_, w| {
|
||||||
|
w.scl(oe)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scl_o(&mut self, o: bool) {
|
||||||
|
self.regs.gpio_output_mask.modify(|_, w| {
|
||||||
|
w.scl_o(o)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self) -> Result<(), &'static str> {
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.sda_oe(false);
|
||||||
|
self.scl_o(false);
|
||||||
|
self.sda_o(false);
|
||||||
|
|
||||||
|
// Check the I2C bus is ready
|
||||||
|
self.half_period();
|
||||||
|
self.half_period();
|
||||||
|
if !self.sda_i() {
|
||||||
|
// Try toggling SCL a few times
|
||||||
|
for _bit in 0..8 {
|
||||||
|
self.scl_oe(true);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.sda_i() {
|
||||||
|
return Err("SDA is stuck low and doesn't get unstuck");
|
||||||
|
}
|
||||||
|
if !self.scl_i() {
|
||||||
|
return Err("SCL is stuck low and doesn't get unstuck");
|
||||||
|
}
|
||||||
|
// postcondition: SCL and SDA high
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) -> Result<(), &'static str> {
|
||||||
|
// precondition: SCL and SDA high
|
||||||
|
if !self.scl_i() {
|
||||||
|
return Err("SCL is stuck low and doesn't get unstuck");
|
||||||
|
}
|
||||||
|
if !self.sda_i() {
|
||||||
|
return Err("SDA arbitration lost");
|
||||||
|
}
|
||||||
|
self.sda_oe(true);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(true);
|
||||||
|
// postcondition: SCL and SDA low
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restart(&mut self) -> Result<(), &'static str> {
|
||||||
|
// precondition SCL and SDA low
|
||||||
|
self.sda_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
self.start()?;
|
||||||
|
// postcondition: SCL and SDA low
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) -> Result<(), &'static str> {
|
||||||
|
// precondition: SCL and SDA low
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
self.sda_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
if !self.sda_i() {
|
||||||
|
return Err("SDA arbitration lost");
|
||||||
|
}
|
||||||
|
// postcondition: SCL and SDA high
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, data: u8) -> Result<bool, &'static str> {
|
||||||
|
// precondition: SCL and SDA low
|
||||||
|
// MSB first
|
||||||
|
for bit in (0..8).rev() {
|
||||||
|
self.sda_oe(data & (1 << bit) == 0);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(true);
|
||||||
|
}
|
||||||
|
self.sda_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
// Read ack/nack
|
||||||
|
let ack = !self.sda_i();
|
||||||
|
self.scl_oe(true);
|
||||||
|
self.sda_oe(true);
|
||||||
|
// postcondition: SCL and SDA low
|
||||||
|
|
||||||
|
Ok(ack)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self, ack: bool) -> Result<u8, &'static str> {
|
||||||
|
// precondition: SCL and SDA low
|
||||||
|
self.sda_oe(false);
|
||||||
|
|
||||||
|
let mut data: u8 = 0;
|
||||||
|
|
||||||
|
// MSB first
|
||||||
|
for bit in (0..8).rev() {
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
if self.sda_i() { data |= 1 << bit }
|
||||||
|
self.scl_oe(true);
|
||||||
|
}
|
||||||
|
// Send ack/nack
|
||||||
|
self.sda_oe(ack);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(false);
|
||||||
|
self.half_period();
|
||||||
|
self.scl_oe(true);
|
||||||
|
self.sda_oe(true);
|
||||||
|
// postcondition: SCL and SDA low
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pca9548_select(&mut self, address: u8, channels: u8) -> Result<(), &'static str> {
|
||||||
|
self.start()?;
|
||||||
|
if !self.write(address << 1)? {
|
||||||
|
return Err("PCA9548 failed to ack write address")
|
||||||
|
}
|
||||||
|
if !self.write(channels)? {
|
||||||
|
return Err("PCA9548 failed to ack control word")
|
||||||
|
}
|
||||||
|
self.stop()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
use volatile_register::{RO, WO, RW};
|
||||||
|
|
||||||
|
use libregister::{
|
||||||
|
register, register_at,
|
||||||
|
register_bit, register_bits
|
||||||
|
};
|
||||||
|
|
||||||
|
// With reference to:
|
||||||
|
//
|
||||||
|
// artiq:artiq/gateware/targets/kasli.py:
|
||||||
|
// self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
||||||
|
//
|
||||||
|
// misoc:misoc/cores/gpio.py:
|
||||||
|
// class GPIOTristate(Module, AutoCSR):
|
||||||
|
// def __init__(self, signals, reset_out=0, reset_oe=0):
|
||||||
|
// l = len(signals)
|
||||||
|
// self._in = CSRStatus(l)
|
||||||
|
// self._out = CSRStorage(l, reset=reset_out)
|
||||||
|
// self._oe = CSRStorage(l, reset=reset_oe)
|
||||||
|
//
|
||||||
|
// Hence, using GPIOs as SCL and SDA GPIOs respectively.
|
||||||
|
//
|
||||||
|
// Current compatibility:
|
||||||
|
// zc706: GPIO 50, 51 == SCL, SDA
|
||||||
|
|
||||||
|
pub struct RegisterWrapper {
|
||||||
|
pub gpio_output_mask: &'static mut GPIOOutputMask,
|
||||||
|
pub gpio_input: &'static mut GPIOInput,
|
||||||
|
pub gpio_direction: &'static mut GPIODirection,
|
||||||
|
pub gpio_output_enable: &'static mut GPIOOutputEnable,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterWrapper {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
gpio_output_mask: GPIOOutputMask::new(),
|
||||||
|
gpio_input: GPIOInput::new(),
|
||||||
|
gpio_direction: GPIODirection::new(),
|
||||||
|
gpio_output_enable: GPIOOutputEnable::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MASK_DATA_1_MSW:
|
||||||
|
// Maskable output data for MIO[53:48]
|
||||||
|
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIOOutputMask, 0xE000A00C, new);
|
||||||
|
// Output for SCL
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_output_mask, scl_o, 2);
|
||||||
|
// Output for SDA
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_output_mask, sda_o, 3);
|
||||||
|
// Mask for keeping bits except SCL and SDA unchanged
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bits!(gpio_output_mask, mask, u16, 16, 31);
|
||||||
|
|
||||||
|
// DATA_1_RO:
|
||||||
|
// Input data for MIO[53:32]
|
||||||
|
register!(gpio_input, GPIOInput, RO, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIOInput, 0xE000A064, new);
|
||||||
|
// Input for SCL
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_input, scl, 18);
|
||||||
|
// Input for SDA
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_input, sda, 19);
|
||||||
|
|
||||||
|
// DIRM_1:
|
||||||
|
// Direction mode for MIO[53:32]; 0/1 = in/out
|
||||||
|
register!(gpio_direction, GPIODirection, RW, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIODirection, 0xE000A244, new);
|
||||||
|
// Direction for SCL
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_direction, scl, 18);
|
||||||
|
// Direction for SDA
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_direction, sda, 19);
|
||||||
|
|
||||||
|
// OEN_1:
|
||||||
|
// Output enable for MIO[53:32]
|
||||||
|
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_at!(GPIOOutputEnable, 0xE000A248, new);
|
||||||
|
// Output enable for SCL
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_output_enable, scl, 18);
|
||||||
|
// Output enable for SDA
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
register_bit!(gpio_output_enable, sda, 19);
|
|
@ -15,9 +15,12 @@ pub mod axi_hp;
|
||||||
pub mod axi_gp;
|
pub mod axi_gp;
|
||||||
pub mod ddr;
|
pub mod ddr;
|
||||||
pub mod mpcore;
|
pub mod mpcore;
|
||||||
|
pub mod gic;
|
||||||
pub mod flash;
|
pub mod flash;
|
||||||
pub mod dmac;
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod sdio;
|
pub mod sdio;
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
pub mod i2c;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
|
pub mod ps7_init;
|
||||||
|
|
|
@ -19,7 +19,7 @@ impl log::Log for Logger {
|
||||||
if self.enabled(record.metadata()) {
|
if self.enabled(record.metadata()) {
|
||||||
let timestamp = unsafe {
|
let timestamp = unsafe {
|
||||||
GlobalTimer::get()
|
GlobalTimer::get()
|
||||||
}.get_us();
|
}.get_us().0;
|
||||||
let seconds = timestamp / 1_000_000;
|
let seconds = timestamp / 1_000_000;
|
||||||
let micros = timestamp % 1_000_000;
|
let micros = timestamp % 1_000_000;
|
||||||
|
|
||||||
|
|
|
@ -8,48 +8,141 @@ use libregister::{
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct RegisterBlock {
|
pub struct RegisterBlock {
|
||||||
|
/// SCU Control Register
|
||||||
pub scu_control: ScuControl,
|
pub scu_control: ScuControl,
|
||||||
pub scu_config: RO<u32>,
|
/// SCU Configuration Register
|
||||||
pub scu_cpu_power: RW<u32>,
|
pub scu_config: ScuConfig,
|
||||||
|
/// SCU CPU Power Status Register
|
||||||
|
pub scu_cpu_power_status: SCUCPUPowerStatusRegister,
|
||||||
|
/// SCU Invalidate All Registers in Secure State
|
||||||
pub scu_invalidate: ScuInvalidate,
|
pub scu_invalidate: ScuInvalidate,
|
||||||
reserved0: [u32; 12],
|
unused0: [u32; 12],
|
||||||
pub filter_start: RW<u32>,
|
/// Filtering Start Address Register
|
||||||
pub filter_end: RW<u32>,
|
pub filtering_start_address: FilteringStartAddressRegister,
|
||||||
reserved1: [u32; 2],
|
/// Defined by FILTEREND input
|
||||||
pub scu_access_control: RW<u32>,
|
pub filtering_end_address: FilteringEndAddressRegister,
|
||||||
pub scu_non_secure_access_control: RW<u32>,
|
unused1: [u32; 2],
|
||||||
reserved2: [u32; 42],
|
/// SCU Access Control (SAC) Register
|
||||||
pub iccicr: RW<u32>,
|
pub scu_access_control_sac: SCUAccessControlRegisterSAC,
|
||||||
pub iccpmw: RW<u32>,
|
/// SCU Non-secure Access Control Register SNSAC
|
||||||
pub iccbpr: RW<u32>,
|
pub scu_non_secure_access_control: SCUNonSecureAccessControlRegister,
|
||||||
pub icciar: RW<u32>,
|
unused2: [u32; 42],
|
||||||
pub icceoir: RW<u32>,
|
/// CPU Interface Control Register
|
||||||
pub iccrpr: RW<u32>,
|
pub iccicr: ICCICR,
|
||||||
pub icchpir: RW<u32>,
|
/// Interrupt Priority Mask Register
|
||||||
pub iccabpr: RW<u32>,
|
pub iccpmr: ICCPMR,
|
||||||
reserved3: [u32; 55],
|
/// Binary Point Register
|
||||||
pub iccidr: RW<u32>,
|
pub iccbpr: ICCBPR,
|
||||||
|
/// Interrupt Acknowledge Register
|
||||||
|
pub icciar: ICCIAR,
|
||||||
|
/// End Of Interrupt Register
|
||||||
|
pub icceoir: ICCEOIR,
|
||||||
|
/// Running Priority Register
|
||||||
|
pub iccrpr: ICCRPR,
|
||||||
|
/// Highest Pending Interrupt Register
|
||||||
|
pub icchpir: ICCHPIR,
|
||||||
|
/// Aliased Non-secure Binary Point Register
|
||||||
|
pub iccabpr: ICCABPR,
|
||||||
|
unused3: [u32; 55],
|
||||||
|
/// CPU Interface Implementer Identification Register
|
||||||
|
pub iccidr: ICCIDR,
|
||||||
|
/// Global Timer Counter Register 0
|
||||||
pub global_timer_counter0: ValueRegister,
|
pub global_timer_counter0: ValueRegister,
|
||||||
pub global_timer_counter1: ValueRegister,
|
pub global_timer_counter1: ValueRegister,
|
||||||
|
/// Global Timer Control Register
|
||||||
pub global_timer_control: GlobalTimerControl,
|
pub global_timer_control: GlobalTimerControl,
|
||||||
pub global_timer_interrupt_status: RW<u32>,
|
/// Global Timer Interrupt Status Register
|
||||||
|
pub global_timer_interrupt_status: GlobalTimerInterruptStatusRegister,
|
||||||
|
/// Comparator Value Register_0
|
||||||
pub comparator_value0: ValueRegister,
|
pub comparator_value0: ValueRegister,
|
||||||
pub comparator_value1: ValueRegister,
|
pub comparator_value1: ValueRegister,
|
||||||
pub auto_increment: ValueRegister,
|
/// Auto-increment Register
|
||||||
reserved4: [u32; 249],
|
pub auto_increment: RW<u32>,
|
||||||
pub private_timer_load: ValueRegister,
|
unused4: [u32; 249],
|
||||||
pub private_timer_counter: ValueRegister,
|
/// Private Timer Load Register
|
||||||
pub private_timer_control: RW<u32>,
|
pub private_timer_load: RW<u32>,
|
||||||
pub private_timer_interrupt_status: RW<u32>,
|
/// Private Timer Counter Register
|
||||||
reserved5: [u32; 4],
|
pub private_timer_counter: RW<u32>,
|
||||||
pub watchdog_load: ValueRegister,
|
/// Private Timer Control Register
|
||||||
pub watchdog_counter: ValueRegister,
|
pub private_timer_control: PrivateTimerControlRegister,
|
||||||
pub watchdog_control: RW<u32>,
|
/// Private Timer Interrupt Status Register
|
||||||
pub watchdog_interrupt_status: RW<u32>,
|
pub private_timer_interrupt_status: PrivateTimerInterruptStatusRegister,
|
||||||
// there is plenty more (unimplemented)
|
unused5: [u32; 4],
|
||||||
|
/// Watchdog Load Register
|
||||||
|
pub watchdog_load: RW<u32>,
|
||||||
|
/// Watchdog Counter Register
|
||||||
|
pub watchdog_counter: RW<u32>,
|
||||||
|
/// Watchdog Control Register
|
||||||
|
pub watchdog_control: WatchdogControlRegister,
|
||||||
|
/// Watchdog Interrupt Status Register
|
||||||
|
pub watchdog_interrupt_status: WatchdogInterruptStatusRegister,
|
||||||
|
/// Watchdog Reset Status Register
|
||||||
|
pub watchdog_reset_status: WatchdogResetStatusRegister,
|
||||||
|
/// Watchdog Disable Register
|
||||||
|
pub watchdog_disable: RW<u32>,
|
||||||
|
unused6: [u32; 626],
|
||||||
|
/// Distributor Control Register
|
||||||
|
pub icddcr: ICDDCR,
|
||||||
|
/// Interrupt Controller Type Register
|
||||||
|
pub icdictr: ICDICTR,
|
||||||
|
/// Distributor Implementer Identification Register
|
||||||
|
pub icdiidr: ICDIIDR,
|
||||||
|
unused7: [u32; 29],
|
||||||
|
/// Interrupt Security Register
|
||||||
|
pub icdisr0: RW<u32>,
|
||||||
|
pub icdisr1: RW<u32>,
|
||||||
|
pub icdisr2: RW<u32>,
|
||||||
|
unused8: [u32; 29],
|
||||||
|
/// Interrupt Set-enable Registers
|
||||||
|
pub icdiser: [RW<u32>; 3],
|
||||||
|
unused9: [u32; 29],
|
||||||
|
/// Interrupt Clear-Enable Register 0
|
||||||
|
pub icdicer0: RW<u32>,
|
||||||
|
/// Interrupt Clear-Enable Register 1
|
||||||
|
pub icdicer1: RW<u32>,
|
||||||
|
/// Interrupt Clear-Enable Register 2
|
||||||
|
pub icdicer2: RW<u32>,
|
||||||
|
unused10: [u32; 29],
|
||||||
|
/// Interrupt Set-pending Register
|
||||||
|
pub icdispr0: RW<u32>,
|
||||||
|
pub icdispr1: RW<u32>,
|
||||||
|
pub icdispr2: RW<u32>,
|
||||||
|
unused11: [u32; 29],
|
||||||
|
/// Interrupt Clear-Pending Register
|
||||||
|
pub icdicpr0: RW<u32>,
|
||||||
|
pub icdicpr1: RW<u32>,
|
||||||
|
pub icdicpr2: RW<u32>,
|
||||||
|
unused12: [u32; 29],
|
||||||
|
/// Active Bit register
|
||||||
|
pub icdabr0: RW<u32>,
|
||||||
|
pub icdabr1: RW<u32>,
|
||||||
|
pub icdabr2: RW<u32>,
|
||||||
|
unused13: [u32; 61],
|
||||||
|
/// Interrupt Priority Register
|
||||||
|
pub icdipr: [RW<u32>; 24],
|
||||||
|
unused14: [u32; 232],
|
||||||
|
/// Interrupt Processor Targets Registers
|
||||||
|
pub icdiptr: [RW<u32>; 24],
|
||||||
|
unused15: [u32; 232],
|
||||||
|
/// Interrupt Configuration Registers
|
||||||
|
pub icdicfr: [RW<u32>; 6],
|
||||||
|
unused16: [u32; 58],
|
||||||
|
/// PPI Status Register
|
||||||
|
pub ppi_status: PpiStatus,
|
||||||
|
/// SPI Status Register 0
|
||||||
|
pub spi_status_0: RO<u32>,
|
||||||
|
/// SPI Status Register 1
|
||||||
|
pub spi_status_1: RO<u32>,
|
||||||
|
unused17: [u32; 125],
|
||||||
|
/// Software Generated Interrupt Register
|
||||||
|
pub icdsgir: ICDSGIR,
|
||||||
}
|
}
|
||||||
|
|
||||||
register_at!(RegisterBlock, 0xF8F00000, new);
|
register_at!(RegisterBlock, 0xF8F00000, new);
|
||||||
|
|
||||||
|
register!(value_register, ValueRegister, RW, u32);
|
||||||
|
register_bits!(value_register, value, u32, 0, 31);
|
||||||
|
|
||||||
register!(scu_control, ScuControl, RW, u32);
|
register!(scu_control, ScuControl, RW, u32);
|
||||||
register_bit!(scu_control, ic_standby_enable, 6);
|
register_bit!(scu_control, ic_standby_enable, 6);
|
||||||
register_bit!(scu_control, scu_standby_enable, 5);
|
register_bit!(scu_control, scu_standby_enable, 5);
|
||||||
|
@ -65,6 +158,17 @@ impl ScuControl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register!(scu_config, ScuConfig, RO, u32);
|
||||||
|
register_bits!(scu_config, tag_ram_sizes, u8, 8, 15);
|
||||||
|
register_bits!(scu_config, cpus_smp, u8, 4, 7);
|
||||||
|
register_bits!(scu_config, cpu_number, u8, 0, 1);
|
||||||
|
|
||||||
|
register!(scu_cpu_power_status, SCUCPUPowerStatusRegister, RW, u32);
|
||||||
|
register_bits!(scu_cpu_power_status, cpu3_status, u8, 24, 25);
|
||||||
|
register_bits!(scu_cpu_power_status, cpu2_status, u8, 16, 17);
|
||||||
|
register_bits!(scu_cpu_power_status, cpu1_status, u8, 8, 9);
|
||||||
|
register_bits!(scu_cpu_power_status, cpu0_status, u8, 0, 1);
|
||||||
|
|
||||||
register!(scu_invalidate, ScuInvalidate, WO, u32);
|
register!(scu_invalidate, ScuInvalidate, WO, u32);
|
||||||
register_bits!(scu_invalidate, cpu0_ways, u8, 0, 3);
|
register_bits!(scu_invalidate, cpu0_ways, u8, 0, 3);
|
||||||
register_bits!(scu_invalidate, cpu1_ways, u8, 4, 7);
|
register_bits!(scu_invalidate, cpu1_ways, u8, 4, 7);
|
||||||
|
@ -88,8 +192,71 @@ impl ScuInvalidate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
register!(value_register, ValueRegister, RW, u32);
|
register!(filtering_start_address, FilteringStartAddressRegister, RW, u32);
|
||||||
register_bits!(value_register, value, u32, 0, 31);
|
register_bits!(filtering_start_address, filtering_start_address, u32, 20, 31);
|
||||||
|
register_bits!(filtering_start_address, sbz, u32, 0, 19);
|
||||||
|
|
||||||
|
register!(filtering_end_address, FilteringEndAddressRegister, RW, u32);
|
||||||
|
register_bits!(filtering_end_address, filtering_end_address, u32, 20, 31);
|
||||||
|
register_bits!(filtering_end_address, sbz, u32, 0, 19);
|
||||||
|
|
||||||
|
register!(scu_access_control_sac, SCUAccessControlRegisterSAC, RW, u32);
|
||||||
|
register_bit!(scu_access_control_sac, cp_u3, 3);
|
||||||
|
register_bit!(scu_access_control_sac, cp_u2, 2);
|
||||||
|
register_bit!(scu_access_control_sac, cp_u1, 1);
|
||||||
|
register_bit!(scu_access_control_sac, cp_u0, 0);
|
||||||
|
|
||||||
|
register!(scu_non_secure_access_control, SCUNonSecureAccessControlRegister, RO, u32);
|
||||||
|
register_bits!(scu_non_secure_access_control, sbz, u32, 12, 31);
|
||||||
|
register_bit!(scu_non_secure_access_control, cpu3_global_timer, 11);
|
||||||
|
register_bit!(scu_non_secure_access_control, cpu2_global_timer, 10);
|
||||||
|
register_bit!(scu_non_secure_access_control, cpu1_global_timer, 9);
|
||||||
|
register_bit!(scu_non_secure_access_control, cpu0_global_timer, 8);
|
||||||
|
register_bit!(scu_non_secure_access_control, private_timers_for_cpu3, 7);
|
||||||
|
register_bit!(scu_non_secure_access_control, private_timers_for_cpu2, 6);
|
||||||
|
register_bit!(scu_non_secure_access_control, private_timers_for_cpu1, 5);
|
||||||
|
register_bit!(scu_non_secure_access_control, private_timers_for_cpu0, 4);
|
||||||
|
register_bit!(scu_non_secure_access_control, component_access_for_cpu3, 3);
|
||||||
|
register_bit!(scu_non_secure_access_control, component_access_for_cpu2, 2);
|
||||||
|
register_bit!(scu_non_secure_access_control, component_access_for_cpu1, 1);
|
||||||
|
register_bit!(scu_non_secure_access_control, component_access_for_cpu0, 0);
|
||||||
|
|
||||||
|
register!(iccicr, ICCICR, RW, u32);
|
||||||
|
register_bit!(iccicr, sbpr, 4);
|
||||||
|
register_bit!(iccicr, fiq_en, 3);
|
||||||
|
register_bit!(iccicr, ack_ctl, 2);
|
||||||
|
register_bit!(iccicr, enable_ns, 1);
|
||||||
|
register_bit!(iccicr, enable_s, 0);
|
||||||
|
|
||||||
|
register!(iccpmr, ICCPMR, RW, u32);
|
||||||
|
register_bits!(iccpmr, priority, u8, 0, 7);
|
||||||
|
|
||||||
|
register!(iccbpr, ICCBPR, RW, u32);
|
||||||
|
register_bits!(iccbpr, binary_point, u8, 0, 2);
|
||||||
|
|
||||||
|
register!(icciar, ICCIAR, RW, u32);
|
||||||
|
register_bits!(icciar, cpuid, u8, 10, 12);
|
||||||
|
register_bits!(icciar, ackintid, u32, 0, 9);
|
||||||
|
|
||||||
|
register!(icceoir, ICCEOIR, RW, u32);
|
||||||
|
register_bits!(icceoir, cpuid, u8, 10, 12);
|
||||||
|
register_bits!(icceoir, eoiintid, u32, 0, 9);
|
||||||
|
|
||||||
|
register!(iccrpr, ICCRPR, RW, u32);
|
||||||
|
register_bits!(iccrpr, priority, u8, 0, 7);
|
||||||
|
|
||||||
|
register!(icchpir, ICCHPIR, RW, u32);
|
||||||
|
register_bits!(icchpir, cpuid, u8, 10, 12);
|
||||||
|
register_bits!(icchpir, pendintid, u32, 0, 9);
|
||||||
|
|
||||||
|
register!(iccabpr, ICCABPR, RW, u32);
|
||||||
|
register_bits!(iccabpr, binary_point, u8, 0, 2);
|
||||||
|
|
||||||
|
register!(iccidr, ICCIDR, RO, u32);
|
||||||
|
register_bits!(iccidr, part_number, u32, 20, 31);
|
||||||
|
register_bits!(iccidr, architecture_version, u8, 16, 19);
|
||||||
|
register_bits!(iccidr, revision_number, u8, 12, 15);
|
||||||
|
register_bits!(iccidr, implementer, u32, 0, 11);
|
||||||
|
|
||||||
register!(global_timer_control, GlobalTimerControl, RW, u32);
|
register!(global_timer_control, GlobalTimerControl, RW, u32);
|
||||||
register_bits!(global_timer_control, prescaler, u8, 8, 15);
|
register_bits!(global_timer_control, prescaler, u8, 8, 15);
|
||||||
|
@ -97,3 +264,58 @@ register_bit!(global_timer_control, auto_increment_mode, 3);
|
||||||
register_bit!(global_timer_control, irq_enable, 2);
|
register_bit!(global_timer_control, irq_enable, 2);
|
||||||
register_bit!(global_timer_control, comp_enablea, 1);
|
register_bit!(global_timer_control, comp_enablea, 1);
|
||||||
register_bit!(global_timer_control, timer_enable, 0);
|
register_bit!(global_timer_control, timer_enable, 0);
|
||||||
|
|
||||||
|
register!(global_timer_interrupt_status, GlobalTimerInterruptStatusRegister, RW, u32);
|
||||||
|
register_bit!(global_timer_interrupt_status, event_flag, 0);
|
||||||
|
|
||||||
|
register!(private_timer_control, PrivateTimerControlRegister, RW, u32);
|
||||||
|
register_bits!(private_timer_control, sbzp, u32, 16, 31);
|
||||||
|
register_bits!(private_timer_control, prescaler, u8, 8, 15);
|
||||||
|
register_bits!(private_timer_control, unk_sbzp, u8, 3, 7);
|
||||||
|
register_bit!(private_timer_control, irq_enable, 2);
|
||||||
|
register_bit!(private_timer_control, auto_reload, 1);
|
||||||
|
register_bit!(private_timer_control, timer_enable, 0);
|
||||||
|
|
||||||
|
register!(private_timer_interrupt_status, PrivateTimerInterruptStatusRegister, RW, u32);
|
||||||
|
register_bits!(private_timer_interrupt_status, unk_sbzp, u32, 1, 31);
|
||||||
|
|
||||||
|
register!(watchdog_control, WatchdogControlRegister, RW, u32);
|
||||||
|
register_bits!(watchdog_control, prescaler, u8, 8, 15);
|
||||||
|
register_bit!(watchdog_control, watchdog_mode, 3);
|
||||||
|
register_bit!(watchdog_control, it_enable, 2);
|
||||||
|
register_bit!(watchdog_control, auto_reload, 1);
|
||||||
|
register_bit!(watchdog_control, watchdog_enable, 0);
|
||||||
|
|
||||||
|
register!(watchdog_interrupt_status, WatchdogInterruptStatusRegister, RW, u32);
|
||||||
|
register_bit!(watchdog_interrupt_status, event_flag, 0);
|
||||||
|
|
||||||
|
register!(watchdog_reset_status, WatchdogResetStatusRegister, RW, u32);
|
||||||
|
register_bit!(watchdog_reset_status, reset_flag, 0);
|
||||||
|
|
||||||
|
register!(icddcr, ICDDCR, RW, u32);
|
||||||
|
register_bit!(icddcr, enable_non_secure, 1);
|
||||||
|
register_bit!(icddcr, enable_secure, 0);
|
||||||
|
|
||||||
|
register!(icdictr, ICDICTR, RO, u32);
|
||||||
|
register_bits!(icdictr, lspi, u8, 11, 15);
|
||||||
|
register_bit!(icdictr, security_extn, 10);
|
||||||
|
register_bits!(icdictr, sbz, u8, 8, 9);
|
||||||
|
register_bits!(icdictr, cpu_number, u8, 5, 7);
|
||||||
|
register_bits!(icdictr, it_lines_number, u8, 0, 4);
|
||||||
|
|
||||||
|
register!(icdiidr, ICDIIDR, RO, u32);
|
||||||
|
register_bits!(icdiidr, implementation_version, u8, 24, 31);
|
||||||
|
register_bits!(icdiidr, revision_number, u32, 12, 23);
|
||||||
|
register_bits!(icdiidr, implementer, u32, 0, 11);
|
||||||
|
|
||||||
|
register!(ppi_status, PpiStatus, RO, u32);
|
||||||
|
register_bits!(ppi_status, ppi_status, u8, 11, 15);
|
||||||
|
register_bits!(ppi_status, sbz, u32, 0, 10);
|
||||||
|
|
||||||
|
register!(icdsgir, ICDSGIR, RW, u32);
|
||||||
|
register_bits!(icdsgir, target_list_filter, u8, 24, 25);
|
||||||
|
register_bits!(icdsgir, cpu_target_list, u8, 16, 23);
|
||||||
|
register_bit!(icdsgir, satt, 15);
|
||||||
|
register_bits!(icdsgir, sbz, u32, 4, 14);
|
||||||
|
register_bits!(icdsgir, sgiintid, u8, 0, 3);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
#![cfg(feature = "target_zc706")]
|
||||||
|
|
||||||
|
use crate::println;
|
||||||
|
|
||||||
|
mod zc706;
|
||||||
|
// mod cora_z7_10;
|
||||||
|
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
use zc706 as target;
|
||||||
|
// #[cfg(feature = "target_cora_z7_10")]
|
||||||
|
// use cora_z7_10 as target;
|
||||||
|
|
||||||
|
pub fn report_differences() {
|
||||||
|
for (i, op) in target::INIT_DATA.iter().enumerate() {
|
||||||
|
let address = op.address();
|
||||||
|
let overwritten_later = target::INIT_DATA[(i + 1)..].iter()
|
||||||
|
.any(|later_op| later_op.address() == address);
|
||||||
|
|
||||||
|
if !overwritten_later {
|
||||||
|
op.report_difference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply() {
|
||||||
|
for op in target::INIT_DATA {
|
||||||
|
op.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum InitOp {
|
||||||
|
MaskWrite(usize, usize, usize),
|
||||||
|
MaskPoll(usize, usize),
|
||||||
|
MaskDelay(usize, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InitOp {
|
||||||
|
fn address(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
InitOp::MaskWrite(address, _, _) => *address,
|
||||||
|
InitOp::MaskPoll(address, _) => *address,
|
||||||
|
InitOp::MaskDelay(address, _) => *address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&self) -> usize {
|
||||||
|
unsafe { *(self.address() as *const usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn difference(&self) -> Option<(usize, usize)> {
|
||||||
|
let expected = match self {
|
||||||
|
InitOp::MaskWrite(_, mask, expected) =>
|
||||||
|
Some((*mask, *expected)),
|
||||||
|
InitOp::MaskPoll(_, mask) =>
|
||||||
|
Some((*mask, *mask)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
match expected {
|
||||||
|
Some((mask, expected)) => {
|
||||||
|
let actual = self.read();
|
||||||
|
if actual & mask == expected {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((actual & mask, expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None =>
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report_difference(&self) {
|
||||||
|
if let Some((actual, expected)) = self.difference() {
|
||||||
|
println!(
|
||||||
|
"Register {:08X} is {:08X}&={:08X} != {:08X} expected",
|
||||||
|
self.address(),
|
||||||
|
self.read(),
|
||||||
|
actual,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply(&self) {
|
||||||
|
let reg = self.address() as *mut usize;
|
||||||
|
println!("apply {:?}", self);
|
||||||
|
match self {
|
||||||
|
InitOp::MaskWrite(_, mask, val) =>
|
||||||
|
unsafe {
|
||||||
|
*reg = (val & mask) | (*reg & !mask);
|
||||||
|
},
|
||||||
|
InitOp::MaskPoll(_, mask) =>
|
||||||
|
while unsafe { *reg } & mask == 0 {},
|
||||||
|
InitOp::MaskDelay(_, mask) => {
|
||||||
|
let delay = get_number_of_cycles_for_delay(*mask);
|
||||||
|
while unsafe { *reg } < delay {
|
||||||
|
println!("W");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_number_of_cycles_for_delay(delay: usize) -> usize {
|
||||||
|
const APU_FREQ: usize = 666666687;
|
||||||
|
APU_FREQ * delay/ (2 * 1000)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -14,7 +14,7 @@ use nb;
|
||||||
/// Basic SDIO Struct with common low-level functions.
|
/// Basic SDIO Struct with common low-level functions.
|
||||||
pub struct SDIO {
|
pub struct SDIO {
|
||||||
regs: &'static mut regs::RegisterBlock,
|
regs: &'static mut regs::RegisterBlock,
|
||||||
count_down: super::timer::global::CountDown,
|
count_down: super::timer::global::CountDown<Milliseconds>,
|
||||||
input_clk_hz: u32,
|
input_clk_hz: u32,
|
||||||
card_type: CardType,
|
card_type: CardType,
|
||||||
card_detect: bool,
|
card_detect: bool,
|
||||||
|
|
|
@ -102,19 +102,19 @@ pub struct RegisterBlock {
|
||||||
pub dbg_clk_ctrl: RW<u32>,
|
pub dbg_clk_ctrl: RW<u32>,
|
||||||
pub pcap_clk_ctrl: RW<u32>,
|
pub pcap_clk_ctrl: RW<u32>,
|
||||||
pub topsw_clk_ctrl: RW<u32>,
|
pub topsw_clk_ctrl: RW<u32>,
|
||||||
pub fpga0_clk_ctrl: RW<u32>,
|
pub fpga0_clk_ctrl: Fpga0ClkCtrl,
|
||||||
pub fpga0_thr_ctrl: RW<u32>,
|
pub fpga0_thr_ctrl: RW<u32>,
|
||||||
pub fpga0_thr_cnt: RW<u32>,
|
pub fpga0_thr_cnt: RW<u32>,
|
||||||
pub fpga0_thr_sta: RO<u32>,
|
pub fpga0_thr_sta: RO<u32>,
|
||||||
pub fpga1_clk_ctrl: RW<u32>,
|
pub fpga1_clk_ctrl: Fpga1ClkCtrl,
|
||||||
pub fpga1_thr_ctrl: RW<u32>,
|
pub fpga1_thr_ctrl: RW<u32>,
|
||||||
pub fpga1_thr_cnt: RW<u32>,
|
pub fpga1_thr_cnt: RW<u32>,
|
||||||
pub fpga1_thr_sta: RO<u32>,
|
pub fpga1_thr_sta: RO<u32>,
|
||||||
pub fpga2_clk_ctrl: RW<u32>,
|
pub fpga2_clk_ctrl: Fpga2ClkCtrl,
|
||||||
pub fpga2_thr_ctrl: RW<u32>,
|
pub fpga2_thr_ctrl: RW<u32>,
|
||||||
pub fpga2_thr_cnt: RW<u32>,
|
pub fpga2_thr_cnt: RW<u32>,
|
||||||
pub fpga2_thr_sta: RO<u32>,
|
pub fpga2_thr_sta: RO<u32>,
|
||||||
pub fpga3_clk_ctrl: RW<u32>,
|
pub fpga3_clk_ctrl: Fpga3ClkCtrl,
|
||||||
pub fpga3_thr_ctrl: RW<u32>,
|
pub fpga3_thr_ctrl: RW<u32>,
|
||||||
pub fpga3_thr_cnt: RW<u32>,
|
pub fpga3_thr_cnt: RW<u32>,
|
||||||
pub fpga3_thr_sta: RO<u32>,
|
pub fpga3_thr_sta: RO<u32>,
|
||||||
|
@ -132,7 +132,7 @@ pub struct RegisterBlock {
|
||||||
pub can_rst_ctrl: RW<u32>,
|
pub can_rst_ctrl: RW<u32>,
|
||||||
pub i2c_rst_ctrl: RW<u32>,
|
pub i2c_rst_ctrl: RW<u32>,
|
||||||
pub uart_rst_ctrl: UartRstCtrl,
|
pub uart_rst_ctrl: UartRstCtrl,
|
||||||
pub gpio_rst_ctrl: RW<u32>,
|
pub gpio_rst_ctrl: GpioRstCtrl,
|
||||||
pub lqspi_rst_ctrl: LqspiRstCtrl,
|
pub lqspi_rst_ctrl: LqspiRstCtrl,
|
||||||
pub smc_rst_ctrl: RW<u32>,
|
pub smc_rst_ctrl: RW<u32>,
|
||||||
pub ocm_rst_ctrl: RW<u32>,
|
pub ocm_rst_ctrl: RW<u32>,
|
||||||
|
@ -265,14 +265,6 @@ impl RegisterBlock {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a soft reset
|
|
||||||
pub fn soft_reset(&mut self) {
|
|
||||||
self.pss_rst_ctrl.write(
|
|
||||||
PssRstCtrl::zeroed()
|
|
||||||
.soft_rst(true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_preload_fpga(&mut self) {
|
pub fn init_preload_fpga(&mut self) {
|
||||||
// Assert FPGA top level output resets
|
// Assert FPGA top level output resets
|
||||||
self.fpga_rst_ctrl.write(
|
self.fpga_rst_ctrl.write(
|
||||||
|
@ -539,6 +531,20 @@ impl UartRstCtrl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register!(gpio_rst_ctrl, GpioRstCtrl, RW, u32);
|
||||||
|
register_bit!(gpio_rst_ctrl, gpio_cpu1x_rst, 0);
|
||||||
|
register_at!(GpioRstCtrl, 0xF800022C, new);
|
||||||
|
impl GpioRstCtrl {
|
||||||
|
pub fn reset_gpio(&mut self) {
|
||||||
|
self.modify(|_, w|
|
||||||
|
w.gpio_cpu1x_rst(true)
|
||||||
|
);
|
||||||
|
self.modify(|_, w|
|
||||||
|
w.gpio_cpu1x_rst(false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
|
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
|
||||||
register_bit!(lqspi_clk_ctrl, clkact, 0);
|
register_bit!(lqspi_clk_ctrl, clkact, 0);
|
||||||
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
||||||
|
@ -548,6 +554,26 @@ register!(lqspi_rst_ctrl, LqspiRstCtrl, RW, u32);
|
||||||
register_bit!(lqspi_rst_ctrl, ref_rst, 1);
|
register_bit!(lqspi_rst_ctrl, ref_rst, 1);
|
||||||
register_bit!(lqspi_rst_ctrl, cpu1x_rst, 0);
|
register_bit!(lqspi_rst_ctrl, cpu1x_rst, 0);
|
||||||
|
|
||||||
|
register!(fpga0_clk_ctrl, Fpga0ClkCtrl, RW, u32);
|
||||||
|
register_bits!(fpga0_clk_ctrl, divisor1, u8, 20, 25);
|
||||||
|
register_bits!(fpga0_clk_ctrl, divisor0, u8, 8, 13);
|
||||||
|
register_bits_typed!(fpga0_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
||||||
|
|
||||||
|
register!(fpga1_clk_ctrl, Fpga1ClkCtrl, RW, u32);
|
||||||
|
register_bits!(fpga1_clk_ctrl, divisor1, u8, 20, 25);
|
||||||
|
register_bits!(fpga1_clk_ctrl, divisor0, u8, 8, 13);
|
||||||
|
register_bits_typed!(fpga1_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
||||||
|
|
||||||
|
register!(fpga2_clk_ctrl, Fpga2ClkCtrl, RW, u32);
|
||||||
|
register_bits!(fpga2_clk_ctrl, divisor1, u8, 20, 25);
|
||||||
|
register_bits!(fpga2_clk_ctrl, divisor0, u8, 8, 13);
|
||||||
|
register_bits_typed!(fpga2_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
||||||
|
|
||||||
|
register!(fpga3_clk_ctrl, Fpga3ClkCtrl, RW, u32);
|
||||||
|
register_bits!(fpga3_clk_ctrl, divisor1, u8, 20, 25);
|
||||||
|
register_bits!(fpga3_clk_ctrl, divisor0, u8, 8, 13);
|
||||||
|
register_bits_typed!(fpga3_clk_ctrl, src_sel, u8, PllSource, 4, 5);
|
||||||
|
|
||||||
register!(fpga_rst_ctrl, FpgaRstCtrl, RW, u32);
|
register!(fpga_rst_ctrl, FpgaRstCtrl, RW, u32);
|
||||||
register_bit!(fpga_rst_ctrl, fpga0_out_rst, 0);
|
register_bit!(fpga_rst_ctrl, fpga0_out_rst, 0);
|
||||||
register_bit!(fpga_rst_ctrl, fpga1_out_rst, 1);
|
register_bit!(fpga_rst_ctrl, fpga1_out_rst, 1);
|
||||||
|
|
|
@ -8,3 +8,18 @@ impl core::ops::Add for Milliseconds {
|
||||||
Milliseconds(self.0 + rhs.0)
|
Milliseconds(self.0 + rhs.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
pub struct Microseconds(pub u64);
|
||||||
|
|
||||||
|
impl core::ops::Add for Microseconds {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Microseconds(self.0 + rhs.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TimeSource<U> {
|
||||||
|
fn now(&self) -> U;
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
use core::ops::Add;
|
||||||
use void::Void;
|
use void::Void;
|
||||||
use libregister::{RegisterR, RegisterW};
|
use libregister::{RegisterR, RegisterW};
|
||||||
use crate::{
|
use crate::{
|
||||||
clocks::Clocks,
|
clocks::Clocks,
|
||||||
mpcore,
|
mpcore,
|
||||||
time::Milliseconds,
|
time::{Milliseconds, Microseconds, TimeSource},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// "uptime"
|
/// "uptime"
|
||||||
|
@ -79,41 +80,91 @@ impl GlobalTimer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read with high precision
|
/// read with high precision
|
||||||
pub fn get_us(&self) -> u64 {
|
pub fn get_us(&self) -> Microseconds {
|
||||||
let prescaler = self.regs.global_timer_control.read().prescaler() as u64;
|
let prescaler = self.regs.global_timer_control.read().prescaler() as u64;
|
||||||
let clocks = Clocks::get();
|
let clocks = Clocks::get();
|
||||||
|
|
||||||
1_000_000 * self.get_counter() * (prescaler + 1) / clocks.cpu_3x2x() as u64
|
Microseconds(1_000_000 * self.get_counter() * (prescaler + 1) / clocks.cpu_3x2x() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return a handle that has implements
|
/// return a handle that has implements
|
||||||
/// `embedded_hal::timer::CountDown`
|
/// `embedded_hal::timer::CountDown`
|
||||||
pub fn countdown(&self) -> CountDown {
|
pub fn countdown<U>(&self) -> CountDown<U>
|
||||||
|
where
|
||||||
|
Self: TimeSource<U>,
|
||||||
|
{
|
||||||
CountDown {
|
CountDown {
|
||||||
timer: self.clone(),
|
timer: self.clone(),
|
||||||
timeout: Milliseconds(0),
|
timeout: self.now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
impl TimeSource<Milliseconds> for GlobalTimer {
|
||||||
pub struct CountDown {
|
fn now(&self) -> Milliseconds {
|
||||||
timer: GlobalTimer,
|
self.get_time()
|
||||||
timeout: Milliseconds,
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_hal::timer::CountDown for CountDown {
|
impl TimeSource<Microseconds> for GlobalTimer {
|
||||||
type Time = Milliseconds;
|
fn now(&self) -> Microseconds {
|
||||||
|
self.get_us()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CountDown<U> {
|
||||||
|
timer: GlobalTimer,
|
||||||
|
timeout: U,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// embedded-hal async API
|
||||||
|
impl<U: Add<Output=U> + PartialOrd> embedded_hal::timer::CountDown for CountDown<U>
|
||||||
|
where
|
||||||
|
GlobalTimer: TimeSource<U>,
|
||||||
|
{
|
||||||
|
type Time = U;
|
||||||
|
|
||||||
fn start<T: Into<Self::Time>>(&mut self, count: T) {
|
fn start<T: Into<Self::Time>>(&mut self, count: T) {
|
||||||
self.timeout = self.timer.get_time() + count.into();
|
self.timeout = self.timer.now() + count.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait(&mut self) -> nb::Result<(), Void> {
|
fn wait(&mut self) -> nb::Result<(), Void> {
|
||||||
if self.timer.get_time() < self.timeout {
|
if self.timer.now() <= self.timeout {
|
||||||
Err(nb::Error::WouldBlock)
|
Err(nb::Error::WouldBlock)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<U: PartialOrd> CountDown<U>
|
||||||
|
where
|
||||||
|
GlobalTimer: TimeSource<U>,
|
||||||
|
{
|
||||||
|
pub fn waiting(&self) -> bool {
|
||||||
|
self.timer.now() <= self.timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// embedded-hal sync API
|
||||||
|
impl embedded_hal::blocking::delay::DelayMs<u64> for GlobalTimer {
|
||||||
|
fn delay_ms(&mut self, ms: u64) {
|
||||||
|
use embedded_hal::timer::CountDown;
|
||||||
|
|
||||||
|
let mut countdown = self.countdown::<Milliseconds>();
|
||||||
|
countdown.start(Milliseconds(ms));
|
||||||
|
nb::block!(countdown.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// embedded-hal sync API
|
||||||
|
impl embedded_hal::blocking::delay::DelayUs<u64> for GlobalTimer {
|
||||||
|
fn delay_us(&mut self, us: u64) {
|
||||||
|
use embedded_hal::timer::CountDown;
|
||||||
|
|
||||||
|
let mut countdown = self.countdown::<Microseconds>();
|
||||||
|
countdown.start(Microseconds(us));
|
||||||
|
nb::block!(countdown.wait()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = []
|
target_zc706 = []
|
||||||
target_cora_z7_10 = []
|
target_cora_z7_10 = []
|
||||||
|
power_saving = []
|
||||||
default = ["target_zc706"]
|
default = ["target_zc706"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
@ -33,3 +33,44 @@ pub fn dsb() {
|
||||||
pub fn isb() {
|
pub fn isb() {
|
||||||
unsafe { llvm_asm!("isb" :::: "volatile") }
|
unsafe { llvm_asm!("isb" :::: "volatile") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable IRQ
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn enable_irq() {
|
||||||
|
llvm_asm!("cpsie i":::: "volatile");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable IRQ, return if IRQ was originally enabled.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn enter_critical() -> bool {
|
||||||
|
let mut cpsr: u32;
|
||||||
|
llvm_asm!(
|
||||||
|
"mrs $0, cpsr
|
||||||
|
cpsid i"
|
||||||
|
: "=r"(cpsr) ::: "volatile");
|
||||||
|
(cpsr & (1 << 7)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn exit_critical(enable: bool) {
|
||||||
|
// https://stackoverflow.com/questions/40019929/temporarily-disable-interrupts-on-arm
|
||||||
|
let mask: u32 = if enable {
|
||||||
|
1 << 7
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
llvm_asm!(
|
||||||
|
"mrs r1, cpsr
|
||||||
|
bic r1, r1, $0
|
||||||
|
msr cpsr_c, r1"
|
||||||
|
:: "r"(mask) : "r1");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exiting IRQ
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn exit_irq() {
|
||||||
|
llvm_asm!("
|
||||||
|
mrs r0, SPSR
|
||||||
|
msr CPSR, r0
|
||||||
|
" ::: "r0");
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,15 @@ pub fn dcisw(setway: u32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data cache clean by set/way
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn dccisw(setway: u32) {
|
||||||
|
unsafe {
|
||||||
|
llvm_asm!("mcr p15, 0, $0, c7, c14, 2" :: "r" (setway) :: "volatile");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A made-up "instruction": invalidate all of the L1 D-Cache
|
/// A made-up "instruction": invalidate all of the L1 D-Cache
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn dciall() {
|
pub fn dciall() {
|
||||||
|
@ -71,6 +80,33 @@ pub fn dciall() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A made-up "instruction": flush and invalidate all of the L1 D-Cache
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn dcciall() {
|
||||||
|
// the cache associativity could be read from a register, but will
|
||||||
|
// always be 4 in L1 data cache of a cortex a9
|
||||||
|
let ways = 4;
|
||||||
|
let bit_pos_of_way = 30; // 32 - log2(ways)
|
||||||
|
|
||||||
|
// the cache sets could be read from a register, but are always
|
||||||
|
// 256 for the cores in the zync-7000; in general, 128 or 512 are
|
||||||
|
// also possible.
|
||||||
|
let sets = 256;
|
||||||
|
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
|
||||||
|
|
||||||
|
// select L1 data cache
|
||||||
|
unsafe {
|
||||||
|
llvm_asm!("mcr p15, 2, $0, c0, c0, 0" :: "r" (0) :: "volatile");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate entire D-Cache by iterating every set and every way
|
||||||
|
for set in 0..sets {
|
||||||
|
for way in 0..ways {
|
||||||
|
dccisw((set << bit_pos_of_set) | (way << bit_pos_of_way));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const CACHE_LINE: usize = 0x20;
|
const CACHE_LINE: usize = 0x20;
|
||||||
const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
|
const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/// Enable FPU in the current core.
|
||||||
|
pub fn enable_fpu() {
|
||||||
|
unsafe {
|
||||||
|
llvm_asm!("
|
||||||
|
mrc p15, 0, r1, c1, c0, 2
|
||||||
|
orr r1, r1, (0b1111<<20)
|
||||||
|
mcr p15, 0, r1, c1, c0, 2
|
||||||
|
|
||||||
|
vmrs r1, fpexc
|
||||||
|
orr r1, r1, (1<<30)
|
||||||
|
vmsr fpexc, r1
|
||||||
|
":::"r1");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(llvm_asm, global_asm)]
|
#![feature(llvm_asm, global_asm)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
|
#![feature(const_fn)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
@ -10,7 +11,26 @@ pub mod cache;
|
||||||
pub mod mmu;
|
pub mod mmu;
|
||||||
pub mod mutex;
|
pub mod mutex;
|
||||||
pub mod sync_channel;
|
pub mod sync_channel;
|
||||||
|
pub mod semaphore;
|
||||||
mod uncached;
|
mod uncached;
|
||||||
|
mod fpu;
|
||||||
pub use uncached::UncachedSlice;
|
pub use uncached::UncachedSlice;
|
||||||
|
pub use fpu::enable_fpu;
|
||||||
|
|
||||||
global_asm!(include_str!("exceptions.s"));
|
global_asm!(include_str!("exceptions.s"));
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn spin_lock_yield() {
|
||||||
|
#[cfg(feature = "power_saving")]
|
||||||
|
asm::wfe();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn notify_spin_lock() {
|
||||||
|
#[cfg(feature = "power_saving")]
|
||||||
|
{
|
||||||
|
asm::dsb();
|
||||||
|
asm::sev();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use bit_field::BitField;
|
use bit_field::BitField;
|
||||||
use super::{regs::*, asm, cache};
|
use super::{regs::*, asm::*, cache::*};
|
||||||
use libregister::RegisterW;
|
use libregister::RegisterW;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -338,7 +338,7 @@ impl L1Table {
|
||||||
/* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */
|
/* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */
|
||||||
self.direct_mapped_section(0xfff, L1Section {
|
self.direct_mapped_section(0xfff, L1Section {
|
||||||
global: true,
|
global: true,
|
||||||
shareable: false,
|
shareable: true,
|
||||||
access: AccessPermissions::FullAccess,
|
access: AccessPermissions::FullAccess,
|
||||||
tex: 0b100,
|
tex: 0b100,
|
||||||
domain: 0,
|
domain: 0,
|
||||||
|
@ -368,10 +368,19 @@ impl L1Table {
|
||||||
let result = f(&mut section);
|
let result = f(&mut section);
|
||||||
entry.set_section(section);
|
entry.set_section(section);
|
||||||
|
|
||||||
asm::dmb();
|
// Flush L1Dcache
|
||||||
cache::tlbiall();
|
dcciall();
|
||||||
asm::dsb();
|
// // TODO: L2?
|
||||||
asm::isb();
|
|
||||||
|
// Invalidate TLB
|
||||||
|
tlbiall();
|
||||||
|
// Invalidate all branch predictors
|
||||||
|
bpiall();
|
||||||
|
|
||||||
|
// ensure completion of the BP and TLB invalidation
|
||||||
|
dsb();
|
||||||
|
// synchronize context on this processor
|
||||||
|
isb();
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -406,9 +415,9 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
|
||||||
|
|
||||||
// Synchronization barriers
|
// Synchronization barriers
|
||||||
// Allows MMU to start
|
// Allows MMU to start
|
||||||
asm::dsb();
|
dsb();
|
||||||
// Flushes pre-fetch buffer
|
// Flushes pre-fetch buffer
|
||||||
asm::isb();
|
isb();
|
||||||
|
|
||||||
f();
|
f();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,10 @@
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::UnsafeCell;
|
||||||
use super::asm::*;
|
use super::{
|
||||||
|
spin_lock_yield, notify_spin_lock,
|
||||||
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
|
asm::{dmb, enter_critical, exit_critical}
|
||||||
#[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 LOCKED: u32 = 1;
|
||||||
const UNLOCKED: u32 = 0;
|
const UNLOCKED: u32 = 0;
|
||||||
|
@ -41,18 +31,34 @@ impl<T> Mutex<T> {
|
||||||
|
|
||||||
/// Lock the Mutex, blocks when already locked
|
/// Lock the Mutex, blocks when already locked
|
||||||
pub fn lock(&self) -> MutexGuard<T> {
|
pub fn lock(&self) -> MutexGuard<T> {
|
||||||
|
let mut irq = unsafe { enter_critical() };
|
||||||
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
|
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
|
||||||
wait_for_update();
|
unsafe {
|
||||||
|
exit_critical(irq);
|
||||||
|
spin_lock_yield();
|
||||||
|
irq = enter_critical();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dmb();
|
dmb();
|
||||||
MutexGuard { mutex: self }
|
MutexGuard { mutex: self, irq }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
|
||||||
|
let irq = unsafe { enter_critical() };
|
||||||
|
if self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
|
||||||
|
unsafe { exit_critical(irq) };
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
dmb();
|
||||||
|
Some(MutexGuard { mutex: self, irq })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unlock(&self) {
|
fn unlock(&self) {
|
||||||
dmb();
|
dmb();
|
||||||
self.locked.store(UNLOCKED, Ordering::Release);
|
self.locked.store(UNLOCKED, Ordering::Release);
|
||||||
|
|
||||||
signal_update();
|
notify_spin_lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +66,7 @@ impl<T> Mutex<T> {
|
||||||
/// `Deref`/`DerefMutx`
|
/// `Deref`/`DerefMutx`
|
||||||
pub struct MutexGuard<'a, T> {
|
pub struct MutexGuard<'a, T> {
|
||||||
mutex: &'a Mutex<T>,
|
mutex: &'a Mutex<T>,
|
||||||
|
irq: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Deref for MutexGuard<'a, T> {
|
impl<'a, T> Deref for MutexGuard<'a, T> {
|
||||||
|
@ -79,5 +86,6 @@ impl<'a, T> DerefMut for MutexGuard<'a, T> {
|
||||||
impl<'a, T> Drop for MutexGuard<'a, T> {
|
impl<'a, T> Drop for MutexGuard<'a, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.mutex.unlock();
|
self.mutex.unlock();
|
||||||
|
unsafe { exit_critical(self.irq) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,8 +91,19 @@ pub struct HVBAR;
|
||||||
def_reg_r!(HVBAR, u32, "mrc p15, 4, $0, c12, c0, 0");
|
def_reg_r!(HVBAR, u32, "mrc p15, 4, $0, c12, c0, 0");
|
||||||
def_reg_w!(HVBAR, u32, "mcr p15, 4, $0, c12, c0, 0");
|
def_reg_w!(HVBAR, u32, "mcr p15, 4, $0, c12, c0, 0");
|
||||||
|
|
||||||
|
/// Multiprocess Affinity Register
|
||||||
pub struct MPIDR;
|
pub struct MPIDR;
|
||||||
def_reg_r!(MPIDR, u32, "mrc p15, 0, $0, c0, c0, 5");
|
def_reg_r!(MPIDR, mpidr::Read, "mrc p15, 0, $0, c0, c0, 5");
|
||||||
|
wrap_reg!(mpidr);
|
||||||
|
register_bits!(mpidr,
|
||||||
|
/// CPU core index
|
||||||
|
cpu_id, u8, 0, 1);
|
||||||
|
register_bits!(mpidr,
|
||||||
|
/// Processor index in "multi-socket" systems
|
||||||
|
cluster_id, u8, 8, 11);
|
||||||
|
register_bit!(mpidr,
|
||||||
|
/// true if part of uniprocessor system
|
||||||
|
u, 30);
|
||||||
|
|
||||||
pub struct DFAR;
|
pub struct DFAR;
|
||||||
def_reg_r!(DFAR, u32, "mrc p15, 0, $0, c6, c0, 0");
|
def_reg_r!(DFAR, u32, "mrc p15, 0, $0, c6, c0, 0");
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
use super::{spin_lock_yield, notify_spin_lock};
|
||||||
|
use core::{
|
||||||
|
task::{Context, Poll},
|
||||||
|
pin::Pin,
|
||||||
|
future::Future,
|
||||||
|
sync::atomic::{AtomicI32, Ordering}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Semaphore {
|
||||||
|
value: AtomicI32,
|
||||||
|
max: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Semaphore {
|
||||||
|
pub fn new(value: i32, max: i32) -> Self {
|
||||||
|
Semaphore { value: AtomicI32::new(value), max}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_wait(&self) -> Option<()> {
|
||||||
|
loop {
|
||||||
|
let value = self.value.load(Ordering::Relaxed);
|
||||||
|
if value > 0 {
|
||||||
|
if self.value.compare_and_swap(value, value - 1, Ordering::SeqCst) == value {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait(&self) {
|
||||||
|
while self.try_wait().is_none() {
|
||||||
|
spin_lock_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn async_wait(&self) {
|
||||||
|
struct Fut<'a>(&'a Semaphore);
|
||||||
|
|
||||||
|
impl Future for Fut<'_> {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.0.try_wait() {
|
||||||
|
Some(_) => Poll::Ready(()),
|
||||||
|
None => {
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Fut(&self).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn signal(&self) {
|
||||||
|
loop {
|
||||||
|
let value = self.value.load(Ordering::Relaxed);
|
||||||
|
if value < self.max {
|
||||||
|
if self.value.compare_and_swap(value, value + 1, Ordering::SeqCst) == value {
|
||||||
|
notify_spin_lock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,115 +1,73 @@
|
||||||
use core::{
|
use core::{
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
ptr::null_mut,
|
future::Future,
|
||||||
sync::atomic::{AtomicPtr, Ordering},
|
ptr::drop_in_place,
|
||||||
|
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use alloc::{
|
use alloc::boxed::Box;
|
||||||
boxed::Box,
|
use super::{spin_lock_yield, notify_spin_lock};
|
||||||
sync::Arc,
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
use super::asm::*;
|
|
||||||
|
|
||||||
|
pub struct Sender<'a, T> where T: Clone {
|
||||||
type Channel<T> = Vec<AtomicPtr<T>>;
|
list: &'a [AtomicPtr<T>],
|
||||||
|
write: &'a AtomicUsize,
|
||||||
/// Create a bounded channel
|
read: &'a AtomicUsize,
|
||||||
///
|
|
||||||
/// Returns `(tx, rx)` where one should be used one the local core,
|
|
||||||
/// and the other is to be shared with another core.
|
|
||||||
pub fn sync_channel<T>(bound: usize) -> (Sender<T>, Receiver<T>) {
|
|
||||||
// allow for bound=0
|
|
||||||
let len = bound + 1;
|
|
||||||
let mut channel = Vec::with_capacity(len);
|
|
||||||
for _ in 0..len {
|
|
||||||
channel.push(AtomicPtr::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
let channel = Arc::new(channel);
|
|
||||||
let sender = Sender {
|
|
||||||
channel: channel.clone(),
|
|
||||||
pos: 0,
|
|
||||||
};
|
|
||||||
let receiver = Receiver {
|
|
||||||
channel: channel,
|
|
||||||
pos: 0,
|
|
||||||
};
|
|
||||||
(sender, receiver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sending half of a channel
|
pub struct Receiver<'a, T> where T: Clone {
|
||||||
pub struct Sender<T> {
|
list: &'a [AtomicPtr<T>],
|
||||||
channel: Arc<Channel<T>>,
|
write: &'a AtomicUsize,
|
||||||
pos: usize,
|
read: &'a AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Sender<T> {
|
impl<'a, T> Sender<'a, T> where T: Clone {
|
||||||
/// Blocking send
|
pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
|
||||||
pub fn send<B: Into<Box<T>>>(&mut self, content: B) {
|
Sender {list, write, read}
|
||||||
let ptr = Box::into_raw(content.into());
|
|
||||||
let entry = &self.channel[self.pos];
|
|
||||||
// try to write the new pointer if the current pointer is
|
|
||||||
// NULL, retrying while it is not NULL
|
|
||||||
while entry.compare_and_swap(null_mut(), ptr, Ordering::Acquire) != null_mut() {
|
|
||||||
// power-saving
|
|
||||||
wfe();
|
|
||||||
}
|
|
||||||
dsb();
|
|
||||||
// wake power-saving receivers
|
|
||||||
sev();
|
|
||||||
|
|
||||||
// advance
|
|
||||||
self.pos += 1;
|
|
||||||
// wrap
|
|
||||||
if self.pos >= self.channel.len() {
|
|
||||||
self.pos = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Non-blocking send, handing you back ownership of the content on **failure**
|
pub fn try_send<B: Into<Box<T>>>(&mut self, content: B) -> Result<(), B> {
|
||||||
pub fn try_send<B: Into<Box<T>>>(&mut self, content: B) -> Option<Box<T>> {
|
let write = self.write.load(Ordering::Relaxed);
|
||||||
let ptr = Box::into_raw(content.into());
|
if (write + 1) % self.list.len() == self.read.load(Ordering::Acquire) {
|
||||||
let entry = &self.channel[self.pos];
|
Err(content)
|
||||||
// try to write the new pointer if the current pointer is
|
|
||||||
// NULL
|
|
||||||
if entry.compare_and_swap(null_mut(), ptr, Ordering::Acquire) == null_mut() {
|
|
||||||
dsb();
|
|
||||||
// wake power-saving receivers
|
|
||||||
sev();
|
|
||||||
|
|
||||||
// advance
|
|
||||||
self.pos += 1;
|
|
||||||
// wrap
|
|
||||||
if self.pos >= self.channel.len() {
|
|
||||||
self.pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// success
|
|
||||||
None
|
|
||||||
} else {
|
} else {
|
||||||
let content = unsafe { Box::from_raw(ptr) };
|
let ptr = Box::into_raw(content.into());
|
||||||
// failure
|
let entry = &self.list[write];
|
||||||
Some(content)
|
let prev = entry.swap(ptr, Ordering::Relaxed);
|
||||||
|
// we allow other end get it first
|
||||||
|
self.write.store((write + 1) % self.list.len(), Ordering::Release);
|
||||||
|
notify_spin_lock();
|
||||||
|
if !prev.is_null() {
|
||||||
|
unsafe {
|
||||||
|
drop_in_place(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send<B: Into<Box<T>>>(&mut self, content: B) {
|
||||||
|
let mut content = content;
|
||||||
|
while let Err(back) = self.try_send(content) {
|
||||||
|
content = back;
|
||||||
|
spin_lock_yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn async_send<B: Into<Box<T>>>(&mut self, content: B) {
|
pub async fn async_send<B: Into<Box<T>>>(&mut self, content: B) {
|
||||||
struct Send<'a, T> {
|
struct Send<'a, 'b, T> where T: Clone, 'b: 'a {
|
||||||
sender: &'a mut Sender<T>,
|
sender: &'a mut Sender<'b, T>,
|
||||||
content: Option<Box<T>>,
|
content: Result<(), Box<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Future for Send<'_, T> {
|
impl<T> Future for Send<'_, '_, T> where T: Clone {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match self.content.take() {
|
match core::mem::replace(&mut self.content, Ok(())) {
|
||||||
Some(content) => {
|
Err(content) => {
|
||||||
if let Some(content) = self.sender.try_send(content) {
|
if let Err(content) = self.sender.try_send(content) {
|
||||||
// failure
|
// failure
|
||||||
self.content = Some(content);
|
self.content = Err(content);
|
||||||
cx.waker().wake_by_ref();
|
cx.waker().wake_by_ref();
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
} else {
|
} else {
|
||||||
|
@ -117,93 +75,80 @@ impl<T> Sender<T> {
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => panic!("Send future polled after success"),
|
Ok(_) => panic!("Send future polled after success"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Send {
|
Send {
|
||||||
sender: self,
|
sender: self,
|
||||||
content: Some(content.into()),
|
content: Err(content.into()),
|
||||||
}.await
|
}.await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/// free all items in the queue. It is the user's responsibility to
|
||||||
|
/// ensure no reader is trying to copy the data.
|
||||||
|
pub unsafe fn drop_elements(&mut self) {
|
||||||
/// Receiving half of a channel
|
for v in self.list.iter() {
|
||||||
pub struct Receiver<T> {
|
let original = v.swap(core::ptr::null_mut(), Ordering::Relaxed);
|
||||||
channel: Arc<Channel<T>>,
|
if !original.is_null() {
|
||||||
pos: usize,
|
drop_in_place(original);
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Receiver<T> {
|
|
||||||
/// Blocking receive
|
|
||||||
pub fn recv(&mut self) -> Box<T> {
|
|
||||||
let entry = &self.channel[self.pos];
|
|
||||||
|
|
||||||
loop {
|
|
||||||
dmb();
|
|
||||||
let ptr = entry.swap(null_mut(), Ordering::Release);
|
|
||||||
if ptr != null_mut() {
|
|
||||||
dsb();
|
|
||||||
// wake power-saving senders
|
|
||||||
sev();
|
|
||||||
|
|
||||||
let content = unsafe { Box::from_raw(ptr) };
|
|
||||||
|
|
||||||
// advance
|
|
||||||
self.pos += 1;
|
|
||||||
// wrap
|
|
||||||
if self.pos >= self.channel.len() {
|
|
||||||
self.pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// power-saving
|
|
||||||
wfe();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Non-blocking receive
|
/// Reset the `sync_channel`, *forget* all items in the queue. Affects both the sender and
|
||||||
pub fn try_recv(&mut self) -> Option<Box<T>> {
|
/// receiver.
|
||||||
let entry = &self.channel[self.pos];
|
pub unsafe fn reset(&mut self) {
|
||||||
|
self.write.store(0, Ordering::Relaxed);
|
||||||
|
self.read.store(0, Ordering::Relaxed);
|
||||||
|
for v in self.list.iter() {
|
||||||
|
v.store(core::ptr::null_mut(), Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dmb();
|
impl<'a, T> Receiver<'a, T> where T: Clone {
|
||||||
let ptr = entry.swap(null_mut(), Ordering::Release);
|
pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
|
||||||
if ptr != null_mut() {
|
Receiver {list, write, read}
|
||||||
dsb();
|
}
|
||||||
// wake power-saving senders
|
|
||||||
sev();
|
|
||||||
|
|
||||||
let content = unsafe { Box::from_raw(ptr) };
|
pub fn try_recv(&mut self) -> Result<T, ()> {
|
||||||
|
let read = self.read.load(Ordering::Relaxed);
|
||||||
// advance
|
if read == self.write.load(Ordering::Acquire) {
|
||||||
self.pos += 1;
|
Err(())
|
||||||
// wrap
|
|
||||||
if self.pos >= self.channel.len() {
|
|
||||||
self.pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(content)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
let entry = &self.list[read];
|
||||||
|
let data = unsafe {
|
||||||
|
// we cannot deallocate the box
|
||||||
|
Box::leak(Box::from_raw(entry.load(Ordering::Relaxed)))
|
||||||
|
};
|
||||||
|
let result = data.clone();
|
||||||
|
self.read.store((read + 1) % self.list.len(), Ordering::Release);
|
||||||
|
notify_spin_lock();
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn async_recv(&mut self) -> Box<T> {
|
pub fn recv(&mut self) -> T {
|
||||||
struct Recv<'a, T> {
|
loop {
|
||||||
receiver: &'a mut Receiver<T>,
|
if let Ok(data) = self.try_recv() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
spin_lock_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn async_recv(&mut self) -> T {
|
||||||
|
struct Recv<'a, 'b, T> where T: Clone, 'b: 'a {
|
||||||
|
receiver: &'a mut Receiver<'b, T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Future for Recv<'_, T> {
|
impl<T> Future for Recv<'_, '_, T> where T: Clone {
|
||||||
type Output = Box<T>;
|
type Output = T;
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
if let Some(content) = self.receiver.try_recv() {
|
if let Ok(content) = self.receiver.try_recv() {
|
||||||
Poll::Ready(content)
|
Poll::Ready(content)
|
||||||
} else {
|
} else {
|
||||||
cx.waker().wake_by_ref();
|
cx.waker().wake_by_ref();
|
||||||
|
@ -218,10 +163,26 @@ impl<T> Receiver<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Iterator for Receiver<T> {
|
impl<'a, T> Iterator for Receiver<'a, T> where T: Clone {
|
||||||
type Item = Box<T>;
|
type Item = T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
Some(self.recv())
|
Some(self.recv())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
/// Macro for initializing the sync_channel with static buffer and indexes.
|
||||||
|
/// Note that this requires `#![feature(const_in_array_repeat_expressions)]`
|
||||||
|
macro_rules! sync_channel {
|
||||||
|
($t: ty, $cap: expr) => {
|
||||||
|
{
|
||||||
|
use core::sync::atomic::{AtomicUsize, AtomicPtr};
|
||||||
|
use $crate::sync_channel::{Sender, Receiver};
|
||||||
|
static LIST: [AtomicPtr<$t>; $cap + 1] = [AtomicPtr::new(core::ptr::null_mut()); $cap + 1];
|
||||||
|
static WRITE: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
static READ: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
(Sender::new(&LIST, &WRITE, &READ), Receiver::new(&LIST, &WRITE, &READ))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ impl<T> UncachedSlice<T> {
|
||||||
for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) {
|
for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) {
|
||||||
L1Table::get()
|
L1Table::get()
|
||||||
.update(page_start as *const (), |l1_section| {
|
.update(page_start as *const (), |l1_section| {
|
||||||
|
l1_section.tex = 0b100;
|
||||||
l1_section.cacheable = false;
|
l1_section.cacheable = false;
|
||||||
l1_section.bufferable = false;
|
l1_section.bufferable = false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,11 @@ edition = "2018"
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = ["libboard_zynq/target_zc706"]
|
target_zc706 = ["libboard_zynq/target_zc706"]
|
||||||
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10"]
|
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10"]
|
||||||
|
panic_handler = []
|
||||||
|
dummy_irq_handler = []
|
||||||
|
alloc_core = []
|
||||||
|
|
||||||
|
default = ["panic_handler", "dummy_irq_handler"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
r0 = "1"
|
r0 = "1"
|
||||||
|
|
|
@ -1,21 +1,70 @@
|
||||||
use libboard_zynq::{println, slcr, stdio};
|
use libregister::RegisterR;
|
||||||
|
use libcortex_a9::regs::{DFSR, MPIDR};
|
||||||
|
use libboard_zynq::{println, stdio};
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn PrefetchAbort() {
|
#[naked]
|
||||||
|
pub unsafe extern "C" fn UndefinedInstruction() {
|
||||||
stdio::drop_uart();
|
stdio::drop_uart();
|
||||||
|
println!("UndefinedInstruction");
|
||||||
println!("PrefetchAbort");
|
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
|
pub unsafe extern "C" fn SoftwareInterrupt() {
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("SoftwareInterrupt");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
|
pub unsafe extern "C" fn PrefetchAbort() {
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("PrefetchAbort");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
pub unsafe extern "C" fn DataAbort() {
|
pub unsafe extern "C" fn DataAbort() {
|
||||||
stdio::drop_uart();
|
stdio::drop_uart();
|
||||||
|
|
||||||
println!("DataAbort");
|
println!("DataAbort on core {}", MPIDR.read().cpu_id());
|
||||||
|
println!("DFSR: {:03X}", DFSR.read());
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
|
pub unsafe extern "C" fn ReservedException() {
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("ReservedException");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
|
#[cfg(feature = "dummy_irq_handler")]
|
||||||
|
pub unsafe extern "C" fn IRQ() {
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("IRQ");
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link_section = ".text.boot"]
|
||||||
|
#[no_mangle]
|
||||||
|
#[naked]
|
||||||
|
pub unsafe extern "C" fn FIQ() {
|
||||||
|
stdio::drop_uart();
|
||||||
|
println!("FIQ");
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use libregister::{
|
||||||
VolatileCell,
|
VolatileCell,
|
||||||
RegisterR, RegisterW, RegisterRW,
|
RegisterR, RegisterW, RegisterRW,
|
||||||
};
|
};
|
||||||
use libcortex_a9::{asm, regs::*, cache, mmu};
|
use libcortex_a9::{asm, regs::*, cache, mmu, spin_lock_yield, notify_spin_lock};
|
||||||
use libboard_zynq::{slcr, mpcore};
|
use libboard_zynq::{slcr, mpcore};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -21,17 +21,15 @@ static mut CORE1_ENABLED: VolatileCell<bool> = VolatileCell::new(false);
|
||||||
#[link_section = ".text.boot"]
|
#[link_section = ".text.boot"]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[naked]
|
#[naked]
|
||||||
pub unsafe extern "C" fn _boot_cores() -> ! {
|
pub unsafe extern "C" fn Reset() -> ! {
|
||||||
const CORE_MASK: u32 = 0x3;
|
match MPIDR.read().cpu_id() {
|
||||||
|
|
||||||
match MPIDR.read() & CORE_MASK {
|
|
||||||
0 => {
|
0 => {
|
||||||
SP.write(&mut __stack0_start as *mut _ as u32);
|
SP.write(&mut __stack0_start as *mut _ as u32);
|
||||||
boot_core0();
|
boot_core0();
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
while !CORE1_ENABLED.get() {
|
while !CORE1_ENABLED.get() {
|
||||||
asm::wfe();
|
spin_lock_yield();
|
||||||
}
|
}
|
||||||
SP.write(&mut __stack1_start as *mut _ as u32);
|
SP.write(&mut __stack1_start as *mut _ as u32);
|
||||||
boot_core1();
|
boot_core1();
|
||||||
|
@ -59,6 +57,7 @@ unsafe fn boot_core0() -> ! {
|
||||||
asm::dmb();
|
asm::dmb();
|
||||||
asm::dsb();
|
asm::dsb();
|
||||||
|
|
||||||
|
asm::enable_irq();
|
||||||
main_core0();
|
main_core0();
|
||||||
panic!("return from main");
|
panic!("return from main");
|
||||||
});
|
});
|
||||||
|
@ -79,6 +78,7 @@ unsafe fn boot_core1() -> ! {
|
||||||
asm::dmb();
|
asm::dmb();
|
||||||
asm::dsb();
|
asm::dsb();
|
||||||
|
|
||||||
|
asm::enable_irq();
|
||||||
main_core1();
|
main_core1();
|
||||||
panic!("return from main_core1");
|
panic!("return from main_core1");
|
||||||
});
|
});
|
||||||
|
@ -144,6 +144,7 @@ impl Core1 {
|
||||||
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
|
||||||
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
|
||||||
});
|
});
|
||||||
|
notify_spin_lock();
|
||||||
|
|
||||||
Core1 {}
|
Core1 {}
|
||||||
}
|
}
|
||||||
|
@ -151,6 +152,8 @@ impl Core1 {
|
||||||
pub fn disable(&self) {
|
pub fn disable(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
CORE1_ENABLED.set(false);
|
CORE1_ENABLED.set(false);
|
||||||
|
cache::dccmvac(&CORE1_ENABLED as *const _ as usize);
|
||||||
|
asm::dsb();
|
||||||
}
|
}
|
||||||
self.restart();
|
self.restart();
|
||||||
}
|
}
|
||||||
|
@ -158,7 +161,9 @@ impl Core1 {
|
||||||
pub fn restart(&self) {
|
pub fn restart(&self) {
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(true));
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(true));
|
||||||
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(true));
|
||||||
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
|
||||||
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,5 +9,6 @@ pub extern crate compiler_builtins;
|
||||||
|
|
||||||
pub mod boot;
|
pub mod boot;
|
||||||
mod abort;
|
mod abort;
|
||||||
|
#[cfg(feature = "panic_handler")]
|
||||||
mod panic;
|
mod panic;
|
||||||
pub mod ram;
|
pub mod ram;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use libboard_zynq::{slcr, print, println};
|
use libboard_zynq::{print, println};
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
@ -14,6 +14,5 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +1,97 @@
|
||||||
|
use alloc::alloc::Layout;
|
||||||
use core::alloc::GlobalAlloc;
|
use core::alloc::GlobalAlloc;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use alloc::alloc::Layout;
|
use libcortex_a9::{
|
||||||
|
mutex::Mutex,
|
||||||
|
regs::MPIDR
|
||||||
|
};
|
||||||
|
use libregister::RegisterR;
|
||||||
use linked_list_allocator::Heap;
|
use linked_list_allocator::Heap;
|
||||||
use libcortex_a9::mutex::Mutex;
|
#[cfg(not(feature = "alloc_core"))]
|
||||||
use libboard_zynq::ddr::DdrRam;
|
use libboard_zynq::ddr::DdrRam;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(Mutex::new(Heap::empty()));
|
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(
|
||||||
|
Mutex::new(Heap::empty()),
|
||||||
|
Mutex::new(Heap::empty()),
|
||||||
|
);
|
||||||
|
|
||||||
/// LockedHeap doesn't lock properly
|
struct CortexA9Alloc(Mutex<Heap>, Mutex<Heap>);
|
||||||
struct CortexA9Alloc(Mutex<Heap>);
|
|
||||||
|
|
||||||
unsafe impl Sync for CortexA9Alloc {}
|
unsafe impl Sync for CortexA9Alloc {}
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for CortexA9Alloc {
|
unsafe impl GlobalAlloc for CortexA9Alloc {
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
self.0.lock()
|
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
|
||||||
.allocate_first_fit(layout)
|
&self.0
|
||||||
.ok()
|
} else {
|
||||||
.map_or(0 as *mut u8, |allocation| allocation.as_ptr())
|
&self.1
|
||||||
|
}
|
||||||
|
.lock()
|
||||||
|
.allocate_first_fit(layout)
|
||||||
|
.ok()
|
||||||
|
.map_or(0 as *mut u8, |allocation| allocation.as_ptr())
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||||
self.0.lock()
|
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
|
||||||
.deallocate(NonNull::new_unchecked(ptr), layout)
|
&self.0
|
||||||
|
} else {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
.lock()
|
||||||
|
.deallocate(NonNull::new_unchecked(ptr), layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "alloc_core"))]
|
||||||
pub fn init_alloc_ddr(ddr: &mut DdrRam) {
|
pub fn init_alloc_ddr(ddr: &mut DdrRam) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ALLOCATOR.0.lock()
|
ALLOCATOR
|
||||||
|
.0
|
||||||
|
.lock()
|
||||||
.init(ddr.ptr::<u8>() as usize, ddr.size());
|
.init(ddr.ptr::<u8>() as usize, ddr.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static __heap_start: usize;
|
static __heap0_start: usize;
|
||||||
static __heap_end: usize;
|
static __heap0_end: usize;
|
||||||
|
#[cfg(feature = "alloc_core")]
|
||||||
|
static __heap1_start: usize;
|
||||||
|
#[cfg(feature = "alloc_core")]
|
||||||
|
static __heap1_end: usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_alloc_linker() {
|
pub fn init_alloc_core0() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let start = &__heap_start as *const usize as usize;
|
let start = &__heap0_start as *const usize as usize;
|
||||||
let end = &__heap_end as *const usize as usize;
|
let end = &__heap0_end as *const usize as usize;
|
||||||
ALLOCATOR.0.lock()
|
ALLOCATOR.0.lock().init(start, end - start);
|
||||||
.init(start, end - start);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc_core")]
|
||||||
|
pub fn init_alloc_core1() {
|
||||||
|
unsafe {
|
||||||
|
let start = &__heap1_start as *const usize as usize;
|
||||||
|
let end = &__heap1_end as *const usize as usize;
|
||||||
|
ALLOCATOR.1.lock().init(start, end - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[alloc_error_handler]
|
#[alloc_error_handler]
|
||||||
fn alloc_error(_: core::alloc::Layout) -> ! {
|
fn alloc_error(layout: core::alloc::Layout) -> ! {
|
||||||
panic!("alloc_error")
|
let id = MPIDR.read().cpu_id();
|
||||||
|
let used = if cfg!(not(feature = "alloc_core")) || id == 0 {
|
||||||
|
ALLOCATOR.0.lock().used()
|
||||||
|
} else {
|
||||||
|
ALLOCATOR.1.lock().used()
|
||||||
|
};
|
||||||
|
panic!(
|
||||||
|
"Core {} alloc_error, layout: {:?}, used memory: {}",
|
||||||
|
id,
|
||||||
|
layout,
|
||||||
|
used
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ stdenv.mkDerivation {
|
||||||
buildInputs = (with rustPlatform.rust; [
|
buildInputs = (with rustPlatform.rust; [
|
||||||
rustc cargo
|
rustc cargo
|
||||||
cargo-xbuild rustcSrc
|
cargo-xbuild rustcSrc
|
||||||
gcc
|
|
||||||
]) ++ (with pkgs; [ openocd gdb ]);
|
]) ++ (with pkgs; [ openocd gdb ]);
|
||||||
|
|
||||||
# Set Environment Variables
|
# Set Environment Variables
|
||||||
|
@ -20,6 +19,6 @@ stdenv.mkDerivation {
|
||||||
XARGO_RUST_SRC = "${rustcSrc}/src";
|
XARGO_RUST_SRC = "${rustcSrc}/src";
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
echo "Run 'cargo xbuild --release' to build."
|
echo "Run 'cargo xbuild --release -p experiments' to build."
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue