Skip to main content

sbi_testing/
time.rs

1//! Timer programmer extension test suite.
2
3use crate::thread::Thread;
4use riscv::{
5    interrupt::supervisor::{Exception, Interrupt},
6    register::{
7        scause::{self, Trap},
8        sie, time,
9    },
10};
11
12/// Timer programmer extension test cases.
13#[derive(Clone, Debug)]
14pub enum Case {
15    /// Can't proceed test for Timer extension does not exist.
16    NotExist,
17    /// Test begin.
18    Begin,
19    /// Test process for time interval overhead between two reads.
20    Interval {
21        /// The time counter value for the first read.
22        begin: u64,
23        /// The time counter value for the second read.
24        end: u64,
25    },
26    /// Test failed for can't read `time` register.
27    ReadFailed,
28    /// Test failed for time counter has decreased during period of two reads.
29    TimeDecreased {
30        /// The time counter value for the first read.
31        a: u64,
32        /// The time counter value for the second read.
33        b: u64,
34    },
35    /// Test process for timer has been set.
36    SetTimer,
37    /// Test failed for unexpected trap during timer test.
38    UnexpectedTrap(Trap<usize, usize>),
39    /// All test cases on timer extension has passed.
40    Pass,
41}
42
43/// Test timer extension.
44pub fn test(delay: u64, mut f: impl FnMut(Case)) {
45    if sbi::probe_extension(sbi::Timer).is_unavailable() {
46        f(Case::NotExist);
47        return;
48    }
49    f(Case::Begin);
50    let begin: usize;
51    let end: usize;
52    let mut ok = 0xffusize;
53    unsafe {
54        core::arch::asm!(
55            "   la   {stvec}, 2f
56                csrw stvec,   {stvec}
57                csrr {begin}, time
58                csrr {end},   time
59                mv   {ok},    zero
60            .align 2
61            2:
62            ",
63            stvec = out(reg) _,
64            begin = out(reg) begin,
65            end   = out(reg) end,
66            ok    = inlateout(reg) ok,
67        );
68    }
69    // TODO: support RV32 where there are time and timeh registers.
70    let begin: u64 = begin as u64;
71    let end: u64 = end as u64;
72    if ok != 0 {
73        f(Case::ReadFailed);
74        return;
75    }
76    if begin >= end {
77        f(Case::TimeDecreased { a: begin, b: end });
78        return;
79    }
80    f(Case::Interval { begin, end });
81
82    let mut stack = [0usize; 32];
83    let mut thread = Thread::new(riscv::asm::wfi as *const () as _);
84    *thread.sp_mut() = stack.as_mut_ptr_range().end as _;
85
86    sbi::set_timer(time::read64() + delay);
87    unsafe {
88        sie::set_stimer();
89        thread.execute();
90    }
91    let trap = scause::read().cause();
92    match trap.try_into::<Interrupt, Exception>() {
93        Ok(Trap::Interrupt(Interrupt::SupervisorTimer)) => {
94            sbi::set_timer(u64::MAX);
95            f(Case::SetTimer);
96            f(Case::Pass);
97        }
98        _ => {
99            f(Case::UnexpectedTrap(trap));
100        }
101    }
102}