Skip to main content

rp_hal/
rom_data.rs

1//! Functions and data from the RPI Bootrom.
2
3/// A bootrom function table code.
4pub type RomFnTableCode = [u8; 2];
5
6/// This function searches for (table)
7type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T;
8
9/// The following addresses are described at `2.8.3. Bootrom Contents`
10/// Pointer to the lookup table function supplied by the rom.
11const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x18 as _;
12
13/// Pointer to helper functions lookup table.
14const FUNC_TABLE: *const u16 = 0x14 as _;
15
16/// Pointer to the public data lookup table.
17const DATA_TABLE: *const u16 = 0x16 as _;
18
19/// Retrive rom content from a table using a code.
20fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
21    unsafe {
22        let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
23        let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);
24        rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32)
25    }
26}
27
28unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {
29    let ptr: u16 = *rom_address;
30    ptr as *const u32
31}
32
33macro_rules! rom_funcs {
34    (
35        $(
36            $(#[$outer:meta])*
37            $c:literal $name:ident (
38                $( $aname:ident : $aty:ty ),*
39            ) -> $ret:ty ;
40        )*
41    ) => {
42        $(
43            $(#[$outer])*
44            pub fn $name($( $aname:$aty ),*) -> $ret{
45                let func:  extern "C" fn( $( $aty ),* ) -> $ret = rom_table_lookup(FUNC_TABLE, *$c);
46                func($( $aname ),*)
47            }
48        )*
49    }
50}
51
52rom_funcs! {
53    /// Return a count of the number of 1 bits in value.
54    b"P3" popcount32(value: u32) -> u32;
55
56    /// Return the bits of value in the reverse order.
57    b"R3" reverse32(value: u32) -> u32;
58
59    /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32.
60    b"L3" clz32(value: u32) -> u32;
61
62    /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32.
63    b"T3" ctz32(value: u32) -> u32;
64
65    /// Sets n bytes start at ptr to the value c and returns ptr
66    b"MS" memset(ptr: *mut u8, c: u8, n: u8) -> *mut u8;
67
68    /// Sets n bytes start at ptr to the value c and returns ptr. Note this is a slightly more
69    /// efficient variant of _memset that may only be used if ptr is word aligned.
70    b"M4" memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;
71
72    /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
73    /// regions overlap.
74    b"MC" memcpy(dest: *mut u8, src: *mut u8, n: u32) -> u8;
75
76    /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
77    /// regions overlap. Note this is a slightly more efficient variant of _memcpy that may only be
78    /// used if dest and src are word aligned.
79    b"C4" memcpy44(dest: *mut u32, src: *mut u32, n: u32) -> *mut u8;
80
81    /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads.
82    b"IF" connect_internal_flash() -> ();
83
84    /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence.
85    /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be
86    /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This
87    /// function configures the SSI with a fixed SCK clock divisor of /6.
88    b"EX" flash_exit_xip() -> ();
89
90    /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a
91    /// block erase command e.g. D8h block erase, and the size of the block erased by this
92    /// command — this function will use the larger block erase where possible, for much higher
93    /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of
94    /// 4096 bytes.
95    b"RE" flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();
96
97    /// Program data to a range of flash addresses starting at addr (offset from the start of flash)
98    /// and count bytesin size. addr must be aligned to a 256-byte boundary, and count must be a
99    /// multiple of 256.
100    b"RP" flash_range_program(addr: u32, data: *const u8, count: usize) -> ();
101
102    /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can
103    /// drive the flashchip select as normal.
104    b"FC" flash_flush_cache() -> ();
105
106    /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits,
107    /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported.
108    /// The debugger calls this function after performing a flash erase/programming operation, so
109    /// that the freshly-programmed code and data is visible to the debug host, without having to
110    /// know exactly what kind of flash device is connected.
111    b"CX" flash_enter_cmd_xip() -> ();
112
113    /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:
114    ///   * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED
115    ///     for the USB Mass Storage Device:
116    ///     * 0 No pins are used as per cold boot.
117    ///     * Otherwise a single bit set indicating which GPIO pin should be set to output and
118    ///       raised whenever there is mass storage activity from the host.
119    ///  * disable_interface_mask may be used to control the exposed USB interfaces:
120    ///    * 0 To enable both interfaces (as per cold boot).
121    ///    * 1 To disable the USB Mass Storage Interface.
122    ///    * 2 to Disable the USB PICOBOOT Interface.
123    b"UB" reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();
124
125    /// This is the method that is entered by core 1 on reset to wait to be launched by core 0.
126    /// There are few cases where you should call this method (resetting core 1 is much better).
127    /// This method does not return and should only ever be called on core 1.
128    b"WV" wait_for_vector() -> !;
129}
130
131unsafe fn convert_str(s: *const u8) -> &'static str {
132    let mut end = s;
133    while *end != 0 {
134        end = end.add(1);
135    }
136    let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);
137    core::str::from_utf8_unchecked(s)
138}
139
140/// The Raspberry Pi Trading Ltd copyright string.
141pub fn copyright_string() -> &'static str {
142    let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR");
143    unsafe { convert_str(s) }
144}
145
146/// The 8 most significant hex digits of the Bootrom git revision.
147pub fn git_revision() -> &'static str {
148    let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"GR");
149    unsafe { convert_str(s) }
150}
151
152/// The start address of the floating point library code and data. This and fplib_end along with the individual
153/// function pointers in soft_float_table can be used to copy the floating point implementation into RAM if
154/// desired.
155pub fn fplib_start() -> *const u8 {
156    rom_table_lookup(DATA_TABLE, *b"FS")
157}
158
159/// See Table 181 for the contents of this table.
160pub fn soft_float_table() -> *const u16 {
161    rom_table_lookup(DATA_TABLE, *b"SF")
162}
163
164/// The end address of the floating point library code and data.
165pub fn fplib_end() -> *const u8 {
166    rom_table_lookup(DATA_TABLE, *b"FE")
167}
168
169/// This entry is only present in the V2 bootrom. See Table 182 for the contents of this table.
170pub fn soft_double_table() -> *const u16 {
171    rom_table_lookup(DATA_TABLE, *b"SD")
172}
173
174macro_rules! float_funcs {
175    (
176        $(
177            $(#[$outer:meta])*
178            $offset:literal $name:ident (
179                $( $aname:ident : $aty:ty ),*
180            ) -> $ret:ty;
181        )*
182    ) => {
183        $(
184            $(#[$outer])*
185            pub fn $name() -> extern "C" fn( $( $aname : $aty ),* ) -> $ret {
186                let table: *const *const u16 = rom_table_lookup(DATA_TABLE, *b"SF");
187                unsafe {
188                    core::mem::transmute_copy(&table.add($offset))
189                }
190            }
191        )*
192    }
193}
194
195float_funcs! {
196    /// Return a + b.
197    0x00 fadd(a: f32, b: f32) -> f32;
198    /// Return a - b.
199    0x04 fsub(a: f32, b: f32) -> f32;
200    /// Return a * b.
201    0x08 fmul(a: f32, b: f32) -> f32;
202    /// Return a / b.
203    0x0c fdiv(a: f32, b: f32) -> f32;
204    /// Return the square root of v or -INFINITY if v is negative.
205    0x18 fsqrt(v: f32) -> f32;
206    /// Convert a float to a signed integer, rounding towards -INFINITY, and clamping the result
207    /// to lie within the range -0x80000000 to 0x7FFFFFFF.
208    0x1c float_to_int(v: f32) -> i32;
209    /// Convert a float to a signed fixed point integer reprsentation where n specifies the
210    /// position of the binary point in the resulting fixed point representation. e.g.
211    /// float_to_fix(0.5, 16) == 0x8000. This method rounds towards -INFINITY, and clamps
212    /// the resulting integer to lie within the range -800000000 to 0x7FFFFFFF.
213    0x20 float_to_fix(v: f32, n: i32) -> i32;
214    /// Convert a float to an unsigned integer, rounding towards -INFINITY, and clamping the result
215    /// to lie within the range 0x00000000 to 0xFFFFFFFF
216    0x24 float_to_uint(v: f32) -> u32;
217    /// Convert a float to an unsigned fixed point integer representation where n specifies the
218    /// position of the binary point in the resulting fixed point representation, e.g.
219    /// float_to_ufix(0.5f, 16) == 0x8000. This method rounds towards -Infinity, and clamps the
220    /// resulting integer to lie within the range 0x00000000 to 0xFFFFFFFF.
221    0x28 float_to_ufix(v: f32, n: i32) -> u32;
222    /// Convert a signed integer to the nearest float value, rounding to even on tie.
223    0x2c int_to_float(v: i32) -> f32;
224    /// Convert a signed fixed point integer representation to the nearest float value, rounding
225    /// to even on tie. n specifies the position of the binary point in fixed point, so
226    /// f = nearest(v/2^n).
227    0x30 fix_to_float(v: i32, n: i32) -> f32;
228    /// Convert an unsigned integer to the nearest float value, rounding to even on tie.
229    0x34 uint_to_float(v: u32) -> f32;
230    /// Convert a unsigned fixed point integer representation to the nearest float value, rounding
231    /// to even on tie. n specifies the position of the binary point in fixed point, so
232    /// f = nearest(v/2^n).
233    0x38 ufix_to_float(v: u32, n: i32) -> f32;
234    /// Return the cosine of angle. angle is in radians, and must be in the range -128 to 128.
235    0x3c fcos(angle: f32) -> f32;
236    /// Return the sine of angle. angle is in radians, and must be in the range -128 to 128.
237    0x40 fsin(angle: f32) -> f32;
238    /// Return the tangent of angle. angle is in radians, and must be in the range -128 to 128.
239    0x44 ftan(angle: f32) -> f32;
240    /// Return the exponential value of v, i.e. so e^v.
241    0x4c fexp(v: f32) -> f32;
242    /// Return the natural logarithm of v. If v <= 0 return -Infinity.
243    0x50 fln(v: f32) -> f32;
244    /// Compares two floating point numbers, returning:
245    ///   * 0 if a == b
246    ///   * -1 if a < b
247    ///   * 1 if a > b
248    0x54 fcmp(a: f32, b: f32) -> i32;
249    /// Computes the arc tangent of y/x using the signs of arguments to determine the correct quadrant.
250    0x58 fatan2(y: f32, x: f32) -> f32;
251    /// Convert a signed 64-bit integer to the nearest float value, rounding to even on tie.
252    0x5c int64_to_float(v: i64) -> f32;
253    /// Convert a signed fixed point integer representation to the nearest float value, rounding
254    /// to even on tie. n specifies the position of the binary point in fixed point, so
255    /// f = nearest(v/2^n).
256    0x60 fix64_to_float(v: i64, n: i32) -> f32;
257    /// Convert an unsigned 64-bit integer to the nearest float value, rounding to even on tie.
258    0x64 uint64_to_float(v: u64) -> f32;
259    /// Convert an unsigned fixed point integer representation to the nearest float value, rounding
260    /// to even on tie. n specifies the position of the binary point in fixed point, so
261    /// f = nearest(v/2^n).
262    0x68 ufix64_to_float(v: u64, n: i32) -> f32;
263    /// Convert a float to a signed 64-bit integer, rounding towards -Infinity, and clamping
264    /// the result to lie within the range -0x8000000000000000 to 0x7FFFFFFFFFFFFFFF
265    0x6c float_to_int64(v: f32) -> i64;
266    ///     Convert a float to a signed fixed point 64-bit integer representation where n
267    /// specifies the position of the binary point in the resulting fixed point representation -
268    /// e.g. _float2fix(0.5f, 16) == 0x8000. This method rounds towards -Infinity, and
269    /// clamps the resulting integer to lie within the range -0x8000000000000000 to
270    /// 0x7FFFFFFFFFFFFFF
271    0x70 float_to_fix64(v: f32, n: i32) -> f32;
272    /// Convert a float to an unsigned 64-bit integer, rounding towards -Infinity, and
273    /// clamping the result to lie within the range 0x0000000000000000 to 0xFFFFFFFFFFFFFFFF
274    0x74 float_to_uint64(v: f32) -> u64;
275    /// Convert a float to an unsigned fixed point 64-bit integer representation where n
276    /// specifies the position of the binary point in the resulting fixed point representation,
277    /// e.g. _float2ufix(0.5f, 16) == 0x8000. This method rounds towards -Infinity, and
278    /// clamps the resulting integer to lie within the range 0x0000000000000000 to
279    /// 0xFFFFFFFFFFFFFFFF
280    /// 0x78 float_to_ufix64(v: f32, n: i32) -> u64;
281    /// Converts a float to a double.
282    0x7c float_to_double(v: f32) -> f64;
283}
284
285macro_rules! double_funcs {
286    (
287        $(
288            $(#[$outer:meta])*
289            $offset:literal $name:ident (
290                $( $aname:ident : $aty:ty ),*
291            ) -> $ret:ty;
292        )*
293    ) => {
294        $(
295            $(#[$outer])*
296            pub fn $name() -> extern "C" fn( $( $aname : $aty ),* ) -> $ret {
297                let table: *const *const u16 = rom_table_lookup(DATA_TABLE, *b"SD");
298                unsafe {
299                    core::mem::transmute_copy(&table.add($offset))
300                }
301            }
302        )*
303    }
304}
305
306double_funcs! {
307    /// Return a + b
308    0x00 dadd(a: f64, b: f64) -> f64;
309    /// Return a - b
310    0x04 dsub(a: f64, b: f64) -> f64;
311    /// Return a * b
312    0x08 dmul(a: f64, b: f64) -> f64;
313    /// Return a / b
314    0x0c ddiv(a: f64, b: f64) -> f64;
315    /// Return sqrt(v) or -Infinity if v is negative
316    0x18 dsqrt(v: f64) -> f64;
317    /// Convert a double to a signed integer, rounding towards -Infinity, and clamping the result to lie
318    /// within the range -0x80000000 to 0x7FFFFFFF
319    0x1c double_to_int(v: f64) -> i32;
320    /// Convert a double to an unsigned fixed point integer representation where n specifies the
321    /// position of the binary point in the resulting fixed point representation, e.g. _double2ufix(0.5f,
322    /// 16) == 0x8000. This method rounds towards -Infinity, and clamps the resulting integer to lie
323    /// within the range 0x00000000 to 0xFFFFFFFF
324    0x20 double_to_fix(v: f64, n: i32) -> i32;
325    /// Convert a double to an unsigned integer, rounding towards -Infinity, and clamping the result
326    /// to lie within the range 0x00000000 to 0xFFFFFFFF    0x24 double_to_uint(v: f64) -> u32;
327    0x28 double_to_ufix(v: f64, n: i32) -> u32;
328    /// Convert a signed integer to the nearest double value, rounding to even on tie
329    0x2c int_to_double(v: i32) -> f64;
330    /// Convert a signed fixed point integer representation to the nearest double value, rounding to
331    /// even on tie. n specifies the position of the binary point in fixed point, so f = nearest(v/(2^n))
332    0x30 fix_to_double(v: i32, n: i32) -> f64;
333    /// Convert an unsigned integer to the nearest double value, rounding to even on tie
334    0x34 uint_to_double(v: u32) -> f64;
335    /// Convert an unsigned fixed point integer representation to the nearest double value, rounding
336    /// to even on tie. n specifies the position of the binary point in fixed point, so
337    /// f = nearest(v/(2^n))
338    0x38 ufix_to_double(v: u32, n: i32) -> f64;
339    /// Return the cosine of angle. angle is in radians, and must be in the range -1024 to 1024
340    0x3c dcos(angle: f64) -> f64;
341    /// Return the sine of angle. angle is in radians, and must be in the range -1024 to 1024
342    0x40 dsin(angle: f64) -> f64;
343    /// Return the tangent of angle. angle is in radians, and must be in the range -1024 to 1024
344    0x44 dtan(angle: f64) -> f64;
345    /// Return the exponential value of v, i.e. so 
346    0x4c dexp(v: f64) -> f64;
347    /// Return the natural logarithm of v. If v <= 0 return -Infinity
348    0x50 dln(v: f64) -> f64;
349    /// Compares two floating point numbers, returning:
350    ///     • 0 if a == b
351    ///     • -1 if a < b
352    ///     • 1 if a > b
353    0x54 dcmp(a: f64, b: f64) -> i32;
354    /// Computes the arc tangent of y/x using the signs of arguments to determine the correct
355    /// quadrant
356    0x58 datan2(y: f64, x: f64) -> f64;
357    /// Convert a signed 64-bit integer to the nearest double value, rounding to even on tie
358    0x5c int64_to_double(v: i64) -> f64;
359    /// Convert a signed fixed point 64-bit integer representation to the nearest double value,
360    /// rounding to even on tie. n specifies the position of the binary point in fixed point, so
361    /// f = nearest(v/(2^n))
362    0x60 fix64_to_doubl(v: i64, n: i32) -> f64;
363    /// Convert an unsigned 64-bit integer to the nearest double value, rounding to even on tie
364    0x64 uint64_to_double(v: u64) -> f64;
365    /// Convert an unsigned fixed point 64-bit integer representation to the nearest double value,
366    /// rounding to even on tie. n specifies the position of the binary point in fixed point, so
367    /// f = nearest(v/(2^n))
368    0x68 ufix64_to_double(v: u64, n: i32) -> f64;
369    /// Convert a double to a signed 64-bit integer, rounding towards -Infinity, and
370    0x6c double_to_int64(v: f64) -> i64;
371    /// Convert a double to a signed fixed point 64-bit integer representation where n specifies the
372    /// position of the binary point in the resulting fixed point representation - e.g. _double2fix(0.5f,
373    /// 16) == 0x8000. This method rounds towards -Infinity, and clamps the resulting integer to lie
374    /// within the range -0x8000000000000000 to 0x7FFFFFFFFFFFFFFF    
375    0x70 double_to_fix64(v: f64, n: i32) -> i64;
376    /// Convert a double to an unsigned 64-bit integer, rounding towards -Infinity, and clamping the
377    /// result to lie within the range 0x0000000000000000 to 0xFFFFFFFFFFFFFFFF
378    0x74 double_to_uint64(v: f64) -> u64;
379    /// Convert a double to an unsigned fixed point 64-bit integer representation where n specifies
380    /// the position of the binary point in the resulting fixed point representation, e.g.
381    /// _double2ufix(0.5f, 16) == 0x8000. This method rounds towards -Infinity, and clamps the
382    /// resulting integer to lie within the range 0x0000000000000000 to 0xFFFFFFFFFFFFFFFF
383    0x78 double_to_ufix64(v: f64, n: i32) -> u64;
384    /// Converts a double to a float
385    0x7c double_to_float(v: f64) -> f32;
386}