#[cfg(target_arch = "riscv32")]
use core::hint::spin_loop;
#[cfg(target_arch = "riscv32")]
use core::ptr::{read_volatile, write_volatile};
#[cfg(target_arch = "riscv32")]
const SYSTIMER_BASE: usize = 0x500E_2000;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_UNIT0_OP: *mut u32 = (SYSTIMER_BASE + 0x04) as *mut u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_UNIT0_VAL_HI: *const u32 = (SYSTIMER_BASE + 0x40) as *const u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_UNIT0_VAL_LO: *const u32 = (SYSTIMER_BASE + 0x44) as *const u32;
#[cfg(target_arch = "riscv32")]
const UNIT_OP_UPDATE_BIT: u32 = 1 << 30;
#[cfg(target_arch = "riscv32")]
const UNIT_OP_VALID_BIT: u32 = 1 << 29;
pub const TICK_RATE_HZ: u64 = 16_000_000;
pub const TICKS_PER_US: u64 = TICK_RATE_HZ / 1_000_000;
pub const TARGET0_HI_MASK: u32 = 0x000F_FFFF;
#[inline]
#[cfg(target_arch = "riscv32")]
pub fn now_ticks() -> u64 {
unsafe {
write_volatile(SYSTIMER_UNIT0_OP, UNIT_OP_UPDATE_BIT);
while read_volatile(SYSTIMER_UNIT0_OP) & UNIT_OP_VALID_BIT == 0 {
spin_loop();
}
let hi = read_volatile(SYSTIMER_UNIT0_VAL_HI) as u64;
let lo = read_volatile(SYSTIMER_UNIT0_VAL_LO) as u64;
(hi << 32) | lo
}
}
#[inline]
#[cfg(not(target_arch = "riscv32"))]
pub fn now_ticks() -> u64 {
0
}
#[inline]
pub const fn ticks_to_us(ticks: u64) -> u64 {
ticks / TICKS_PER_US
}
#[inline]
pub const fn us_to_ticks(us: u64) -> u64 {
us * TICKS_PER_US
}
#[inline]
pub fn now_us() -> u64 {
ticks_to_us(now_ticks())
}
#[cfg(target_arch = "riscv32")]
const SYSTIMER_CONF: *mut u32 = SYSTIMER_BASE as *mut u32;
#[cfg(target_arch = "riscv32")]
const CONF_TARGET0_WORK_EN: u32 = 1 << 24;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_TARGET0_HI: *mut u32 = (SYSTIMER_BASE + 0x1C) as *mut u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_TARGET0_LO: *mut u32 = (SYSTIMER_BASE + 0x20) as *mut u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_TARGET0_CONF: *mut u32 = (SYSTIMER_BASE + 0x34) as *mut u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_COMP0_LOAD: *mut u32 = (SYSTIMER_BASE + 0x50) as *mut u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_INT_ENA: *mut u32 = (SYSTIMER_BASE + 0x64) as *mut u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_INT_RAW: *const u32 = (SYSTIMER_BASE + 0x68) as *const u32;
#[cfg(target_arch = "riscv32")]
const SYSTIMER_INT_CLR: *mut u32 = (SYSTIMER_BASE + 0x6C) as *mut u32;
pub const TARGET0_INT_BIT: u32 = 1 << 0;
#[inline]
pub const fn target_ticks_split(target_ticks: u64) -> (u32, u32) {
let hi = ((target_ticks >> 32) as u32) & TARGET0_HI_MASK;
let lo = target_ticks as u32;
(hi, lo)
}
pub fn init_alarm0() {
#[cfg(target_arch = "riscv32")]
unsafe {
let conf = read_volatile(SYSTIMER_CONF);
write_volatile(SYSTIMER_CONF, conf | CONF_TARGET0_WORK_EN);
}
}
pub fn arm_alarm0(_target_ticks: u64) {
#[cfg(target_arch = "riscv32")]
{
let (hi, lo) = target_ticks_split(_target_ticks);
unsafe {
write_volatile(SYSTIMER_INT_ENA, 0);
write_volatile(SYSTIMER_INT_CLR, TARGET0_INT_BIT);
write_volatile(SYSTIMER_TARGET0_HI, hi);
write_volatile(SYSTIMER_TARGET0_LO, lo);
write_volatile(SYSTIMER_TARGET0_CONF, 0);
write_volatile(SYSTIMER_COMP0_LOAD, 1);
write_volatile(SYSTIMER_INT_ENA, TARGET0_INT_BIT);
}
}
}
#[inline]
pub fn disarm_alarm0() {
#[cfg(target_arch = "riscv32")]
unsafe {
write_volatile(SYSTIMER_INT_ENA, 0)
};
}
#[inline]
pub fn clear_alarm0() {
#[cfg(target_arch = "riscv32")]
unsafe {
write_volatile(SYSTIMER_INT_CLR, TARGET0_INT_BIT)
};
}
#[inline]
pub fn alarm0_pending() -> bool {
#[cfg(target_arch = "riscv32")]
{
return unsafe { read_volatile(SYSTIMER_INT_RAW) & TARGET0_INT_BIT != 0 };
}
#[cfg(not(target_arch = "riscv32"))]
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tick_rate_hz_is_sixteen_mhz() {
assert_eq!(TICK_RATE_HZ, 16_000_000);
}
#[test]
fn ticks_per_us_is_sixteen() {
assert_eq!(TICKS_PER_US, 16);
}
#[test]
fn ticks_to_us_round_trips_one_second() {
let one_sec_ticks: u64 = TICK_RATE_HZ;
assert_eq!(ticks_to_us(one_sec_ticks), 1_000_000);
assert_eq!(us_to_ticks(1_000_000), one_sec_ticks);
}
#[test]
fn ticks_to_us_truncates_sub_microsecond() {
assert_eq!(ticks_to_us(15), 0);
assert_eq!(ticks_to_us(16), 1);
assert_eq!(ticks_to_us(17), 1);
}
#[test]
fn us_to_ticks_round_trips_arbitrary_microsecond_values() {
for us in [
0u64,
1,
500,
1_000_000, 60 * 1_000_000, 3_600 * 1_000_000, ] {
let ticks = us_to_ticks(us);
assert_eq!(ticks, us * 16, "us_to_ticks({}) wrong", us);
assert_eq!(ticks_to_us(ticks), us);
}
}
#[test]
fn target0_hi_mask_is_twenty_bits() {
assert_eq!(TARGET0_HI_MASK, (1u32 << 20) - 1);
}
#[test]
fn target_ticks_split_zero_is_zero_zero() {
assert_eq!(target_ticks_split(0), (0, 0));
}
#[test]
fn target_ticks_split_lo_carries_low_thirty_two_bits() {
let (hi, lo) = target_ticks_split(0x0000_0000_DEAD_BEEF);
assert_eq!(hi, 0);
assert_eq!(lo, 0xDEAD_BEEF);
let (hi, lo) = target_ticks_split(0x0000_0001_DEAD_BEEF);
assert_eq!(hi, 1);
assert_eq!(lo, 0xDEAD_BEEF);
}
#[test]
fn target_ticks_split_hi_truncates_above_twenty_bits() {
let target: u64 = 0xFFFF_FFFF_0000_0000;
let (hi, lo) = target_ticks_split(target);
assert_eq!(hi, 0x000F_FFFF);
assert_eq!(lo, 0);
}
#[test]
fn target0_int_bit_is_lowest_bit() {
assert_eq!(TARGET0_INT_BIT, 1);
}
}