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