diff options
| -rw-r--r-- | embassy-rp/src/rom_data.rs | 115 |
1 files changed, 54 insertions, 61 deletions
diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 757a27114..805c1f09f 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs | |||
| @@ -56,56 +56,32 @@ macro_rules! declare_rom_function { | |||
| 56 | fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty | 56 | fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty |
| 57 | $lookup:block | 57 | $lookup:block |
| 58 | ) => { | 58 | ) => { |
| 59 | #[doc = r"Additional access for the `"] | 59 | declare_rom_function!{ |
| 60 | #[doc = stringify!($name)] | 60 | __internal , |
| 61 | #[doc = r"` ROM function."] | 61 | $(#[$outer])* |
| 62 | pub mod $name { | 62 | fn $name( $($argname: $ty),* ) -> $ret |
| 63 | /// Retrieve a function pointer. | 63 | $lookup |
| 64 | #[cfg(not(feature = "rom-func-cache"))] | ||
| 65 | pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { | ||
| 66 | let p: *const u32 = $lookup; | ||
| 67 | unsafe { | ||
| 68 | let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | ||
| 69 | func | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Retrieve a function pointer. | ||
| 74 | #[cfg(feature = "rom-func-cache")] | ||
| 75 | pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { | ||
| 76 | use core::sync::atomic::{AtomicU16, Ordering}; | ||
| 77 | |||
| 78 | // All pointers in the ROM fit in 16 bits, so we don't need a | ||
| 79 | // full width word to store the cached value. | ||
| 80 | static CACHED_PTR: AtomicU16 = AtomicU16::new(0); | ||
| 81 | // This is safe because the lookup will always resolve | ||
| 82 | // to the same value. So even if an interrupt or another | ||
| 83 | // core starts at the same time, it just repeats some | ||
| 84 | // work and eventually writes back the correct value. | ||
| 85 | let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { | ||
| 86 | 0 => { | ||
| 87 | let raw: *const u32 = $lookup; | ||
| 88 | CACHED_PTR.store(raw as u16, Ordering::Relaxed); | ||
| 89 | raw | ||
| 90 | }, | ||
| 91 | val => val as *const u32, | ||
| 92 | }; | ||
| 93 | unsafe { | ||
| 94 | let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | ||
| 95 | func | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | 64 | } |
| 65 | }; | ||
| 99 | 66 | ||
| 100 | $(#[$outer])* | 67 | ( |
| 101 | pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { | 68 | $(#[$outer:meta])* |
| 102 | $name::ptr()($($argname),*) | 69 | unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty |
| 70 | $lookup:block | ||
| 71 | ) => { | ||
| 72 | declare_rom_function!{ | ||
| 73 | __internal unsafe , | ||
| 74 | $(#[$outer])* | ||
| 75 | fn $name( $($argname: $ty),* ) -> $ret | ||
| 76 | $lookup | ||
| 103 | } | 77 | } |
| 104 | }; | 78 | }; |
| 105 | 79 | ||
| 106 | ( | 80 | ( |
| 81 | __internal | ||
| 82 | $( $maybe_unsafe:ident )? , | ||
| 107 | $(#[$outer:meta])* | 83 | $(#[$outer:meta])* |
| 108 | unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty | 84 | fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty |
| 109 | $lookup:block | 85 | $lookup:block |
| 110 | ) => { | 86 | ) => { |
| 111 | #[doc = r"Additional access for the `"] | 87 | #[doc = r"Additional access for the `"] |
| @@ -114,43 +90,58 @@ macro_rules! declare_rom_function { | |||
| 114 | pub mod $name { | 90 | pub mod $name { |
| 115 | /// Retrieve a function pointer. | 91 | /// Retrieve a function pointer. |
| 116 | #[cfg(not(feature = "rom-func-cache"))] | 92 | #[cfg(not(feature = "rom-func-cache"))] |
| 117 | pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { | 93 | pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { |
| 118 | let p: *const u32 = $lookup; | 94 | let p: *const u32 = $lookup; |
| 119 | unsafe { | 95 | unsafe { |
| 120 | let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | 96 | let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret |
| 97 | = core::mem::transmute(p); | ||
| 121 | func | 98 | func |
| 122 | } | 99 | } |
| 123 | } | 100 | } |
| 124 | 101 | ||
| 102 | #[cfg(feature = "rom-func-cache")] | ||
| 103 | // unlike rp2040-hal we store a full word, containing the full function pointer. | ||
| 104 | // rp2040-hal saves two bytes by storing only the rom offset, at the cost of | ||
| 105 | // having to do an indirection and an atomic operation on every rom call. | ||
| 106 | static mut CACHE: $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret | ||
| 107 | = trampoline; | ||
| 108 | |||
| 109 | #[cfg(feature = "rom-func-cache")] | ||
| 110 | $( $maybe_unsafe )? extern "C" fn trampoline( $($argname: $ty),* ) -> $ret { | ||
| 111 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 112 | |||
| 113 | let p: *const u32 = $lookup; | ||
| 114 | #[allow(unused_unsafe)] | ||
| 115 | unsafe { | ||
| 116 | CACHE = core::mem::transmute(p); | ||
| 117 | compiler_fence(Ordering::Release); | ||
| 118 | CACHE($($argname),*) | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 125 | /// Retrieve a function pointer. | 122 | /// Retrieve a function pointer. |
| 126 | #[cfg(feature = "rom-func-cache")] | 123 | #[cfg(feature = "rom-func-cache")] |
| 127 | pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { | 124 | pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { |
| 128 | use core::sync::atomic::{AtomicU16, Ordering}; | 125 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 129 | 126 | ||
| 130 | // All pointers in the ROM fit in 16 bits, so we don't need a | ||
| 131 | // full width word to store the cached value. | ||
| 132 | static CACHED_PTR: AtomicU16 = AtomicU16::new(0); | ||
| 133 | // This is safe because the lookup will always resolve | 127 | // This is safe because the lookup will always resolve |
| 134 | // to the same value. So even if an interrupt or another | 128 | // to the same value. So even if an interrupt or another |
| 135 | // core starts at the same time, it just repeats some | 129 | // core starts at the same time, it just repeats some |
| 136 | // work and eventually writes back the correct value. | 130 | // work and eventually writes back the correct value. |
| 137 | let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { | 131 | // |
| 138 | 0 => { | 132 | // We easily get away with using only compiler fences here |
| 139 | let raw: *const u32 = $lookup; | 133 | // because RP2040 SRAM is not cached. If it were we'd need |
| 140 | CACHED_PTR.store(raw as u16, Ordering::Relaxed); | 134 | // to make sure updates propagate quickly, or just take the |
| 141 | raw | 135 | // hit and let each core resolve every function once. |
| 142 | }, | 136 | compiler_fence(Ordering::Acquire); |
| 143 | val => val as *const u32, | ||
| 144 | }; | ||
| 145 | unsafe { | 137 | unsafe { |
| 146 | let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); | 138 | CACHE |
| 147 | func | ||
| 148 | } | 139 | } |
| 149 | } | 140 | } |
| 150 | } | 141 | } |
| 151 | 142 | ||
| 152 | $(#[$outer])* | 143 | $(#[$outer])* |
| 153 | pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { | 144 | pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret { |
| 154 | $name::ptr()($($argname),*) | 145 | $name::ptr()($($argname),*) |
| 155 | } | 146 | } |
| 156 | }; | 147 | }; |
| @@ -369,6 +360,7 @@ pub fn fplib_start() -> *const u8 { | |||
| 369 | } | 360 | } |
| 370 | 361 | ||
| 371 | /// See Table 180 in the RP2040 datasheet for the contents of this table. | 362 | /// See Table 180 in the RP2040 datasheet for the contents of this table. |
| 363 | #[cfg_attr(feature = "rom-func-cache", inline(never))] | ||
| 372 | pub fn soft_float_table() -> *const usize { | 364 | pub fn soft_float_table() -> *const usize { |
| 373 | rom_table_lookup(DATA_TABLE, *b"SF") | 365 | rom_table_lookup(DATA_TABLE, *b"SF") |
| 374 | } | 366 | } |
| @@ -379,6 +371,7 @@ pub fn fplib_end() -> *const u8 { | |||
| 379 | } | 371 | } |
| 380 | 372 | ||
| 381 | /// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. | 373 | /// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. |
| 374 | #[cfg_attr(feature = "rom-func-cache", inline(never))] | ||
| 382 | pub fn soft_double_table() -> *const usize { | 375 | pub fn soft_double_table() -> *const usize { |
| 383 | if rom_version_number() < 2 { | 376 | if rom_version_number() < 2 { |
| 384 | panic!( | 377 | panic!( |
