#![cfg_attr(feature = "std", allow(unreachable_code, unused_variables))]
use crate::reg::{
field::{RegFieldBit, RegFieldBits, WWRegField, WWRegFieldBit, WWRegFieldBits},
tag::RegAtomic,
RReg, Reg, RegHold, RegRef, WReg, WRegAtomic,
};
use drone_core::bitfield::Bitfield;
pub trait RwRegAtomic<'a, T: RegAtomic>: RReg<T> + WRegAtomic<'a, T> + RegRef<'a, T> {
fn modify<F>(&'a self, f: F)
where
F: for<'b> Fn(
&'b mut <Self as RegRef<'a, T>>::Hold,
) -> &'b mut <Self as RegRef<'a, T>>::Hold;
fn modify_reg<F>(&'a self, f: F)
where
F: for<'b> Fn(&'b Self, &'b mut Self::Val);
}
pub trait WRwRegFieldAtomic<T: RegAtomic>
where
Self: WWRegField<T>,
Self::Reg: RReg<T> + WReg<T>,
{
fn modify<F>(&self, f: F)
where
F: Fn(&mut <Self::Reg as Reg<T>>::Val);
}
pub trait WRwRegFieldBitAtomic<T: RegAtomic>
where
Self: WRwRegFieldAtomic<T> + RegFieldBit<T>,
Self::Reg: RReg<T> + WReg<T>,
{
fn set_bit(&self);
fn clear_bit(&self);
fn toggle_bit(&self);
}
pub trait WRwRegFieldBitsAtomic<T: RegAtomic>
where
Self: WRwRegFieldAtomic<T> + RegFieldBits<T>,
Self::Reg: RReg<T> + WReg<T>,
{
fn write_bits(&self, bits: <<Self::Reg as Reg<T>>::Val as Bitfield>::Bits);
}
pub trait AtomicBits: Sized {
unsafe fn load_excl(address: usize) -> Self;
unsafe fn store_excl(self, address: usize) -> bool;
}
impl<'a, T, R> RwRegAtomic<'a, T> for R
where
T: RegAtomic,
R: RReg<T> + WRegAtomic<'a, T> + RegRef<'a, T>,
<R::Val as Bitfield>::Bits: AtomicBits,
{
#[inline]
fn modify<F>(&'a self, f: F)
where
F: for<'b> Fn(
&'b mut <Self as RegRef<'a, T>>::Hold,
) -> &'b mut <Self as RegRef<'a, T>>::Hold,
{
loop {
let mut val = unsafe { self.hold(load_excl::<T, Self>()) };
f(&mut val);
if unsafe { store_excl::<T, Self>(val.val()) } {
break;
}
}
}
#[inline]
fn modify_reg<F>(&'a self, f: F)
where
F: for<'b> Fn(&'b Self, &'b mut Self::Val),
{
loop {
let mut val = unsafe { load_excl::<T, Self>() };
f(self, &mut val);
if unsafe { store_excl::<T, Self>(val) } {
break;
}
}
}
}
impl<T, R> WRwRegFieldAtomic<T> for R
where
T: RegAtomic,
R: WWRegField<T>,
R::Reg: RReg<T> + WReg<T>,
<<R::Reg as Reg<T>>::Val as Bitfield>::Bits: AtomicBits,
{
#[inline]
fn modify<F>(&self, f: F)
where
F: Fn(&mut <Self::Reg as Reg<T>>::Val),
{
loop {
let mut val = unsafe { load_excl::<T, Self::Reg>() };
f(&mut val);
if unsafe { store_excl::<T, Self::Reg>(val) } {
break;
}
}
}
}
impl<T, R> WRwRegFieldBitAtomic<T> for R
where
T: RegAtomic,
R: WRwRegFieldAtomic<T> + RegFieldBit<T>,
R::Reg: RReg<T> + WReg<T>,
{
#[inline]
fn set_bit(&self) {
self.modify(|val| {
self.set(val);
});
}
#[inline]
fn clear_bit(&self) {
self.modify(|val| {
self.clear(val);
});
}
#[inline]
fn toggle_bit(&self) {
self.modify(|val| {
self.toggle(val);
});
}
}
impl<T, R> WRwRegFieldBitsAtomic<T> for R
where
T: RegAtomic,
R: WRwRegFieldAtomic<T> + RegFieldBits<T>,
R::Reg: RReg<T> + WReg<T>,
{
#[inline]
fn write_bits(&self, bits: <<Self::Reg as Reg<T>>::Val as Bitfield>::Bits) {
self.modify(|val| {
self.write(val, bits);
});
}
}
unsafe fn load_excl<T, R>() -> R::Val
where
T: RegAtomic,
R: Reg<T>,
<R::Val as Bitfield>::Bits: AtomicBits,
{
unsafe { R::val_from(<R::Val as Bitfield>::Bits::load_excl(R::ADDRESS)) }
}
unsafe fn store_excl<T, R>(val: R::Val) -> bool
where
T: RegAtomic,
R: Reg<T>,
<R::Val as Bitfield>::Bits: AtomicBits,
{
unsafe { val.bits().store_excl(R::ADDRESS) }
}
macro_rules! atomic_bits {
($type:ty, $ldrex:expr, $strex:expr) => {
impl AtomicBits for $type {
unsafe fn load_excl(address: usize) -> Self {
#[cfg(feature = "std")]
return unimplemented!();
let output: Self;
#[cfg(not(feature = "std"))]
unsafe {
asm!(
$ldrex,
address = in(reg) address,
output = lateout(reg) output,
options(readonly, nostack, preserves_flags),
);
}
output
}
unsafe fn store_excl(self, address: usize) -> bool {
#[cfg(feature = "std")]
return unimplemented!();
let status: Self;
#[cfg(not(feature = "std"))]
unsafe {
asm!(
$strex,
input = in(reg) self,
address = in(reg) address,
status = lateout(reg) status,
options(nostack, preserves_flags),
);
}
status == 0
}
}
};
}
atomic_bits!(u32, "ldrex {output}, [{address}]", "strex {status}, {input}, [{address}]");
atomic_bits!(u16, "ldrexh {output}, [{address}]", "strexh {status}, {input}, [{address}]");
atomic_bits!(u8, "ldrexb {output}, [{address}]", "strexb {status}, {input}, [{address}]");