Skip to content

Commit fa3fea0

Browse files
committed
feat(port_riscv): add use_sbi_timer!
1 parent ea09e6d commit fa3fea0

File tree

4 files changed

+358
-1
lines changed

4 files changed

+358
-1
lines changed

src/r3_port_riscv/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1010
### Added
1111

1212
- The new option `ThreadingOptions::PRIVILEGE_LEVEL` allows for running the kernel in other privilege levels than M-mode.
13+
- `use_sbi_timer!` can be used to install a timer driver based on [the RISC-V Supervisor Binary Interface](https://github.com/riscv-non-isa/riscv-sbi-doc).
1314

1415
### Changed
1516

src/r3_port_riscv/src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,17 @@ pub mod mtime {
7575
pub mod imp;
7676
}
7777

78+
/// The SBI-based timer driver.
79+
#[doc(hidden)]
80+
pub mod sbi_timer {
81+
pub mod cfg;
82+
pub mod imp;
83+
}
84+
7885
pub use self::mtime::cfg::*;
7986
pub use self::plic::cfg::*;
8087
pub use self::rt::cfg::*;
88+
pub use self::sbi_timer::cfg::*;
8189
pub use self::threading::cfg::*;
8290

8391
/// Defines the entry points of a port instantiation. Implemented by
@@ -110,7 +118,7 @@ pub trait EntryPoint {
110118
}
111119

112120
/// An abstract inferface to a port timer driver. Implemented by
113-
/// [`use_mtime!`].
121+
/// [`use_mtime!`] and [`use_sbi_timer!`].
114122
pub trait Timer {
115123
/// Initialize the driver. This will be called just before entering
116124
/// [`PortToKernel::boot`].
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//! The public interface for the SBI-based timer driver.
2+
use r3_core::kernel::InterruptNum;
3+
4+
/// Attach the implementation of [`PortTimer`] based on [the RISC-V Supervisor
5+
/// Binary Interface][1] Timer Extension (EID #0x54494D45 "TIME") and `time[h]`
6+
/// CSR to a given kernel trait type.
7+
/// This macro also implements [`Timer`] on the system type.
8+
/// **Requires [`SbiTimerOptions`].**
9+
///
10+
/// [1]: https://github.com/riscv-non-isa/riscv-sbi-doc
11+
/// [`PortTimer`]: r3_kernel::PortTimer
12+
/// [`Timer`]: crate::Timer
13+
///
14+
/// You should do the following:
15+
///
16+
/// - Implement [`SbiTimerOptions`] on the system type `$Traits`.
17+
/// - Call `$Traits::configure_timer()` in your configuration function.
18+
/// See the following example.
19+
///
20+
/// ```rust,ignore
21+
/// r3_port_riscv::use_sbi_timer!(unsafe impl PortTimer for System);
22+
///
23+
/// impl r3_port_riscv::SbiTimerOptions for System {
24+
/// const FREQUENCY: u64 = 1_000_000;
25+
/// }
26+
///
27+
/// const fn configure_app(b: &mut CfgBuilder<System>) -> Objects {
28+
/// System::configure_timer(b);
29+
/// /* ... */
30+
/// }
31+
/// ```
32+
///
33+
/// # Safety
34+
///
35+
/// - `SbiTimerOptions` must be configured correctly.
36+
///
37+
#[macro_export]
38+
macro_rules! use_sbi_timer {
39+
(unsafe impl PortTimer for $Traits:ty) => {
40+
const _: () = {
41+
use $crate::r3_core::{
42+
kernel::{traits, Cfg},
43+
utils::Init,
44+
};
45+
use $crate::r3_kernel::{PortTimer, System, UTicks};
46+
use $crate::r3_portkit::tickless;
47+
use $crate::{sbi_timer, SbiTimerOptions, Timer};
48+
49+
impl PortTimer for $Traits {
50+
const MAX_TICK_COUNT: UTicks = u32::MAX;
51+
const MAX_TIMEOUT: UTicks = u32::MAX;
52+
53+
unsafe fn tick_count() -> UTicks {
54+
// Safety: We are just forwarding the call
55+
unsafe { sbi_timer::imp::tick_count::<Self>() }
56+
}
57+
58+
unsafe fn pend_tick() {
59+
// Safety: We are just forwarding the call
60+
unsafe { sbi_timer::imp::pend_tick::<Self>() }
61+
}
62+
63+
unsafe fn pend_tick_after(tick_count_delta: UTicks) {
64+
// Safety: We are just forwarding the call
65+
unsafe { sbi_timer::imp::pend_tick_after::<Self>(tick_count_delta) }
66+
}
67+
}
68+
69+
impl Timer for $Traits {
70+
unsafe fn init() {
71+
unsafe { sbi_timer::imp::init::<Self>() }
72+
}
73+
}
74+
75+
const TICKLESS_CFG: tickless::TicklessCfg =
76+
match tickless::TicklessCfg::new(tickless::TicklessOptions {
77+
hw_freq_num: <$Traits as SbiTimerOptions>::FREQUENCY,
78+
hw_freq_denom: <$Traits as SbiTimerOptions>::FREQUENCY_DENOMINATOR,
79+
hw_headroom_ticks: <$Traits as SbiTimerOptions>::HEADROOM,
80+
// `stime` is a 64-bit free-running counter and it is
81+
// expensive to create a 32-bit timer with an arbitrary
82+
// period out of it.
83+
force_full_hw_period: true,
84+
// Clearing `stime` is not possible, so we must record the
85+
// starting value of `stime` by calling `reset`.
86+
resettable: true,
87+
}) {
88+
Ok(x) => x,
89+
Err(e) => e.panic(),
90+
};
91+
92+
static mut TIMER_STATE: tickless::TicklessState<TICKLESS_CFG> = Init::INIT;
93+
94+
// Safety: Only `use_sbi_timer!` is allowed to `impl` this
95+
unsafe impl sbi_timer::imp::TimerInstance for $Traits {
96+
const TICKLESS_CFG: tickless::TicklessCfg = TICKLESS_CFG;
97+
98+
type TicklessState = tickless::TicklessState<TICKLESS_CFG>;
99+
100+
fn tickless_state() -> *mut Self::TicklessState {
101+
unsafe { core::ptr::addr_of_mut!(TIMER_STATE) }
102+
}
103+
}
104+
105+
impl $Traits {
106+
pub const fn configure_timer<C>(b: &mut Cfg<C>)
107+
where
108+
C: ~const traits::CfgInterruptLine<System = System<Self>>,
109+
{
110+
sbi_timer::imp::configure(b);
111+
}
112+
}
113+
};
114+
};
115+
}
116+
117+
/// The options for [`use_sbi_timer!`].
118+
pub trait SbiTimerOptions {
119+
/// The numerator of the effective timer clock rate of the dual timer.
120+
const FREQUENCY: u64;
121+
122+
/// The denominator of the effective timer clock rate of the dual timer.
123+
/// Defaults to `1`.
124+
const FREQUENCY_DENOMINATOR: u64 = 1;
125+
126+
/// The maximum permissible timer interrupt latency, measured in hardware
127+
/// timer cycles.
128+
///
129+
/// Defaults to `min(FREQUENCY * 60 / FREQUENCY_DENOMINATOR, 0x40000000)`.
130+
const HEADROOM: u32 = min128(
131+
Self::FREQUENCY as u128 * 60 / Self::FREQUENCY_DENOMINATOR as u128,
132+
0x40000000,
133+
) as u32;
134+
135+
/// The timer's interrupt number. Defaults to [`INTERRUPT_TIMER`].
136+
///
137+
/// [`INTERRUPT_TIMER`]: crate::INTERRUPT_TIMER
138+
const INTERRUPT_NUM: InterruptNum = crate::INTERRUPT_TIMER;
139+
}
140+
141+
const fn min128(x: u128, y: u128) -> u128 {
142+
if x < y {
143+
x
144+
} else {
145+
y
146+
}
147+
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
//! The implementation of the SBI-based timer driver.
2+
use core::arch::asm;
3+
use r3_core::kernel::{traits, Cfg, StaticInterruptHandler};
4+
use r3_kernel::{KernelTraits, PortToKernel, System, UTicks};
5+
use r3_portkit::tickless::{TicklessCfg, TicklessStateTrait};
6+
7+
use crate::sbi_timer::cfg::SbiTimerOptions;
8+
9+
/// Implemented on a system type by [`use_sbi_timer!`].
10+
///
11+
/// # Safety
12+
///
13+
/// Only meant to be implemented by [`use_sbi_timer!`].
14+
pub unsafe trait TimerInstance: KernelTraits + SbiTimerOptions {
15+
// FIXME: Specifying `TicklessCfg::new(...)` here causes a "cycle
16+
// detected" error
17+
const TICKLESS_CFG: TicklessCfg;
18+
19+
// FIXME: Specifying `TicklessState<{ Self::TICKLESS_CFG }>` here
20+
// fails with an error message similar to
21+
// <https://github.com/rust-lang/rust/issues/72821>
22+
type TicklessState: TicklessStateTrait;
23+
24+
fn tickless_state() -> *mut Self::TicklessState;
25+
}
26+
27+
#[cfg(any(
28+
target_arch = "riscv32",
29+
target_arch = "riscv64",
30+
target_arch = "riscv128"
31+
))]
32+
trait TimerInstanceExt: TimerInstance {
33+
#[inline(always)]
34+
fn time_lo() -> usize {
35+
let read: usize;
36+
unsafe { asm!("csrr {read}, time", read = lateout(reg) read) };
37+
read
38+
}
39+
40+
#[cfg(target_arch = "riscv32")]
41+
#[inline(always)]
42+
fn time_hi() -> usize {
43+
let read: usize;
44+
unsafe { asm!("csrr {read}, timeh", read = lateout(reg) read) };
45+
read
46+
}
47+
48+
#[inline(always)]
49+
#[cfg(target_arch = "riscv32")]
50+
fn set_timecmp(value: u64) {
51+
unsafe {
52+
asm!(
53+
"ecall",
54+
inout("a0") value as u32 => _, // param0 => error
55+
inout("a1") (value >> 32) as u32 => _, // param => value
56+
out("a2") _,
57+
out("a3") _,
58+
out("a4") _,
59+
out("a5") _,
60+
inout("a6") 0 => _, //fid
61+
inout("a7") 0x54494D45 => _, // eid
62+
)
63+
};
64+
}
65+
66+
#[inline(always)]
67+
#[cfg(not(target_arch = "riscv32"))]
68+
fn set_timecmp(value: u64) {
69+
unsafe {
70+
asm!(
71+
"ecall",
72+
inout("a0") value as usize => _, // param0 => error
73+
out("a1") _,
74+
out("a2") _,
75+
out("a3") _,
76+
out("a4") _,
77+
out("a5") _,
78+
inout("a6") 0 => _, //fid
79+
inout("a7") 0x54494D45 => _, // eid
80+
)
81+
};
82+
}
83+
84+
#[cfg(not(target_arch = "riscv32"))]
85+
#[inline(always)]
86+
fn time() -> u64 {
87+
Self::time_lo() as u64
88+
}
89+
90+
#[cfg(target_arch = "riscv32")]
91+
#[inline(always)]
92+
fn time() -> u64 {
93+
loop {
94+
let hi1 = Self::time_hi();
95+
let lo = Self::time_lo();
96+
let hi2 = Self::time_hi();
97+
if hi1 == hi2 {
98+
return lo as u64 | ((hi2 as u64) << 32);
99+
}
100+
}
101+
}
102+
}
103+
#[cfg(not(any(
104+
target_arch = "riscv32",
105+
target_arch = "riscv64",
106+
target_arch = "riscv128"
107+
)))]
108+
trait TimerInstanceExt: TimerInstance {
109+
fn time_lo() -> usize {
110+
unimplemented!("target mismatch")
111+
}
112+
113+
fn set_timecmp(value: u64) {
114+
unimplemented!("target mismatch")
115+
}
116+
117+
fn time() -> u64 {
118+
unimplemented!("target mismatch")
119+
}
120+
}
121+
impl<T: TimerInstance> TimerInstanceExt for T {}
122+
123+
/// The configuration function.
124+
pub const fn configure<C, Traits: TimerInstance>(b: &mut Cfg<C>)
125+
where
126+
C: ~const traits::CfgInterruptLine<System = System<Traits>>,
127+
{
128+
StaticInterruptHandler::define()
129+
.line(Traits::INTERRUPT_NUM)
130+
.start(handle_tick::<Traits>)
131+
.finish(b);
132+
}
133+
134+
/// Implements [`crate::Timer::init`]
135+
#[inline]
136+
pub fn init<Traits: TimerInstance>() {
137+
let tcfg = &Traits::TICKLESS_CFG;
138+
139+
// Safety: No context switching during boot
140+
let tstate = unsafe { &mut *Traits::tickless_state() };
141+
142+
tstate.reset(tcfg, Traits::time_lo() as u32);
143+
}
144+
145+
/// Implements [`r3_kernel::PortTimer::tick_count`]
146+
///
147+
/// # Safety
148+
///
149+
/// Only meant to be referenced by `use_sbi_timer!`.
150+
pub unsafe fn tick_count<Traits: TimerInstance>() -> UTicks {
151+
let tcfg = &Traits::TICKLESS_CFG;
152+
153+
let hw_tick_count = Traits::time_lo() as u32;
154+
155+
// Safety: CPU Lock protects it from concurrent access
156+
let tstate = unsafe { &mut *Traits::tickless_state() };
157+
tstate.tick_count(tcfg, hw_tick_count)
158+
}
159+
160+
/// Implements [`r3_kernel::PortTimer::pend_tick`]
161+
///
162+
/// # Safety
163+
///
164+
/// Only meant to be referenced by `use_sbi_timer!`.
165+
pub unsafe fn pend_tick<Traits: TimerInstance>() {
166+
Traits::set_timecmp(0);
167+
}
168+
169+
/// Implements [`r3_kernel::PortTimer::pend_tick_after`]
170+
///
171+
/// # Safety
172+
///
173+
/// Only meant to be referenced by `use_sbi_timer!`.
174+
pub unsafe fn pend_tick_after<Traits: TimerInstance>(tick_count_delta: UTicks) {
175+
let tcfg = &Traits::TICKLESS_CFG;
176+
// Safety: CPU Lock protects it from concurrent access
177+
let tstate = unsafe { &mut *Traits::tickless_state() };
178+
179+
let cur_hw_tick_count = Traits::time();
180+
let hw_ticks = tstate
181+
.mark_reference_and_measure(tcfg, cur_hw_tick_count as u32, tick_count_delta)
182+
.hw_ticks;
183+
184+
let next_hw_tick_count = cur_hw_tick_count + hw_ticks as u64;
185+
186+
Traits::set_timecmp(next_hw_tick_count);
187+
}
188+
189+
#[inline]
190+
fn handle_tick<Traits: TimerInstance>(_: usize) {
191+
let tcfg = &Traits::TICKLESS_CFG;
192+
193+
// Safety: CPU Lock protects it from concurrent access
194+
let tstate = unsafe { &mut *Traits::tickless_state() };
195+
196+
let cur_hw_tick_count = Traits::time_lo() as u32;
197+
tstate.mark_reference(tcfg, cur_hw_tick_count);
198+
199+
// Safety: CPU Lock inactive, an interrupt context
200+
unsafe { Traits::timer_tick() };
201+
}

0 commit comments

Comments
 (0)