Skip to content

Commit a943011

Browse files
committed
Make natmods work again.
And put back our magic number, because our bytecode format differs from upstream drop btree & framebuf natmods, they had additional problems I didn't want to fix right now.
1 parent 6598fc0 commit a943011

32 files changed

Lines changed: 636 additions & 28 deletions

.github/workflows/run-tests.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ jobs:
5555
run: |
5656
make -C examples/natmod/features1
5757
make -C examples/natmod/features2
58-
make -C examples/natmod/btree
59-
make -C examples/natmod/framebuf
6058
make -C examples/natmod/uheapq
6159
make -C examples/natmod/urandom
6260
make -C examples/natmod/ure

docs/reference/mpyfiles.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ If importing an .mpy file fails then try the following:
6666
print()
6767

6868
* Check the validity of the .mpy file by inspecting the first two bytes of
69-
the file. The first byte should be an uppercase 'M' and the second byte
69+
the file. The first byte should be an uppercase 'C' and the second byte
7070
will be the version number, which should match the system version from above.
7171
If it doesn't match then rebuild the .mpy file.
7272

@@ -144,7 +144,7 @@ The .mpy header is:
144144
====== ================================
145145
size field
146146
====== ================================
147-
byte value 0x4d (ASCII 'M')
147+
byte value 0x43 (ASCII 'C')
148148
byte .mpy version number
149149
byte feature flags
150150
byte number of bits in a small int

examples/natmod/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.mpy

examples/natmod/features0/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Location of top-level MicroPython directory
2+
MPY_DIR = ../../..
3+
4+
# Name of module
5+
MOD = features0
6+
7+
# Source files (.c or .py)
8+
SRC = features0.c
9+
10+
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
11+
ARCH = x64
12+
13+
# Include to get the rules for compiling and linking the module
14+
include $(MPY_DIR)/py/dynruntime.mk
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* This example demonstrates the following features in a native module:
2+
- defining a simple function exposed to Python
3+
- defining a local, helper C function
4+
- getting and creating integer objects
5+
*/
6+
7+
// Include the header file to get access to the MicroPython API
8+
#include "py/dynruntime.h"
9+
10+
// Helper function to compute factorial
11+
STATIC mp_int_t factorial_helper(mp_int_t x) {
12+
if (x == 0) {
13+
return 1;
14+
}
15+
return x * factorial_helper(x - 1);
16+
}
17+
18+
// This is the function which will be called from Python, as factorial(x)
19+
STATIC mp_obj_t factorial(mp_obj_t x_obj) {
20+
// Extract the integer from the MicroPython input object
21+
mp_int_t x = mp_obj_get_int(x_obj);
22+
// Calculate the factorial
23+
mp_int_t result = factorial_helper(x);
24+
// Convert the result to a MicroPython integer object and return it
25+
return mp_obj_new_int(result);
26+
}
27+
// Define a Python reference to the function above
28+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial);
29+
30+
// This is the entry point and is called when the module is imported
31+
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
32+
// This must be first, it sets up the globals dict and other things
33+
MP_DYNRUNTIME_INIT_ENTRY
34+
35+
// Make the function available in the module's namespace
36+
mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj));
37+
38+
// This must be last, it restores the globals dict
39+
MP_DYNRUNTIME_INIT_EXIT
40+
}

