Last active
February 11, 2020 00:41
-
-
Save rubberduck203/20415cb0bdc0726b2ebf0903e7193665 to your computer and use it in GitHub Desktop.
Spinlock that provides a bare_metal::CriticalSection to bare_metal::Mutex
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
//! Example implementation of a different way than interrupt::free to provide a critical section. | |
//! | |
#![no_main] | |
#![no_std] | |
extern crate panic_halt; | |
use cortex_m::peripheral::syst::SystClkSource; | |
use cortex_m::Peripherals; | |
use cortex_m_rt::{entry, exception}; | |
use cortex_m_semihosting::hprintln; | |
use core::cell::Cell; | |
use bare_metal::{Mutex,CriticalSection}; | |
use core::sync::atomic::{AtomicBool, Ordering}; | |
static SPINLOCK: Spinlock = Spinlock(AtomicBool::new(false)); | |
static COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0)); | |
#[entry] | |
fn main() -> ! { | |
let p = Peripherals::take().unwrap(); | |
let mut syst = p.SYST; | |
// configures the system timer to trigger a SysTick exception every second | |
syst.set_clock_source(SystClkSource::Core); | |
syst.set_reload(8_000_000); // period = 1s | |
syst.enable_counter(); | |
syst.enable_interrupt(); | |
loop {} | |
} | |
#[exception] | |
fn SysTick() { | |
let mut count = 0; | |
SPINLOCK.transaction(|cs| { | |
count = COUNTER.borrow(cs).get(); | |
count = count + 1; | |
COUNTER.borrow(cs).set(count); | |
}); | |
hprintln!("{}", count).unwrap(); | |
} | |
struct Spinlock(AtomicBool); | |
impl Spinlock { | |
fn lock(&self) { | |
while self.0.swap(true, Ordering::Acquire) { | |
continue; | |
} | |
} | |
fn unlock(&self) { | |
self.0.store(false, Ordering::Release); | |
} | |
//nearly identical to cortex-m's interrupt::free function | |
// if interrupt::free was a struct that implemented `lock` and `unlock`, the same code could be used. | |
fn transaction<F,R>(&self, f: F) -> R | |
where F: FnOnce(&CriticalSection) -> R { | |
self.lock(); | |
let r = f(unsafe { &CriticalSection::new() }); | |
self.unlock(); | |
r | |
} | |
} |
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
//! Spinlock that actually locks | |
//! Beware the deadlocks that can happen when using inside on an interrupt. | |
#![no_main] | |
#![no_std] | |
extern crate panic_halt; | |
use cortex_m::peripheral::syst::SystClkSource; | |
use cortex_m::Peripherals; | |
use cortex_m_rt::{entry, exception}; | |
use cortex_m_semihosting::hprintln; | |
use core::cell::Cell; | |
use bare_metal::{Mutex,CriticalSection}; | |
use core::sync::atomic::{AtomicBool, Ordering}; | |
static SPINLOCK: Spinlock = Spinlock(AtomicBool::new(false)); | |
static COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0)); | |
#[entry] | |
fn main() -> ! { | |
let p = Peripherals::take().unwrap(); | |
let mut syst = p.SYST; | |
// configures the system timer to trigger a SysTick exception every second | |
syst.set_clock_source(SystClkSource::Core); | |
syst.set_reload(8_000_000); // period = 1s | |
// syst.set_reload(4_000_000); // period 0.5s | |
syst.enable_counter(); | |
syst.enable_interrupt(); | |
loop { | |
let mut count = 0; | |
SPINLOCK.transaction(|cs| { | |
// if you're in here when systick fires, deadlock, so keep it quick | |
count = COUNTER.borrow(cs).get(); | |
}); | |
hprintln!("{}", count).unwrap(); | |
//cortex_m::asm::wfi(); | |
} | |
} | |
#[exception] | |
fn SysTick() { | |
SPINLOCK.transaction(|cs| { | |
let count = COUNTER.borrow(cs).get(); | |
COUNTER.borrow(cs).set(count + 1); | |
}); | |
} | |
trait MutexT<T> { | |
fn lock(&self); | |
fn unlock(&self); | |
fn with_lock<F,R>(&self, f: F) -> R | |
where F: FnOnce(&CriticalSection) -> R { | |
self.lock(); | |
let r = f(unsafe { &CriticalSection::new() }); | |
self.unlock(); | |
r | |
} | |
} | |
// impl<T> MutexT<T> for Spinlock { | |
// fn lock(&self) { | |
// } | |
// fn unlock(&self) { | |
// } | |
// } | |
struct Spinlock(AtomicBool); | |
impl Spinlock { | |
fn lock(&self) { | |
/* | |
* If current value is true (locked), | |
* spin until it is false (unlocked), | |
* then set it to true (locked) and exit. | |
*/ | |
loop { | |
match self.0.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) { | |
Ok(false) => break, | |
_ => continue | |
} | |
} | |
} | |
fn unlock(&self) { | |
self.0.store(false, Ordering::Release); | |
} | |
fn transaction<F,R>(&self, f: F) -> R | |
where F: FnOnce(&CriticalSection) -> R { | |
self.lock(); | |
let r = f(unsafe { &CriticalSection::new() }); | |
self.unlock(); | |
r | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment