Skip to content

Commit 3f2c423

Browse files
committed
rp2: Switch to locally provided math library.
This commit fixes all known floating-point bugs with the pico-sdk. There are two things going on here: - Use a custom pico float component so that the pico-sdk doesn't include its math functions, and then provide our own from lib/libm. - Provide a wrapper for __aeabi_fadd to fix the infinity addition bug. Prior to this commit, the following tests failed on the rp2 port: cmath_fun float_parse math_domain math_domain_special math_fun_special. With this commit, all these tests pass. Thanks to @projectgus for how to approach this fix. Signed-off-by: Damien George <[email protected]>
1 parent 1323a71 commit 3f2c423

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

ports/rp2/CMakeLists.txt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,73 @@ set(PICO_SDK_COMPONENTS
196196
tinyusb_device
197197
)
198198

199+
# Use our custom pico_float_micropython float implementation. This is needed for two reasons:
200+
# - to fix inf handling in pico-sdk's __wrap___aeabi_fadd();
201+
# - so we can use our own libm functions, to fix inaccuracies in the pico-sdk versions.
202+
pico_set_float_implementation(${MICROPY_TARGET} micropython)
203+
204+
# Define our custom pico_float_micropython component.
205+
pico_add_library(pico_float_micropython)
206+
207+
# pico_float_micropython: add pico-sdk float and our libm source files.
208+
target_sources(pico_float_micropython INTERFACE
209+
${PICO_SDK_PATH}/src/rp2_common/pico_float/float_aeabi.S
210+
${PICO_SDK_PATH}/src/rp2_common/pico_float/float_init_rom.c
211+
${PICO_SDK_PATH}/src/rp2_common/pico_float/float_v1_rom_shim.S
212+
${MICROPY_DIR}/lib/libm/math.c
213+
${MICROPY_DIR}/lib/libm/acoshf.c
214+
${MICROPY_DIR}/lib/libm/asinfacosf.c
215+
${MICROPY_DIR}/lib/libm/asinhf.c
216+
${MICROPY_DIR}/lib/libm/atan2f.c
217+
${MICROPY_DIR}/lib/libm/atanf.c
218+
${MICROPY_DIR}/lib/libm/atanhf.c
219+
${MICROPY_DIR}/lib/libm/ef_rem_pio2.c
220+
${MICROPY_DIR}/lib/libm/ef_sqrt.c
221+
${MICROPY_DIR}/lib/libm/erf_lgamma.c
222+
${MICROPY_DIR}/lib/libm/fmodf.c
223+
${MICROPY_DIR}/lib/libm/kf_cos.c
224+
${MICROPY_DIR}/lib/libm/kf_rem_pio2.c
225+
${MICROPY_DIR}/lib/libm/kf_sin.c
226+
${MICROPY_DIR}/lib/libm/kf_tan.c
227+
${MICROPY_DIR}/lib/libm/log1pf.c
228+
${MICROPY_DIR}/lib/libm/nearbyintf.c
229+
${MICROPY_DIR}/lib/libm/roundf.c
230+
${MICROPY_DIR}/lib/libm/sf_cos.c
231+
${MICROPY_DIR}/lib/libm/sf_erf.c
232+
${MICROPY_DIR}/lib/libm/sf_frexp.c
233+
${MICROPY_DIR}/lib/libm/sf_ldexp.c
234+
${MICROPY_DIR}/lib/libm/sf_modf.c
235+
${MICROPY_DIR}/lib/libm/sf_sin.c
236+
${MICROPY_DIR}/lib/libm/sf_tan.c
237+
${MICROPY_DIR}/lib/libm/wf_lgamma.c
238+
${MICROPY_DIR}/lib/libm/wf_tgamma.c
239+
${MICROPY_PORT_DIR}/libm_extra.c
240+
)
241+
242+
# pico_float_micropython: wrap low-level floating-point ops, to call the pico-sdk versions.
243+
pico_wrap_function(pico_float_micropython __aeabi_fdiv)
244+
pico_wrap_function(pico_float_micropython __aeabi_fmul)
245+
pico_wrap_function(pico_float_micropython __aeabi_frsub)
246+
pico_wrap_function(pico_float_micropython __aeabi_fsub)
247+
pico_wrap_function(pico_float_micropython __aeabi_cfcmpeq)
248+
pico_wrap_function(pico_float_micropython __aeabi_cfrcmple)
249+
pico_wrap_function(pico_float_micropython __aeabi_cfcmple)
250+
pico_wrap_function(pico_float_micropython __aeabi_fcmpeq)
251+
pico_wrap_function(pico_float_micropython __aeabi_fcmplt)
252+
pico_wrap_function(pico_float_micropython __aeabi_fcmple)
253+
pico_wrap_function(pico_float_micropython __aeabi_fcmpge)
254+
pico_wrap_function(pico_float_micropython __aeabi_fcmpgt)
255+
pico_wrap_function(pico_float_micropython __aeabi_fcmpun)
256+
pico_wrap_function(pico_float_micropython __aeabi_i2f)
257+
pico_wrap_function(pico_float_micropython __aeabi_l2f)
258+
pico_wrap_function(pico_float_micropython __aeabi_ui2f)
259+
pico_wrap_function(pico_float_micropython __aeabi_ul2f)
260+
pico_wrap_function(pico_float_micropython __aeabi_f2iz)
261+
pico_wrap_function(pico_float_micropython __aeabi_f2lz)
262+
pico_wrap_function(pico_float_micropython __aeabi_f2uiz)
263+
pico_wrap_function(pico_float_micropython __aeabi_f2ulz)
264+
pico_wrap_function(pico_float_micropython __aeabi_f2d)
265+
199266
if (MICROPY_PY_LWIP)
200267
target_link_libraries(${MICROPY_TARGET} micropy_lib_lwip)
201268

ports/rp2/libm_extra.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2023 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <math.h>
28+
#include <stdint.h>
29+
30+
#define INF_POS (0x7f800000)
31+
#define INF_NEG (0xff800000)
32+
33+
union float_int_t {
34+
float f;
35+
uint32_t i;
36+
};
37+
38+
float __wrap___aeabi_fadd(float x, float y);
39+
40+
// The pico-sdk wraps __aeabi_fadd() with __wrap___aeabi_fadd() in order to use the ROM
41+
// floating-point functions. But __wrap___aeabi_fadd() does not handle inf+(-inf) or
42+
// (-inf)+inf correctly. To fix this we provide our own __aeabi_fadd() that fixes the
43+
// inf calculation, and do not use the "--wrap=" linker command. The compiler then
44+
// picks our __aeabi_fadd() instead of its built-in one. And the pico-sdk function
45+
// still exists for us to call.
46+
float __aeabi_fadd(float x, float y) {
47+
// Handle addition of inf/-inf. This is optimised to reduce C stack usage, and
48+
// only have one comparison/jump in the common case of x != inf.
49+
union float_int_t xu = {.f = x};
50+
union float_int_t yu = {.f = y};
51+
if ((xu.i << 1) == (INF_POS << 1)) {
52+
if (xu.i == INF_POS) {
53+
if (yu.i == INF_POS) {
54+
return INFINITY;
55+
} else if (yu.i == INF_NEG) {
56+
return NAN;
57+
}
58+
} else {
59+
if (yu.i == INF_POS) {
60+
return NAN;
61+
} else if (yu.i == INF_NEG) {
62+
return -INFINITY;
63+
}
64+
}
65+
}
66+
67+
// Use the pico-sdk function for all other calculations.
68+
return __wrap___aeabi_fadd(x, y);
69+
}

0 commit comments

Comments
 (0)