Skip to content

Commit 5a20108

Browse files
committed
Add a generic timer driver.
1 parent 9ac645c commit 5a20108

28 files changed

+1454
-224
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//! Generic-timer example for Arm Cortex-R52
2+
3+
#![no_std]
4+
#![no_main]
5+
6+
// pull in our start-up code
7+
use cortex_r::generic_timer::{El1PhysicalTimer, El1VirtualTimer, GenericTimer};
8+
use cortex_r_examples as _;
9+
10+
use semihosting::println;
11+
12+
/// The entry-point to the Rust application.
13+
///
14+
/// It is called by the start-up code in `cortex-m-rt`.
15+
#[no_mangle]
16+
pub extern "C" fn kmain() {
17+
if let Err(e) = main() {
18+
panic!("main returned {:?}", e);
19+
}
20+
semihosting::process::exit(0);
21+
}
22+
23+
/// The main function of our Rust application.
24+
///
25+
/// Called by [`kmain`].
26+
fn main() -> Result<(), core::fmt::Error> {
27+
let cntfrq = cortex_r::register::Cntfrq::read().0;
28+
println!("cntfrq = {:.03} MHz", cntfrq as f32 / 1_000_000.0);
29+
30+
let delay_ticks = cntfrq * 2;
31+
32+
let mut pgt = unsafe { El1PhysicalTimer::new() };
33+
let mut vgt = unsafe { El1VirtualTimer::new() };
34+
35+
let pgt_ref: &mut dyn GenericTimer = &mut pgt;
36+
let vgt_ref: &mut dyn GenericTimer = &mut vgt;
37+
38+
for (timer, name) in [(pgt_ref, "physical"), (vgt_ref, "virtual")] {
39+
println!("Using {} timer ************************", name);
40+
41+
println!("Print five, one per second...");
42+
for i in 0..5 {
43+
println!("i = {}", i);
44+
timer.delay_ms(1000);
45+
}
46+
47+
let now = timer.counter();
48+
println!("{} is now: {}", name, now);
49+
println!("Waiting for {} {} ticks to count up...", delay_ticks, name);
50+
timer.counter_compare_set(now + delay_ticks as u64);
51+
timer.enable(true);
52+
while !timer.interrupt_status() {
53+
core::hint::spin_loop();
54+
}
55+
println!("Matched! {} count now {}", name, timer.counter());
56+
57+
println!(
58+
"Waiting for {} {} ticks to count down...",
59+
delay_ticks, name
60+
);
61+
timer.countdown_set(delay_ticks);
62+
while !timer.interrupt_status() {
63+
core::hint::spin_loop();
64+
}
65+
println!(
66+
"{} countdown hit zero! (and is now {})",
67+
name,
68+
timer.countdown() as i32
69+
);
70+
}
71+
72+
Ok(())
73+
}

cortex-r/src/generic_timer/el0.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//! Code and types for Generic Timer support at EL0 on Armv8-R.
2+
3+
use crate::register;
4+
5+
/// Represents our Generic Timer when we are running at EL0.
6+
///
7+
/// Note that for most of these APIs to work, EL0 needs to have been granted
8+
/// access using methods like
9+
/// [El2GenericTimer::el0_access_physical_counter](crate::generic_timer::El2GenericTimer::el0_access_physical_counter).
10+
pub struct El0PhysicalTimer();
11+
12+
impl El0PhysicalTimer {
13+
/// Create an EL0 Timer handle for the Physical Timer.
14+
///
15+
/// EL2/EL1 has to grant permission for EL0 to use the Physical Timer, so
16+
/// check they did that.
17+
///
18+
/// # Safety
19+
///
20+
/// Only create one of these at any given time, as they access shared
21+
/// mutable state within the processor and do read-modify-writes on that
22+
/// state.
23+
pub unsafe fn new() -> El0PhysicalTimer {
24+
El0PhysicalTimer()
25+
}
26+
}
27+
28+
impl super::GenericTimer for El0PhysicalTimer {
29+
fn frequency_hz(&self) -> u32 {
30+
register::Cntfrq::read().0
31+
}
32+
33+
fn counter(&mut self) -> u64 {
34+
register::CntPct::read().0
35+
}
36+
37+
fn counter_compare(&mut self) -> u64 {
38+
register::CntpCval::read().0
39+
}
40+
41+
fn counter_compare_set(&mut self, value: u64) {
42+
register::CntpCval::write(register::CntpCval(value))
43+
}
44+
45+
fn countdown(&self) -> u32 {
46+
register::CntpTval::read().0
47+
}
48+
49+
fn countdown_set(&mut self, duration_ticks: u32) {
50+
register::CntpTval::write(register::CntpTval(duration_ticks))
51+
}
52+
53+
fn enabled(&self) -> bool {
54+
register::CntpCtl::read().enable()
55+
}
56+
57+
fn enable(&self, enabled: bool) {
58+
register::CntpCtl::modify(|r| {
59+
r.set_enable(enabled);
60+
});
61+
}
62+
63+
fn interrupt_masked(&self) -> bool {
64+
register::CntpCtl::read().imask()
65+
}
66+
67+
fn interrupt_mask(&mut self, mask: bool) {
68+
register::CntpCtl::modify(|r| {
69+
r.set_imask(mask);
70+
});
71+
}
72+
73+
fn interrupt_status(&self) -> bool {
74+
register::CntpCtl::read().istatus()
75+
}
76+
}
77+
78+
pub struct El0VirtualTimer();
79+
80+
impl El0VirtualTimer {
81+
/// Create an EL0 Timer handle for the Virtual Timer.
82+
///
83+
/// # Safety
84+
///
85+
/// Only create one of these at any given time, as they access shared
86+
/// mutable state within the processor and do read-modify-writes on that state.
87+
pub unsafe fn new() -> El0VirtualTimer {
88+
El0VirtualTimer()
89+
}
90+
}
91+
92+
impl super::GenericTimer for El0VirtualTimer {
93+
fn frequency_hz(&self) -> u32 {
94+
register::Cntfrq::read().0
95+
}
96+
97+
fn counter(&mut self) -> u64 {
98+
register::CntVct::read().0
99+
}
100+
101+
fn counter_compare(&mut self) -> u64 {
102+
register::CntvCval::read().0
103+
}
104+
105+
fn counter_compare_set(&mut self, value: u64) {
106+
register::CntvCval::write(register::CntvCval(value))
107+
}
108+
109+
fn countdown(&self) -> u32 {
110+
register::CntvTval::read().0
111+
}
112+
113+
fn countdown_set(&mut self, duration_ticks: u32) {
114+
register::CntvTval::write(register::CntvTval(duration_ticks))
115+
}
116+
117+
fn enabled(&self) -> bool {
118+
register::CntvCtl::read().enable()
119+
}
120+
121+
fn enable(&self, enabled: bool) {
122+
register::CntvCtl::modify(|r| {
123+
r.set_enable(enabled);
124+
});
125+
}
126+
127+
fn interrupt_masked(&self) -> bool {
128+
register::CntvCtl::read().imask()
129+
}
130+
131+
fn interrupt_mask(&mut self, mask: bool) {
132+
register::CntvCtl::modify(|r| {
133+
r.set_imask(mask);
134+
});
135+
}
136+
137+
fn interrupt_status(&self) -> bool {
138+
register::CntvCtl::read().istatus()
139+
}
140+
}

