From 8d62eb758049c0aa1ce8fad331eff1424b86236f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 27 Jul 2025 17:18:49 +0200 Subject: [PATCH] l2 cache init now done in user app --- .../embassy/src/bin/dht22-open-drain-pins.rs | 5 +- .../embassy/src/bin/logger-non-blocking.rs | 5 +- examples/embassy/src/bin/pwm.rs | 5 +- examples/embassy/src/main.rs | 6 +- examples/simple/src/bin/gtc-ticks.rs | 5 +- examples/simple/src/bin/logger.rs | 5 +- examples/simple/src/main.rs | 6 +- examples/zedboard/src/bin/ethernet.rs | 10 +- examples/zedboard/src/bin/l3gd20h-i2c-mio.rs | 7 +- examples/zedboard/src/bin/l3gd20h-spi-mio.rs | 6 +- examples/zedboard/src/bin/uart-blocking.rs | 5 +- .../zedboard/src/bin/uart-non-blocking.rs | 6 +- examples/zedboard/src/main.rs | 6 +- zynq7000-hal/src/cache.rs | 2 +- zynq7000-hal/src/l2_cache.rs | 81 +++++++++ zynq7000-hal/src/lib.rs | 1 + zynq7000-rt/src/rt.rs | 61 +------ zynq7000/src/l2_cache.rs | 163 +++++++++++++++++- zynq7000/src/lib.rs | 2 + 19 files changed, 304 insertions(+), 83 deletions(-) create mode 100644 zynq7000-hal/src/l2_cache.rs diff --git a/examples/embassy/src/bin/dht22-open-drain-pins.rs b/examples/embassy/src/bin/dht22-open-drain-pins.rs index 4b6e5d3..0e7711d 100644 --- a/examples/embassy/src/bin/dht22-open-drain-pins.rs +++ b/examples/embassy/src/bin/dht22-open-drain-pins.rs @@ -15,6 +15,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{Flex, Output, PinState, mio}, gtc::Gtc, + l2_cache, time::Hertz, uart::{ClkConfigRaw, Uart, UartConfig}, }; @@ -43,7 +44,9 @@ const OPEN_DRAIN_PINS_MIO9_TO_MIO14: bool = false; #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(_spawner: Spawner) -> ! { - let dp = PsPeripherals::take().unwrap(); + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/embassy/src/bin/logger-non-blocking.rs b/examples/embassy/src/bin/logger-non-blocking.rs index 7f36514..145e66d 100644 --- a/examples/embassy/src/bin/logger-non-blocking.rs +++ b/examples/embassy/src/bin/logger-non-blocking.rs @@ -16,6 +16,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{Output, PinState, mio}, gtc::Gtc, + l2_cache, time::Hertz, uart::{ClkConfigRaw, TxAsync, Uart, UartConfig, on_interrupt_tx}, }; @@ -37,7 +38,9 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[unsafe(export_name = "main")] #[embassy_executor::main] async fn main(spawner: Spawner) -> ! { - let dp = PsPeripherals::take().unwrap(); + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/embassy/src/bin/pwm.rs b/examples/embassy/src/bin/pwm.rs index 0f2f204..0d2c4bd 100644 --- a/examples/embassy/src/bin/pwm.rs +++ b/examples/embassy/src/bin/pwm.rs @@ -21,6 +21,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{Output, PinState, mio}, gtc::Gtc, + l2_cache, time::Hertz, uart::{ClkConfigRaw, Uart, UartConfig}, }; @@ -43,7 +44,9 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(_spawner: Spawner) -> ! { - let dp = PsPeripherals::take().unwrap(); + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/embassy/src/main.rs b/examples/embassy/src/main.rs index 1d99002..31fc883 100644 --- a/examples/embassy/src/main.rs +++ b/examples/embassy/src/main.rs @@ -14,6 +14,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{Output, PinState, mio}, gtc::Gtc, + l2_cache, time::Hertz, uart::{ClkConfigRaw, Uart, UartConfig}, }; @@ -33,11 +34,12 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { main(); } -// TODO: The export name is ignored.. #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(_spawner: Spawner) -> ! { - let dp = PsPeripherals::take().unwrap(); + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/simple/src/bin/gtc-ticks.rs b/examples/simple/src/bin/gtc-ticks.rs index 0316ff7..7fe7a35 100644 --- a/examples/simple/src/bin/gtc-ticks.rs +++ b/examples/simple/src/bin/gtc-ticks.rs @@ -12,6 +12,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{Output, PinState, mio}, gtc::Gtc, + l2_cache, prelude::*, time::Hertz, uart::{ClkConfigRaw, Uart, UartConfig}, @@ -35,7 +36,9 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[unsafe(export_name = "main")] pub fn main() -> ! { - let dp = zynq7000::PsPeripherals::take().unwrap(); + let mut dp = zynq7000::PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/simple/src/bin/logger.rs b/examples/simple/src/bin/logger.rs index 46f4a68..579bcb7 100644 --- a/examples/simple/src/bin/logger.rs +++ b/examples/simple/src/bin/logger.rs @@ -13,6 +13,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{Output, PinState, mio}, gtc::Gtc, + l2_cache, prelude::*, time::Hertz, uart::{ClkConfigRaw, Uart, UartConfig}, @@ -36,7 +37,9 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[unsafe(export_name = "main")] pub fn main() -> ! { - let dp = zynq7000::PsPeripherals::take().unwrap(); + let mut dp = zynq7000::PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs index f13a15d..dc86303 100644 --- a/examples/simple/src/main.rs +++ b/examples/simple/src/main.rs @@ -6,7 +6,10 @@ use core::panic::PanicInfo; use cortex_ar::asm::nop; use embedded_hal::digital::StatefulOutputPin; use zynq7000::PsPeripherals; -use zynq7000_hal::gpio::{Output, PinState, mio}; +use zynq7000_hal::{ + gpio::{Output, PinState, mio}, + l2_cache, +}; use zynq7000_rt as _; /// One user LED is MIO7 @@ -31,6 +34,7 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[unsafe(export_name = "main")] pub fn main() -> ! { + l2_cache::init_with_defaults(&mut unsafe { zynq7000::l2_cache::L2Cache::new_mmio_fixed() }); match LIB { Lib::Pac => { let mut gpio = unsafe { zynq7000::gpio::Gpio::new_mmio_fixed() }; diff --git a/examples/zedboard/src/bin/ethernet.rs b/examples/zedboard/src/bin/ethernet.rs index f0c570a..6a468c8 100644 --- a/examples/zedboard/src/bin/ethernet.rs +++ b/examples/zedboard/src/bin/ethernet.rs @@ -48,6 +48,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::Gtc, + l2_cache, uart::{ClkConfigRaw, Uart, UartConfig}, }; @@ -208,14 +209,17 @@ async fn tcp_task(mut tcp: TcpSocket<'static>) -> ! { #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(spawner: Spawner) -> ! { + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + + // Enable PS-PL level shifters. + configure_level_shifter(LevelShifterConfig::EnableAll); + // Configure the uncached memory region using the MMU. mmu_l1_table_mut() .update(UNCACHED_ADDR, SHAREABLE_DEVICE) .expect("configuring uncached memory section failed"); - // Enable PS-PL level shifters. - configure_level_shifter(LevelShifterConfig::EnableAll); - let dp = PsPeripherals::take().unwrap(); // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs b/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs index 7279991..1a6ec09 100644 --- a/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs +++ b/examples/zedboard/src/bin/l3gd20h-i2c-mio.rs @@ -24,7 +24,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::Gtc, - i2c, + i2c, l2_cache, time::Hertz, uart, }; @@ -48,9 +48,12 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(_spawner: Spawner) -> ! { + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Enable PS-PL level shifters. configure_level_shifter(LevelShifterConfig::EnableAll); - let dp = PsPeripherals::take().unwrap(); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); diff --git a/examples/zedboard/src/bin/l3gd20h-spi-mio.rs b/examples/zedboard/src/bin/l3gd20h-spi-mio.rs index 40bdcc7..d7b38b9 100644 --- a/examples/zedboard/src/bin/l3gd20h-spi-mio.rs +++ b/examples/zedboard/src/bin/l3gd20h-spi-mio.rs @@ -24,6 +24,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::Gtc, + l2_cache, spi::{self, SpiAsync, SpiId, SpiWithHwCs, SpiWithHwCsAsync, on_interrupt}, time::Hertz, uart::{self, TxAsync, on_interrupt_tx}, @@ -50,9 +51,12 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(spawner: Spawner) -> ! { + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Enable PS-PL level shifters. configure_level_shifter(LevelShifterConfig::EnableAll); - let dp = PsPeripherals::take().unwrap(); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let mut clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); diff --git a/examples/zedboard/src/bin/uart-blocking.rs b/examples/zedboard/src/bin/uart-blocking.rs index 267b861..617cfae 100644 --- a/examples/zedboard/src/bin/uart-blocking.rs +++ b/examples/zedboard/src/bin/uart-blocking.rs @@ -19,6 +19,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::Gtc, + l2_cache, uart::{ClkConfigRaw, Uart, UartConfig}, }; @@ -100,9 +101,11 @@ impl UartMultiplexer { #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(_spawner: Spawner) -> ! { + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Enable PS-PL level shifters. configure_level_shifter(LevelShifterConfig::EnableAll); - let dp = PsPeripherals::take().unwrap(); // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/zedboard/src/bin/uart-non-blocking.rs b/examples/zedboard/src/bin/uart-non-blocking.rs index 0a0e846..a2b712e 100644 --- a/examples/zedboard/src/bin/uart-non-blocking.rs +++ b/examples/zedboard/src/bin/uart-non-blocking.rs @@ -45,6 +45,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::Gtc, + l2_cache, time::Hertz, uart::{ClkConfigRaw, Uart, UartConfig}, }; @@ -165,9 +166,12 @@ impl UartMultiplexer { #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(spawner: Spawner) -> ! { + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Enable PS-PL level shifters. configure_level_shifter(LevelShifterConfig::EnableAll); - let dp = PsPeripherals::take().unwrap(); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/examples/zedboard/src/main.rs b/examples/zedboard/src/main.rs index 7e659b7..b6aaa33 100644 --- a/examples/zedboard/src/main.rs +++ b/examples/zedboard/src/main.rs @@ -16,6 +16,7 @@ use zynq7000_hal::{ gic::{GicConfigurator, GicInterruptHelper, Interrupt}, gpio::{GpioPins, Output, PinState}, gtc::Gtc, + l2_cache, uart::{ClkConfigRaw, Uart, UartConfig}, }; @@ -36,9 +37,12 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! { #[embassy_executor::main] #[unsafe(export_name = "main")] async fn main(_spawner: Spawner) -> ! { + let mut dp = PsPeripherals::take().unwrap(); + l2_cache::init_with_defaults(&mut dp.l2c); + // Enable PS-PL level shifters. configure_level_shifter(LevelShifterConfig::EnableAll); - let dp = PsPeripherals::take().unwrap(); + // Clock was already initialized by PS7 Init TCL script or FSBL, we just read it. let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap(); // Set up the global interrupt controller. diff --git a/zynq7000-hal/src/cache.rs b/zynq7000-hal/src/cache.rs index 122e9f5..318b552 100644 --- a/zynq7000-hal/src/cache.rs +++ b/zynq7000-hal/src/cache.rs @@ -29,7 +29,7 @@ pub fn clean_and_invalidate_data_cache() { // Clean all ways in L2 cache. let mut l2c = unsafe { L2Cache::new_mmio_fixed() }; - l2c.write_clean_invalidate_by_way(0xff); + l2c.write_clean_invalidate_by_way(0xffff); while l2c.read_cache_sync().busy() {} compiler_fence(core::sync::atomic::Ordering::SeqCst); diff --git a/zynq7000-hal/src/l2_cache.rs b/zynq7000-hal/src/l2_cache.rs new file mode 100644 index 0000000..5a6c4a0 --- /dev/null +++ b/zynq7000-hal/src/l2_cache.rs @@ -0,0 +1,81 @@ +use core::sync::atomic::compiler_fence; + +use arbitrary_int::{u2, u3}; +pub use zynq7000::l2_cache::LatencyConfig; +use zynq7000::l2_cache::{ + Associativity, AuxControl, Control, InterruptControl, MmioL2Cache, ReplacementPolicy, WaySize, +}; + +use crate::slcr::Slcr; + +/// This is the default configuration used by Xilinx/AMD. +pub const AUX_CTRL_DEFAULT: AuxControl = AuxControl::builder() + .with_early_bresp_enable(true) + .with_isntruction_prefetch_enable(true) + .with_data_prefetch_enable(true) + .with_nonsec_interrupt_access_control(false) + .with_nonsec_lockdown_enable(false) + .with_cache_replace_policy(ReplacementPolicy::RoundRobin) + .with_force_write_alloc(u2::new(0)) + .with_shared_attr_override(false) + .with_parity_enable(true) + .with_event_monitor_bus_enable(true) + .with_way_size(WaySize::_64kB) + .with_associativity(Associativity::_8Way) + .with_shared_attribute_invalidate(false) + .with_exclusive_cache_config(false) + .with_store_buff_device_limitation_enable(false) + .with_high_priority_so_dev_reads(false) + .with_full_line_zero_enable(false) + .build(); + +/// Xilinx/AMD default configuration. 2 cycles for setup, write and read. +pub const DEFAULT_TAG_RAM_LATENCY: LatencyConfig = LatencyConfig::builder() + .with_write_access_latency(u3::new(0b001)) + .with_read_access_latency(u3::new(0b001)) + .with_setup_latency(u3::new(0b001)) + .build(); + +/// Xilinx/AMD default configuration. 2 cycles for setup and write, 3 cycles for read. +pub const DEFAULT_DATA_RAM_LATENCY: LatencyConfig = LatencyConfig::builder() + .with_write_access_latency(u3::new(0b001)) + .with_read_access_latency(u3::new(0b010)) + .with_setup_latency(u3::new(0b001)) + .build(); + +// SLCR L2C ram configuration. +pub const SLCR_L2C_CONFIG_MAGIC_VALUE: u32 = 0x00020202; + +/// Similar to [init], but uses Xilinx/AMD defaults for the latency configurations. +pub fn init_with_defaults(l2c_mmio: &mut MmioL2Cache<'static>) { + init(l2c_mmio, DEFAULT_TAG_RAM_LATENCY, DEFAULT_DATA_RAM_LATENCY); +} + +/// Generic intializer function for the L2 cache. +/// +/// This function is based on the initialization sequence specified in the TRM p.94 and on +/// the runtime initialization provided by Xilinx/AMD. +pub fn init( + l2c_mmio: &mut MmioL2Cache<'static>, + tag_ram_latency: LatencyConfig, + data_ram_latency: LatencyConfig, +) { + l2c_mmio.write_control(Control::new_disabled()); + l2c_mmio.write_aux_control(AUX_CTRL_DEFAULT); + l2c_mmio.write_tag_ram_latency(tag_ram_latency); + l2c_mmio.write_data_ram_latency(data_ram_latency); + + // Invalidate the whole cache. + l2c_mmio.write_clean_invalidate_by_way(0xffff); + while l2c_mmio.read_cache_sync().busy() {} + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + let pending = l2c_mmio.read_interrupt_raw_status(); + l2c_mmio.write_interrupt_clear(InterruptControl::new_with_raw_value(pending.raw_value())); + unsafe { + Slcr::with(|slcr| { + slcr.write_magic_l2c_register(SLCR_L2C_CONFIG_MAGIC_VALUE); + }); + } + l2c_mmio.write_control(Control::new_enabled()); +} diff --git a/zynq7000-hal/src/lib.rs b/zynq7000-hal/src/lib.rs index 6e06397..7d0f45b 100644 --- a/zynq7000-hal/src/lib.rs +++ b/zynq7000-hal/src/lib.rs @@ -19,6 +19,7 @@ pub mod gic; pub mod gpio; pub mod gtc; pub mod i2c; +pub mod l2_cache; pub mod log; pub mod prelude; pub mod slcr; diff --git a/zynq7000-rt/src/rt.rs b/zynq7000-rt/src/rt.rs index a674f23..441a158 100644 --- a/zynq7000-rt/src/rt.rs +++ b/zynq7000-rt/src/rt.rs @@ -1,8 +1,10 @@ //! Start-up code for Zynq 7000 //! -//! The bootup routine was kepts as similar to the one +//! The bootup routine was is based on the one //! [provided by Xilinx](https://github.com/Xilinx/embeddedsw/blob/master/lib/bsp/standalone/src/arm/cortexa9/gcc/boot.S) -//! as possible. The boot routine includes stack, MMU, cache and .bss/.data section initialization. +//! but does NOT provide the L2 cache initialization. +//! +//! The boot routine includes stack, MMU and .bss/.data section initialization. use cortex_a_rt as _; use cortex_ar::register::{Cpsr, cpsr::ProcessorMode}; @@ -196,61 +198,6 @@ initialize: orr r0, r0, #(0x01 ) /* Cache/TLB maintenance broadcast */ mcr p15, 0, r0, c1, c0, 1 /* Write ACTLR*/ - /* Invalidate L2 Cache and enable L2 Cache*/ - /* For AMP, assume running on CPU1. Don't initialize L2 Cache (up to Linux) */ - ldr r0,=L2CCCrtl /* Load L2CC base address base + control register */ - mov r1, #0 /* force the disable bit */ - str r1, [r0] /* disable the L2 Caches */ - - ldr r0,=L2CCAuxCrtl /* Load L2CC base address base + Aux control register */ - ldr r1,[r0] /* read the register */ - ldr r2,=L2CCAuxControl /* set the default bits */ - orr r1,r1,r2 - str r1, [r0] /* store the Aux Control Register */ - - ldr r0,=L2CCTAGLatReg /* Load L2CC base address base + TAG Latency address */ - ldr r1,=L2CCTAGLatency /* set the latencies for the TAG*/ - str r1, [r0] /* store the TAG Latency register Register */ - - ldr r0,=L2CCDataLatReg /* Load L2CC base address base + Data Latency address */ - ldr r1,=L2CCDataLatency /* set the latencies for the Data*/ - str r1, [r0] /* store the Data Latency register Register */ - - ldr r0,=L2CCWay /* Load L2CC base address base + way register*/ - ldr r2, =0xFFFF - str r2, [r0] /* force invalidate */ - - ldr r0,=L2CCSync /* need to poll 0x730, PSS_L2CC_CACHE_SYNC_OFFSET */ - /* Load L2CC base address base + sync register*/ - /* poll for completion */ -Sync: - ldr r1, [r0] - cmp r1, #0 - bne Sync - - ldr r0,=L2CCIntRaw /* clear pending interrupts */ - ldr r1,[r0] - ldr r0,=L2CCIntClear - str r1,[r0] - - ldr r0,=SLCRUnlockReg /* Load SLCR base address base + unlock register */ - ldr r1,=SLCRUnlockKey /* set unlock key */ - str r1, [r0] /* Unlock SLCR */ - - ldr r0,=SLCRL2cRamReg /* Load SLCR base address base + l2c Ram Control register */ - ldr r1,=SLCRL2cRamConfig /* set the configuration value */ - str r1, [r0] /* store the L2c Ram Control Register */ - - ldr r0,=SLCRlockReg /* Load SLCR base address base + lock register */ - ldr r1,=SLCRlockKey /* set lock key */ - str r1, [r0] /* lock SLCR */ - - ldr r0,=L2CCCrtl /* Load L2CC base address base + control register */ - ldr r1,[r0] /* read the register */ - mov r2, #L2CCControl /* set the enable bit */ - orr r1,r1,r2 - str r1, [r0] /* enable the L2 Caches */ - mov r0, r0 mrc p15, 0, r1, c1, c0, 2 /* read cp access control register (CACR) into r1 */ orr r1, r1, #(0xf << 20) /* enable full access for p10 & p11 */ diff --git a/zynq7000/src/l2_cache.rs b/zynq7000/src/l2_cache.rs index 2ddf544..68036d9 100644 --- a/zynq7000/src/l2_cache.rs +++ b/zynq7000/src/l2_cache.rs @@ -1,4 +1,4 @@ -use arbitrary_int::{u4, u6}; +use arbitrary_int::{u2, u3, u4, u6}; pub const L2C_BASE_ADDR: usize = 0xF8F0_2000; @@ -40,6 +40,153 @@ pub struct CacheId { rtl_release: u6, } +#[repr(transparent)] +pub struct Control(u32); + +impl Control { + pub fn new_enabled() -> Self { + Self(0x1) + } + + pub fn new_disabled() -> Self { + Self(0x0) + } + + #[inline(always)] + pub fn enabled(&mut self) -> bool { + self.0 == 0x1 + } +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +pub enum ReplacementPolicy { + PseudoRandomWithLfsr = 0, + RoundRobin = 1, +} + +#[bitbybit::bitenum(u3, exhaustive = true)] +#[derive(Default, Debug)] +pub enum WaySize { + __Reserved0 = 0b000, + _16kB = 0b001, + _32kB = 0b010, + #[default] + _64kB = 0b011, + _128kB = 0b100, + _256kB = 0b101, + _512kB = 0b110, + __Reserved1 = 0b111, +} + +#[bitbybit::bitenum(u1, exhaustive = true)] +#[derive(Default, Debug)] +pub enum Associativity { + #[default] + _8Way = 0, + _16Way = 1, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +pub struct AuxControl { + #[bit(30, rw)] + early_bresp_enable: bool, + #[bit(29, rw)] + isntruction_prefetch_enable: bool, + #[bit(28, rw)] + data_prefetch_enable: bool, + #[bit(27, rw)] + nonsec_interrupt_access_control: bool, + #[bit(26, rw)] + nonsec_lockdown_enable: bool, + #[bit(25, rw)] + cache_replace_policy: ReplacementPolicy, + #[bits(23..=24, rw)] + force_write_alloc: u2, + #[bit(22, rw)] + shared_attr_override: bool, + #[bit(21, rw)] + parity_enable: bool, + #[bit(20, rw)] + event_monitor_bus_enable: bool, + #[bits(17..=19, rw)] + way_size: WaySize, + #[bit(16, rw)] + associativity: Associativity, + #[bit(13, rw)] + shared_attribute_invalidate: bool, + #[bit(12, rw)] + exclusive_cache_config: bool, + #[bit(11, rw)] + store_buff_device_limitation_enable: bool, + #[bit(10, rw)] + high_priority_so_dev_reads: bool, + /// Disabled by default. + #[bit(0, rw)] + full_line_zero_enable: bool, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug, PartialEq, Eq)] +pub struct LatencyConfig { + /// Latency is the numerical value + 1 cycles. + #[bits(8..=10, rw)] + write_access_latency: u3, + /// Latency is the numerical value + 1 cycles. + #[bits(4..=6, rw)] + read_access_latency: u3, + /// Latency is the numerical value + 1 cycles. + #[bits(0..=2, rw)] + setup_latency: u3, +} + +#[bitbybit::bitfield(u32)] +#[derive(Debug)] +pub struct InterruptStatus { + #[bit(8, r)] + dec_error_l3: bool, + #[bit(7, r)] + slave_error_l3: bool, + #[bit(6, r)] + error_data_ram_read: bool, + #[bit(5, r)] + error_tag_ram_read: bool, + #[bit(4, r)] + error_data_ram_write: bool, + #[bit(3, r)] + error_tag_ram_write: bool, + #[bit(2, r)] + parity_error_data_ram_read: bool, + #[bit(1, r)] + parity_error_tag_ram_read: bool, + /// ECNTR + #[bit(0, r)] + event_counter_overflow_increment: bool, +} + +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] +pub struct InterruptControl { + #[bit(8, w)] + dec_error_l3: bool, + #[bit(7, w)] + slave_error_l3: bool, + #[bit(6, w)] + error_data_ram_read: bool, + #[bit(5, w)] + error_tag_ram_read: bool, + #[bit(4, w)] + error_data_ram_write: bool, + #[bit(3, w)] + error_tag_ram_write: bool, + #[bit(2, w)] + parity_error_data_ram_read: bool, + #[bit(1, w)] + parity_error_tag_ram_read: bool, + /// ECNTR + #[bit(0, w)] + event_counter_overflow_increment: bool, +} + #[derive(derive_mmio::Mmio)] #[repr(C)] pub struct L2Cache { @@ -50,10 +197,10 @@ pub struct L2Cache { _reserved: [u32; 0x3E], - control: u32, - aux_control: u32, - tag_ram_control: u32, - data_ram_control: u32, + control: Control, + aux_control: AuxControl, + tag_ram_latency: LatencyConfig, + data_ram_latency: LatencyConfig, _reserved2: [u32; 0x3C], @@ -64,11 +211,11 @@ pub struct L2Cache { event_counter_0: u32, interrupt_mask: u32, #[mmio(PureRead)] - interrupt_mask_status: u32, + interrupt_mask_status: InterruptStatus, #[mmio(PureRead)] - interrupt_raw_status: u32, + interrupt_raw_status: InterruptStatus, #[mmio(Write)] - interrupt_clear: u32, + interrupt_clear: InterruptControl, _reserved3: [u32; 0x143], diff --git a/zynq7000/src/lib.rs b/zynq7000/src/lib.rs index 059dd97..a9df228 100644 --- a/zynq7000/src/lib.rs +++ b/zynq7000/src/lib.rs @@ -39,6 +39,7 @@ static PERIPHERALS_TAKEN: AtomicBool = AtomicBool::new(false); pub struct PsPeripherals { pub gicc: gic::MmioGicc<'static>, pub gicd: gic::MmioGicd<'static>, + pub l2c: l2_cache::MmioL2Cache<'static>, pub uart_0: uart::MmioUart<'static>, pub uart_1: uart::MmioUart<'static>, pub spi_0: spi::MmioSpi<'static>, @@ -74,6 +75,7 @@ impl PsPeripherals { Self { gicc: gic::Gicc::new_mmio_fixed(), gicd: gic::Gicd::new_mmio_fixed(), + l2c: l2_cache::L2Cache::new_mmio_fixed(), uart_0: uart::Uart::new_mmio_fixed_0(), uart_1: uart::Uart::new_mmio_fixed_1(), gtc: gtc::Gtc::new_mmio_fixed(), -- 2.43.0