mirror of
https://github.com/micropython/micropython.git
synced 2025-08-14 06:30:32 +02:00
This commit introduces an additional symbol resolution mechanism to the natmod linking process. This allows the build scripts to look for required symbols into selected libraries that are provided by the compiler installation (libgcc and libm at the moment). For example, using soft-float code in natmods, whilst technically possible, was not an easy process and required some additional work to pull it off. With this addition all the manual (and error-prone) operations have been automated and folded into `tools/mpy_ld.py`. Both newlib and picolibc toolchains are supported, albeit the latter may require a bit of extra configuration depending on the environment the build process runs on. Picolibc's soft-float functions aren't in libm - in fact the shipped libm is nothing but a stub - but they are inside libc. This is usually not a problem as these changes cater for that configuration quirk, but on certain compilers the include paths used to find libraries in may not be updated to take Picolibc's library directory into account. The bare metal RISC-V compiler shipped with the CI OS image (GCC 10.2.0 on Ubuntu 22.04LTS) happens to exhibit this very problem. To work around that for CI builds, the Picolibc libraries' path is hardcoded in the Makefile directives used by the linker, but this can be changed by setting the PICOLIBC_ROOT environment library when building natmods. Signed-off-by: Volodymyr Shymanskyy <vshymanskyi@gmail.com> Co-authored-by: Alessandro Gatti <a.gatti@frob.it>
95 lines
3.6 KiB
C
95 lines
3.6 KiB
C
/* This example demonstrates the following features in a native module:
|
|
- using floats
|
|
- calling math functions from libm.a
|
|
- defining additional code in Python (see test.py)
|
|
- have extra C code in a separate file (see prod.c)
|
|
*/
|
|
|
|
// Include the header file to get access to the MicroPython API
|
|
#include "py/dynruntime.h"
|
|
|
|
// Include the header for auxiliary C code for this module
|
|
#include "prod.h"
|
|
|
|
// Include standard library header
|
|
#include <math.h>
|
|
|
|
// Automatically detect if this module should include double-precision code.
|
|
// If double precision is supported by the target architecture then it can
|
|
// be used in native module regardless of what float setting the target
|
|
// MicroPython runtime uses (being none, float or double).
|
|
#if defined(__i386__) || defined(__x86_64__) || (defined(__ARM_FP) && (__ARM_FP & 8))
|
|
#define USE_DOUBLE 1
|
|
#else
|
|
#define USE_DOUBLE 0
|
|
#endif
|
|
|
|
// A function that uses the default float type configured for the current target
|
|
// This default can be overridden by specifying MICROPY_FLOAT_IMPL at the make level
|
|
static mp_obj_t add(mp_obj_t x, mp_obj_t y) {
|
|
return mp_obj_new_float(mp_obj_get_float(x) + mp_obj_get_float(y));
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add);
|
|
|
|
// A function that explicitly uses single precision floats
|
|
static mp_obj_t add_f(mp_obj_t x, mp_obj_t y) {
|
|
return mp_obj_new_float_from_f(mp_obj_get_float_to_f(x) + mp_obj_get_float_to_f(y));
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_2(add_f_obj, add_f);
|
|
|
|
#if USE_DOUBLE
|
|
// A function that explicitly uses double precision floats
|
|
static mp_obj_t add_d(mp_obj_t x, mp_obj_t y) {
|
|
return mp_obj_new_float_from_d(mp_obj_get_float_to_d(x) + mp_obj_get_float_to_d(y));
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_2(add_d_obj, add_d);
|
|
#endif
|
|
|
|
// A function that uses libm
|
|
static mp_obj_t call_round(mp_obj_t x) {
|
|
return mp_obj_new_float_from_f(roundf(mp_obj_get_float_to_f(x)));
|
|
}
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(round_obj, call_round);
|
|
|
|
// A function that computes the product of floats in an array.
|
|
// This function uses the most general C argument interface, which is more difficult
|
|
// to use but has access to the globals dict of the module via self->globals.
|
|
static mp_obj_t productf(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
|
// Check number of arguments is valid
|
|
mp_arg_check_num(n_args, n_kw, 1, 1, false);
|
|
|
|
// Extract buffer pointer and verify typecode
|
|
mp_buffer_info_t bufinfo;
|
|
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_RW);
|
|
if (bufinfo.typecode != 'f') {
|
|
mp_raise_ValueError(MP_ERROR_TEXT("expecting float array"));
|
|
}
|
|
|
|
// Compute product, store result back in first element of array
|
|
float *ptr = bufinfo.buf;
|
|
float prod = prod_array(bufinfo.len / sizeof(*ptr), ptr);
|
|
ptr[0] = prod;
|
|
|
|
return mp_const_none;
|
|
}
|
|
|
|
// This is the entry point and is called when the module is imported
|
|
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
|
|
// This must be first, it sets up the globals dict and other things
|
|
MP_DYNRUNTIME_INIT_ENTRY
|
|
|
|
// Make the functions available in the module's namespace
|
|
mp_store_global(MP_QSTR_add, MP_OBJ_FROM_PTR(&add_obj));
|
|
mp_store_global(MP_QSTR_add_f, MP_OBJ_FROM_PTR(&add_f_obj));
|
|
#if USE_DOUBLE
|
|
mp_store_global(MP_QSTR_add_d, MP_OBJ_FROM_PTR(&add_d_obj));
|
|
#endif
|
|
mp_store_global(MP_QSTR_round, MP_OBJ_FROM_PTR(&round_obj));
|
|
|
|
// The productf function uses the most general C argument interface
|
|
mp_store_global(MP_QSTR_productf, MP_DYNRUNTIME_MAKE_FUNCTION(productf));
|
|
|
|
// This must be last, it restores the globals dict
|
|
MP_DYNRUNTIME_INIT_EXIT
|
|
}
|