Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
cd8abae839 | |||
77c3998dbd | |||
5c0953d869 | |||
1121c67a4e | |||
fb4022f04d | |||
c72402b966 | |||
f76e9321d7 | |||
875bc74df9 |
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -79,6 +79,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libregister 0.0.0",
|
||||
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -12,7 +12,7 @@
|
||||
"emit-debug-gdb-scripts": false,
|
||||
"env": "",
|
||||
"executables": true,
|
||||
"features": "+v7,+vfp3,-d32,+thumb2,-neon",
|
||||
"features": "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align",
|
||||
"is-builtin": false,
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
|
@ -15,6 +15,7 @@ use libboard_zynq::{
|
||||
clocks::source::{ArmPll, ClockSource, IoPll},
|
||||
clocks::Clocks,
|
||||
print, println,
|
||||
setup_l2cache,
|
||||
sdio::sd_card::SdCard,
|
||||
smoltcp::{
|
||||
self,
|
||||
@ -32,7 +33,7 @@ use libregister::RegisterR;
|
||||
use libsupport_zynq::{
|
||||
boot, ram,
|
||||
};
|
||||
use log::info;
|
||||
use log::{info, warn};
|
||||
|
||||
mod ps7_init;
|
||||
|
||||
@ -81,7 +82,11 @@ pub fn main_core0() {
|
||||
clocks.cpu_2x(),
|
||||
clocks.cpu_1x()
|
||||
);
|
||||
info!("Setup L2Cache");
|
||||
setup_l2cache();
|
||||
info!("L2Cache done");
|
||||
|
||||
if false {
|
||||
let sd = libboard_zynq::sdio::SDIO::sdio0(true);
|
||||
// only test SD card if it is inserted
|
||||
if sd.is_card_inserted() {
|
||||
@ -114,6 +119,7 @@ pub fn main_core0() {
|
||||
println!("");
|
||||
}
|
||||
let mut flash = flash.stop();
|
||||
}
|
||||
|
||||
let timer = libboard_zynq::timer::GlobalTimer::start();
|
||||
|
||||
@ -122,6 +128,7 @@ pub fn main_core0() {
|
||||
ddr.memtest();
|
||||
ram::init_alloc_ddr(&mut ddr);
|
||||
|
||||
if false {
|
||||
#[cfg(dev)]
|
||||
for i in 0..=1 {
|
||||
let mut flash_io = flash.manual_mode(i);
|
||||
@ -175,14 +182,15 @@ pub fn main_core0() {
|
||||
}
|
||||
});
|
||||
core1.disable();
|
||||
}
|
||||
|
||||
let eth = zynq::eth::Eth::default(HWADDR.clone());
|
||||
println!("Eth on");
|
||||
|
||||
const RX_LEN: usize = 8;
|
||||
const RX_LEN: usize = 4096;
|
||||
// Number of transmission buffers (minimum is two because with
|
||||
// one, duplicate packet transmission occurs)
|
||||
const TX_LEN: usize = 8;
|
||||
const TX_LEN: usize = 4096;
|
||||
let eth = eth.start_rx(RX_LEN);
|
||||
let mut eth = eth.start_tx(TX_LEN);
|
||||
|
||||
@ -237,18 +245,42 @@ pub fn main_core0() {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let counter = alloc::rc::Rc::new(core::cell::RefCell::new(0));
|
||||
// (rx, tx)
|
||||
let stats = alloc::rc::Rc::new(core::cell::RefCell::new((0, 0)));
|
||||
let stats_tx = stats.clone();
|
||||
task::spawn(async move {
|
||||
while let Ok(stream) = TcpStream::accept(TCP_PORT, 2048, 2408).await {
|
||||
let counter = counter.clone();
|
||||
while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
|
||||
let stats_tx = stats_tx.clone();
|
||||
task::spawn(async move {
|
||||
*counter.borrow_mut() += 1;
|
||||
println!("Serving {} connections", *counter.borrow());
|
||||
handle_connection(stream)
|
||||
.await
|
||||
.unwrap_or_else(|e| println!("Connection: {:?}", e));
|
||||
*counter.borrow_mut() -= 1;
|
||||
println!("Now serving {} connections", *counter.borrow());
|
||||
let tx_data = (0..=255).take(65536).collect::<alloc::vec::Vec<u8>>();
|
||||
loop {
|
||||
// const CHUNK_SIZE: usize = 65536;
|
||||
// match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
|
||||
match stream.send_slice(&tx_data[..]).await {
|
||||
Ok(len) => stats_tx.borrow_mut().1 += tx_data.len(), //CHUNK_SIZE,
|
||||
Err(e) => {
|
||||
warn!("tx: {:?}", e);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
let stats_rx = stats.clone();
|
||||
task::spawn(async move {
|
||||
while let Ok(stream) = TcpStream::accept(TCP_PORT+1, 0x10_0000, 0x10_0000).await {
|
||||
let stats_rx = stats_rx.clone();
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
match stream.recv(|buf| Poll::Ready((buf.len(), buf.len()))).await {
|
||||
Ok(len) => stats_rx.borrow_mut().0 += len,
|
||||
Err(e) => {
|
||||
warn!("rx: {:?}", e);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -261,7 +293,13 @@ pub fn main_core0() {
|
||||
let timestamp = timer.get_us();
|
||||
let seconds = timestamp / 1_000_000;
|
||||
let micros = timestamp % 1_000_000;
|
||||
info!("time: {:6}.{:06}s", seconds, micros);
|
||||
let (rx, tx) = {
|
||||
let mut stats = stats.borrow_mut();
|
||||
let result = *stats;
|
||||
*stats = (0, 0);
|
||||
result
|
||||
};
|
||||
info!("time: {:6}.{:06}s, rx: {}k/s, tx: {}k/s", seconds, micros, rx / 1024, tx / 1024);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -21,5 +21,6 @@ libcortex_a9 = { path = "../libcortex_a9" }
|
||||
|
||||
[dependencies.smoltcp]
|
||||
version = "0.6"
|
||||
# features = ["ethernet", "proto-ipv4", "socket-tcp", "log"]
|
||||
features = ["ethernet", "proto-ipv4", "socket-tcp"]
|
||||
default-features = false
|
||||
|
@ -2,6 +2,8 @@ use core::ops::Deref;
|
||||
use alloc::{vec, vec::Vec};
|
||||
use libcortex_a9::{asm::*, cache::*, UncachedSlice};
|
||||
use libregister::*;
|
||||
use log::debug;
|
||||
use crate::l2cache;
|
||||
use super::Buffer;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -81,9 +83,6 @@ impl DescList {
|
||||
entry.word1.write(
|
||||
DescWord1::zeroed()
|
||||
);
|
||||
// Flush buffer from cache, to be filled by the peripheral
|
||||
// before next read
|
||||
dcci_slice(&buffer[..]);
|
||||
}
|
||||
|
||||
DescList {
|
||||
@ -105,6 +104,9 @@ impl DescList {
|
||||
let word1 = entry.word1.read();
|
||||
let len = word1.frame_length_lsbs().into();
|
||||
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;
|
||||
if self.next >= list_len {
|
||||
@ -113,8 +115,10 @@ impl DescList {
|
||||
|
||||
let pkt = PktRef { entry, buffer };
|
||||
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))
|
||||
} else {
|
||||
debug!("pkt trunc");
|
||||
Err(Error::Truncated)
|
||||
}
|
||||
} else {
|
||||
@ -131,9 +135,6 @@ pub struct PktRef<'a> {
|
||||
|
||||
impl<'a> Drop for PktRef<'a> {
|
||||
fn drop(&mut self) {
|
||||
// Flush buffer from cache, to be filled by the peripheral
|
||||
// before next read
|
||||
dcci_slice(self.buffer);
|
||||
|
||||
self.entry.word0.modify(|_, w| w.used(false));
|
||||
dmb();
|
||||
|
@ -1,7 +1,9 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use libcortex_a9::{cache::dcc_slice, UncachedSlice};
|
||||
use libcortex_a9::{asm::dmb, cache::dcc_slice, UncachedSlice};
|
||||
use libregister::*;
|
||||
use log::{debug, warn};
|
||||
use crate::l2cache;
|
||||
use super::{Buffer, regs};
|
||||
|
||||
/// Descriptor entry
|
||||
@ -90,8 +92,10 @@ impl DescList {
|
||||
}
|
||||
|
||||
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 entry = &mut self.list[self.next];
|
||||
dmb();
|
||||
if entry.word1.read().used() {
|
||||
let buffer = &mut self.buffers[self.next][0..length];
|
||||
entry.word1.write(DescWord1::zeroed()
|
||||
@ -109,6 +113,7 @@ impl DescList {
|
||||
Some(PktRef { entry, buffer, regs })
|
||||
} else {
|
||||
// Still in use by HW (sending too fast, ring exceeded)
|
||||
warn!("tx ring overflow");
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -124,10 +129,13 @@ pub struct PktRef<'a> {
|
||||
|
||||
impl<'a> Drop for PktRef<'a> {
|
||||
fn drop(&mut self) {
|
||||
// Write back all dirty cachelines of this buffer
|
||||
// Write back all dirty cachelines of packet buffer
|
||||
dcc_slice(self.buffer);
|
||||
l2cache().clean_slice(self.buffer);
|
||||
|
||||
self.entry.word1.modify(|_, w| w.used(false));
|
||||
dmb();
|
||||
// dsb();
|
||||
if ! self.regs.tx_status.read().tx_go() {
|
||||
// Start TX if not already running
|
||||
self.regs.net_ctrl.modify(|_, w| w.start_tx(true));
|
||||
|
@ -21,3 +21,31 @@ pub mod time;
|
||||
pub mod timer;
|
||||
pub mod sdio;
|
||||
pub mod logger;
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -229,15 +229,18 @@ pub struct RegisterBlock {
|
||||
pub lvl_shftr_en: LvlShftr,
|
||||
reserved18: [u32; 3],
|
||||
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_cfg_cmos18: RW<u32>,
|
||||
pub gpiob_cfg_cmos25: RW<u32>,
|
||||
pub gpiob_cfg_cmos33: RW<u32>,
|
||||
reserved20: [u32; 1],
|
||||
reserved21: [u32; 1],
|
||||
pub gpiob_cfg_hstl: RW<u32>,
|
||||
pub gpiob_drvr_bias_ctrl: RW<u32>,
|
||||
reserved21: [u32; 9],
|
||||
reserved22: [u32; 9],
|
||||
pub ddriob_addr0: DdriobConfig,
|
||||
pub ddriob_addr1: DdriobConfig,
|
||||
pub ddriob_data0: DdriobConfig,
|
||||
|
@ -11,4 +11,5 @@ default = ["target_zc706"]
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.10"
|
||||
volatile-register = "0.2"
|
||||
libregister = { path = "../libregister" }
|
||||
|
@ -44,6 +44,15 @@ pub fn dcisw(setway: u32) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Data cache clean by set/way
|
||||
#[inline(always)]
|
||||
pub fn dccisw(setway: u32) {
|
||||
unsafe {
|
||||
llvm_asm!("mcr p15, 0, $0, c7, c14, 2" :: "r" (setway) :: "volatile");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A made-up "instruction": invalidate all of the L1 D-Cache
|
||||
#[inline(always)]
|
||||
pub fn dciall() {
|
||||
@ -71,6 +80,33 @@ pub fn dciall() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A made-up "instruction": flush and invalidate all of the L1 D-Cache
|
||||
#[inline(always)]
|
||||
pub fn dcciall() {
|
||||
// the cache associativity could be read from a register, but will
|
||||
// always be 4 in L1 data cache of a cortex a9
|
||||
let ways = 4;
|
||||
let bit_pos_of_way = 30; // 32 - log2(ways)
|
||||
|
||||
// the cache sets could be read from a register, but are always
|
||||
// 256 for the cores in the zync-7000; in general, 128 or 512 are
|
||||
// also possible.
|
||||
let sets = 256;
|
||||
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
|
||||
|
||||
// select L1 data cache
|
||||
unsafe {
|
||||
llvm_asm!("mcr p15, 2, $0, c0, c0, 0" :: "r" (0) :: "volatile");
|
||||
}
|
||||
|
||||
// Invalidate entire D-Cache by iterating every set and every way
|
||||
for set in 0..sets {
|
||||
for way in 0..ways {
|
||||
dccisw((set << bit_pos_of_set) | (way << bit_pos_of_way));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const CACHE_LINE: usize = 0x20;
|
||||
const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
|
||||
@ -161,7 +197,7 @@ pub unsafe fn dcimvac(addr: usize) {
|
||||
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) {
|
||||
let first_addr = object as *const _ as usize;
|
||||
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
|
||||
|
@ -12,5 +12,6 @@ pub mod mutex;
|
||||
pub mod sync_channel;
|
||||
mod uncached;
|
||||
pub use uncached::UncachedSlice;
|
||||
pub mod pl310;
|
||||
|
||||
global_asm!(include_str!("exceptions.s"));
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bit_field::BitField;
|
||||
use super::{regs::*, asm, cache};
|
||||
use super::{regs::*, asm::*, cache::*};
|
||||
use libregister::RegisterW;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -158,7 +158,7 @@ impl L1Table {
|
||||
global: true,
|
||||
shareable: true,
|
||||
access: AccessPermissions::FullAccess,
|
||||
tex: 0b101,
|
||||
tex: 0b111,
|
||||
domain: 0b1111,
|
||||
exec: true,
|
||||
cacheable: true,
|
||||
@ -368,10 +368,19 @@ impl L1Table {
|
||||
let result = f(&mut section);
|
||||
entry.set_section(section);
|
||||
|
||||
asm::dmb();
|
||||
cache::tlbiall();
|
||||
asm::dsb();
|
||||
asm::isb();
|
||||
// Flush L1Dcache
|
||||
dcciall();
|
||||
// // TODO: L2?
|
||||
|
||||
// Invalidate TLB
|
||||
tlbiall();
|
||||
// Invalidate all branch predictors
|
||||
bpiall();
|
||||
|
||||
// ensure completion of the BP and TLB invalidation
|
||||
dsb();
|
||||
// synchronize context on this processor
|
||||
isb();
|
||||
|
||||
result
|
||||
}
|
||||
@ -406,9 +415,9 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
|
||||
|
||||
// Synchronization barriers
|
||||
// Allows MMU to start
|
||||
asm::dsb();
|
||||
dsb();
|
||||
// Flushes pre-fetch buffer
|
||||
asm::isb();
|
||||
isb();
|
||||
|
||||
f();
|
||||
}
|
||||
|
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);
|
@ -138,7 +138,6 @@ pub struct ACTLR;
|
||||
wrap_reg!(actlr);
|
||||
def_reg_r!(ACTLR, actlr::Read, "mrc p15, 0, $0, c1, c0, 1");
|
||||
def_reg_w!(ACTLR, actlr::Write, "mcr p15, 0, $0, c1, c0, 1");
|
||||
// SMP bit
|
||||
register_bit!(actlr, parity_on, 9);
|
||||
register_bit!(actlr, alloc_one_way, 8);
|
||||
register_bit!(actlr, excl, 7);
|
||||
|
@ -25,8 +25,10 @@ impl<T> UncachedSlice<T> {
|
||||
for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) {
|
||||
L1Table::get()
|
||||
.update(page_start as *const (), |l1_section| {
|
||||
// Shareable Device
|
||||
l1_section.tex = 0b000;
|
||||
l1_section.cacheable = false;
|
||||
l1_section.bufferable = false;
|
||||
l1_section.bufferable = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
use libregister::RegisterR;
|
||||
use libcortex_a9::regs::DFSR;
|
||||
use libboard_zynq::{println, slcr, stdio};
|
||||
|
||||
#[no_mangle]
|
||||
@ -15,6 +17,7 @@ pub unsafe extern "C" fn DataAbort() {
|
||||
stdio::drop_uart();
|
||||
|
||||
println!("DataAbort");
|
||||
println!("DFSR: {:03X}", DFSR.read());
|
||||
|
||||
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
|
||||
loop {}
|
||||
|
Loading…
Reference in New Issue
Block a user