Compare commits

..

9 Commits

37 changed files with 500 additions and 1267 deletions

7
Cargo.lock generated
View File

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

View File

@ -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
``` ```

View File

@ -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",

View File

@ -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

View File

@ -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;

View File

@ -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" }

View File

@ -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/ :
{ {

View File

@ -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);

View File

@ -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

View File

@ -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
} }

View File

@ -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();

View File

@ -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));

View File

@ -1,150 +0,0 @@
//! ARM Generic Interrupt Controller
use bit_field::BitField;
use libregister::{RegisterW, RegisterRW, RegisterR};
use super::mpcore;
#[derive(Debug, Clone, Copy)]
pub struct InterruptId(pub u8);
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum CPUCore {
Core0 = 0b01,
Core1 = 0b10
}
#[derive(Debug, Clone, Copy)]
pub struct TargetCPU(u8);
impl TargetCPU {
pub const fn none() -> TargetCPU {
TargetCPU(0)
}
pub const fn and(self, other: TargetCPU) -> TargetCPU {
TargetCPU(self.0 | other.0)
}
}
impl From<CPUCore> for TargetCPU {
fn from(core: CPUCore) -> Self {
TargetCPU(core as u8)
}
}
pub enum TargetList {
CPUList(TargetCPU),
Others,
This
}
impl From<CPUCore> for TargetList {
fn from(core: CPUCore) -> Self {
TargetList::CPUList(TargetCPU(core as u8))
}
}
impl From<TargetCPU> for TargetList {
fn from(cpu: TargetCPU) -> Self {
TargetList::CPUList(cpu)
}
}
#[derive(Debug, Clone, Copy)]
pub enum InterruptSensitivity {
Level,
Edge,
}
pub struct InterruptController {
mpcore: &'static mut mpcore::RegisterBlock,
}
impl InterruptController {
pub fn new(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
InterruptController { mpcore }
}
pub fn disable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(false)
.enable_s(false));
// FIXME: Should we disable the distributor globally when we disable interrupt (for a single
// core)?
// self.mpcore.icddcr.modify(|_, w| w.enable_secure(false)
// .enable_non_secure(false));
}
/// enable interrupt signaling
pub fn enable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(true)
.enable_s(true));
self.mpcore.icddcr.modify(|_, w| w.enable_secure(true));
// Enable all interrupts except those of the lowest priority.
self.mpcore.iccpmr.write(mpcore::ICCPMR::zeroed().priority(0xFF));
}
/// send software generated interrupt
pub fn send_sgi(&mut self, id: InterruptId, targets: TargetList) {
assert!(id.0 < 16);
self.mpcore.icdsgir.modify(|_, w| match targets {
TargetList::CPUList(list) => w.target_list_filter(0).cpu_target_list(list.0),
TargetList::Others => w.target_list_filter(0b01),
TargetList::This => w.target_list_filter(0b10)
}.sgiintid(id.0).satt(false));
}
/// enable the interrupt *for this core*.
/// Not needed for SGI.
pub fn enable(&mut self, id: InterruptId, target_cpu: CPUCore, sensitivity: InterruptSensitivity, priority: u8) {
// only 5 bits of the priority is useful
assert!(priority < 32);
self.disable_interrupts();
// enable
let m = (id.0 >> 5) as usize;
let n = (id.0 & 0x1F) as usize;
assert!(m < 3);
unsafe {
self.mpcore.icdiser[m].modify(|mut icdiser| *icdiser.set_bit(n, true));
}
// target cpu
let m = (id.0 >> 2) as usize;
let n = (8 * (id.0 & 3)) as usize;
unsafe {
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32 + 1));
}
// sensitivity
let m = (id.0 >> 4) as usize;
let n = (2 * (id.0 & 0xF)) as usize;
unsafe {
self.mpcore.icdicfr[m].modify(|mut icdicfr| *icdicfr.set_bits(n..=n+1, match sensitivity {
InterruptSensitivity::Level => 0b00,
InterruptSensitivity::Edge => 0b10,
}));
}
// priority
let offset = (id.0 % 4) * 8;
let priority: u32 = (priority as u32) << (offset + 3);
let mask: u32 = 0xFFFFFFFF ^ (0xFF << offset);
unsafe {
self.mpcore.icdipr[id.0 as usize / 4].modify(|v| (v & mask) | priority);
}
self.enable_interrupts();
}
pub fn end_interrupt(&mut self, id: InterruptId) {
self.mpcore.icceoir.modify(|_, w| w.eoiintid(id.0 as u32));
}
pub fn get_interrupt_id(&self) -> InterruptId {
InterruptId(self.mpcore.icciar.read().ackintid() as u8)
}
}

