forked from M-Labs/zynq-rs
Compare commits
9 Commits
master
...
eth-uncach
Author | SHA1 | Date | |
---|---|---|---|
7c8ec46c8f | |||
8267acfcba | |||
7ae8be58cf | |||
7f3e75e20c | |||
8c26974816 | |||
c86d8b2af2 | |||
ac89367f8f | |||
0b99b0a864 | |||
4cb71e4f3d |
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -79,6 +79,7 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libregister 0.0.0",
|
"libregister 0.0.0",
|
||||||
|
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -98,13 +99,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.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"linked_list_allocator 0.8.3 (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.4"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -170,7 +171,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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
|
"checksum linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d6b60501dd4c850950bb43f970d544f6ce04e0ca021da2db2538fbe9d923f19e"
|
||||||
"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 -p experiments"
|
nix-shell --command "cargo xbuild --release"
|
||||||
```
|
```
|
||||||
|
|
||||||
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 -p experiments"
|
nix-shell --command "cargo xbuild --release"
|
||||||
cd openocd
|
cd openocd
|
||||||
openocd -f zc706.cfg
|
openocd -f zc706.cfg
|
||||||
```
|
```
|
||||||
@ -57,26 +57,23 @@ 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 ~/zynq-rs
|
cd ~/zc706
|
||||||
./build.sh $your_user_or_ssh_id
|
./build.sh $your_user/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 zynq-rs
|
cd zc706
|
||||||
# For ZC706, run:
|
|
||||||
./tmux.sh 0
|
|
||||||
# For Cora Z7, run:
|
|
||||||
./tmux.sh
|
./tmux.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"emit-debug-gdb-scripts": false,
|
"emit-debug-gdb-scripts": false,
|
||||||
"env": "",
|
"env": "",
|
||||||
"executables": true,
|
"executables": true,
|
||||||
"features": "+v7,+vfp3,-d32,+thumb2,-neon",
|
"features": "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align",
|
||||||
"is-builtin": false,
|
"is-builtin": false,
|
||||||
"linker": "rust-lld",
|
"linker": "rust-lld",
|
||||||
"linker-flavor": "ld.lld",
|
"linker-flavor": "ld.lld",
|
||||||
|
2
build.sh
2
build.sh
@ -1 +1 @@
|
|||||||
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
|
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
|
||||||
|
@ -49,7 +49,7 @@ let
|
|||||||
name = "${crate}";
|
name = "${crate}";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
crateSubdir = crate;
|
crateSubdir = crate;
|
||||||
cargoSha256 = "1c1qpg9by8bg93yhgllb5xs155g27qmh99pbrb681wazm8k7nwim";
|
cargoSha256 = "0xlynsr94dyv0g41qwk5490w3wnzd5g70msaih6mcbgr3v4s2q34";
|
||||||
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", default-features = false, features = ["panic_handler"]}
|
libsupport_zynq = { path = "../libsupport_zynq" }
|
||||||
libasync = { path = "../libasync" }
|
libasync = { path = "../libasync" }
|
||||||
|
@ -32,23 +32,19 @@ SECTIONS
|
|||||||
*(.bss .bss.*);
|
*(.bss .bss.*);
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
__bss_end = .;
|
__bss_end = .;
|
||||||
} > OCM3
|
} > OCM
|
||||||
|
|
||||||
.stack1 (NOLOAD) : ALIGN(8) {
|
.stack1 (NOLOAD) : ALIGN(8) {
|
||||||
__stack1_end = .;
|
__stack1_end = .;
|
||||||
. += 0x200;
|
. += 0x200;
|
||||||
__stack1_start = .;
|
__stack1_start = .;
|
||||||
} > OCM3
|
} > OCM
|
||||||
|
|
||||||
.stack0 (NOLOAD) : ALIGN(8) {
|
.stack0 (NOLOAD) : ALIGN(8) {
|
||||||
__stack0_end = .;
|
__stack0_end = .;
|
||||||
. = ORIGIN(OCM3) + LENGTH(OCM3) - 8;
|
. = ORIGIN(OCM) + LENGTH(OCM) - 8;
|
||||||
__stack0_start = .;
|
__stack0_start = .;
|
||||||
|
} > OCM
|
||||||
/* unused heap0 to prevent the linker from complaining*/
|
|
||||||
__heap0_start = .;
|
|
||||||
__heap0_end = .;
|
|
||||||
} > OCM3
|
|
||||||
|
|
||||||
/DISCARD/ :
|
/DISCARD/ :
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(const_in_array_repeat_expressions)]
|
#![feature(const_in_array_repeat_expressions)]
|
||||||
#![feature(naked_functions)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
@ -15,9 +14,9 @@ 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, stdio,
|
print, println,
|
||||||
mpcore,
|
setup_l2cache,
|
||||||
gic,
|
sdio::sd_card::SdCard,
|
||||||
smoltcp::{
|
smoltcp::{
|
||||||
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
|
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
|
||||||
time::Instant,
|
time::Instant,
|
||||||
@ -25,70 +24,24 @@ use libboard_zynq::{
|
|||||||
},
|
},
|
||||||
time::Milliseconds,
|
time::Milliseconds,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
use libboard_zynq::ps7_init;
|
||||||
use libcortex_a9::{
|
use libcortex_a9::{
|
||||||
mutex::Mutex,
|
mutex::Mutex,
|
||||||
sync_channel::{Sender, Receiver},
|
|
||||||
sync_channel,
|
sync_channel,
|
||||||
regs::{MPIDR, SP},
|
|
||||||
spin_lock_yield, notify_spin_lock,
|
|
||||||
asm
|
|
||||||
};
|
};
|
||||||
use libregister::{RegisterR, RegisterW};
|
use libregister::RegisterR;
|
||||||
use libsupport_zynq::{
|
use libsupport_zynq::{
|
||||||
boot, ram,
|
boot, ram,
|
||||||
};
|
};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
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();
|
// ps7_init::apply();
|
||||||
libboard_zynq::stdio::drop_uart();
|
libboard_zynq::stdio::drop_uart();
|
||||||
|
|
||||||
@ -130,6 +83,29 @@ pub fn main_core0() {
|
|||||||
clocks.cpu_2x(),
|
clocks.cpu_2x(),
|
||||||
clocks.cpu_1x()
|
clocks.cpu_1x()
|
||||||
);
|
);
|
||||||
|
info!("Setup L2Cache");
|
||||||
|
setup_l2cache();
|
||||||
|
info!("L2Cache done");
|
||||||
|
|
||||||
|
// commented out due to OCM full
|
||||||
|
// 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();
|
||||||
|
// if false {
|
||||||
|
// 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(0 /*0x1*/, 2, &mut buffer[1..]).unwrap();
|
||||||
|
// info!("buffer = {:?}", &buffer[..]);
|
||||||
|
// }
|
||||||
|
|
||||||
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()) };
|
||||||
@ -149,6 +125,7 @@ pub fn main_core0() {
|
|||||||
ddr.memtest();
|
ddr.memtest();
|
||||||
ram::init_alloc_ddr(&mut ddr);
|
ram::init_alloc_ddr(&mut ddr);
|
||||||
|
|
||||||
|
if false {
|
||||||
#[cfg(dev)]
|
#[cfg(dev)]
|
||||||
for i in 0..=1 {
|
for i in 0..=1 {
|
||||||
let mut flash_io = flash.manual_mode(i);
|
let mut flash_io = flash.manual_mode(i);
|
||||||
@ -188,62 +165,29 @@ pub fn main_core0() {
|
|||||||
flash = flash_io.stop();
|
flash = flash_io.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
boot::Core1::start(false);
|
let core1 = boot::Core1::start(false);
|
||||||
|
|
||||||
let core1_req = unsafe { &mut CORE1_REQ.0 };
|
let (mut core1_req, rx) = sync_channel!(usize, 10);
|
||||||
let core1_res = unsafe { &mut CORE1_RES.1 };
|
*CORE1_REQ.lock() = Some(rx);
|
||||||
|
let (tx, mut core1_res) = sync_channel!(usize, 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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
unsafe {
|
core1.disable();
|
||||||
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 = 4096;
|
const RX_LEN: usize = 8192;
|
||||||
// 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 = 4096;
|
const TX_LEN: usize = 8192;
|
||||||
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);
|
||||||
|
|
||||||
@ -260,6 +204,9 @@ 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);
|
||||||
|
|
||||||
const TCP_PORT: u16 = 19;
|
const TCP_PORT: u16 = 19;
|
||||||
@ -270,7 +217,7 @@ pub fn main_core0() {
|
|||||||
while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
|
while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
|
||||||
let stats_tx = stats_tx.clone();
|
let stats_tx = stats_tx.clone();
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let tx_data = (0..=255).take(4096).collect::<alloc::vec::Vec<u8>>();
|
let tx_data = (0..=255).take(8192).collect::<alloc::vec::Vec<u8>>();
|
||||||
loop {
|
loop {
|
||||||
// const CHUNK_SIZE: usize = 65536;
|
// const CHUNK_SIZE: usize = 65536;
|
||||||
// match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
|
// match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
|
||||||
@ -326,15 +273,24 @@ 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());
|
|
||||||
interrupt_controller.enable_interrupts();
|
let mut req = None;
|
||||||
let req = unsafe { &mut CORE1_REQ.1 };
|
while req.is_none() {
|
||||||
let res = unsafe { &mut CORE1_RES.0 };
|
req = CORE1_REQ.lock().take();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
@ -21,5 +21,5 @@ libcortex_a9 = { path = "../libcortex_a9" }
|
|||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.6"
|
version = "0.6"
|
||||||
features = ["ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"]
|
features = ["ethernet", "proto-ipv4", "socket-tcp"]
|
||||||
default-features = false
|
default-features = false
|
||||||
|
@ -2,7 +2,7 @@ use core::{
|
|||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
};
|
};
|
||||||
use log::{debug, info, warn, error};
|
use log::{error, info, warn};
|
||||||
use libregister::*;
|
use libregister::*;
|
||||||
use super::slcr;
|
use super::slcr;
|
||||||
use super::clocks::Clocks;
|
use super::clocks::Clocks;
|
||||||
@ -132,10 +132,10 @@ fn calculate_tx_divisors(tx_clock: u32) -> (u8, u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let result = best.unwrap();
|
let result = best.unwrap();
|
||||||
debug!("Eth TX clock for {}: {} / {} / {} = {}",
|
info!("Eth TX clock for {}: {} / {} / {} = {}",
|
||||||
tx_clock, io_pll,
|
tx_clock, io_pll,
|
||||||
result.0, result.1,
|
result.0, result.1,
|
||||||
io_pll / result.0 as u32 / result.1 as u32
|
io_pll / result.0 as u32 / result.1 as u32
|
||||||
);
|
);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ use core::ops::Deref;
|
|||||||
use alloc::{vec, vec::Vec};
|
use alloc::{vec, vec::Vec};
|
||||||
use libcortex_a9::{asm::*, cache::*, UncachedSlice};
|
use libcortex_a9::{asm::*, cache::*, UncachedSlice};
|
||||||
use libregister::*;
|
use libregister::*;
|
||||||
|
use log::debug;
|
||||||
|
use crate::l2cache;
|
||||||
use super::Buffer;
|
use super::Buffer;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -57,7 +59,7 @@ register_bit!(desc_word1, global_broadcast, 31);
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct DescList {
|
pub struct DescList {
|
||||||
list: UncachedSlice<DescEntry>,
|
list: UncachedSlice<DescEntry>,
|
||||||
buffers: Vec<Buffer>,
|
buffers: UncachedSlice<Buffer>,
|
||||||
next: usize,
|
next: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +67,8 @@ impl DescList {
|
|||||||
pub fn new(size: usize) -> Self {
|
pub fn new(size: usize) -> Self {
|
||||||
let mut list = UncachedSlice::new(size, || DescEntry::zeroed())
|
let mut list = UncachedSlice::new(size, || DescEntry::zeroed())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut buffers = vec![Buffer::new(); size];
|
let mut buffers = UncachedSlice::new(size, || Buffer::new())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let last = list.len().min(buffers.len()) - 1;
|
let last = list.len().min(buffers.len()) - 1;
|
||||||
for (i, (entry, buffer)) in list.iter_mut().zip(buffers.iter_mut()).enumerate() {
|
for (i, (entry, buffer)) in list.iter_mut().zip(buffers.iter_mut()).enumerate() {
|
||||||
@ -81,9 +84,8 @@ impl DescList {
|
|||||||
entry.word1.write(
|
entry.word1.write(
|
||||||
DescWord1::zeroed()
|
DescWord1::zeroed()
|
||||||
);
|
);
|
||||||
// Flush buffer from cache, to be filled by the peripheral
|
// l2cache().invalidate_slice(&mut buffer[..]);
|
||||||
// before next read
|
// dcci_slice(&buffer[..]);
|
||||||
dcci_slice(&buffer[..]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DescList {
|
DescList {
|
||||||
@ -109,6 +111,9 @@ impl DescList {
|
|||||||
let word1 = entry.word1.read();
|
let word1 = entry.word1.read();
|
||||||
let len = word1.frame_length_lsbs().into();
|
let len = word1.frame_length_lsbs().into();
|
||||||
let buffer = &mut self.buffers[self.next][0..len];
|
let buffer = &mut self.buffers[self.next][0..len];
|
||||||
|
// // Invalidate caches for packet buffer
|
||||||
|
// l2cache().invalidate_slice(&mut buffer[..]);
|
||||||
|
// dcci_slice(&buffer[..]);
|
||||||
|
|
||||||
self.next += 1;
|
self.next += 1;
|
||||||
if self.next >= list_len {
|
if self.next >= list_len {
|
||||||
@ -117,8 +122,10 @@ impl DescList {
|
|||||||
|
|
||||||
let pkt = PktRef { entry, buffer };
|
let pkt = PktRef { entry, buffer };
|
||||||
if word1.start_of_frame() && word1.end_of_frame() {
|
if word1.start_of_frame() && word1.end_of_frame() {
|
||||||
|
// debug!("pkt {}: {:08X}..{:08X}", len, &pkt.buffer[0] as *const _ as usize, &pkt.buffer[pkt.len()-1] as *const _ as usize);
|
||||||
Ok(Some(pkt))
|
Ok(Some(pkt))
|
||||||
} else {
|
} else {
|
||||||
|
debug!("pkt trunc");
|
||||||
Err(Error::Truncated)
|
Err(Error::Truncated)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -135,9 +142,10 @@ pub struct PktRef<'a> {
|
|||||||
|
|
||||||
impl<'a> Drop for PktRef<'a> {
|
impl<'a> Drop for PktRef<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Flush buffer from cache, to be filled by the peripheral
|
// // Flush buffer from cache, to be filled by the peripheral
|
||||||
// before next read
|
// // before next read
|
||||||
dcci_slice(self.buffer);
|
// l2cache().invalidate_slice(self.buffer);
|
||||||
|
// dcci_slice(self.buffer);
|
||||||
|
|
||||||
self.entry.word0.modify(|_, w| w.used(false));
|
self.entry.word0.modify(|_, w| w.used(false));
|
||||||
dmb();
|
dmb();
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
use alloc::{vec, vec::Vec};
|
use alloc::{vec, vec::Vec};
|
||||||
use libcortex_a9::{cache::dcc_slice, UncachedSlice};
|
use libcortex_a9::{asm::dmb, cache::dcc_slice, UncachedSlice};
|
||||||
|
//use libcortex_a9::{asm::*, cache::*, UncachedSlice};
|
||||||
use libregister::*;
|
use libregister::*;
|
||||||
|
use log::{debug, warn};
|
||||||
|
use crate::l2cache;
|
||||||
use super::{Buffer, regs};
|
use super::{Buffer, regs};
|
||||||
|
|
||||||
/// Descriptor entry
|
/// Descriptor entry
|
||||||
@ -44,7 +47,7 @@ pub const DESCS: usize = 8;
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct DescList {
|
pub struct DescList {
|
||||||
list: UncachedSlice<DescEntry>,
|
list: UncachedSlice<DescEntry>,
|
||||||
buffers: Vec<Buffer>,
|
buffers: UncachedSlice<Buffer>,
|
||||||
next: usize,
|
next: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +55,8 @@ impl DescList {
|
|||||||
pub fn new(size: usize) -> Self {
|
pub fn new(size: usize) -> Self {
|
||||||
let mut list = UncachedSlice::new(size, || DescEntry::zeroed())
|
let mut list = UncachedSlice::new(size, || DescEntry::zeroed())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut buffers = vec![Buffer::new(); size];
|
let mut buffers = UncachedSlice::new(size, || Buffer::new())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let last = list.len().min(buffers.len()) - 1;
|
let last = list.len().min(buffers.len()) - 1;
|
||||||
// Sending seems to not work properly with only one packet
|
// Sending seems to not work properly with only one packet
|
||||||
@ -94,8 +98,10 @@ impl DescList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn send<'s: 'p, 'p>(&'s mut self, regs: &'s mut regs::RegisterBlock, length: usize) -> Option<PktRef<'p>> {
|
pub fn send<'s: 'p, 'p>(&'s mut self, regs: &'s mut regs::RegisterBlock, length: usize) -> Option<PktRef<'p>> {
|
||||||
|
// debug!("send {}", length);
|
||||||
let list_len = self.list.len();
|
let list_len = self.list.len();
|
||||||
let entry = &mut self.list[self.next];
|
let entry = &mut self.list[self.next];
|
||||||
|
dmb();
|
||||||
if entry.word1.read().used() {
|
if entry.word1.read().used() {
|
||||||
let buffer = &mut self.buffers[self.next][0..length];
|
let buffer = &mut self.buffers[self.next][0..length];
|
||||||
entry.word1.write(DescWord1::zeroed()
|
entry.word1.write(DescWord1::zeroed()
|
||||||
@ -113,6 +119,7 @@ impl DescList {
|
|||||||
Some(PktRef { entry, buffer, regs })
|
Some(PktRef { entry, buffer, regs })
|
||||||
} else {
|
} else {
|
||||||
// Still in use by HW (sending too fast, ring exceeded)
|
// Still in use by HW (sending too fast, ring exceeded)
|
||||||
|
warn!("tx ring overflow");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,10 +135,13 @@ pub struct PktRef<'a> {
|
|||||||
|
|
||||||
impl<'a> Drop for PktRef<'a> {
|
impl<'a> Drop for PktRef<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Write back all dirty cachelines of this buffer
|
// // Write back all dirty cachelines of packet buffer
|
||||||
dcc_slice(self.buffer);
|
// dcc_slice(self.buffer);
|
||||||
|
// l2cache().clean_slice(self.buffer);
|
||||||
|
|
||||||
self.entry.word1.modify(|_, w| w.used(false));
|
self.entry.word1.modify(|_, w| w.used(false));
|
||||||
|
dmb();
|
||||||
|
// dsb();
|
||||||
if ! self.regs.tx_status.read().tx_go() {
|
if ! self.regs.tx_status.read().tx_go() {
|
||||||
// Start TX if not already running
|
// Start TX if not already running
|
||||||
self.regs.net_ctrl.modify(|_, w| w.start_tx(true));
|
self.regs.net_ctrl.modify(|_, w| w.start_tx(true));
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
//! ARM Generic Interrupt Controller
|
|
||||||
|
|
||||||
use bit_field::BitField;
|
|
||||||
use libregister::{RegisterW, RegisterRW, RegisterR};
|
|
||||||
use super::mpcore;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct InterruptId(pub u8);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum CPUCore {
|
|
||||||
Core0 = 0b01,
|
|
||||||
Core1 = 0b10
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct TargetCPU(u8);
|
|
||||||
|
|
||||||
impl TargetCPU {
|
|
||||||
pub const fn none() -> TargetCPU {
|
|
||||||
TargetCPU(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn and(self, other: TargetCPU) -> TargetCPU {
|
|
||||||
TargetCPU(self.0 | other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CPUCore> for TargetCPU {
|
|
||||||
fn from(core: CPUCore) -> Self {
|
|
||||||
TargetCPU(core as u8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum TargetList {
|
|
||||||
CPUList(TargetCPU),
|
|
||||||
Others,
|
|
||||||
This
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CPUCore> for TargetList {
|
|
||||||
fn from(core: CPUCore) -> Self {
|
|
||||||
TargetList::CPUList(TargetCPU(core as u8))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TargetCPU> for TargetList {
|
|
||||||
fn from(cpu: TargetCPU) -> Self {
|
|
||||||
TargetList::CPUList(cpu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum InterruptSensitivity {
|
|
||||||
Level,
|
|
||||||
Edge,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InterruptController {
|
|
||||||
mpcore: &'static mut mpcore::RegisterBlock,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InterruptController {
|
|
||||||
pub fn new(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
|
|
||||||
InterruptController { mpcore }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable_interrupts(&mut self) {
|
|
||||||
self.mpcore.iccicr.modify(|_, w| w.enable_ns(false)
|
|
||||||
.enable_s(false));
|
|
||||||
// FIXME: Should we disable the distributor globally when we disable interrupt (for a single
|
|
||||||
// core)?
|
|
||||||
// self.mpcore.icddcr.modify(|_, w| w.enable_secure(false)
|
|
||||||
// .enable_non_secure(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// enable interrupt signaling
|
|
||||||
pub fn enable_interrupts(&mut self) {
|
|
||||||
self.mpcore.iccicr.modify(|_, w| w.enable_ns(true)
|
|
||||||
.enable_s(true));
|
|
||||||
self.mpcore.icddcr.modify(|_, w| w.enable_secure(true));
|
|
||||||
|
|
||||||
// Enable all interrupts except those of the lowest priority.
|
|
||||||
self.mpcore.iccpmr.write(mpcore::ICCPMR::zeroed().priority(0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// send software generated interrupt
|
|
||||||
pub fn send_sgi(&mut self, id: InterruptId, targets: TargetList) {
|
|
||||||
assert!(id.0 < 16);
|
|
||||||
self.mpcore.icdsgir.modify(|_, w| match targets {
|
|
||||||
TargetList::CPUList(list) => w.target_list_filter(0).cpu_target_list(list.0),
|
|
||||||
TargetList::Others => w.target_list_filter(0b01),
|
|
||||||
TargetList::This => w.target_list_filter(0b10)
|
|
||||||
}.sgiintid(id.0).satt(false));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// enable the interrupt *for this core*.
|
|
||||||
/// Not needed for SGI.
|
|
||||||
pub fn enable(&mut self, id: InterruptId, target_cpu: CPUCore, sensitivity: InterruptSensitivity, priority: u8) {
|
|
||||||
// only 5 bits of the priority is useful
|
|
||||||
assert!(priority < 32);
|
|
||||||
|
|
||||||
self.disable_interrupts();
|
|
||||||
|
|
||||||
// enable
|
|
||||||
let m = (id.0 >> 5) as usize;
|
|
||||||
let n = (id.0 & 0x1F) as usize;
|
|
||||||
assert!(m < 3);
|
|
||||||
unsafe {
|
|
||||||
self.mpcore.icdiser[m].modify(|mut icdiser| *icdiser.set_bit(n, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// target cpu
|
|
||||||
let m = (id.0 >> 2) as usize;
|
|
||||||
let n = (8 * (id.0 & 3)) as usize;
|
|
||||||
unsafe {
|
|
||||||
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32 + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// sensitivity
|
|
||||||
let m = (id.0 >> 4) as usize;
|
|
||||||
let n = (2 * (id.0 & 0xF)) as usize;
|
|
||||||
unsafe {
|
|
||||||
self.mpcore.icdicfr[m].modify(|mut icdicfr| *icdicfr.set_bits(n..=n+1, match sensitivity {
|
|
||||||
InterruptSensitivity::Level => 0b00,
|
|
||||||
InterruptSensitivity::Edge => 0b10,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// priority
|
|
||||||
let offset = (id.0 % 4) * 8;
|
|
||||||
let priority: u32 = (priority as u32) << (offset + 3);
|
|
||||||
let mask: u32 = 0xFFFFFFFF ^ (0xFF << offset);
|
|
||||||
unsafe {
|
|
||||||
self.mpcore.icdipr[id.0 as usize / 4].modify(|v| (v & mask) | priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.enable_interrupts();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end_interrupt(&mut self, id: InterruptId) {
|
|
||||||
self.mpcore.icceoir.modify(|_, w| w.eoiintid(id.0 as u32));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_interrupt_id(&self) -> InterruptId {
|
|
||||||
InterruptId(self.mpcore.icciar.read().ackintid() as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
use super::I2C;
|
|
||||||
use crate::time::Milliseconds;
|
|
||||||
use embedded_hal::timer::CountDown;
|
|
||||||
|
|
||||||
pub struct EEPROM<'a> {
|
|
||||||
i2c: &'a mut I2C,
|
|
||||||
port: u8,
|
|
||||||
address: u8,
|
|
||||||
page_size: u8,
|
|
||||||
count_down: crate::timer::global::CountDown<Milliseconds>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> EEPROM<'a> {
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
|
|
||||||
EEPROM {
|
|
||||||
i2c: i2c,
|
|
||||||
port: 2,
|
|
||||||
address: 0b1010100,
|
|
||||||
page_size: page_size,
|
|
||||||
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
fn select(&mut self) -> Result<(), &'static str> {
|
|
||||||
let mask: u16 = 1 << self.port;
|
|
||||||
self.i2c.pca9548_select(0b1110100, mask as u8)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Random read
|
|
||||||
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
|
|
||||||
self.select()?;
|
|
||||||
|
|
||||||
self.i2c.start()?;
|
|
||||||
self.i2c.write(self.address << 1)?;
|
|
||||||
self.i2c.write(addr)?;
|
|
||||||
|
|
||||||
self.i2c.restart()?;
|
|
||||||
self.i2c.write((self.address << 1) | 1)?;
|
|
||||||
let buf_len = buf.len();
|
|
||||||
for (i, byte) in buf.iter_mut().enumerate() {
|
|
||||||
*byte = self.i2c.read(i < buf_len - 1)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.i2c.stop()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Smart multi-page writing
|
|
||||||
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
|
|
||||||
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
|
|
||||||
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
|
|
||||||
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
|
|
||||||
/// any roll-over when writing ambiguous memory regions.
|
|
||||||
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
|
|
||||||
self.select()?;
|
|
||||||
|
|
||||||
let buf_len = buf.len();
|
|
||||||
let mut pb: u8 = addr % self.page_size;
|
|
||||||
for (i, byte) in buf.iter().enumerate() {
|
|
||||||
if (i == 0) || (pb == 0) {
|
|
||||||
self.i2c.start()?;
|
|
||||||
self.i2c.write(self.address << 1)?;
|
|
||||||
self.i2c.write(addr + (i as u8))?;
|
|
||||||
}
|
|
||||||
self.i2c.write(*byte)?;
|
|
||||||
pb += 1;
|
|
||||||
|
|
||||||
if (i == buf_len-1) || (pb == self.page_size) {
|
|
||||||
self.i2c.stop()?;
|
|
||||||
self.poll(1_000)?;
|
|
||||||
pb = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Poll
|
|
||||||
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
|
|
||||||
self.select()?;
|
|
||||||
|
|
||||||
self.count_down.start(Milliseconds(timeout_ms));
|
|
||||||
loop {
|
|
||||||
self.i2c.start()?;
|
|
||||||
let ack = self.i2c.write(self.address << 1)?;
|
|
||||||
self.i2c.stop()?;
|
|
||||||
if ack {
|
|
||||||
break
|
|
||||||
};
|
|
||||||
if !self.count_down.waiting() {
|
|
||||||
return Err("I2C polling timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_eui48<'r>(&mut self) -> Result<[u8; 6], &'static str> {
|
|
||||||
let mut buffer = [0u8; 6];
|
|
||||||
self.read(0xFA, &mut buffer)?;
|
|
||||||
Ok(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,235 +0,0 @@
|
|||||||
//! I2C Bit-banging Controller
|
|
||||||
|
|
||||||
mod regs;
|
|
||||||
pub mod eeprom;
|
|
||||||
use super::clocks::Clocks;
|
|
||||||
use super::slcr;
|
|
||||||
use super::time::Microseconds;
|
|
||||||
use embedded_hal::timer::CountDown;
|
|
||||||
use libregister::{RegisterR, RegisterRW, RegisterW};
|
|
||||||
|
|
||||||
const INVALID_BUS: &'static str = "Invalid I2C bus";
|
|
||||||
|
|
||||||
pub struct I2C {
|
|
||||||
regs: regs::RegisterWrapper,
|
|
||||||
count_down: super::timer::global::CountDown<Microseconds>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl I2C {
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
pub fn i2c() -> Self {
|
|
||||||
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
|
|
||||||
slcr::RegisterBlock::unlocked(|slcr| {
|
|
||||||
// SCL
|
|
||||||
slcr.mio_pin_50.write(
|
|
||||||
slcr::MioPin50::zeroed()
|
|
||||||
.l3_sel(0b000) // as GPIO 50
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
.disable_rcvr(true)
|
|
||||||
);
|
|
||||||
// SDA
|
|
||||||
slcr.mio_pin_51.write(
|
|
||||||
slcr::MioPin51::zeroed()
|
|
||||||
.l3_sel(0b000) // as GPIO 51
|
|
||||||
.io_type(slcr::IoBufferType::Lvcmos18)
|
|
||||||
.pullup(true)
|
|
||||||
.disable_rcvr(true)
|
|
||||||
);
|
|
||||||
// Reset
|
|
||||||
slcr.gpio_rst_ctrl.reset_gpio();
|
|
||||||
});
|
|
||||||
|
|
||||||
Self::ctor_common(0xFFFF - 0x000C)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ctor_common(gpio_output_mask: u16) -> Self {
|
|
||||||
// Setup register block
|
|
||||||
let clocks = Clocks::get();
|
|
||||||
let self_ = Self {
|
|
||||||
regs: regs::RegisterWrapper::new(),
|
|
||||||
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setup GPIO output mask
|
|
||||||
self_.regs.gpio_output_mask.modify(|_, w| {
|
|
||||||
w.mask(gpio_output_mask)
|
|
||||||
});
|
|
||||||
// Setup GPIO driver direction
|
|
||||||
self_.regs.gpio_direction.modify(|_, w| {
|
|
||||||
w.scl(true).sda(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
self_
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delay for I2C operations, simple wrapper for nb.
|
|
||||||
fn delay_us(&mut self, us: u64) {
|
|
||||||
self.count_down.start(Microseconds(us));
|
|
||||||
nb::block!(self.count_down.wait()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn half_period(&mut self) { self.delay_us(100) }
|
|
||||||
|
|
||||||
fn sda_i(&mut self) -> bool {
|
|
||||||
self.regs.gpio_input.read().sda()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scl_i(&mut self) -> bool {
|
|
||||||
self.regs.gpio_input.read().scl()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sda_oe(&mut self, oe: bool) {
|
|
||||||
self.regs.gpio_output_enable.modify(|_, w| {
|
|
||||||
w.sda(oe)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sda_o(&mut self, o: bool) {
|
|
||||||
self.regs.gpio_output_mask.modify(|_, w| {
|
|
||||||
w.sda_o(o)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scl_oe(&mut self, oe: bool) {
|
|
||||||
self.regs.gpio_output_enable.modify(|_, w| {
|
|
||||||
w.scl(oe)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scl_o(&mut self, o: bool) {
|
|
||||||
self.regs.gpio_output_mask.modify(|_, w| {
|
|
||||||
w.scl_o(o)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(&mut self) -> Result<(), &'static str> {
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.sda_oe(false);
|
|
||||||
self.scl_o(false);
|
|
||||||
self.sda_o(false);
|
|
||||||
|
|
||||||
// Check the I2C bus is ready
|
|
||||||
self.half_period();
|
|
||||||
self.half_period();
|
|
||||||
if !self.sda_i() {
|
|
||||||
// Try toggling SCL a few times
|
|
||||||
for _bit in 0..8 {
|
|
||||||
self.scl_oe(true);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.sda_i() {
|
|
||||||
return Err("SDA is stuck low and doesn't get unstuck");
|
|
||||||
}
|
|
||||||
if !self.scl_i() {
|
|
||||||
return Err("SCL is stuck low and doesn't get unstuck");
|
|
||||||
}
|
|
||||||
// postcondition: SCL and SDA high
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&mut self) -> Result<(), &'static str> {
|
|
||||||
// precondition: SCL and SDA high
|
|
||||||
if !self.scl_i() {
|
|
||||||
return Err("SCL is stuck low and doesn't get unstuck");
|
|
||||||
}
|
|
||||||
if !self.sda_i() {
|
|
||||||
return Err("SDA arbitration lost");
|
|
||||||
}
|
|
||||||
self.sda_oe(true);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(true);
|
|
||||||
// postcondition: SCL and SDA low
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn restart(&mut self) -> Result<(), &'static str> {
|
|
||||||
// precondition SCL and SDA low
|
|
||||||
self.sda_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
self.start()?;
|
|
||||||
// postcondition: SCL and SDA low
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stop(&mut self) -> Result<(), &'static str> {
|
|
||||||
// precondition: SCL and SDA low
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
self.sda_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
if !self.sda_i() {
|
|
||||||
return Err("SDA arbitration lost");
|
|
||||||
}
|
|
||||||
// postcondition: SCL and SDA high
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&mut self, data: u8) -> Result<bool, &'static str> {
|
|
||||||
// precondition: SCL and SDA low
|
|
||||||
// MSB first
|
|
||||||
for bit in (0..8).rev() {
|
|
||||||
self.sda_oe(data & (1 << bit) == 0);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(true);
|
|
||||||
}
|
|
||||||
self.sda_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
// Read ack/nack
|
|
||||||
let ack = !self.sda_i();
|
|
||||||
self.scl_oe(true);
|
|
||||||
self.sda_oe(true);
|
|
||||||
// postcondition: SCL and SDA low
|
|
||||||
|
|
||||||
Ok(ack)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self, ack: bool) -> Result<u8, &'static str> {
|
|
||||||
// precondition: SCL and SDA low
|
|
||||||
self.sda_oe(false);
|
|
||||||
|
|
||||||
let mut data: u8 = 0;
|
|
||||||
|
|
||||||
// MSB first
|
|
||||||
for bit in (0..8).rev() {
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
if self.sda_i() { data |= 1 << bit }
|
|
||||||
self.scl_oe(true);
|
|
||||||
}
|
|
||||||
// Send ack/nack
|
|
||||||
self.sda_oe(ack);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(false);
|
|
||||||
self.half_period();
|
|
||||||
self.scl_oe(true);
|
|
||||||
self.sda_oe(true);
|
|
||||||
// postcondition: SCL and SDA low
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pca9548_select(&mut self, address: u8, channels: u8) -> Result<(), &'static str> {
|
|
||||||
self.start()?;
|
|
||||||
if !self.write(address << 1)? {
|
|
||||||
return Err("PCA9548 failed to ack write address")
|
|
||||||
}
|
|
||||||
if !self.write(channels)? {
|
|
||||||
return Err("PCA9548 failed to ack control word")
|
|
||||||
}
|
|
||||||
self.stop()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
use volatile_register::{RO, WO, RW};
|
|
||||||
|
|
||||||
use libregister::{
|
|
||||||
register, register_at,
|
|
||||||
register_bit, register_bits
|
|
||||||
};
|
|
||||||
|
|
||||||
// With reference to:
|
|
||||||
//
|
|
||||||
// artiq:artiq/gateware/targets/kasli.py:
|
|
||||||
// self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
|
|
||||||
//
|
|
||||||
// misoc:misoc/cores/gpio.py:
|
|
||||||
// class GPIOTristate(Module, AutoCSR):
|
|
||||||
// def __init__(self, signals, reset_out=0, reset_oe=0):
|
|
||||||
// l = len(signals)
|
|
||||||
// self._in = CSRStatus(l)
|
|
||||||
// self._out = CSRStorage(l, reset=reset_out)
|
|
||||||
// self._oe = CSRStorage(l, reset=reset_oe)
|
|
||||||
//
|
|
||||||
// Hence, using GPIOs as SCL and SDA GPIOs respectively.
|
|
||||||
//
|
|
||||||
// Current compatibility:
|
|
||||||
// zc706: GPIO 50, 51 == SCL, SDA
|
|
||||||
|
|
||||||
pub struct RegisterWrapper {
|
|
||||||
pub gpio_output_mask: &'static mut GPIOOutputMask,
|
|
||||||
pub gpio_input: &'static mut GPIOInput,
|
|
||||||
pub gpio_direction: &'static mut GPIODirection,
|
|
||||||
pub gpio_output_enable: &'static mut GPIOOutputEnable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RegisterWrapper {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
gpio_output_mask: GPIOOutputMask::new(),
|
|
||||||
gpio_input: GPIOInput::new(),
|
|
||||||
gpio_direction: GPIODirection::new(),
|
|
||||||
gpio_output_enable: GPIOOutputEnable::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MASK_DATA_1_MSW:
|
|
||||||
// Maskable output data for MIO[53:48]
|
|
||||||
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_at!(GPIOOutputMask, 0xE000A00C, new);
|
|
||||||
// Output for SCL
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_output_mask, scl_o, 2);
|
|
||||||
// Output for SDA
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_output_mask, sda_o, 3);
|
|
||||||
// Mask for keeping bits except SCL and SDA unchanged
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bits!(gpio_output_mask, mask, u16, 16, 31);
|
|
||||||
|
|
||||||
// DATA_1_RO:
|
|
||||||
// Input data for MIO[53:32]
|
|
||||||
register!(gpio_input, GPIOInput, RO, u32);
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_at!(GPIOInput, 0xE000A064, new);
|
|
||||||
// Input for SCL
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_input, scl, 18);
|
|
||||||
// Input for SDA
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_input, sda, 19);
|
|
||||||
|
|
||||||
// DIRM_1:
|
|
||||||
// Direction mode for MIO[53:32]; 0/1 = in/out
|
|
||||||
register!(gpio_direction, GPIODirection, RW, u32);
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_at!(GPIODirection, 0xE000A244, new);
|
|
||||||
// Direction for SCL
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_direction, scl, 18);
|
|
||||||
// Direction for SDA
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_direction, sda, 19);
|
|
||||||
|
|
||||||
// OEN_1:
|
|
||||||
// Output enable for MIO[53:32]
|
|
||||||
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_at!(GPIOOutputEnable, 0xE000A248, new);
|
|
||||||
// Output enable for SCL
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_output_enable, scl, 18);
|
|
||||||
// Output enable for SDA
|
|
||||||
#[cfg(feature = "target_zc706")]
|
|
||||||
register_bit!(gpio_output_enable, sda, 19);
|
|
@ -15,12 +15,37 @@ 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 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;
|
pub mod ps7_init;
|
||||||
|
|
||||||
|
pub use libcortex_a9::pl310::L2Cache;
|
||||||
|
|
||||||
|
pub fn l2cache() -> L2Cache {
|
||||||
|
const PL310_BASEADDR: usize = 0xF8F02000;
|
||||||
|
L2Cache::new(PL310_BASEADDR)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_l2cache() {
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
assert_eq!(&slcr.unnamed1 as *const _ as u32, 0xF8000A1C);
|
||||||
|
unsafe { slcr.unnamed1.write(0x020202); }
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut l2 = l2cache();
|
||||||
|
use log::info;
|
||||||
|
info!("l2 aux={:08X}", l2.regs.aux_control.read());
|
||||||
|
// TODO: set prefetch
|
||||||
|
|
||||||
|
// Configure ZYNQ-specific latency
|
||||||
|
l2.set_tag_ram_latencies(1, 1, 1);
|
||||||
|
l2.set_data_ram_latencies(1, 2, 1);
|
||||||
|
|
||||||
|
l2.disable_interrupts();
|
||||||
|
l2.reset_interrupts();
|
||||||
|
l2.invalidate_all();
|
||||||
|
l2.enable();
|
||||||
|
}
|
||||||
|
@ -8,141 +8,48 @@ use libregister::{
|
|||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct RegisterBlock {
|
pub struct RegisterBlock {
|
||||||
/// SCU Control Register
|
|
||||||
pub scu_control: ScuControl,
|
pub scu_control: ScuControl,
|
||||||
/// SCU Configuration Register
|
pub scu_config: RO<u32>,
|
||||||
pub scu_config: ScuConfig,
|
pub scu_cpu_power: RW<u32>,
|
||||||
/// 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,
|
||||||
unused0: [u32; 12],
|
reserved0: [u32; 12],
|
||||||
/// Filtering Start Address Register
|
pub filter_start: RW<u32>,
|
||||||
pub filtering_start_address: FilteringStartAddressRegister,
|
pub filter_end: RW<u32>,
|
||||||
/// Defined by FILTEREND input
|
reserved1: [u32; 2],
|
||||||
pub filtering_end_address: FilteringEndAddressRegister,
|
pub scu_access_control: RW<u32>,
|
||||||
unused1: [u32; 2],
|
pub scu_non_secure_access_control: RW<u32>,
|
||||||
/// SCU Access Control (SAC) Register
|
reserved2: [u32; 42],
|
||||||
pub scu_access_control_sac: SCUAccessControlRegisterSAC,
|
pub iccicr: RW<u32>,
|
||||||
/// SCU Non-secure Access Control Register SNSAC
|
pub iccpmw: RW<u32>,
|
||||||
pub scu_non_secure_access_control: SCUNonSecureAccessControlRegister,
|
pub iccbpr: RW<u32>,
|
||||||
unused2: [u32; 42],
|
pub icciar: RW<u32>,
|
||||||
/// CPU Interface Control Register
|
pub icceoir: RW<u32>,
|
||||||
pub iccicr: ICCICR,
|
pub iccrpr: RW<u32>,
|
||||||
/// Interrupt Priority Mask Register
|
pub icchpir: RW<u32>,
|
||||||
pub iccpmr: ICCPMR,
|
pub iccabpr: RW<u32>,
|
||||||
/// Binary Point Register
|
reserved3: [u32; 55],
|
||||||
pub iccbpr: ICCBPR,
|
pub iccidr: RW<u32>,
|
||||||
/// 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,
|
||||||
/// Global Timer Interrupt Status Register
|
pub global_timer_interrupt_status: RW<u32>,
|
||||||
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,
|
||||||
/// Auto-increment Register
|
pub auto_increment: ValueRegister,
|
||||||
pub auto_increment: RW<u32>,
|
reserved4: [u32; 249],
|
||||||
unused4: [u32; 249],
|
pub private_timer_load: ValueRegister,
|
||||||
/// Private Timer Load Register
|
pub private_timer_counter: ValueRegister,
|
||||||
pub private_timer_load: RW<u32>,
|
pub private_timer_control: RW<u32>,
|
||||||
/// Private Timer Counter Register
|
pub private_timer_interrupt_status: RW<u32>,
|
||||||
pub private_timer_counter: RW<u32>,
|
reserved5: [u32; 4],
|
||||||
/// Private Timer Control Register
|
pub watchdog_load: ValueRegister,
|
||||||
pub private_timer_control: PrivateTimerControlRegister,
|
pub watchdog_counter: ValueRegister,
|
||||||
/// Private Timer Interrupt Status Register
|
pub watchdog_control: RW<u32>,
|
||||||
pub private_timer_interrupt_status: PrivateTimerInterruptStatusRegister,
|
pub watchdog_interrupt_status: RW<u32>,
|
||||||
unused5: [u32; 4],
|
// there is plenty more (unimplemented)
|
||||||
/// 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);
|
||||||
@ -158,17 +65,6 @@ 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);
|
||||||
@ -192,71 +88,8 @@ impl ScuInvalidate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
register!(filtering_start_address, FilteringStartAddressRegister, RW, u32);
|
register!(value_register, ValueRegister, RW, u32);
|
||||||
register_bits!(filtering_start_address, filtering_start_address, u32, 20, 31);
|
register_bits!(value_register, value, u32, 0, 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);
|
||||||
@ -264,58 +97,3 @@ 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);
|
|
||||||
|
|
||||||
|
@ -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: GpioRstCtrl,
|
pub gpio_rst_ctrl: RW<u32>,
|
||||||
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>,
|
||||||
@ -229,15 +229,18 @@ pub struct RegisterBlock {
|
|||||||
pub lvl_shftr_en: LvlShftr,
|
pub lvl_shftr_en: LvlShftr,
|
||||||
reserved18: [u32; 3],
|
reserved18: [u32; 3],
|
||||||
pub ocm_cfg: RW<u32>,
|
pub ocm_cfg: RW<u32>,
|
||||||
reserved19: [u32; 123],
|
reserved19: [u32; 66],
|
||||||
|
/// barely documented unnamed register to prepare L2 cache setup
|
||||||
|
pub unnamed1: RW<u32>,
|
||||||
|
reserved120: [u32; 56],
|
||||||
pub gpiob_ctrl: GpiobCtrl,
|
pub gpiob_ctrl: GpiobCtrl,
|
||||||
pub gpiob_cfg_cmos18: RW<u32>,
|
pub gpiob_cfg_cmos18: RW<u32>,
|
||||||
pub gpiob_cfg_cmos25: RW<u32>,
|
pub gpiob_cfg_cmos25: RW<u32>,
|
||||||
pub gpiob_cfg_cmos33: RW<u32>,
|
pub gpiob_cfg_cmos33: RW<u32>,
|
||||||
reserved20: [u32; 1],
|
reserved21: [u32; 1],
|
||||||
pub gpiob_cfg_hstl: RW<u32>,
|
pub gpiob_cfg_hstl: RW<u32>,
|
||||||
pub gpiob_drvr_bias_ctrl: RW<u32>,
|
pub gpiob_drvr_bias_ctrl: RW<u32>,
|
||||||
reserved21: [u32; 9],
|
reserved22: [u32; 9],
|
||||||
pub ddriob_addr0: DdriobConfig,
|
pub ddriob_addr0: DdriobConfig,
|
||||||
pub ddriob_addr1: DdriobConfig,
|
pub ddriob_addr1: DdriobConfig,
|
||||||
pub ddriob_data0: DdriobConfig,
|
pub ddriob_data0: DdriobConfig,
|
||||||
@ -531,20 +534,6 @@ 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);
|
||||||
|
@ -138,15 +138,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<U: PartialOrd> CountDown<U>
|
|
||||||
where
|
|
||||||
GlobalTimer: TimeSource<U>,
|
|
||||||
{
|
|
||||||
pub fn waiting(&self) -> bool {
|
|
||||||
self.timer.now() <= self.timeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// embedded-hal sync API
|
/// embedded-hal sync API
|
||||||
impl embedded_hal::blocking::delay::DelayMs<u64> for GlobalTimer {
|
impl embedded_hal::blocking::delay::DelayMs<u64> for GlobalTimer {
|
||||||
fn delay_ms(&mut self, ms: u64) {
|
fn delay_ms(&mut self, ms: u64) {
|
||||||
|
@ -7,9 +7,9 @@ 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]
|
||||||
bit_field = "0.10"
|
bit_field = "0.10"
|
||||||
|
volatile-register = "0.2"
|
||||||
libregister = { path = "../libregister" }
|
libregister = { path = "../libregister" }
|
||||||
|
@ -33,44 +33,3 @@ 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");
|
|
||||||
}
|
|
||||||
|
@ -197,7 +197,7 @@ pub unsafe fn dcimvac(addr: usize) {
|
|||||||
llvm_asm!("mcr p15, 0, $0, c7, c6, 1" :: "r" (addr) :: "volatile");
|
llvm_asm!("mcr p15, 0, $0, c7, c6, 1" :: "r" (addr) :: "volatile");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data cache clean and invalidate for an object.
|
/// Data cache invalidate for an object.
|
||||||
pub unsafe fn dci<T>(object: &mut T) {
|
pub unsafe fn dci<T>(object: &mut T) {
|
||||||
let first_addr = object as *const _ as usize;
|
let first_addr = object as *const _ as usize;
|
||||||
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
|
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
|
||||||
|
@ -11,26 +11,10 @@ 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;
|
mod fpu;
|
||||||
pub use uncached::UncachedSlice;
|
pub use uncached::UncachedSlice;
|
||||||
pub use fpu::enable_fpu;
|
pub use fpu::enable_fpu;
|
||||||
|
pub mod pl310;
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ impl L1Table {
|
|||||||
global: true,
|
global: true,
|
||||||
shareable: true,
|
shareable: true,
|
||||||
access: AccessPermissions::FullAccess,
|
access: AccessPermissions::FullAccess,
|
||||||
tex: 0b101,
|
tex: 0b111,
|
||||||
domain: 0b1111,
|
domain: 0b1111,
|
||||||
exec: true,
|
exec: true,
|
||||||
cacheable: true,
|
cacheable: true,
|
||||||
@ -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: true,
|
shareable: false,
|
||||||
access: AccessPermissions::FullAccess,
|
access: AccessPermissions::FullAccess,
|
||||||
tex: 0b100,
|
tex: 0b100,
|
||||||
domain: 0,
|
domain: 0,
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
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::{
|
use super::asm::*;
|
||||||
spin_lock_yield, notify_spin_lock,
|
|
||||||
asm::{dmb, enter_critical, exit_critical}
|
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
|
||||||
};
|
#[inline]
|
||||||
|
fn wait_for_update() {
|
||||||
|
wfe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
|
||||||
|
#[inline]
|
||||||
|
fn signal_update() {
|
||||||
|
dsb();
|
||||||
|
sev();
|
||||||
|
}
|
||||||
|
|
||||||
const LOCKED: u32 = 1;
|
const LOCKED: u32 = 1;
|
||||||
const UNLOCKED: u32 = 0;
|
const UNLOCKED: u32 = 0;
|
||||||
@ -31,26 +41,19 @@ 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 {
|
||||||
unsafe {
|
wait_for_update();
|
||||||
exit_critical(irq);
|
|
||||||
spin_lock_yield();
|
|
||||||
irq = enter_critical();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dmb();
|
dmb();
|
||||||
MutexGuard { mutex: self, irq }
|
MutexGuard { mutex: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
|
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
|
||||||
let irq = unsafe { enter_critical() };
|
|
||||||
if self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
|
if self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
|
||||||
unsafe { exit_critical(irq) };
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
dmb();
|
dmb();
|
||||||
Some(MutexGuard { mutex: self, irq })
|
Some(MutexGuard { mutex: self })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +61,7 @@ impl<T> Mutex<T> {
|
|||||||
dmb();
|
dmb();
|
||||||
self.locked.store(UNLOCKED, Ordering::Release);
|
self.locked.store(UNLOCKED, Ordering::Release);
|
||||||
|
|
||||||
notify_spin_lock();
|
signal_update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +69,6 @@ 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> {
|
||||||
@ -86,6 +88,5 @@ 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) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
166
libcortex_a9/src/pl310/mod.rs
Normal file
166
libcortex_a9/src/pl310/mod.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
//! L2 cache controller
|
||||||
|
|
||||||
|
use libregister::RegisterW;
|
||||||
|
use crate::asm::*;
|
||||||
|
|
||||||
|
mod regs;
|
||||||
|
|
||||||
|
const CACHE_LINE: usize = 0x20;
|
||||||
|
const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cache_line_addrs(first_addr: usize, beyond_addr: usize) -> impl Iterator<Item = usize> {
|
||||||
|
let first_addr = first_addr & !CACHE_LINE_MASK;
|
||||||
|
let beyond_addr = (beyond_addr | CACHE_LINE_MASK) + 1;
|
||||||
|
|
||||||
|
(first_addr..beyond_addr).step_by(CACHE_LINE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn object_cache_line_addrs<T>(object: &T) -> impl Iterator<Item = usize> {
|
||||||
|
let first_addr = object as *const _ as usize;
|
||||||
|
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
|
||||||
|
cache_line_addrs(first_addr, beyond_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slice_cache_line_addrs<T>(slice: &[T]) -> impl Iterator<Item = usize> {
|
||||||
|
let first_addr = &slice[0] as *const _ as usize;
|
||||||
|
let beyond_addr = (&slice[slice.len() - 1] as *const _ as usize) +
|
||||||
|
core::mem::size_of_val(&slice[slice.len() - 1]);
|
||||||
|
cache_line_addrs(first_addr, beyond_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct L2Cache {
|
||||||
|
pub regs: &'static mut regs::RegisterBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl L2Cache {
|
||||||
|
pub fn new(register_baseaddr: usize) -> Self {
|
||||||
|
let regs = unsafe {
|
||||||
|
regs::RegisterBlock::new_at(register_baseaddr)
|
||||||
|
};
|
||||||
|
L2Cache { regs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_tag_ram_latencies(&mut self, setup_lat: u8, rd_access_lat: u8, wr_access_lat: u8) {
|
||||||
|
self.regs.tag_ram_control.write(
|
||||||
|
regs::RamControl::zeroed()
|
||||||
|
.setup_lat(setup_lat)
|
||||||
|
.rd_access_lat(rd_access_lat)
|
||||||
|
.wr_access_lat(wr_access_lat)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_data_ram_latencies(&mut self, setup_lat: u8, rd_access_lat: u8, wr_access_lat: u8) {
|
||||||
|
self.regs.data_ram_control.write(
|
||||||
|
regs::RamControl::zeroed()
|
||||||
|
.setup_lat(setup_lat)
|
||||||
|
.rd_access_lat(rd_access_lat)
|
||||||
|
.wr_access_lat(wr_access_lat)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_interrupts(&mut self) {
|
||||||
|
self.regs.int_mask.write(
|
||||||
|
regs::Interrupts::zeroed()
|
||||||
|
.ecntr(true)
|
||||||
|
.parrt(true)
|
||||||
|
.parrd(true)
|
||||||
|
.errwt(true)
|
||||||
|
.errwd(true)
|
||||||
|
.errrt(true)
|
||||||
|
.errrd(true)
|
||||||
|
.slverr(true)
|
||||||
|
.decerr(true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_interrupts(&mut self) {
|
||||||
|
self.regs.int_clear.write(
|
||||||
|
regs::Interrupts::zeroed()
|
||||||
|
.ecntr(true)
|
||||||
|
.parrt(true)
|
||||||
|
.parrd(true)
|
||||||
|
.errwt(true)
|
||||||
|
.errwd(true)
|
||||||
|
.errrt(true)
|
||||||
|
.errrd(true)
|
||||||
|
.slverr(true)
|
||||||
|
.decerr(true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalidate_all(&mut self) {
|
||||||
|
unsafe { self.regs.inv_way.write(0xFFFF); }
|
||||||
|
unsafe { self.regs.cache_sync.write(1); }
|
||||||
|
while self.regs.cache_sync.read() != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
dmb();
|
||||||
|
self.regs.control.write(
|
||||||
|
regs::Control::zeroed()
|
||||||
|
.l2_enable(true)
|
||||||
|
);
|
||||||
|
dsb();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_invalidate<T>(&mut self, obj: &T) {
|
||||||
|
dmb();
|
||||||
|
for addr in object_cache_line_addrs(obj) {
|
||||||
|
unsafe {
|
||||||
|
self.regs.clean_inv_pa.write(addr as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dsb();
|
||||||
|
unsafe { self.regs.cache_sync.write(1); }
|
||||||
|
while self.regs.cache_sync.read() != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_invalidate_slice<T>(&mut self, slice: &[T]) {
|
||||||
|
dmb();
|
||||||
|
for addr in slice_cache_line_addrs(slice) {
|
||||||
|
unsafe {
|
||||||
|
self.regs.clean_inv_pa.write(addr as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dsb();
|
||||||
|
unsafe { self.regs.cache_sync.write(1); }
|
||||||
|
while self.regs.cache_sync.read() != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_slice<T>(&mut self, slice: &[T]) {
|
||||||
|
dmb();
|
||||||
|
for addr in slice_cache_line_addrs(slice) {
|
||||||
|
unsafe {
|
||||||
|
self.regs.clean_pa.write(addr as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dsb();
|
||||||
|
unsafe { self.regs.cache_sync.write(1); }
|
||||||
|
while self.regs.cache_sync.read() != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalidate<T>(&mut self, obj: &mut T) {
|
||||||
|
dmb();
|
||||||
|
for addr in object_cache_line_addrs(obj) {
|
||||||
|
unsafe {
|
||||||
|
self.regs.inv_pa.write(addr as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dsb();
|
||||||
|
unsafe { self.regs.cache_sync.write(1); }
|
||||||
|
while self.regs.cache_sync.read() != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalidate_slice<T>(&mut self, slice: &mut [T]) {
|
||||||
|
dmb();
|
||||||
|
for addr in slice_cache_line_addrs(slice) {
|
||||||
|
unsafe {
|
||||||
|
self.regs.inv_pa.write(addr as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dsb();
|
||||||
|
unsafe { self.regs.cache_sync.write(1); }
|
||||||
|
while self.regs.cache_sync.read() != 0 {}
|
||||||
|
}
|
||||||
|
}
|
93
libcortex_a9/src/pl310/regs.rs
Normal file
93
libcortex_a9/src/pl310/regs.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use volatile_register::{RO, WO, RW};
|
||||||
|
use libregister::{register, register_bit, register_bits, RegisterW};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RegisterBlock {
|
||||||
|
pub cache_id: RW<u32>,
|
||||||
|
pub cache_type: RW<u32>,
|
||||||
|
pub _unused1: [RO<u32>; 62],
|
||||||
|
pub control: Control,
|
||||||
|
pub aux_control: RW<u32>,
|
||||||
|
pub tag_ram_control: RamControl,
|
||||||
|
pub data_ram_control: RamControl,
|
||||||
|
pub _unused2: [RO<u32>; 60],
|
||||||
|
pub ev_counter_ctrl: RW<u32>,
|
||||||
|
pub ev_counter1_cfg: RW<u32>,
|
||||||
|
pub ev_counter2_cfg: RW<u32>,
|
||||||
|
pub ev_counter1: RW<u32>,
|
||||||
|
pub ev_counter2: RW<u32>,
|
||||||
|
pub int_mask: Interrupts,
|
||||||
|
pub int_mask_status: Interrupts,
|
||||||
|
pub int_raw_status: Interrupts,
|
||||||
|
pub int_clear: Interrupts,
|
||||||
|
pub _unused3: [RO<u32>; 323],
|
||||||
|
pub cache_sync: RW<u32>,
|
||||||
|
pub _unused4: [RO<u32>; 15],
|
||||||
|
pub inv_pa: RW<u32>,
|
||||||
|
pub _unused5: [RO<u32>; 2],
|
||||||
|
pub inv_way: RW<u32>,
|
||||||
|
pub _unused6: [RO<u32>; 12],
|
||||||
|
pub clean_pa: RW<u32>,
|
||||||
|
pub _unused7: [RO<u32>; 1],
|
||||||
|
pub clean_index: RW<u32>,
|
||||||
|
pub clean_way: RW<u32>,
|
||||||
|
pub _unused8: [RO<u32>; 12],
|
||||||
|
pub clean_inv_pa: RW<u32>,
|
||||||
|
pub _unused9: [RO<u32>; 1],
|
||||||
|
pub clean_inv_index: RW<u32>,
|
||||||
|
pub clean_inv_way: RW<u32>,
|
||||||
|
pub _unused10: [RO<u32>; 64],
|
||||||
|
pub d_lockdown0: RW<u32>,
|
||||||
|
pub i_lockdown0: RW<u32>,
|
||||||
|
pub d_lockdown1: RW<u32>,
|
||||||
|
pub i_lockdown1: RW<u32>,
|
||||||
|
pub d_lockdown2: RW<u32>,
|
||||||
|
pub i_lockdown2: RW<u32>,
|
||||||
|
pub d_lockdown3: RW<u32>,
|
||||||
|
pub i_lockdown3: RW<u32>,
|
||||||
|
pub d_lockdown4: RW<u32>,
|
||||||
|
pub i_lockdown4: RW<u32>,
|
||||||
|
pub d_lockdown5: RW<u32>,
|
||||||
|
pub i_lockdown5: RW<u32>,
|
||||||
|
pub d_lockdown6: RW<u32>,
|
||||||
|
pub i_lockdown6: RW<u32>,
|
||||||
|
pub d_lockdown7: RW<u32>,
|
||||||
|
pub i_lockdown7: RW<u32>,
|
||||||
|
pub _unused11: [RO<u32>; 4],
|
||||||
|
pub lock_line_en: RW<u32>,
|
||||||
|
pub unlock_way: RW<u32>,
|
||||||
|
pub _unused12: [RO<u32>; 170],
|
||||||
|
pub addr_filtering_start: RW<u32>,
|
||||||
|
pub addr_filtering_end: RW<u32>,
|
||||||
|
pub _unused13: [RO<u32>; 206],
|
||||||
|
pub debug_ctrl: RW<u32>,
|
||||||
|
pub _unused14: [RO<u32>; 7],
|
||||||
|
pub prefetch_ctrl: RW<u32>,
|
||||||
|
pub _unused15: [RO<u32>; 7],
|
||||||
|
pub power_ctrl: RW<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterBlock {
|
||||||
|
pub unsafe fn new_at(baseaddr: usize) -> &'static mut Self {
|
||||||
|
&mut *(baseaddr as *mut _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(control, Control, RW, u32);
|
||||||
|
register_bit!(control, l2_enable, 0);
|
||||||
|
|
||||||
|
register!(ram_control, RamControl, RW, u32);
|
||||||
|
register_bits!(ram_control, setup_lat, u8, 0, 2);
|
||||||
|
register_bits!(ram_control, rd_access_lat, u8, 4, 6);
|
||||||
|
register_bits!(ram_control, wr_access_lat, u8, 8, 10);
|
||||||
|
|
||||||
|
register!(interrupts, Interrupts, RW, u32);
|
||||||
|
register_bit!(interrupts, ecntr, 0);
|
||||||
|
register_bit!(interrupts, parrt, 1);
|
||||||
|
register_bit!(interrupts, parrd, 2);
|
||||||
|
register_bit!(interrupts, errwt, 3);
|
||||||
|
register_bit!(interrupts, errwd, 4);
|
||||||
|
register_bit!(interrupts, errrt, 5);
|
||||||
|
register_bit!(interrupts, errrd, 6);
|
||||||
|
register_bit!(interrupts, slverr, 7);
|
||||||
|
register_bit!(interrupts, decerr, 8);
|
@ -149,7 +149,6 @@ pub struct ACTLR;
|
|||||||
wrap_reg!(actlr);
|
wrap_reg!(actlr);
|
||||||
def_reg_r!(ACTLR, actlr::Read, "mrc p15, 0, $0, c1, c0, 1");
|
def_reg_r!(ACTLR, actlr::Read, "mrc p15, 0, $0, c1, c0, 1");
|
||||||
def_reg_w!(ACTLR, actlr::Write, "mcr p15, 0, $0, c1, c0, 1");
|
def_reg_w!(ACTLR, actlr::Write, "mcr p15, 0, $0, c1, c0, 1");
|
||||||
// SMP bit
|
|
||||||
register_bit!(actlr, parity_on, 9);
|
register_bit!(actlr, parity_on, 9);
|
||||||
register_bit!(actlr, alloc_one_way, 8);
|
register_bit!(actlr, alloc_one_way, 8);
|
||||||
register_bit!(actlr, excl, 7);
|
register_bit!(actlr, excl, 7);
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
use super::{spin_lock_yield, notify_spin_lock};
|
|
||||||
use core::{
|
|
||||||
task::{Context, Poll},
|
|
||||||
pin::Pin,
|
|
||||||
future::Future,
|
|
||||||
sync::atomic::{AtomicI32, Ordering}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Semaphore {
|
|
||||||
value: AtomicI32,
|
|
||||||
max: i32
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Semaphore {
|
|
||||||
pub fn new(value: i32, max: i32) -> Self {
|
|
||||||
Semaphore { value: AtomicI32::new(value), max}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_wait(&self) -> Option<()> {
|
|
||||||
loop {
|
|
||||||
let value = self.value.load(Ordering::Relaxed);
|
|
||||||
if value > 0 {
|
|
||||||
if self.value.compare_and_swap(value, value - 1, Ordering::SeqCst) == value {
|
|
||||||
return Some(());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wait(&self) {
|
|
||||||
while self.try_wait().is_none() {
|
|
||||||
spin_lock_yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn async_wait(&self) {
|
|
||||||
struct Fut<'a>(&'a Semaphore);
|
|
||||||
|
|
||||||
impl Future for Fut<'_> {
|
|
||||||
type Output = ();
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
match self.0.try_wait() {
|
|
||||||
Some(_) => Poll::Ready(()),
|
|
||||||
None => {
|
|
||||||
cx.waker().wake_by_ref();
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Fut(&self).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn signal(&self) {
|
|
||||||
loop {
|
|
||||||
let value = self.value.load(Ordering::Relaxed);
|
|
||||||
if value < self.max {
|
|
||||||
if self.value.compare_and_swap(value, value + 1, Ordering::SeqCst) == value {
|
|
||||||
notify_spin_lock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,7 @@ use core::{
|
|||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use super::{spin_lock_yield, notify_spin_lock};
|
use super::asm::*;
|
||||||
|
|
||||||
pub struct Sender<'a, T> where T: Clone {
|
pub struct Sender<'a, T> where T: Clone {
|
||||||
list: &'a [AtomicPtr<T>],
|
list: &'a [AtomicPtr<T>],
|
||||||
@ -35,7 +35,9 @@ impl<'a, T> Sender<'a, T> where T: Clone {
|
|||||||
let prev = entry.swap(ptr, Ordering::Relaxed);
|
let prev = entry.swap(ptr, Ordering::Relaxed);
|
||||||
// we allow other end get it first
|
// we allow other end get it first
|
||||||
self.write.store((write + 1) % self.list.len(), Ordering::Release);
|
self.write.store((write + 1) % self.list.len(), Ordering::Release);
|
||||||
notify_spin_lock();
|
// wake up other core, actually I wonder if the dsb is really needed...
|
||||||
|
dsb();
|
||||||
|
sev();
|
||||||
if !prev.is_null() {
|
if !prev.is_null() {
|
||||||
unsafe {
|
unsafe {
|
||||||
drop_in_place(prev);
|
drop_in_place(prev);
|
||||||
@ -49,7 +51,7 @@ impl<'a, T> Sender<'a, T> where T: Clone {
|
|||||||
let mut content = content;
|
let mut content = content;
|
||||||
while let Err(back) = self.try_send(content) {
|
while let Err(back) = self.try_send(content) {
|
||||||
content = back;
|
content = back;
|
||||||
spin_lock_yield();
|
wfe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,27 +87,6 @@ impl<'a, T> Sender<'a, T> where T: Clone {
|
|||||||
content: Err(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) {
|
|
||||||
for v in self.list.iter() {
|
|
||||||
let original = v.swap(core::ptr::null_mut(), Ordering::Relaxed);
|
|
||||||
if !original.is_null() {
|
|
||||||
drop_in_place(original);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the `sync_channel`, *forget* all items in the queue. Affects both the sender and
|
|
||||||
/// receiver.
|
|
||||||
pub unsafe fn reset(&mut self) {
|
|
||||||
self.write.store(0, Ordering::Relaxed);
|
|
||||||
self.read.store(0, Ordering::Relaxed);
|
|
||||||
for v in self.list.iter() {
|
|
||||||
v.store(core::ptr::null_mut(), Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Receiver<'a, T> where T: Clone {
|
impl<'a, T> Receiver<'a, T> where T: Clone {
|
||||||
@ -125,7 +106,9 @@ impl<'a, T> Receiver<'a, T> where T: Clone {
|
|||||||
};
|
};
|
||||||
let result = data.clone();
|
let result = data.clone();
|
||||||
self.read.store((read + 1) % self.list.len(), Ordering::Release);
|
self.read.store((read + 1) % self.list.len(), Ordering::Release);
|
||||||
notify_spin_lock();
|
// wake up other core, still idk if the dsb is needed...
|
||||||
|
dsb();
|
||||||
|
sev();
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,7 +118,7 @@ impl<'a, T> Receiver<'a, T> where T: Clone {
|
|||||||
if let Ok(data) = self.try_recv() {
|
if let Ok(data) = self.try_recv() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
spin_lock_yield();
|
wfe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +25,10 @@ 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;
|
// Shareable Device
|
||||||
|
l1_section.tex = 0b000;
|
||||||
l1_section.cacheable = false;
|
l1_section.cacheable = false;
|
||||||
l1_section.bufferable = false;
|
l1_section.bufferable = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,10 +9,8 @@ edition = "2018"
|
|||||||
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 = []
|
panic_handler = []
|
||||||
dummy_irq_handler = []
|
|
||||||
alloc_core = []
|
|
||||||
|
|
||||||
default = ["panic_handler", "dummy_irq_handler"]
|
default = ["panic_handler"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
r0 = "1"
|
r0 = "1"
|
||||||
|
@ -53,7 +53,6 @@ pub unsafe extern "C" fn ReservedException() {
|
|||||||
#[link_section = ".text.boot"]
|
#[link_section = ".text.boot"]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[naked]
|
#[naked]
|
||||||
#[cfg(feature = "dummy_irq_handler")]
|
|
||||||
pub unsafe extern "C" fn IRQ() {
|
pub unsafe extern "C" fn IRQ() {
|
||||||
stdio::drop_uart();
|
stdio::drop_uart();
|
||||||
println!("IRQ");
|
println!("IRQ");
|
||||||
|
@ -4,7 +4,7 @@ use libregister::{
|
|||||||
VolatileCell,
|
VolatileCell,
|
||||||
RegisterR, RegisterW, RegisterRW,
|
RegisterR, RegisterW, RegisterRW,
|
||||||
};
|
};
|
||||||
use libcortex_a9::{asm, regs::*, cache, mmu, spin_lock_yield, notify_spin_lock};
|
use libcortex_a9::{asm, regs::*, cache, mmu};
|
||||||
use libboard_zynq::{slcr, mpcore};
|
use libboard_zynq::{slcr, mpcore};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -29,7 +29,7 @@ pub unsafe extern "C" fn Reset() -> ! {
|
|||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
while !CORE1_ENABLED.get() {
|
while !CORE1_ENABLED.get() {
|
||||||
spin_lock_yield();
|
asm::wfe();
|
||||||
}
|
}
|
||||||
SP.write(&mut __stack1_start as *mut _ as u32);
|
SP.write(&mut __stack1_start as *mut _ as u32);
|
||||||
boot_core1();
|
boot_core1();
|
||||||
@ -57,7 +57,6 @@ 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");
|
||||||
});
|
});
|
||||||
@ -78,7 +77,6 @@ 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,7 +142,6 @@ 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 {}
|
||||||
}
|
}
|
||||||
|
@ -1,97 +1,55 @@
|
|||||||
use alloc::alloc::Layout;
|
|
||||||
use core::alloc::GlobalAlloc;
|
use core::alloc::GlobalAlloc;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use libcortex_a9::{
|
use alloc::alloc::Layout;
|
||||||
mutex::Mutex,
|
|
||||||
regs::MPIDR
|
|
||||||
};
|
|
||||||
use libregister::RegisterR;
|
|
||||||
use linked_list_allocator::Heap;
|
use linked_list_allocator::Heap;
|
||||||
#[cfg(not(feature = "alloc_core"))]
|
use libcortex_a9::mutex::Mutex;
|
||||||
use libboard_zynq::ddr::DdrRam;
|
use libboard_zynq::ddr::DdrRam;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(
|
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(Mutex::new(Heap::empty()));
|
||||||
Mutex::new(Heap::empty()),
|
|
||||||
Mutex::new(Heap::empty()),
|
|
||||||
);
|
|
||||||
|
|
||||||
struct CortexA9Alloc(Mutex<Heap>, Mutex<Heap>);
|
/// LockedHeap doesn't lock properly
|
||||||
|
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 {
|
||||||
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
|
self.0.lock()
|
||||||
&self.0
|
.allocate_first_fit(layout)
|
||||||
} else {
|
.ok()
|
||||||
&self.1
|
.map_or(0 as *mut u8, |allocation| allocation.as_ptr())
|
||||||
}
|
|
||||||
.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) {
|
||||||
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
|
self.0.lock()
|
||||||
&self.0
|
.deallocate(NonNull::new_unchecked(ptr), layout)
|
||||||
} 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
|
ALLOCATOR.0.lock()
|
||||||
.0
|
|
||||||
.lock()
|
|
||||||
.init(ddr.ptr::<u8>() as usize, ddr.size());
|
.init(ddr.ptr::<u8>() as usize, ddr.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static __heap0_start: usize;
|
static __heap_start: usize;
|
||||||
static __heap0_end: usize;
|
static __heap_end: usize;
|
||||||
#[cfg(feature = "alloc_core")]
|
|
||||||
static __heap1_start: usize;
|
|
||||||
#[cfg(feature = "alloc_core")]
|
|
||||||
static __heap1_end: usize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_alloc_core0() {
|
pub fn init_alloc_linker() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let start = &__heap0_start as *const usize as usize;
|
let start = &__heap_start as *const usize as usize;
|
||||||
let end = &__heap0_end as *const usize as usize;
|
let end = &__heap_end as *const usize as usize;
|
||||||
ALLOCATOR.0.lock().init(start, end - start);
|
ALLOCATOR.0.lock()
|
||||||
|
.init(start, end - start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc_core")]
|
|
||||||
pub fn init_alloc_core1() {
|
|
||||||
unsafe {
|
|
||||||
let start = &__heap1_start as *const usize as usize;
|
|
||||||
let end = &__heap1_end as *const usize as usize;
|
|
||||||
ALLOCATOR.1.lock().init(start, end - start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[alloc_error_handler]
|
#[alloc_error_handler]
|
||||||
fn alloc_error(layout: core::alloc::Layout) -> ! {
|
fn alloc_error(_: core::alloc::Layout) -> ! {
|
||||||
let id = MPIDR.read().cpu_id();
|
panic!("alloc_error")
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user