Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cortex-ar/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added

- `dmb` data memory barrier in ASM module.
- API for inner cache maintenance as part of the new `cache` module. This includes functions to
completely clean, invalidate or clean & invalidate the L1 data cache or perform data cache
maintenance by MVA (specific address).

## [v0.2.0]

Expand Down
108 changes: 108 additions & 0 deletions cortex-ar/src/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use arbitrary_int::u3;

use crate::register::{Dccimvac, Dccisw, Dccmvac, Dccsw, Dcimvac, Dcisw, SysRegWrite};

/// Invalidate the full L1 data cache.
///
/// ## Generics
///
/// - A: log2(ASSOCIATIVITY) rounded up to the next integer if necessary. For example, a 4-way
/// associative cache will have a value of 2 and a 8-way associative cache will have a value of
/// 3.
/// - N: log2(LINE LENGTH). For example, a 32-byte line length (4 words) will have a value of
/// 5.
/// - S: log2(NUM OF SETS). For systems with a fixed cache size, the number of sets can be
/// calculated using `CACHE SIZE / (LINE LENGTH * ASSOCIATIVITY)`.
/// For example, a 4-way associative 32kB L1 cache with a 32-byte line length (4 words) will
/// have 32768 / (32 * 4) == 256 sets and S will have a value of 8.
#[inline]
pub fn invalidate_l1_data_cache<const A: usize, const N: usize, const S: usize>() {
let ways = 1 << A;
let sets = 1 << S;

for set in 0..sets {
for way in 0..ways {
unsafe { Dcisw::write(Dcisw::new::<A, N>(way, set, u3::new(0))) };
}
}
}

/// Clean the full L1 data cache.
///
/// ## Generics
///
/// - A: log2(ASSOCIATIVITY) rounded up to the next integer if necessary. For example, a 4-way
/// associative cache will have a value of 2 and a 8-way associative cache will have a value of
/// 3.
/// - N: log2(LINE LENGTH). For example, a 32-byte line length (4 words) will have a value of
/// 5.
/// - S: log2(NUM OF SETS). For systems with a fixed cache size, the number of sets can be
/// calculated using `CACHE SIZE / (LINE LENGTH * ASSOCIATIVITY)`.
/// For example, a 4-way associative 32kB L1 cache with a 32-byte line length (4 words) will
/// have 32768 / (32 * 4) == 256 sets and S will have a value of 8.
#[inline]
pub fn clean_l1_data_cache<const A: usize, const N: usize, const S: usize>() {
let ways = 1 << A;
let sets = 1 << S;

for set in 0..sets {
for way in 0..ways {
unsafe { Dccsw::write(Dccsw::new::<A, N>(way, set, u3::new(0))) };
}
}
}

/// Clean and Invalidate the full L1 data cache.
///
/// ## Generics
///
/// - A: log2(ASSOCIATIVITY) rounded up to the next integer if necessary. For example, a 4-way
/// associative cache will have a value of 2 and a 8-way associative cache will have a value of
/// 3.
/// - N: log2(LINE LENGTH). For example, a 32-byte line length (4 words) will have a value of
/// 5.
/// - S: log2(NUM OF SETS). For systems with a fixed cache size, the number of sets can be
/// calculated using `CACHE SIZE / (LINE LENGTH * ASSOCIATIVITY)`.
/// For example, a 4-way associative 32kB L1 cache with a 32-byte line length (4 words) will
/// have 32768 / (32 * 4) == 256 sets and S will have a value of 8.
#[inline]
pub fn clean_and_invalidate_l1_data_cache<const A: usize, const N: usize, const S: usize>() {
let ways = 1 << A;
let sets = 1 << S;

for set in 0..sets {
for way in 0..ways {
unsafe { Dccisw::write(Dccisw::new::<A, N>(way, set, u3::new(0))) };
}
}
}

/// Invalidates a data cache line to the point of coherence.
///
/// See p.1735 of the ARMv7-A Architecture Reference Manual for more information.
#[inline]
pub fn invalidate_data_cache_line_to_poc(addr: u32) {
unsafe {
Dcimvac::write_raw(addr);
}
}

/// Cleans a data cache line to the point of coherence.
///
/// See p.1735 of the ARMv7-A Architecture Reference Manual for more information.
#[inline]
pub fn clean_data_cache_line_to_poc(addr: u32) {
unsafe {
Dccmvac::write_raw(addr);
}
}

/// Cleans and invalidates a data cache line to the point of coherence.
///
/// See p.1735 of the ARMv7-A Architecture Reference Manual for more information.
#[inline]
pub fn clean_and_invalidate_data_cache_line_to_poc(addr: u32) {
unsafe {
Dccimvac::write_raw(addr);
}
}
1 change: 1 addition & 0 deletions cortex-ar/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod critical_section;
#[cfg(target_arch = "arm")]
pub mod asm;

pub mod cache;
pub mod interrupt;
pub mod mmu;
pub mod register;
Expand Down
23 changes: 23 additions & 0 deletions cortex-ar/src/register/bpiall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! BPIALL: Invalidate all entries from branch predictors

use crate::register::SysReg;

/// BPIALL: Invalidate all entries from branch predictors
pub struct BpIAll;

impl SysReg for BpIAll {
const CP: u32 = 15;
const CRN: u32 = 7;
const OP1: u32 = 0;
const CRM: u32 = 5;
const OP2: u32 = 6;
}

impl crate::register::SysRegWrite for BpIAll {}

impl BpIAll {
#[inline]
pub fn write() {
unsafe { <Self as crate::register::SysRegWrite>::write_raw(0) }
}
}
25 changes: 23 additions & 2 deletions cortex-ar/src/register/ccsidr.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
//! Code for managing CCSIDR (*Current Cache Size ID Register*)

use crate::register::{SysReg, SysRegRead};
use arbitrary_int::{u10, u15, u3};

/// CCSIDR (*Current Cache Size ID Register*)
pub struct Ccsidr(pub u32);
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Ccsidr {
#[bit(31, rw)]
write_through: bool,
#[bit(30, rw)]
write_back: bool,
#[bit(29, rw)]
read_alloc: bool,
#[bit(28, rw)]
write_alloc: bool,
#[bits(13..=27, rw)]
num_sets: u15,
#[bits(3..=12, rw)]
associativity: u10,
#[bits(0..=2, rw)]
line_size: u3,
}

impl SysReg for Ccsidr {
const CP: u32 = 15;
const CRN: u32 = 0;
const OP1: u32 = 1;
const CRM: u32 = 0;
const OP2: u32 = 0;
}

impl crate::register::SysRegRead for Ccsidr {}

impl Ccsidr {
#[inline]
/// Reads CCSIDR (*Current Cache Size ID Register*)
pub fn read() -> Ccsidr {
unsafe { Self(<Self as SysRegRead>::read_raw()) }
unsafe { Self::new_with_raw_value(<Self as SysRegRead>::read_raw()) }
}
}
23 changes: 20 additions & 3 deletions cortex-ar/src/register/csselr.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
//! Code for managing CSSELR (*Cache Size Selection Register*)
use arbitrary_int::u3;

use crate::register::{SysReg, SysRegRead, SysRegWrite};

#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum CacheType {
DataOrUnified = 0,
Instruction = 1,
}

/// CSSELR (*Cache Size Selection Register*)
pub struct Csselr(pub u32);
#[bitbybit::bitfield(u32)]
#[derive(Debug, PartialEq, Eq)]
pub struct Csselr {
/// 0 for L1 cache, 1 for L2, etc.
#[bits(1..=3, rw)]
level: u3,
#[bit(0, rw)]
cache_type: CacheType,
}