View File

@ -1,107 +0,0 @@
use super::I2C;
use crate::time::Milliseconds;
use embedded_hal::timer::CountDown;
pub struct EEPROM<'a> {
i2c: &'a mut I2C,
port: u8,
address: u8,
page_size: u8,
count_down: crate::timer::global::CountDown<Milliseconds>
}
impl<'a> EEPROM<'a> {
#[cfg(feature = "target_zc706")]
pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
EEPROM {
i2c: i2c,
port: 2,
address: 0b1010100,
page_size: page_size,
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
}
}
#[cfg(feature = "target_zc706")]
fn select(&mut self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
self.i2c.pca9548_select(0b1110100, mask as u8)?;
Ok(())
}
/// Random read
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
self.select()?;
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr)?;
self.i2c.restart()?;
self.i2c.write((self.address << 1) | 1)?;
let buf_len = buf.len();
for (i, byte) in buf.iter_mut().enumerate() {
*byte = self.i2c.read(i < buf_len - 1)?;
}
self.i2c.stop()?;
Ok(())
}
/// Smart multi-page writing
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
/// any roll-over when writing ambiguous memory regions.
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
self.select()?;
let buf_len = buf.len();
let mut pb: u8 = addr % self.page_size;
for (i, byte) in buf.iter().enumerate() {
if (i == 0) || (pb == 0) {
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr + (i as u8))?;
}
self.i2c.write(*byte)?;
pb += 1;
if (i == buf_len-1) || (pb == self.page_size) {
self.i2c.stop()?;
self.poll(1_000)?;
pb = 0;
}
}
Ok(())
}
/// Poll
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
self.select()?;
self.count_down.start(Milliseconds(timeout_ms));
loop {
self.i2c.start()?;
let ack = self.i2c.write(self.address << 1)?;
self.i2c.stop()?;
if ack {
break
};
if !self.count_down.waiting() {
return Err("I2C polling timeout")
}
}
Ok(())
}
pub fn read_eui48<'r>(&mut self) -> Result<[u8; 6], &'static str> {
let mut buffer = [0u8; 6];
self.read(0xFA, &mut buffer)?;
Ok(buffer)
}
}

View File

