Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Delay

The Delay trait provides a way to wait for a certain amount of time and to get the time. It is defined in awkernel_lib/src/delay.rs as follows.

#![allow(unused)]
fn main() {
pub trait Delay {
    /// Wait interrupt.
    fn wait_interrupt();

    /// Wait microseconds.
    fn wait_microsec(usec: u64);

    /// Never return.
    fn wait_forever() -> ! {
        loop {
            Self::wait_interrupt();
        }
    }

    /// Wait milliseconds.
    fn wait_millisec(msec: u64) {
        assert!(msec < u64::MAX / 1000);
        Self::wait_microsec(msec * 1000);
    }

    /// Wait seconds.
    fn wait_sec(sec: u64) {
        assert!(sec < u64::MAX / 1_000_000);
        Self::wait_microsec(sec * 1000 * 1000);
    }

    /// This function returns uptime in microseconds.
    fn uptime() -> u64;

    /// Return CPU cycle counter.
    fn cpu_counter() -> u64;

    /// Pause a CPU during busy loop to reduce CPU power consumption.
    fn pause() {
        core::hint::spin_loop();
    }
}
}

There are several functions regarding the Delay trait in awkernel_lib/src/delay.rs.

functiondescription
fn wait_interrupt()Wait interrupt.
fn wait_microsec(usec: u64)Wait microseconds.
fn wait_millisec(msec: u64)Wait milliseconds.
fn wait_sec(sec: u64)Wait seconds.
fn wait_forever() -> !Never return.
fn uptime() -> u64Return uptime in microseconds.
fn cpu_counter() -> u64Return CPU cycle counter.
fn pause()Pause a CPU during busy loop to reduce CPU power consumption.

Implementation

x86_64

For x86_64, the the X86 structure implements the Delay trait in awkernel_lib/src/arch/x86_64/delay.rs as follows.

#![allow(unused)]
fn main() {
impl Delay for super::X86 {
    fn wait_interrupt() {
        unsafe { core::arch::asm!("hlt") };
    }

    fn wait_microsec(usec: u64) {
        let start = uptime();
        loop {
            let diff = uptime() - start;
            if diff >= usec {
                break;
            }

            core::hint::spin_loop();
        }
    }

    fn uptime() -> u64 {
        let base = HPET_BASE.load(Ordering::Relaxed);
        let hz = HPET_COUNTER_HZ.load(Ordering::Relaxed);
        let start = HPET_COUNTER_START.load(Ordering::Relaxed);

        if hz == 0 {
            0
        } else {
            let now = HPET_MAIN_COUNTER.read(base);
            let diff = now - start;

            diff * 1_000_000 / hz
        }
    }

    fn cpu_counter() -> u64 {
        unsafe { core::arch::x86_64::_rdtsc() }
    }
}
}

Awkernel currently uses High Precision Event Timer (HPET) to get uptime in microseconds. So, if you want to use Awkernel on KVM, you need to enable HPET in the virtual machine settings. To get the cpu cycle counter, Awkernel uses the _rdtsc instruction.

AArch64

For x86_64, the AArch64 structure implements the Delay trait in awkernel_lib/src/arch/aarch64/delay.rs as follows.

#![allow(unused)]
fn main() {
impl Delay for super::AArch64 {
    fn wait_interrupt() {
        unsafe { core::arch::asm!("wfi") };
    }

    fn wait_microsec(usec: u64) {
        let frq = awkernel_aarch64::cntfrq_el0::get();
        let t = awkernel_aarch64::cntvct_el0::get();

        let end = t + ((frq / 1000) * usec) / 1000;

        while awkernel_aarch64::cntvct_el0::get() < end {
            awkernel_aarch64::isb();
        }
    }

    fn uptime() -> u64 {
        let start = unsafe { read_volatile(addr_of!(COUNT_START)) };

        let frq = awkernel_aarch64::cntfrq_el0::get();
        let now = awkernel_aarch64::cntvct_el0::get();

        let diff = now - start;

        diff * 1_000_000 / frq
    }

    fn cpu_counter() -> u64 {
        awkernel_aarch64::pmccntr_el0::get()
    }
}
}

To get uptime in microseconds, Awkernel uses the counter-timer frequency register (CNTFRQ_EL0) and the counter-timer virtual count register (CNTVCT_EL0) of AArch64. To get the cpu cycle counter, Awkernel uses the performance monitors cycle count register (PMCCNTR_EL0).