use portable_atomic::{AtomicU32, AtomicU64, Ordering};
static LAST_LOW: AtomicU32 = AtomicU32::new(0);
static HIGH: AtomicU64 = AtomicU64::new(0);
pub fn initialize() {
unsafe {
let mut pmcr: u32;
core::arch::asm!(
"mrc p15, 0, {pmcr}, c9, c12, 0",
pmcr = out(reg) pmcr,
options(nomem, nostack, preserves_flags)
);
pmcr |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
core::arch::asm!(
"mcr p15, 0, {pmcr}, c9, c12, 0",
pmcr = in(reg) pmcr,
options(nomem, nostack, preserves_flags)
);
let cycle_counter_enable: u32 = 1 << 31;
core::arch::asm!(
"mcr p15, 0, {cycle_counter_enable}, c9, c12, 1",
cycle_counter_enable = in(reg) cycle_counter_enable,
options(nomem, nostack, preserves_flags)
);
core::arch::asm!("isb", options(nomem, nostack, preserves_flags));
}
LAST_LOW.store(read_pmccntr(), Ordering::Relaxed);
HIGH.store(0, Ordering::Relaxed);
}
#[inline(always)]
pub fn read_raw_counter() -> u64 {
let low = read_pmccntr();
let previous = LAST_LOW.swap(low, Ordering::Relaxed);
if low < previous {
HIGH.fetch_add(1u64 << 32, Ordering::Relaxed);
}
HIGH.load(Ordering::Relaxed) | low as u64
}
#[inline(always)]
fn read_pmccntr() -> u32 {
let value: u32;
unsafe {
core::arch::asm!(
"mrc p15, 0, {value}, c9, c13, 0",
value = out(reg) value,
options(nomem, nostack, preserves_flags)
);
}
value
}