Damien George
5 years ago
7 changed files with 253 additions and 0 deletions
@ -0,0 +1,14 @@ |
|||
# Location of top-level MicroPython directory
|
|||
MPY_DIR = ../../.. |
|||
|
|||
# Name of module
|
|||
MOD = features1 |
|||
|
|||
# Source files (.c or .py)
|
|||
SRC = features1.c |
|||
|
|||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
|||
ARCH = x64 |
|||
|
|||
# Include to get the rules for compiling and linking the module
|
|||
include $(MPY_DIR)/py/dynruntime.mk |
@ -0,0 +1,106 @@ |
|||
/* This example demonstrates the following features in a native module:
|
|||
- defining simple functions exposed to Python |
|||
- defining local, helper C functions |
|||
- defining constant integers and strings exposed to Python |
|||
- getting and creating integer objects |
|||
- creating Python lists |
|||
- raising exceptions |
|||
- allocating memory |
|||
- BSS and constant data (rodata) |
|||
- relocated pointers in rodata |
|||
*/ |
|||
|
|||
// Include the header file to get access to the MicroPython API
|
|||
#include "py/dynruntime.h" |
|||
|
|||
// BSS (zero) data
|
|||
uint16_t data16[4]; |
|||
|
|||
// Constant data (rodata)
|
|||
const uint8_t table8[] = { 0, 1, 1, 2, 3, 5, 8, 13 }; |
|||
const uint16_t table16[] = { 0x1000, 0x2000 }; |
|||
|
|||
// Constant data pointing to BSS/constant data
|
|||
uint16_t *const table_ptr16a[] = { &data16[0], &data16[1], &data16[2], &data16[3] }; |
|||
const uint16_t *const table_ptr16b[] = { &table16[0], &table16[1] }; |
|||
|
|||
// A simple function that adds its 2 arguments (must be integers)
|
|||
STATIC mp_obj_t add(mp_obj_t x_in, mp_obj_t y_in) { |
|||
mp_int_t x = mp_obj_get_int(x_in); |
|||
mp_int_t y = mp_obj_get_int(y_in); |
|||
return mp_obj_new_int(x + y); |
|||
} |
|||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add); |
|||
|
|||
// A local helper function (not exposed to Python)
|
|||
STATIC mp_int_t fibonacci_helper(mp_int_t x) { |
|||
if (x < MP_ARRAY_SIZE(table8)) { |
|||
return table8[x]; |
|||
} else { |
|||
return fibonacci_helper(x - 1) + fibonacci_helper(x - 2); |
|||
} |
|||
} |
|||
|
|||
// A function which computes Fibonacci numbers
|
|||
STATIC mp_obj_t fibonacci(mp_obj_t x_in) { |
|||
mp_int_t x = mp_obj_get_int(x_in); |
|||
if (x < 0) { |
|||
mp_raise_ValueError("can't compute negative Fibonacci number"); |
|||
} |
|||
return mp_obj_new_int(fibonacci_helper(x)); |
|||
} |
|||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fibonacci_obj, fibonacci); |
|||
|
|||
// A function that accesses the BSS data
|
|||
STATIC mp_obj_t access(size_t n_args, const mp_obj_t *args) { |
|||
if (n_args == 0) { |
|||
// Create a list holding all items from data16
|
|||
mp_obj_list_t *lst = MP_OBJ_TO_PTR(mp_obj_new_list(MP_ARRAY_SIZE(data16), NULL)); |
|||
for (int i = 0; i < MP_ARRAY_SIZE(data16); ++i) { |
|||
lst->items[i] = mp_obj_new_int(data16[i]); |
|||
} |
|||
return MP_OBJ_FROM_PTR(lst); |
|||
} else if (n_args == 1) { |
|||
// Get one item from data16
|
|||
mp_int_t idx = mp_obj_get_int(args[0]) & 3; |
|||
return mp_obj_new_int(data16[idx]); |
|||
} else { |
|||
// Set one item in data16 (via table_ptr16a)
|
|||
mp_int_t idx = mp_obj_get_int(args[0]) & 3; |
|||
*table_ptr16a[idx] = mp_obj_get_int(args[1]); |
|||
return mp_const_none; |
|||
} |
|||
} |
|||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(access_obj, 0, 2, access); |
|||
|
|||
// A function that allocates memory and creates a bytearray
|
|||
STATIC mp_obj_t make_array(void) { |
|||
uint16_t *ptr = m_new(uint16_t, MP_ARRAY_SIZE(table_ptr16b)); |
|||
for (int i = 0; i < MP_ARRAY_SIZE(table_ptr16b); ++i) { |
|||
ptr[i] = *table_ptr16b[i]; |
|||
} |
|||
return mp_obj_new_bytearray_by_ref(sizeof(uint16_t) * MP_ARRAY_SIZE(table_ptr16b), ptr); |
|||
} |
|||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(make_array_obj, make_array); |
|||
|
|||
// 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 |
|||
|
|||
// Messages can be printed as usualy
|
|||
mp_printf(&mp_plat_print, "initialising module self=%p\n", self); |
|||
|
|||
// 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_fibonacci, MP_OBJ_FROM_PTR(&fibonacci_obj)); |
|||
mp_store_global(MP_QSTR_access, MP_OBJ_FROM_PTR(&access_obj)); |
|||
mp_store_global(MP_QSTR_make_array, MP_OBJ_FROM_PTR(&make_array_obj)); |
|||
|
|||
// Add some constants to the module's namespace
|
|||
mp_store_global(MP_QSTR_VAL, MP_OBJ_NEW_SMALL_INT(42)); |
|||
mp_store_global(MP_QSTR_MSG, MP_OBJ_NEW_QSTR(MP_QSTR_HELLO_MICROPYTHON)); |
|||
|
|||
// This must be last, it restores the globals dict
|
|||
MP_DYNRUNTIME_INIT_EXIT |
|||
} |
@ -0,0 +1,14 @@ |
|||
# Location of top-level MicroPython directory
|
|||
MPY_DIR = ../../.. |
|||
|
|||
# Name of module
|
|||
MOD = features2 |
|||
|
|||
# Source files (.c or .py)
|
|||
SRC = main.c prod.c test.py |
|||
|
|||
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
|
|||
ARCH = x64 |
|||
|
|||
# Include to get the rules for compiling and linking the module
|
|||
include $(MPY_DIR)/py/dynruntime.mk |
@ -0,0 +1,83 @@ |
|||
/* This example demonstrates the following features in a native module:
|
|||
- using floats |
|||
- 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" |
|||
|
|||
// 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 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("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 |
|||
|
|||
// 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 |
|||
} |
@ -0,0 +1,9 @@ |
|||
#include "prod.h" |
|||
|
|||
float prod_array(int n, float *ar) { |
|||
float ans = 1; |
|||
for (int i = 0; i < n; ++i) { |
|||
ans *= ar[i]; |
|||
} |
|||
return ans; |
|||
} |
@ -0,0 +1 @@ |
|||
float prod_array(int n, float *ar); |
@ -0,0 +1,26 @@ |
|||
# This Python code will be merged with the C code in main.c |
|||
|
|||
import array |
|||
|
|||
def isclose(a, b): |
|||
return abs(a - b) < 1e-3 |
|||
|
|||
def test(): |
|||
tests = [ |
|||
isclose(add(0.1, 0.2), 0.3), |
|||
isclose(add_f(0.1, 0.2), 0.3), |
|||
] |
|||
|
|||
ar = array.array('f', [1, 2, 3.5]) |
|||
productf(ar) |
|||
tests.append(isclose(ar[0], 7)) |
|||
|
|||
if 'add_d' in globals(): |
|||
tests.append(isclose(add_d(0.1, 0.2), 0.3)) |
|||
|
|||
print(tests) |
|||
|
|||
if not all(tests): |
|||
raise SystemExit(1) |
|||
|
|||
test() |
Loading…
Reference in new issue