@ -1,235 +0,0 @@
//! I2C Bit-banging Controller
mod regs;
pub mod eeprom;
use super::clocks::Clocks;
use super::slcr;
use super::time::Microseconds;
use embedded_hal::timer::CountDown;
use libregister::{RegisterR, RegisterRW, RegisterW};
const INVALID_BUS: &'static str = "Invalid I2C bus";
pub struct I2C {
regs: regs::RegisterWrapper,
count_down: super::timer::global::CountDown<Microseconds>
}
impl I2C {
#[cfg(feature = "target_zc706")]
pub fn i2c() -> Self {
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
slcr::RegisterBlock::unlocked(|slcr| {
// SCL
slcr.mio_pin_50.write(
slcr::MioPin50::zeroed()
.l3_sel(0b000) // as GPIO 50
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// SDA
slcr.mio_pin_51.write(
slcr::MioPin51::zeroed()
.l3_sel(0b000) // as GPIO 51
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// Reset
slcr.gpio_rst_ctrl.reset_gpio();
});
Self::ctor_common(0xFFFF - 0x000C)
}
fn ctor_common(gpio_output_mask: u16) -> Self {
// Setup register block
let clocks = Clocks::get();
let self_ = Self {
regs: regs::RegisterWrapper::new(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
};
// Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| {
w.mask(gpio_output_mask)
});
// Setup GPIO driver direction
self_.regs.gpio_direction.modify(|_, w| {
w.scl(true).sda(true)
});
self_
}
/// Delay for I2C operations, simple wrapper for nb.
fn delay_us(&mut self, us: u64) {
self.count_down.start(Microseconds(us));
nb::block!(self.count_down.wait()).unwrap();
}
fn half_period(&mut self) { self.delay_us(100) }
fn sda_i(&mut self) -> bool {
self.regs.gpio_input.read().sda()
}
fn scl_i(&mut self) -> bool {
self.regs.gpio_input.read().scl()
}
fn sda_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.sda(oe)
})
}
fn sda_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.sda_o(o)
})
}
fn scl_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.scl(oe)
})
}
fn scl_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.scl_o(o)
})
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.scl_oe(false);
self.sda_oe(false);
self.scl_o(false);
self.sda_o(false);
// Check the I2C bus is ready
self.half_period();
self.half_period();
if !self.sda_i() {
// Try toggling SCL a few times
for _bit in 0..8 {
self.scl_oe(true);
self.half_period();
self.scl_oe(false);
self.half_period();
}
}
if !self.sda_i() {
return Err("SDA is stuck low and doesn't get unstuck");
}
if !self.scl_i() {
return Err("SCL is stuck low and doesn't get unstuck");
}
// postcondition: SCL and SDA high
Ok(())
}
pub fn start(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA high
if !self.scl_i() {
return Err("SCL is stuck low and doesn't get unstuck");
}
if !self.sda_i() {
return Err("SDA arbitration lost");
}
self.sda_oe(true);
self.half_period();
self.scl_oe(true);
// postcondition: SCL and SDA low
Ok(())
}
pub fn restart(&mut self) -> Result<(), &'static str> {
// precondition SCL and SDA low
self.sda_oe(false);
self.half_period();
self.scl_oe(false);
self.half_period();
self.start()?;
// postcondition: SCL and SDA low
Ok(())
}
pub fn stop(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA low
self.half_period();
self.scl_oe(false);
self.half_period();
self.sda_oe(false);
self.half_period();
if !self.sda_i() {
return Err("SDA arbitration lost");
}
// postcondition: SCL and SDA high
Ok(())
}
pub fn write(&mut self, data: u8) -> Result<bool, &'static str> {
// precondition: SCL and SDA low
// MSB first
for bit in (0..8).rev() {
self.sda_oe(data & (1 << bit) == 0);
self.half_period();
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
}
self.sda_oe(false);
self.half_period();
self.scl_oe(false);
self.half_period();
// Read ack/nack
let ack = !self.sda_i();
self.scl_oe(true);
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(ack)
}
pub fn read(&mut self, ack: bool) -> Result<u8, &'static str> {
// precondition: SCL and SDA low
self.sda_oe(false);
let mut data: u8 = 0;
// MSB first
for bit in (0..8).rev() {
self.half_period();
self.scl_oe(false);
self.half_period();
if self.sda_i() { data |= 1 << bit }
self.scl_oe(true);
}
// Send ack/nack
self.sda_oe(ack);
self.half_period();
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(data)
}
pub fn pca9548_select(&mut self, address: u8, channels: u8) -> Result<(), &'static str> {
self.start()?;
if !self.write(address << 1)? {
return Err("PCA9548 failed to ack write address")
}
if !self.write(channels)? {
return Err("PCA9548 failed to ack control word")
}
self.stop()?;
Ok(())
}
}

View File