examples/natmod/features1/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Location of top-level MicroPython directory
2+
MPY_DIR = ../../..
3+
4+
# Name of module
5+
MOD = features1
6+
7+
# Source files (.c or .py)
8+
SRC = features1.c
9+
10+
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
11+
ARCH = x64
12+
13+
# Include to get the rules for compiling and linking the module
14+
include $(MPY_DIR)/py/dynruntime.mk
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* This example demonstrates the following features in a native module:
2+
- defining simple functions exposed to Python
3+
- defining local, helper C functions
4+
- defining constant integers and strings exposed to Python
5+
- getting and creating integer objects
6+
- creating Python lists
7+
- raising exceptions
8+
- allocating memory
9+
- BSS and constant data (rodata)
10+
- relocated pointers in rodata
11+
*/
12+
13+
// Include the header file to get access to the MicroPython API
14+
#include "py/dynruntime.h"
15+
16+
// BSS (zero) data
17+
uint16_t data16[4];
18+
19+
// Constant data (rodata)
20+
const uint8_t table8[] = { 0, 1, 1, 2, 3, 5, 8, 13 };
21+
const uint16_t table16[] = { 0x1000, 0x2000 };
22+
23+
// Constant data pointing to BSS/constant data
24+
uint16_t *const table_ptr16a[] = { &data16[0], &data16[1], &data16[2], &data16[3] };
25+
const uint16_t *const table_ptr16b[] = { &table16[0], &table16[1] };
26+
27+
// A simple function that adds its 2 arguments (must be integers)
28+
STATIC mp_obj_t add(mp_obj_t x_in, mp_obj_t y_in) {
29+
mp_int_t x = mp_obj_get_int(x_in);
30+
mp_int_t y = mp_obj_get_int(y_in);
31+
return mp_obj_new_int(x + y);
32+
}
33+
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add);
34+
35+
// A local helper function (not exposed to Python)
36+
STATIC mp_int_t fibonacci_helper(mp_int_t x) {
37+
if (x < MP_ARRAY_SIZE(table8)) {
38+
return table8[x];
39+
} else {
40+
return fibonacci_helper(x - 1) + fibonacci_helper(x - 2);
41+
}
42+
}
43+
44+
// A function which computes Fibonacci numbers
45+
STATIC mp_obj_t fibonacci(mp_obj_t x_in) {
46+
mp_int_t x = mp_obj_get_int(x_in);
47+
if (x < 0) {
48+
mp_raise_ValueError(MP_ERROR_TEXT("can't compute negative Fibonacci number"));
49+
}
50+
return mp_obj_new_int(fibonacci_helper(x));
51+
}
52+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(fibonacci_obj, fibonacci);
53+
54+
// A function that accesses the BSS data
55+
STATIC mp_obj_t access(size_t n_args, const mp_obj_t *args) {
56+
if (n_args == 0) {
57+
// Create a list holding all items from data16
58+
mp_obj_list_t *lst = MP_OBJ_TO_PTR(mp_obj_new_list(MP_ARRAY_SIZE(data16), NULL));
59+
for (int i = 0; i < MP_ARRAY_SIZE(data16); ++i) {
60+
lst->items[i] = mp_obj_new_int(data16[i]);
61+
}
62+
return MP_OBJ_FROM_PTR(lst);
63+
} else if (n_args == 1) {
64+
// Get one item from data16
65+
mp_int_t idx = mp_obj_get_int(args[0]) & 3;
66+
return mp_obj_new_int(data16[idx]);
67+
} else {
68+
// Set one item in data16 (via table_ptr16a)
69+
mp_int_t idx = mp_obj_get_int(args[0]) & 3;
70+
*table_ptr16a[idx] = mp_obj_get_int(args[1]);
71+
return mp_const_none;
72+
}
73+
}
74+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(access_obj, 0, 2, access);
75+
76+
// A function that allocates memory and creates a bytearray
77+
STATIC mp_obj_t make_array(void) {
78+
uint16_t *ptr = m_new(uint16_t, MP_ARRAY_SIZE(table_ptr16b));
79+
for (int i = 0; i < MP_ARRAY_SIZE(table_ptr16b); ++i) {
80+
ptr[i] = *table_ptr16b[i];
81+
}
82+
return mp_obj_new_bytearray_by_ref(sizeof(uint16_t) * MP_ARRAY_SIZE(table_ptr16b), ptr);
83+
}
84+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(make_array_obj, make_array);
85+
86+
// This is the entry point and is called when the module is imported
87+
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
88+
// This must be first, it sets up the globals dict and other things
89+
MP_DYNRUNTIME_INIT_ENTRY
90+
91+
// Messages can be printed as usually
92+
mp_printf(&mp_plat_print, "initialising module self=%p\n", self);
93+
94+
// Make the functions available in the module's namespace
95+
mp_store_global(MP_QSTR_add, MP_OBJ_FROM_PTR(&add_obj));
96+
mp_store_global(MP_QSTR_fibonacci, MP_OBJ_FROM_PTR(&fibonacci_obj));
97+
mp_store_global(MP_QSTR_access, MP_OBJ_FROM_PTR(&access_obj));
98+
mp_store_global(MP_QSTR_make_array, MP_OBJ_FROM_PTR(&make_array_obj));
99+
100+
// Add some constants to the module's namespace
101+
mp_store_global(MP_QSTR_VAL, MP_OBJ_NEW_SMALL_INT(42));
102+
mp_store_global(MP_QSTR_MSG, MP_OBJ_NEW_QSTR(MP_QSTR_HELLO_MICROPYTHON));
103+
104+
// This must be last, it restores the globals dict
105+
MP_DYNRUNTIME_INIT_EXIT
106+
}