impl SysReg for Csselr {
const CP: u32 = 15;
const CRN: u32 = 0;
Expand All @@ -16,7 +33,7 @@ impl Csselr {
#[inline]
/// Reads CSSELR (*Cache Size Selection Register*)
pub fn read() -> Csselr {
unsafe { Self(<Self as SysRegRead>::read_raw()) }
unsafe { Self::new_with_raw_value(<Self as SysRegRead>::read_raw()) }
}
}
impl crate::register::SysRegWrite for Csselr {}
Expand All @@ -29,7 +46,7 @@ impl Csselr {
/// Ensure that this value is appropriate for this register
pub unsafe fn write(value: Self) {
unsafe {
<Self as SysRegWrite>::write_raw(value.0);
<Self as SysRegWrite>::write_raw(value.raw_value());
}
}
}
38 changes: 38 additions & 0 deletions cortex-ar/src/register/dc_sw_ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use arbitrary_int::u3;

/// Create input data register value for cache maintenance operations by set and way.
///
/// ## Generics
///
/// - A: log2(ASSOCIATIVITY) rounded up to the next integer if necessary. For example, a 4-way
/// associative cache will have a value of 2 and a 8-way associative cache will have a value of
/// 4.
/// - N: log2(LINE LENGTH). For example, a 32-byte line length (4 words) will have a value of
/// 5.
#[inline]
pub const fn new<const A: usize, const N: usize>(way: u8, set: u16, level: u3) -> u32 {
if A == 0 {
((set as u32) << N) | level.value() as u32
} else {
((way as u32) << (32 - A)) | ((set as u32) << N) | level.value() as u32
}
}

/// Create input data register value for cache maintenance operations by set and way.
/// Returns [None] on invalid input.
///
/// # Arguments
///
/// - a: log2(ASSOCIATIVITY) rounded up to the next integer if necessary. For example, a 4-way
/// associative cache will have a value of 2 and a 8-way associative cache will have a value of
/// 4.
/// - n: log2(LINE LENGTH). For example, a 32-byte line length (4 words) will have a value of
/// 5.
#[inline]
pub const fn new_with_offsets(a: usize, way: u8, n: usize, set: u16, level: u3) -> u32 {
if a == 0 {
((set as u32) << n) | level.value() as u32
} else {
((way as u32) << (32 - a)) | ((set as u32) << n) | level.value() as u32
}
}
36 changes: 36 additions & 0 deletions cortex-ar/src/register/dccimvac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! DCCIMVAC (*Clean And Invalidate Data Cache Or Unified Cache Line by MVA to Point of Coherence.*)
use crate::register::{SysReg, SysRegWrite};

pub struct Dccimvac(pub u32);

impl Dccimvac {
#[inline]
pub const fn new(addr: u32) -> Self {
Self(addr)
}
}

impl SysReg for Dccimvac {
const CP: u32 = 15;
const CRN: u32 = 7;
const OP1: u32 = 0;
const CRM: u32 = 14;
const OP2: u32 = 1;
}

impl crate::register::SysRegWrite for Dccimvac {}

impl Dccimvac {
#[inline]
/// Writes DCCIMVAC (*Clean And Invalidate Data Cache Or Unified Cache Line by MVA to Point of Coherence.*)
///
/// # Safety
///
/// Ensure that this value is appropriate for this register. Generally, the address passed
/// to the write call should be aligned to the cache line size.
pub unsafe fn write(value: Self) {
unsafe {
<Self as SysRegWrite>::write_raw(value.0);
}
}
}
61 changes: 61 additions & 0 deletions cortex-ar/src/register/dccisw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! DCCISW (*Clean and Invalidate Data or Unified cache line by Set/Way.*)

use arbitrary_int::u3;

use crate::register::{SysReg, SysRegWrite};

pub struct Dccisw(pub u32);

impl Dccisw {
/// Create DCCISW value for cache cleaning and invalidation by set and way.
///
/// ## Generics
///
/// - A: log2(ASSOCIATIVITY) rounded up to the next integer if necessary. For example, a 4-way
/// associative cache will have a value of 2 and a 8-way associative cache will have a value of
/// 3.
/// - N: log2(LINE LENGTH). For example, a 32-byte line length (4 words) will have a value of
/// 5.
#[inline]
pub const fn new<const A: usize, const N: usize>(way: u8, set: u16, level: u3) -> Self {
Self(super::dc_sw_ops::new::<A, N>(way, set, level))
}

/// Create DCCISW value for cache cleaning and invalidation by set and way.
/// Returns [None] on invalid input.
///
/// # Arguments
///
/// - a: log2(ASSOCIATIVITY) rounded up to the next integer if necessary. For example, a 4-way
/// associative cache will have a value of 2 and a 8-way associative cache will have a value of
/// 3.
/// - n: log2(LINE LENGTH). For example, a 32-byte line length (4 words) will have a value of
/// 5.
#[inline]
pub const fn new_with_offsets(a: usize, way: u8, n: usize, set: u16, level: u3) -> Self {
Self(super::dc_sw_ops::new_with_offsets(a, way, n, set, level))
}
}
impl SysReg for Dccisw {
const CP: u32 = 15;
const CRN: u32 = 7;
const OP1: u32 = 0;
const CRM: u32 = 14;
const OP2: u32 = 2;
}

impl crate::register::SysRegWrite for Dccisw {}

impl Dccisw {
#[inline]
/// Writes DCCISW (*Clean and Invalidate data or unified cache line by set/way.*)
///
/// # Safety
///
/// Ensure that this value is appropriate for this register
pub unsafe fn write(value: Self) {
unsafe {
<Self as SysRegWrite>::write_raw(value.0);
}
}
}
Loading