From 4cb71e4f3d659053e2ad95c1d6d0feef25eb48b8 Mon Sep 17 00:00:00 2001 From: Astro Date: Sat, 20 Jun 2020 02:24:46 +0200 Subject: [PATCH] libcortex_a9: implement pl310 l2cache --- Cargo.lock | 1 + libboard_zynq/src/lib.rs | 26 ++++++ libboard_zynq/src/slcr.rs | 9 +- libcortex_a9/Cargo.toml | 1 + libcortex_a9/src/cache.rs | 2 +- libcortex_a9/src/lib.rs | 1 + libcortex_a9/src/pl310/mod.rs | 166 +++++++++++++++++++++++++++++++++ libcortex_a9/src/pl310/regs.rs | 93 ++++++++++++++++++ libcortex_a9/src/regs.rs | 1 - 9 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 libcortex_a9/src/pl310/mod.rs create mode 100644 libcortex_a9/src/pl310/regs.rs diff --git a/Cargo.lock b/Cargo.lock index c665210..e2e183c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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]] diff --git a/libboard_zynq/src/lib.rs b/libboard_zynq/src/lib.rs index 04f7d8b..80eead1 100644 --- a/libboard_zynq/src/lib.rs +++ b/libboard_zynq/src/lib.rs @@ -21,3 +21,29 @@ pub mod timer; pub mod sdio; pub mod logger; 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(); + // 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(); +} diff --git a/libboard_zynq/src/slcr.rs b/libboard_zynq/src/slcr.rs index e8e36bb..f105abe 100644 --- a/libboard_zynq/src/slcr.rs +++ b/libboard_zynq/src/slcr.rs @@ -229,15 +229,18 @@ pub struct RegisterBlock { pub lvl_shftr_en: LvlShftr, reserved18: [u32; 3], pub ocm_cfg: RW, - reserved19: [u32; 123], + reserved19: [u32; 66], + /// barely documented unnamed register to prepare L2 cache setup + pub unnamed1: RW, + reserved120: [u32; 56], pub gpiob_ctrl: GpiobCtrl, pub gpiob_cfg_cmos18: RW, pub gpiob_cfg_cmos25: RW, pub gpiob_cfg_cmos33: RW, - reserved20: [u32; 1], + reserved21: [u32; 1], pub gpiob_cfg_hstl: RW, pub gpiob_drvr_bias_ctrl: RW, - reserved21: [u32; 9], + reserved22: [u32; 9], pub ddriob_addr0: DdriobConfig, pub ddriob_addr1: DdriobConfig, pub ddriob_data0: DdriobConfig, diff --git a/libcortex_a9/Cargo.toml b/libcortex_a9/Cargo.toml index 45f628c..59f2533 100644 --- a/libcortex_a9/Cargo.toml +++ b/libcortex_a9/Cargo.toml @@ -11,4 +11,5 @@ default = ["target_zc706"] [dependencies] bit_field = "0.10" +volatile-register = "0.2" libregister = { path = "../libregister" } diff --git a/libcortex_a9/src/cache.rs b/libcortex_a9/src/cache.rs index 3ea95b4..79d537c 100644 --- a/libcortex_a9/src/cache.rs +++ b/libcortex_a9/src/cache.rs @@ -197,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(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); diff --git a/libcortex_a9/src/lib.rs b/libcortex_a9/src/lib.rs index bd73521..609f270 100644 --- a/libcortex_a9/src/lib.rs +++ b/libcortex_a9/src/lib.rs @@ -15,5 +15,6 @@ mod uncached; mod fpu; pub use uncached::UncachedSlice; pub use fpu::enable_fpu; +pub mod pl310; global_asm!(include_str!("exceptions.s")); diff --git a/libcortex_a9/src/pl310/mod.rs b/libcortex_a9/src/pl310/mod.rs new file mode 100644 index 0000000..ef3b435 --- /dev/null +++ b/libcortex_a9/src/pl310/mod.rs @@ -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 { + 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(object: &T) -> impl Iterator { + 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(slice: &[T]) -> impl Iterator { + 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(&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(&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(&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(&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(&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 {} + } +} diff --git a/libcortex_a9/src/pl310/regs.rs b/libcortex_a9/src/pl310/regs.rs new file mode 100644 index 0000000..3069850 --- /dev/null +++ b/libcortex_a9/src/pl310/regs.rs @@ -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, + pub cache_type: RW, + pub _unused1: [RO; 62], + pub control: Control, + pub aux_control: RW, + pub tag_ram_control: RamControl, + pub data_ram_control: RamControl, + pub _unused2: [RO; 60], + pub ev_counter_ctrl: RW, + pub ev_counter1_cfg: RW, + pub ev_counter2_cfg: RW, + pub ev_counter1: RW, + pub ev_counter2: RW, + pub int_mask: Interrupts, + pub int_mask_status: Interrupts, + pub int_raw_status: Interrupts, + pub int_clear: Interrupts, + pub _unused3: [RO; 323], + pub cache_sync: RW, + pub _unused4: [RO; 15], + pub inv_pa: RW, + pub _unused5: [RO; 2], + pub inv_way: RW, + pub _unused6: [RO; 12], + pub clean_pa: RW, + pub _unused7: [RO; 1], + pub clean_index: RW, + pub clean_way: RW, + pub _unused8: [RO; 12], + pub clean_inv_pa: RW, + pub _unused9: [RO; 1], + pub clean_inv_index: RW, + pub clean_inv_way: RW, + pub _unused10: [RO; 64], + pub d_lockdown0: RW, + pub i_lockdown0: RW, + pub d_lockdown1: RW, + pub i_lockdown1: RW, + pub d_lockdown2: RW, + pub i_lockdown2: RW, + pub d_lockdown3: RW, + pub i_lockdown3: RW, + pub d_lockdown4: RW, + pub i_lockdown4: RW, + pub d_lockdown5: RW, + pub i_lockdown5: RW, + pub d_lockdown6: RW, + pub i_lockdown6: RW, + pub d_lockdown7: RW, + pub i_lockdown7: RW, + pub _unused11: [RO; 4], + pub lock_line_en: RW, + pub unlock_way: RW, + pub _unused12: [RO; 170], + pub addr_filtering_start: RW, + pub addr_filtering_end: RW, + pub _unused13: [RO; 206], + pub debug_ctrl: RW, + pub _unused14: [RO; 7], + pub prefetch_ctrl: RW, + pub _unused15: [RO; 7], + pub power_ctrl: RW, +} + +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); diff --git a/libcortex_a9/src/regs.rs b/libcortex_a9/src/regs.rs index f49d89f..1bf98de 100644 --- a/libcortex_a9/src/regs.rs +++ b/libcortex_a9/src/regs.rs @@ -149,7 +149,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);