cortex-r/src/generic_timer/el1.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//! Code and types for Generic Timer support at EL1 on Armv8-R.
2+
3+
use crate::register;
4+
5+
use super::{El0PhysicalTimer, El0VirtualTimer, GenericTimer};
6+
7+
/// Represents our Physical Timer when we are running at EL1.
8+
pub struct El1PhysicalTimer(pub(crate) El0PhysicalTimer);
9+
10+
impl El1PhysicalTimer {
11+
/// Create an EL1 Generic Timer handle
12+
///
13+
/// # Safety
14+
///
15+
/// Only create one of these at any given time, as they access shared
16+
/// mutable state within the processor and do read-modify-writes on that state.
17+
pub unsafe fn new() -> El1PhysicalTimer {
18+
unsafe { El1PhysicalTimer(El0PhysicalTimer::new()) }
19+
}
20+
21+
/// Control whether user code at EL0 can access the physical counter.
22+
pub fn el0_access_physical_counter(&mut self, access: bool) {
23+
register::Cntkctl::modify(|r| {
24+
r.set_el0pcten(access);
25+
});
26+
}
27+
28+
/// Control whether user code at EL0 can access the physical timer.
29+
pub fn el0_access_physical_timer(&mut self, access: bool) {
30+
register::Cntkctl::modify(|r| {
31+
r.set_el0pten(access);
32+
});
33+
}
34+
}
35+
36+
impl GenericTimer for El1PhysicalTimer {
37+
fn frequency_hz(&self) -> u32 {
38+
self.0.frequency_hz()
39+
}
40+
41+
fn counter(&mut self) -> u64 {
42+
self.0.counter()
43+
}
44+
45+
fn counter_compare(&mut self) -> u64 {
46+
self.0.counter_compare()
47+
}
48+
49+
fn counter_compare_set(&mut self, value: u64) {
50+
self.0.counter_compare_set(value)
51+
}
52+
53+
fn countdown(&self) -> u32 {
54+
self.0.countdown()
55+
}
56+
57+
fn countdown_set(&mut self, duration_ticks: u32) {
58+
self.0.countdown_set(duration_ticks)
59+
}
60+
61+
fn enabled(&self) -> bool {
62+
self.0.enabled()
63+
}
64+
65+
fn enable(&self, enabled: bool) {
66+
self.0.enable(enabled)
67+
}
68+
69+
fn interrupt_masked(&self) -> bool {
70+
self.0.interrupt_masked()
71+
}
72+
73+
fn interrupt_mask(&mut self, mask: bool) {
74+
self.0.interrupt_mask(mask)
75+
}
76+
77+
fn interrupt_status(&self) -> bool {
78+
self.0.interrupt_status()
79+
}
80+
}
81+
82+
/// Represents our Virtual Timer when we are running at EL1.
83+
pub struct El1VirtualTimer(El0VirtualTimer);
84+
85+
impl El1VirtualTimer {
86+
/// Create an EL1 Generic Timer handle
87+
///
88+
/// # Safety
89+
///
90+
/// Only create one of these at any given time, as they access shared
91+
/// mutable state within the processor and do read-modify-writes on that state.
92+
pub unsafe fn new() -> El1VirtualTimer {
93+
unsafe { El1VirtualTimer(El0VirtualTimer::new()) }
94+
}
95+
96+
/// Control whether user code at EL0 can access the virtual counter.
97+
pub fn el0_access_virtual_counter(&mut self, access: bool) {
98+
register::Cntkctl::modify(|r| {
99+
r.set_el0vcten(access);
100+
});
101+
}
102+
103+
/// Control whether user code at EL0 can access the virtual timer.
104+
pub fn el0_access_virtual_timer(&mut self, access: bool) {
105+
register::Cntkctl::modify(|r| {
106+
r.set_el0vten(access);
107+
});
108+
}
109+
110+
/// Configure an event stream from the virtual counter.
111+
///
112+
/// The event stream is tied to one of the bottom 16 bits of the virtual
113+
/// counter. If you select the bottom (0th) bit, the event fires every
114+
/// counter tick. If you select the 3rd bit, the event fires every 2^3 = 8
115+
/// counter ticks.
116+
///
117+
/// This is useful if you want to ensure that a WFE instruction can never
118+
/// wait forever; effectively it allows you to put a timeout on a WFE.
119+
///
120+
/// Pass None to disable.
121+
pub fn virtual_event_stream_configure(&mut self, event_config: Option<&super::EventConfig>) {
122+
if let Some(event_config) = event_config {
123+
register::Cntkctl::modify(|r| {
124+
r.set_evnti(arbitrary_int::u4::from_u8(event_config.rate as u8));
125+
r.set_evntdir(event_config.evntdir == super::EventDir::HighLow);
126+
r.set_evnten(true);
127+
});
128+
} else {
129+
register::Cntkctl::modify(|r| {
130+
r.set_evnten(false);
131+
});
132+
}
133+
}
134+
}
135+
136+
impl GenericTimer for El1VirtualTimer {
137+
fn frequency_hz(&self) -> u32 {
138+
self.0.frequency_hz()
139+
}
140+
141+
fn counter(&mut self) -> u64 {
142+
self.0.counter()
143+
}
144+
145+
fn counter_compare(&mut self) -> u64 {
146+
self.0.counter_compare()
147+
}
148+
149+
fn counter_compare_set(&mut self, value: u64) {
150+
self.0.counter_compare_set(value)
151+
}
152+
153+
fn countdown(&self) -> u32 {
154+
self.0.countdown()
155+
}
156+
157+
fn countdown_set(&mut self, duration_ticks: u32) {
158+
self.0.countdown_set(duration_ticks)
159+
}
160+
161+
fn enabled(&self) -> bool {
162+
self.0.enabled()
163+
}
164+
165+
fn enable(&self, enabled: bool) {
166+
self.0.enable(enabled)
167+
}
168+
169+
fn interrupt_masked(&self) -> bool {
170+
self.0.interrupt_masked()
171+
}
172+
173+
fn interrupt_mask(&mut self, mask: bool) {
174+
self.0.interrupt_mask(mask)
175+
}
176+
177+
fn interrupt_status(&self) -> bool {
178+
self.0.interrupt_status()
179+
}
180+
}

0 commit comments

Comments
 (0)