aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/Cargo.toml4
-rw-r--r--embassy-rp/src/intrinsics.rs276
-rw-r--r--embassy-rp/src/lib.rs3
-rw-r--r--embassy-rp/src/rom_data.rs733
4 files changed, 1016 insertions, 0 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 885a4746d..df0af8dfb 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -22,6 +22,10 @@ unstable-pac = []
22 22
23time-driver = [] 23time-driver = []
24 24
25rom-func-cache = []
26disable-intrinsics = []
27rom-v2-intrinsics = []
28
25# Enable nightly-only features 29# Enable nightly-only features
26nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] 30nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"]
27 31
diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs
new file mode 100644
index 000000000..ac1f54800
--- /dev/null
+++ b/embassy-rp/src/intrinsics.rs
@@ -0,0 +1,276 @@
1#![macro_use]
2
3// Credit: taken from `rp-hal` (also licensed Apache+MIT)
4// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs
5
6/// Generate a series of aliases for an intrinsic function.
7macro_rules! intrinsics_aliases {
8 (
9 extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
10 ) => {};
11 (
12 unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
13 ) => {};
14
15 (
16 extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
17 $alias:ident
18 $($rest:ident)*
19 ) => {
20 #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
21 intrinsics! {
22 extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
23 $name($($argname),*)
24 }
25 }
26
27 intrinsics_aliases! {
28 extern $abi fn $name( $($argname: $ty),* ) -> $ret,
29 $($rest)*
30 }
31 };
32
33 (
34 unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
35 $alias:ident
36 $($rest:ident)*
37 ) => {
38 #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
39 intrinsics! {
40 unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
41 $name($($argname),*)
42 }
43 }
44
45 intrinsics_aliases! {
46 unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
47 $($rest)*
48 }
49 };
50}
51
52/// The macro used to define overridden intrinsics.
53///
54/// This is heavily inspired by the macro used by compiler-builtins. The idea
55/// is to abstract anything special that needs to be done to override an
56/// intrinsic function. Intrinsic generation is disabled for non-ARM targets
57/// so things like CI and docs generation do not have problems. Additionally
58/// they can be disabled with the crate feature `disable-intrinsics` for
59/// testing or comparing performance.
60///
61/// Like the compiler-builtins macro, it accepts a series of functions that
62/// looks like normal Rust code:
63///
64/// intrinsics! {
65/// extern "C" fn foo(a: i32) -> u32 {
66/// // ...
67/// }
68///
69/// #[nonstandard_attribute]
70/// extern "C" fn bar(a: i32) -> u32 {
71/// // ...
72/// }
73/// }
74///
75/// Each function can also be decorated with nonstandard attributes to control
76/// additional behaviour:
77///
78/// * `slower_than_default` - indicates that the override is slower than the
79/// default implementation. Currently this just disables the override
80/// entirely.
81/// * `bootrom_v2` - indicates that the override is only available
82/// on a V2 bootrom or higher. Only enabled when the feature
83/// `rom-v2-intrinsics` is set.
84/// * `alias` - accepts a list of names to alias the intrinsic to.
85/// * `aeabi` - accepts a list of ARM EABI names to alias to.
86///
87macro_rules! intrinsics {
88 () => {};
89
90 (
91 #[slower_than_default]
92 $(#[$($attr:tt)*])*
93 extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
94 $($body:tt)*
95 }
96
97 $($rest:tt)*
98 ) => {
99 // Not exported, but defined so the actual implementation is
100 // considered used
101 #[allow(dead_code)]
102 fn $name( $($argname: $ty),* ) -> $ret {
103 $($body)*
104 }
105
106 intrinsics!($($rest)*);
107 };
108
109 (
110 #[bootrom_v2]
111 $(#[$($attr:tt)*])*
112 extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
113 $($body:tt)*
114 }
115
116 $($rest:tt)*
117 ) => {
118 // Not exported, but defined so the actual implementation is
119 // considered used
120 #[cfg(not(feature = "rom-v2-intrinsics"))]
121 #[allow(dead_code)]
122 fn $name( $($argname: $ty),* ) -> $ret {
123 $($body)*
124 }
125
126 #[cfg(feature = "rom-v2-intrinsics")]
127 intrinsics! {
128 $(#[$($attr)*])*
129 extern $abi fn $name( $($argname: $ty),* ) -> $ret {
130 $($body)*
131 }
132 }
133
134 intrinsics!($($rest)*);
135 };
136
137 (
138 #[alias = $($alias:ident),*]
139 $(#[$($attr:tt)*])*
140 extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
141 $($body:tt)*
142 }
143
144 $($rest:tt)*
145 ) => {
146 intrinsics! {
147 $(#[$($attr)*])*
148 extern $abi fn $name( $($argname: $ty),* ) -> $ret {
149 $($body)*
150 }
151 }
152
153 intrinsics_aliases! {
154 extern $abi fn $name( $($argname: $ty),* ) -> $ret,
155 $($alias) *
156 }
157
158 intrinsics!($($rest)*);
159 };
160
161 (
162 #[alias = $($alias:ident),*]
163 $(#[$($attr:tt)*])*
164 unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
165 $($body:tt)*
166 }
167
168 $($rest:tt)*
169 ) => {
170 intrinsics! {
171 $(#[$($attr)*])*
172 unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
173 $($body)*
174 }
175 }
176
177 intrinsics_aliases! {
178 unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
179 $($alias) *
180 }
181
182 intrinsics!($($rest)*);
183 };
184
185 (
186 #[aeabi = $($alias:ident),*]
187 $(#[$($attr:tt)*])*
188 extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
189 $($body:tt)*
190 }
191
192 $($rest:tt)*
193 ) => {
194 intrinsics! {
195 $(#[$($attr)*])*
196 extern $abi fn $name( $($argname: $ty),* ) -> $ret {
197 $($body)*
198 }
199 }
200
201 intrinsics_aliases! {
202 extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret,
203 $($alias) *
204 }
205
206 intrinsics!($($rest)*);
207 };
208
209 (
210 $(#[$($attr:tt)*])*
211 extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
212 $($body:tt)*
213 }
214
215 $($rest:tt)*
216 ) => {
217 #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
218 $(#[$($attr)*])*
219 extern $abi fn $name( $($argname: $ty),* ) -> $ret {
220 $($body)*
221 }
222
223 #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
224 mod $name {
225 #[no_mangle]
226 $(#[$($attr)*])*
227 pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
228 super::$name($($argname),*)
229 }
230 }
231
232 // Not exported, but defined so the actual implementation is
233 // considered used
234 #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))]
235 #[allow(dead_code)]
236 fn $name( $($argname: $ty),* ) -> $ret {
237 $($body)*
238 }
239
240 intrinsics!($($rest)*);
241 };
242
243 (
244 $(#[$($attr:tt)*])*
245 unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
246 $($body:tt)*
247 }
248
249 $($rest:tt)*
250 ) => {
251 #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
252 $(#[$($attr)*])*
253 unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
254 $($body)*
255 }
256
257 #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))]
258 mod $name {
259 #[no_mangle]
260 $(#[$($attr)*])*
261 pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
262 super::$name($($argname),*)
263 }
264 }
265
266 // Not exported, but defined so the actual implementation is
267 // considered used
268 #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))]
269 #[allow(dead_code)]
270 unsafe fn $name( $($argname: $ty),* ) -> $ret {
271 $($body)*
272 }
273
274 intrinsics!($($rest)*);
275 };
276}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 9ce09064a..9ac98d226 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -4,9 +4,12 @@
4// This mod MUST go first, so that the others see its macros. 4// This mod MUST go first, so that the others see its macros.
5pub(crate) mod fmt; 5pub(crate) mod fmt;
6 6
7mod intrinsics;
8
7pub mod dma; 9pub mod dma;
8pub mod gpio; 10pub mod gpio;
9pub mod interrupt; 11pub mod interrupt;
12pub mod rom_data;
10pub mod rtc; 13pub mod rtc;
11pub mod spi; 14pub mod spi;
12#[cfg(feature = "time-driver")] 15#[cfg(feature = "time-driver")]
diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs
new file mode 100644
index 000000000..757a27114
--- /dev/null
+++ b/embassy-rp/src/rom_data.rs
@@ -0,0 +1,733 @@
1//! Functions and data from the RPI Bootrom.
2//!
3//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
4//!
5//! > The Bootrom contains a number of public functions that provide useful
6//! > RP2040 functionality that might be needed in the absence of any other code
7//! > on the device, as well as highly optimized versions of certain key
8//! > functionality that would otherwise have to take up space in most user
9//! > binaries.
10
11// Credit: taken from `rp-hal` (also licensed Apache+MIT)
12// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs
13
14/// A bootrom function table code.
15pub type RomFnTableCode = [u8; 2];
16
17/// This function searches for (table)
18type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T;
19
20/// The following addresses are described at `2.8.2. Bootrom Contents`
21/// Pointer to the lookup table function supplied by the rom.
22const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _;
23
24/// Pointer to helper functions lookup table.
25const FUNC_TABLE: *const u16 = 0x0000_0014 as _;
26
27/// Pointer to the public data lookup table.
28const DATA_TABLE: *const u16 = 0x0000_0016 as _;
29
30/// Address of the version number of the ROM.
31const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
32
33/// Retrive rom content from a table using a code.
34fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
35 unsafe {
36 let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
37 let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);
38 rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32)
39 }
40}
41
42/// To save space, the ROM likes to store memory pointers (which are 32-bit on
43/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the
44/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped
45/// to address `0x0000_0000` and so 16-bits are always sufficient.
46///
47/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer.
48unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {
49 let ptr: u16 = *rom_address;
50 ptr as *const u32
51}
52
53macro_rules! declare_rom_function {
54 (
55 $(#[$outer:meta])*
56 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
57 $lookup:block
58 ) => {
59 #[doc = r"Additional access for the `"]
60 #[doc = stringify!($name)]
61 #[doc = r"` ROM function."]
62 pub mod $name {
63 /// Retrieve a function pointer.
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 }
99
100 $(#[$outer])*
101 pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
102 $name::ptr()($($argname),*)
103 }
104 };
105
106 (
107 $(#[$outer:meta])*
108 unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
109 $lookup:block
110 ) => {
111 #[doc = r"Additional access for the `"]
112 #[doc = stringify!($name)]
113 #[doc = r"` ROM function."]
114 pub mod $name {
115 /// Retrieve a function pointer.
116 #[cfg(not(feature = "rom-func-cache"))]
117 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
118 let p: *const u32 = $lookup;
119 unsafe {
120 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
121 func
122 }
123 }
124
125 /// Retrieve a function pointer.
126 #[cfg(feature = "rom-func-cache")]
127 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
128 use core::sync::atomic::{AtomicU16, Ordering};
129
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
134 // to the same value. So even if an interrupt or another
135 // core starts at the same time, it just repeats some
136 // work and eventually writes back the correct value.
137 let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
138 0 => {
139 let raw: *const u32 = $lookup;
140 CACHED_PTR.store(raw as u16, Ordering::Relaxed);
141 raw
142 },
143 val => val as *const u32,
144 };
145 unsafe {
146 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
147 func
148 }
149 }
150 }
151
152 $(#[$outer])*
153 pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
154 $name::ptr()($($argname),*)
155 }
156 };
157}
158
159macro_rules! rom_functions {
160 () => {};
161
162 (
163 $(#[$outer:meta])*
164 $c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
165
166 $($rest:tt)*
167 ) => {
168 declare_rom_function! {
169 $(#[$outer])*
170 fn $name( $($argname: $ty),* ) -> $ret {
171 $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
172 }
173 }
174
175 rom_functions!($($rest)*);
176 };
177
178 (
179 $(#[$outer:meta])*
180 $c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
181
182 $($rest:tt)*
183 ) => {
184 declare_rom_function! {
185 $(#[$outer])*
186 unsafe fn $name( $($argname: $ty),* ) -> $ret {
187 $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
188 }
189 }
190
191 rom_functions!($($rest)*);
192 };
193}
194
195rom_functions! {
196 /// Return a count of the number of 1 bits in value.
197 b"P3" fn popcount32(value: u32) -> u32;
198
199 /// Return the bits of value in the reverse order.
200 b"R3" fn reverse32(value: u32) -> u32;
201
202 /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32.
203 b"L3" fn clz32(value: u32) -> u32;
204
205 /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32.
206 b"T3" fn ctz32(value: u32) -> u32;
207
208 /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:
209 /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED
210 /// for the USB Mass Storage Device:
211 /// * 0 No pins are used as per cold boot.
212 /// * Otherwise a single bit set indicating which GPIO pin should be set to output and
213 /// raised whenever there is mass storage activity from the host.
214 /// * disable_interface_mask may be used to control the exposed USB interfaces:
215 /// * 0 To enable both interfaces (as per cold boot).
216 /// * 1 To disable the USB Mass Storage Interface.
217 /// * 2 to Disable the USB PICOBOOT Interface.
218 b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();
219
220 /// Sets n bytes start at ptr to the value c and returns ptr
221 b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8;
222
223 /// Sets n bytes start at ptr to the value c and returns ptr.
224 ///
225 /// Note this is a slightly more efficient variant of _memset that may only
226 /// be used if ptr is word aligned.
227 // Note the datasheet does not match the actual ROM for the code here, see
228 // https://github.com/raspberrypi/pico-feedback/issues/217
229 b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;
230
231 /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
232 /// regions overlap.
233 b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8;
234
235 /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
236 /// regions overlap.
237 ///
238 /// Note this is a slightly more efficient variant of _memcpy that may only be
239 /// used if dest and src are word aligned.
240 b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8;
241
242 /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads.
243 b"IF" unsafe fn connect_internal_flash() -> ();
244
245 /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence.
246 ///
247 /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be
248 /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This
249 /// function configures the SSI with a fixed SCK clock divisor of /6.
250 b"EX" unsafe fn flash_exit_xip() -> ();
251
252 /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a
253 /// block erase command e.g. D8h block erase, and the size of the block erased by this
254 /// command — this function will use the larger block erase where possible, for much higher
255 /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of
256 /// 4096 bytes.
257 b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();
258
259 /// Program data to a range of flash addresses starting at `addr` (and
260 /// offset from the start of flash) and `count` bytes in size. The value
261 /// `addr` must be aligned to a 256-byte boundary, and `count` must be a
262 /// multiple of 256.
263 b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> ();
264
265 /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can
266 /// drive the flashchip select as normal.
267 b"FC" unsafe fn flash_flush_cache() -> ();
268
269 /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits,
270 /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported.
271 /// The debugger calls this function after performing a flash erase/programming operation, so
272 /// that the freshly-programmed code and data is visible to the debug host, without having to
273 /// know exactly what kind of flash device is connected.
274 b"CX" unsafe fn flash_enter_cmd_xip() -> ();
275
276 /// This is the method that is entered by core 1 on reset to wait to be launched by core 0.
277 /// There are few cases where you should call this method (resetting core 1 is much better).
278 /// This method does not return and should only ever be called on core 1.
279 b"WV" unsafe fn wait_for_vector() -> !;
280}
281
282// Various C intrinsics in the ROM
283intrinsics! {
284 #[alias = __popcountdi2]
285 extern "C" fn __popcountsi2(x: u32) -> u32 {
286 popcount32(x)
287 }
288
289 #[alias = __clzdi2]
290 extern "C" fn __clzsi2(x: u32) -> u32 {
291 clz32(x)
292 }
293
294 #[alias = __ctzdi2]
295 extern "C" fn __ctzsi2(x: u32) -> u32 {
296 ctz32(x)
297 }
298
299 // __rbit is only unofficial, but it show up in the ARM documentation,
300 // so may as well hook it up.
301 #[alias = __rbitl]
302 extern "C" fn __rbit(x: u32) -> u32 {
303 reverse32(x)
304 }
305
306 unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () {
307 // Different argument order
308 memset(dest, c as u8, n as u32);
309 }
310
311 #[alias = __aeabi_memset8]
312 unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () {
313 // Different argument order
314 memset4(dest as *mut u32, c as u8, n as u32);
315 }
316
317 unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () {
318 memset(dest, 0, n as u32);
319 }
320
321 #[alias = __aeabi_memclr8]
322 unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () {
323 memset4(dest as *mut u32, 0, n as u32);
324 }
325
326 unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () {
327 memcpy(dest, src, n as u32);
328 }
329
330 #[alias = __aeabi_memcpy8]
331 unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () {
332 memcpy44(dest as *mut u32, src as *const u32, n as u32);
333 }
334}
335
336unsafe fn convert_str(s: *const u8) -> &'static str {
337 let mut end = s;
338 while *end != 0 {
339 end = end.add(1);
340 }
341 let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);
342 core::str::from_utf8_unchecked(s)
343}
344
345/// The version number of the rom.
346pub fn rom_version_number() -> u8 {
347 unsafe { *VERSION_NUMBER }
348}
349
350/// The Raspberry Pi Trading Ltd copyright string.
351pub fn copyright_string() -> &'static str {
352 let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR");
353 unsafe { convert_str(s) }
354}
355
356/// The 8 most significant hex digits of the Bootrom git revision.
357pub fn git_revision() -> u32 {
358 let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR");
359 unsafe { *s }
360}
361
362/// The start address of the floating point library code and data.
363///
364/// This and fplib_end along with the individual function pointers in
365/// soft_float_table can be used to copy the floating point implementation into
366/// RAM if desired.
367pub fn fplib_start() -> *const u8 {
368 rom_table_lookup(DATA_TABLE, *b"FS")
369}
370
371/// See Table 180 in the RP2040 datasheet for the contents of this table.
372pub fn soft_float_table() -> *const usize {
373 rom_table_lookup(DATA_TABLE, *b"SF")
374}
375
376/// The end address of the floating point library code and data.
377pub fn fplib_end() -> *const u8 {
378 rom_table_lookup(DATA_TABLE, *b"FE")
379}
380
381/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
382pub fn soft_double_table() -> *const usize {
383 if rom_version_number() < 2 {
384 panic!(
385 "Double precision operations require V2 bootrom (found: V{})",
386 rom_version_number()
387 );
388 }
389 rom_table_lookup(DATA_TABLE, *b"SD")
390}
391
392/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms)
393pub mod float_funcs {
394
395 macro_rules! make_functions {
396 (
397 $(
398 $(#[$outer:meta])*
399 $offset:literal $name:ident (
400 $( $aname:ident : $aty:ty ),*
401 ) -> $ret:ty;
402 )*
403 ) => {
404 $(
405 declare_rom_function! {
406 $(#[$outer])*
407 fn $name( $( $aname : $aty ),* ) -> $ret {
408 let table: *const usize = $crate::rom_data::soft_float_table();
409 unsafe {
410 // This is the entry in the table. Our offset is given as a
411 // byte offset, but we want the table index (each pointer in
412 // the table is 4 bytes long)
413 let entry: *const usize = table.offset($offset / 4);
414 // Read the pointer from the table
415 core::ptr::read(entry) as *const u32
416 }
417 }
418 }
419 )*
420 }
421 }
422
423 make_functions! {
424 /// Calculates `a + b`
425 0x00 fadd(a: f32, b: f32) -> f32;
426 /// Calculates `a - b`
427 0x04 fsub(a: f32, b: f32) -> f32;
428 /// Calculates `a * b`
429 0x08 fmul(a: f32, b: f32) -> f32;
430 /// Calculates `a / b`
431 0x0c fdiv(a: f32, b: f32) -> f32;
432
433 // 0x10 and 0x14 are deprecated
434
435 /// Calculates `sqrt(v)` (or return -Infinity if v is negative)
436 0x18 fsqrt(v: f32) -> f32;
437 /// Converts an f32 to a signed integer,
438 /// rounding towards -Infinity, and clamping the result to lie within the
439 /// range `-0x80000000` to `0x7FFFFFFF`
440 0x1c float_to_int(v: f32) -> i32;
441 /// Converts an f32 to an signed fixed point
442 /// integer representation where n specifies the position of the binary
443 /// point in the resulting fixed point representation, e.g.
444 /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
445 /// and clamps the resulting integer to lie within the range `0x00000000` to
446 /// `0xFFFFFFFF`
447 0x20 float_to_fix(v: f32, n: i32) -> i32;
448 /// Converts an f32 to an unsigned integer,
449 /// rounding towards -Infinity, and clamping the result to lie within the
450 /// range `0x00000000` to `0xFFFFFFFF`
451 0x24 float_to_uint(v: f32) -> u32;
452 /// Converts an f32 to an unsigned fixed point
453 /// integer representation where n specifies the position of the binary
454 /// point in the resulting fixed point representation, e.g.
455 /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
456 /// and clamps the resulting integer to lie within the range `0x00000000` to
457 /// `0xFFFFFFFF`
458 0x28 float_to_ufix(v: f32, n: i32) -> u32;
459 /// Converts a signed integer to the nearest
460 /// f32 value, rounding to even on tie
461 0x2c int_to_float(v: i32) -> f32;
462 /// Converts a signed fixed point integer
463 /// representation to the nearest f32 value, rounding to even on tie. `n`
464 /// specifies the position of the binary point in fixed point, so `f =
465 /// nearest(v/(2^n))`
466 0x30 fix_to_float(v: i32, n: i32) -> f32;
467 /// Converts an unsigned integer to the nearest
468 /// f32 value, rounding to even on tie
469 0x34 uint_to_float(v: u32) -> f32;
470 /// Converts an unsigned fixed point integer
471 /// representation to the nearest f32 value, rounding to even on tie. `n`
472 /// specifies the position of the binary point in fixed point, so `f =
473 /// nearest(v/(2^n))`
474 0x38 ufix_to_float(v: u32, n: i32) -> f32;
475 /// Calculates the cosine of `angle`. The value
476 /// of `angle` is in radians, and must be in the range `-1024` to `1024`
477 0x3c fcos(angle: f32) -> f32;
478 /// Calculates the sine of `angle`. The value of
479 /// `angle` is in radians, and must be in the range `-1024` to `1024`
480 0x40 fsin(angle: f32) -> f32;
481 /// Calculates the tangent of `angle`. The value
482 /// of `angle` is in radians, and must be in the range `-1024` to `1024`
483 0x44 ftan(angle: f32) -> f32;
484
485 // 0x48 is deprecated
486
487 /// Calculates the exponential value of `v`,
488 /// i.e. `e ** v`
489 0x4c fexp(v: f32) -> f32;
490 /// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity
491 0x50 fln(v: f32) -> f32;
492 }
493
494 macro_rules! make_functions_v2 {
495 (
496 $(
497 $(#[$outer:meta])*
498 $offset:literal $name:ident (
499 $( $aname:ident : $aty:ty ),*
500 ) -> $ret:ty;
501 )*
502 ) => {
503 $(
504 declare_rom_function! {
505 $(#[$outer])*
506 fn $name( $( $aname : $aty ),* ) -> $ret {
507 if $crate::rom_data::rom_version_number() < 2 {
508 panic!(
509 "Floating point function requires V2 bootrom (found: V{})",
510 $crate::rom_data::rom_version_number()
511 );
512 }
513 let table: *const usize = $crate::rom_data::soft_float_table();
514 unsafe {
515 // This is the entry in the table. Our offset is given as a
516 // byte offset, but we want the table index (each pointer in
517 // the table is 4 bytes long)
518 let entry: *const usize = table.offset($offset / 4);
519 // Read the pointer from the table
520 core::ptr::read(entry) as *const u32
521 }
522 }
523 }
524 )*
525 }
526 }
527
528 // These are only on BootROM v2 or higher
529 make_functions_v2! {
530 /// Compares two floating point numbers, returning:
531 /// • 0 if a == b
532 /// • -1 if a < b
533 /// • 1 if a > b
534 0x54 fcmp(a: f32, b: f32) -> i32;
535 /// Computes the arc tangent of `y/x` using the
536 /// signs of arguments to determine the correct quadrant
537 0x58 fatan2(y: f32, x: f32) -> f32;
538 /// Converts a signed 64-bit integer to the
539 /// nearest f32 value, rounding to even on tie
540 0x5c int64_to_float(v: i64) -> f32;
541 /// Converts a signed fixed point 64-bit integer
542 /// representation to the nearest f32 value, rounding to even on tie. `n`
543 /// specifies the position of the binary point in fixed point, so `f =
544 /// nearest(v/(2^n))`
545 0x60 fix64_to_float(v: i64, n: i32) -> f32;
546 /// Converts an unsigned 64-bit integer to the
547 /// nearest f32 value, rounding to even on tie
548 0x64 uint64_to_float(v: u64) -> f32;
549 /// Converts an unsigned fixed point 64-bit
550 /// integer representation to the nearest f32 value, rounding to even on
551 /// tie. `n` specifies the position of the binary point in fixed point, so
552 /// `f = nearest(v/(2^n))`
553 0x68 ufix64_to_float(v: u64, n: i32) -> f32;
554 /// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity,
555 /// and clamping the result to lie within the range `-0x8000000000000000` to
556 /// `0x7FFFFFFFFFFFFFFF`
557 0x6c float_to_int64(v: f32) -> i64;
558 /// Converts an f32 to a signed fixed point
559 /// 64-bit integer representation where n specifies the position of the
560 /// binary point in the resulting fixed point representation - e.g. `f(0.5f,
561 /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
562 /// resulting integer to lie within the range `-0x8000000000000000` to
563 /// `0x7FFFFFFFFFFFFFFF`
564 0x70 float_to_fix64(v: f32, n: i32) -> f32;
565 /// Converts an f32 to an unsigned 64-bit
566 /// integer, rounding towards -Infinity, and clamping the result to lie
567 /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
568 0x74 float_to_uint64(v: f32) -> u64;
569 /// Converts an f32 to an unsigned fixed point
570 /// 64-bit integer representation where n specifies the position of the
571 /// binary point in the resulting fixed point representation, e.g. `f(0.5f,
572 /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
573 /// resulting integer to lie within the range `0x0000000000000000` to
574 /// `0xFFFFFFFFFFFFFFFF`
575 0x78 float_to_ufix64(v: f32, n: i32) -> u64;
576 /// Converts an f32 to an f64.
577 0x7c float_to_double(v: f32) -> f64;
578 }
579}
580
581/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms)
582pub mod double_funcs {
583
584 macro_rules! make_double_funcs {
585 (
586 $(
587 $(#[$outer:meta])*
588 $offset:literal $name:ident (
589 $( $aname:ident : $aty:ty ),*
590 ) -> $ret:ty;
591 )*
592 ) => {
593 $(
594 declare_rom_function! {
595 $(#[$outer])*
596 fn $name( $( $aname : $aty ),* ) -> $ret {
597 let table: *const usize = $crate::rom_data::soft_double_table();
598 unsafe {
599 // This is the entry in the table. Our offset is given as a
600 // byte offset, but we want the table index (each pointer in
601 // the table is 4 bytes long)
602 let entry: *const usize = table.offset($offset / 4);
603 // Read the pointer from the table
604 core::ptr::read(entry) as *const u32
605 }
606 }
607 }
608 )*
609 }
610 }
611
612 make_double_funcs! {
613 /// Calculates `a + b`
614 0x00 dadd(a: f64, b: f64) -> f64;
615 /// Calculates `a - b`
616 0x04 dsub(a: f64, b: f64) -> f64;
617 /// Calculates `a * b`
618 0x08 dmul(a: f64, b: f64) -> f64;
619 /// Calculates `a / b`
620 0x0c ddiv(a: f64, b: f64) -> f64;
621
622 // 0x10 and 0x14 are deprecated
623
624 /// Calculates `sqrt(v)` (or return -Infinity if v is negative)
625 0x18 dsqrt(v: f64) -> f64;
626 /// Converts an f64 to a signed integer,
627 /// rounding towards -Infinity, and clamping the result to lie within the
628 /// range `-0x80000000` to `0x7FFFFFFF`
629 0x1c double_to_int(v: f64) -> i32;
630 /// Converts an f64 to an signed fixed point
631 /// integer representation where n specifies the position of the binary
632 /// point in the resulting fixed point representation, e.g.
633 /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
634 /// and clamps the resulting integer to lie within the range `0x00000000` to
635 /// `0xFFFFFFFF`
636 0x20 double_to_fix(v: f64, n: i32) -> i32;
637 /// Converts an f64 to an unsigned integer,
638 /// rounding towards -Infinity, and clamping the result to lie within the
639 /// range `0x00000000` to `0xFFFFFFFF`
640 0x24 double_to_uint(v: f64) -> u32;
641 /// Converts an f64 to an unsigned fixed point
642 /// integer representation where n specifies the position of the binary
643 /// point in the resulting fixed point representation, e.g.
644 /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
645 /// and clamps the resulting integer to lie within the range `0x00000000` to
646 /// `0xFFFFFFFF`
647 0x28 double_to_ufix(v: f64, n: i32) -> u32;
648 /// Converts a signed integer to the nearest
649 /// double value, rounding to even on tie
650 0x2c int_to_double(v: i32) -> f64;
651 /// Converts a signed fixed point integer
652 /// representation to the nearest double value, rounding to even on tie. `n`
653 /// specifies the position of the binary point in fixed point, so `f =
654 /// nearest(v/(2^n))`
655 0x30 fix_to_double(v: i32, n: i32) -> f64;
656 /// Converts an unsigned integer to the nearest
657 /// double value, rounding to even on tie
658 0x34 uint_to_double(v: u32) -> f64;
659 /// Converts an unsigned fixed point integer
660 /// representation to the nearest double value, rounding to even on tie. `n`
661 /// specifies the position of the binary point in fixed point, so f =
662 /// nearest(v/(2^n))
663 0x38 ufix_to_double(v: u32, n: i32) -> f64;
664 /// Calculates the cosine of `angle`. The value
665 /// of `angle` is in radians, and must be in the range `-1024` to `1024`
666 0x3c dcos(angle: f64) -> f64;
667 /// Calculates the sine of `angle`. The value of
668 /// `angle` is in radians, and must be in the range `-1024` to `1024`
669 0x40 dsin(angle: f64) -> f64;
670 /// Calculates the tangent of `angle`. The value
671 /// of `angle` is in radians, and must be in the range `-1024` to `1024`
672 0x44 dtan(angle: f64) -> f64;
673
674 // 0x48 is deprecated
675
676 /// Calculates the exponential value of `v`,
677 /// i.e. `e ** v`
678 0x4c dexp(v: f64) -> f64;
679 /// Calculates the natural logarithm of v. If v <= 0 return -Infinity
680 0x50 dln(v: f64) -> f64;
681
682 // These are only on BootROM v2 or higher
683
684 /// Compares two floating point numbers, returning:
685 /// • 0 if a == b
686 /// • -1 if a < b
687 /// • 1 if a > b
688 0x54 dcmp(a: f64, b: f64) -> i32;
689 /// Computes the arc tangent of `y/x` using the
690 /// signs of arguments to determine the correct quadrant
691 0x58 datan2(y: f64, x: f64) -> f64;
692 /// Converts a signed 64-bit integer to the
693 /// nearest double value, rounding to even on tie
694 0x5c int64_to_double(v: i64) -> f64;
695 /// Converts a signed fixed point 64-bit integer
696 /// representation to the nearest double value, rounding to even on tie. `n`
697 /// specifies the position of the binary point in fixed point, so `f =
698 /// nearest(v/(2^n))`
699 0x60 fix64_to_doubl(v: i64, n: i32) -> f64;
700 /// Converts an unsigned 64-bit integer to the
701 /// nearest double value, rounding to even on tie
702 0x64 uint64_to_double(v: u64) -> f64;
703 /// Converts an unsigned fixed point 64-bit
704 /// integer representation to the nearest double value, rounding to even on
705 /// tie. `n` specifies the position of the binary point in fixed point, so
706 /// `f = nearest(v/(2^n))`
707 0x68 ufix64_to_double(v: u64, n: i32) -> f64;
708 /// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity,
709 /// and clamping the result to lie within the range `-0x8000000000000000` to
710 /// `0x7FFFFFFFFFFFFFFF`
711 0x6c double_to_int64(v: f64) -> i64;
712 /// Converts an f64 to a signed fixed point
713 /// 64-bit integer representation where n specifies the position of the
714 /// binary point in the resulting fixed point representation - e.g. `f(0.5f,
715 /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
716 /// resulting integer to lie within the range `-0x8000000000000000` to
717 /// `0x7FFFFFFFFFFFFFFF`
718 0x70 double_to_fix64(v: f64, n: i32) -> i64;
719 /// Converts an f64 to an unsigned 64-bit
720 /// integer, rounding towards -Infinity, and clamping the result to lie
721 /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
722 0x74 double_to_uint64(v: f64) -> u64;
723 /// Converts an f64 to an unsigned fixed point
724 /// 64-bit integer representation where n specifies the position of the
725 /// binary point in the resulting fixed point representation, e.g. `f(0.5f,
726 /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
727 /// resulting integer to lie within the range `0x0000000000000000` to
728 /// `0xFFFFFFFFFFFFFFFF`
729 0x78 double_to_ufix64(v: f64, n: i32) -> u64;
730 /// Converts an f64 to an f32
731 0x7c double_to_float(v: f64) -> f32;
732 }
733}