Add ROM function caching

Add a feature to enable automatic caching of the result of ROM table
function lookups.
This commit is contained in:
Derek Hageman 2022-01-29 17:46:45 -07:00
parent a6daaf9fa3
commit 98fd6c1724
2 changed files with 55 additions and 0 deletions

View file

@ -53,3 +53,4 @@ rt = ["rp2040-pac/rt"]
# #
# embassy-traits = ["embassy_traits", "futures"] # embassy-traits = ["embassy_traits", "futures"]
alloc = [] alloc = []
rom-func-cache = []

View file

@ -58,6 +58,7 @@ macro_rules! declare_rom_function {
#[doc = r"` ROM function."] #[doc = r"` ROM function."]
pub mod $name { pub mod $name {
/// Retrieve a function pointer. /// Retrieve a function pointer.
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: *const u32 = $lookup; let p: *const u32 = $lookup;
unsafe { unsafe {
@ -65,6 +66,32 @@ macro_rules! declare_rom_function {
func func
} }
} }
/// Retrieve a function pointer.
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
// All pointers in the ROM fit in 16 bits, so we don't need a
// full width word to store the cached value.
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
// This is safe because the lookup will always resolve
// to the same value. So even if an interrupt or another
// core starts at the same time, it just repeats some
// work and eventually writes back the correct value.
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: *const u32 = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as *const u32,
};
unsafe {
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
} }
$(#[$outer])* $(#[$outer])*
@ -83,6 +110,7 @@ macro_rules! declare_rom_function {
#[doc = r"` ROM function."] #[doc = r"` ROM function."]
pub mod $name { pub mod $name {
/// Retrieve a function pointer. /// Retrieve a function pointer.
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: *const u32 = $lookup; let p: *const u32 = $lookup;
unsafe { unsafe {
@ -90,6 +118,32 @@ macro_rules! declare_rom_function {
func func
} }
} }
/// Retrieve a function pointer.
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
// All pointers in the ROM fit in 16 bits, so we don't need a
// full width word to store the cached value.
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
// This is safe because the lookup will always resolve
// to the same value. So even if an interrupt or another
// core starts at the same time, it just repeats some
// work and eventually writes back the correct value.
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: *const u32 = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as *const u32,
};
unsafe {
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
} }
$(#[$outer])* $(#[$outer])*