examples/natmod/features2/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Location of top-level MicroPython directory
2+
MPY_DIR = ../../..
3+
4+
# Name of module
5+
MOD = features2
6+
7+
# Source files (.c or .py)
8+
SRC = main.c prod.c test.py
9+
10+
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
11+
ARCH = x64
12+
13+
# Include to get the rules for compiling and linking the module
14+
include $(MPY_DIR)/py/dynruntime.mk

examples/natmod/features2/main.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/* This example demonstrates the following features in a native module:
2+
- using floats
3+
- defining additional code in Python (see test.py)
4+
- have extra C code in a separate file (see prod.c)
5+
*/
6+
7+
// Include the header file to get access to the MicroPython API
8+
#include "py/dynruntime.h"
9+
10+
// Include the header for auxiliary C code for this module
11+
#include "prod.h"
12+
13+
// Automatically detect if this module should include double-precision code.
14+
// If double precision is supported by the target architecture then it can
15+
// be used in native module regardless of what float setting the target
16+
// MicroPython runtime uses (being none, float or double).
17+
#if defined(__i386__) || defined(__x86_64__) || (defined(__ARM_FP) && (__ARM_FP & 8))
18+
#define USE_DOUBLE 1
19+
#else
20+
#define USE_DOUBLE 0
21+
#endif
22+
23+
// A function that uses the default float type configured for the current target
24+
// This default can be overridden by specifying MICROPY_FLOAT_IMPL at the make level
25+
STATIC mp_obj_t add(mp_obj_t x, mp_obj_t y) {
26+
return mp_obj_new_float(mp_obj_get_float(x) + mp_obj_get_float(y));
27+
}
28+
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add);
29+
30+
// A function that explicitly uses single precision floats
31+
STATIC mp_obj_t add_f(mp_obj_t x, mp_obj_t y) {
32+
return mp_obj_new_float_from_f(mp_obj_get_float_to_f(x) + mp_obj_get_float_to_f(y));
33+
}
34+
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_f_obj, add_f);
35+
36+
#if USE_DOUBLE
37+
// A function that explicitly uses double precision floats
38+
STATIC mp_obj_t add_d(mp_obj_t x, mp_obj_t y) {
39+
return mp_obj_new_float_from_d(mp_obj_get_float_to_d(x) + mp_obj_get_float_to_d(y));
40+
}
41+
STATIC MP_DEFINE_CONST_FUN_OBJ_2(add_d_obj, add_d);
42+
#endif
43+
44+
// A function that computes the product of floats in an array.
45+
// This function uses the most general C argument interface, which is more difficult
46+
// to use but has access to the globals dict of the module via self->globals.
47+
STATIC mp_obj_t productf(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
48+
// Check number of arguments is valid
49+
mp_arg_check_num(n_args, n_kw, 1, 1, false);
50+
51+
// Extract buffer pointer and verify typecode
52+
mp_buffer_info_t bufinfo;
53+
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_RW);
54+
if (bufinfo.typecode != 'f') {
55+
mp_raise_ValueError(MP_ERROR_TEXT("expecting float array"));
56+
}
57+
58+
// Compute product, store result back in first element of array
59+
float *ptr = bufinfo.buf;
60+
float prod = prod_array(bufinfo.len / sizeof(*ptr), ptr);
61+
ptr[0] = prod;
62+
63+
return mp_const_none;
64+
}
65+
66+
// This is the entry point and is called when the module is imported
67+
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
68+
// This must be first, it sets up the globals dict and other things
69+
MP_DYNRUNTIME_INIT_ENTRY
70+
71+
// Make the functions available in the module's namespace
72+
mp_store_global(MP_QSTR_add, MP_OBJ_FROM_PTR(&add_obj));
73+
mp_store_global(MP_QSTR_add_f, MP_OBJ_FROM_PTR(&add_f_obj));
74+
#if USE_DOUBLE
75+
mp_store_global(MP_QSTR_add_d, MP_OBJ_FROM_PTR(&add_d_obj));
76+
#endif
77+
78+
// The productf function uses the most general C argument interface
79+
mp_store_global(MP_QSTR_productf, MP_DYNRUNTIME_MAKE_FUNCTION(productf));
80+
81+
// This must be last, it restores the globals dict
82+
MP_DYNRUNTIME_INIT_EXIT
83+
}

examples/natmod/features2/prod.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "prod.h"
2+
3+
float prod_array(int n, float *ar) {
4+
float ans = 1;
5+
for (int i = 0; i < n; ++i) {
6+
ans *= ar[i];
7+
}
8+
return ans;
9+
}

0 commit comments

Comments
 (0)