@ -1,93 +0,0 @@
use volatile_register::{RO, WO, RW};
use libregister::{
register, register_at,
register_bit, register_bits
};
// With reference to:
//
// artiq:artiq/gateware/targets/kasli.py:
// self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
//
// misoc:misoc/cores/gpio.py:
// class GPIOTristate(Module, AutoCSR):
// def __init__(self, signals, reset_out=0, reset_oe=0):
// l = len(signals)
// self._in = CSRStatus(l)
// self._out = CSRStorage(l, reset=reset_out)
// self._oe = CSRStorage(l, reset=reset_oe)
//
// Hence, using GPIOs as SCL and SDA GPIOs respectively.
//
// Current compatibility:
// zc706: GPIO 50, 51 == SCL, SDA
pub struct RegisterWrapper {
pub gpio_output_mask: &'static mut GPIOOutputMask,
pub gpio_input: &'static mut GPIOInput,
pub gpio_direction: &'static mut GPIODirection,
pub gpio_output_enable: &'static mut GPIOOutputEnable,
}
impl RegisterWrapper {
pub fn new() -> Self {
Self {
gpio_output_mask: GPIOOutputMask::new(),
gpio_input: GPIOInput::new(),
gpio_direction: GPIODirection::new(),
gpio_output_enable: GPIOOutputEnable::new()
}
}
}
// MASK_DATA_1_MSW:
// Maskable output data for MIO[53:48]
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputMask, 0xE000A00C, new);
// Output for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, scl_o, 2);
// Output for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, sda_o, 3);
// Mask for keeping bits except SCL and SDA unchanged
#[cfg(feature = "target_zc706")]
register_bits!(gpio_output_mask, mask, u16, 16, 31);
// DATA_1_RO:
// Input data for MIO[53:32]
register!(gpio_input, GPIOInput, RO, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOInput, 0xE000A064, new);
// Input for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, scl, 18);
// Input for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, sda, 19);
// DIRM_1:
// Direction mode for MIO[53:32]; 0/1 = in/out
register!(gpio_direction, GPIODirection, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIODirection, 0xE000A244, new);
// Direction for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, scl, 18);
// Direction for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, sda, 19);
// OEN_1:
// Output enable for MIO[53:32]
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputEnable, 0xE000A248, new);
// Output enable for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, scl, 18);
// Output enable for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, sda, 19);

View File

@ -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();
}

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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" }

View File

@ -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");
}

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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,

View File

@ -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) };
} }
} }

View 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 {}
}
}

View 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);

View File

@ -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);

View File

@ -1,71 +0,0 @@
use super::{spin_lock_yield, notify_spin_lock};
use core::{
task::{Context, Poll},
pin::Pin,
future::Future,
sync::atomic::{AtomicI32, Ordering}
};
pub struct Semaphore {
value: AtomicI32,
max: i32
}
impl Semaphore {
pub fn new(value: i32, max: i32) -> Self {
Semaphore { value: AtomicI32::new(value), max}
}
pub fn try_wait(&self) -> Option<()> {
loop {
let value = self.value.load(Ordering::Relaxed);
if value > 0 {
if self.value.compare_and_swap(value, value - 1, Ordering::SeqCst) == value {
return Some(());
}
} else {
return None;
}
}
}
pub fn wait(&self) {
while self.try_wait().is_none() {
spin_lock_yield();
}
}
pub async fn async_wait(&self) {
struct Fut<'a>(&'a Semaphore);
impl Future for Fut<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.0.try_wait() {
Some(_) => Poll::Ready(()),
None => {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
Fut(&self).await
}
pub fn signal(&self) {
loop {
let value = self.value.load(Ordering::Relaxed);
if value < self.max {
if self.value.compare_and_swap(value, value + 1, Ordering::SeqCst) == value {
notify_spin_lock();
return;
}
} else {
return;
}
}
}
}

View File

@ -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();
} }
} }

View File

@ -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;
}); });
} }

View File

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

View File

@ -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");

View File

@ -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 {}
} }

View File

@ -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
);
} }

View File

@ -19,6 +19,6 @@ stdenv.mkDerivation {
XARGO_RUST_SRC = "${rustcSrc}/src"; XARGO_RUST_SRC = "${rustcSrc}/src";
shellHook = '' shellHook = ''
echo "Run 'cargo xbuild --release -p experiments' to build." echo "Run 'cargo xbuild --release' to build."
''; '';
} }