-
-
Save jamesmunns/3a8abd81185a98be8757df6bbee34e15 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! NOTE: This is done in the lowest priority interrupt for MnemOS, PendSV, which is | |
//! below the priority of all other system call, interrupt, and exception handlers. | |
//! | |
//! The timer interrupt triggered below is the highest prio interrupt, but this code | |
//! will work as long as it is in a lower priority level than any other interrupt, | |
//! including the timer interrupt. | |
//! | |
//! For MnemOS specifically, this code is triggered by the userspace application | |
//! requesting a sleep via a syscall. | |
//! | |
//! You could probably replicate this in your code's idle function, with a minor | |
//! penalty to interrupt response latency (probably a couple dozen cycles). | |
// Implement a loop that counts the amount of time the CPU is idle. | |
if let Some(dly) = sleep { | |
// Clear the "timer expired flag". This is set by the timer interrupt task, | |
// which runs when the hardware one-shot timer has expired. | |
SNAP.store(false, Ordering::Release); | |
// Set up the timer to delay for the requested number of microseconds. | |
// Start the timer, and enable the interrupt | |
hwtimer.set_oneshot(); | |
hwtimer.shorts.write(|w| w.compare0_stop().enabled()); | |
hwtimer.timer_reset_event(); | |
hwtimer.enable_interrupt(); | |
hwtimer.timer_start(dly); | |
loop { | |
// In order to make sure that we measure ALL interrupts, we disable them, | |
// which will still *pend* them, but not *service* them | |
cortex_m::interrupt::disable(); | |
// Don't check the "interrupt done" flag until AFTER interrupts are disabled, | |
// to prevent racing with the interrupt | |
let done = SNAP.load(Ordering::Acquire); | |
// If the timer hasn't expired yet, go to sleep with a WFI, and measure how | |
// long we were in that WFI condition | |
if !done { | |
let start = hwtimer.read_counter(); | |
// Despite being in an interrupt at the moment, WFI will still return IF | |
// a higher priority interrupt is fired (which our timer interrupt is). | |
// | |
// This means that we know when we make it past this block, SOME interrupt | |
// has fired, either our high prio timer, or some other interrupt which will | |
// cause us to use CPU. | |
compiler_fence(Ordering::SeqCst); | |
cortex_m::asm::wfi(); | |
compiler_fence(Ordering::SeqCst); | |
// Increment the time spent sleeping, based on how long the time was between | |
// starting WFI, and ending WFI. | |
let end = hwtimer.read_counter(); | |
IDLE_TICKS.fetch_add(end - start, Ordering::SeqCst); | |
} | |
// Whether we are done or not, re-enable interrupts, to allow them to be immediately | |
// serviced. This will either handle our timer event, or whatever other interrupt | |
// is pending. | |
unsafe { | |
cortex_m::interrupt::enable(); | |
} | |
// If we noticed our timer interrupt is done, break out of the loop. | |
if done { | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment