examples/natmod: Add features4 as a class definition example.
Also provide a basic README.md for dynamic native modules. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This commit is contained in:
parent
f52a2cd55a
commit
607548f32d
74
examples/natmod/README.md
Normal file
74
examples/natmod/README.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Dynamic Native Modules
|
||||||
|
|
||||||
|
Dynamic Native Modules are .mpy files that contain native machine code from a
|
||||||
|
language other than Python. For more info see [the documentation]
|
||||||
|
(https://docs.micropython.org/en/latest/develop/natmod.html).
|
||||||
|
|
||||||
|
This should not be confused with [User C Modules]
|
||||||
|
(https://docs.micropython.org/en/latest/develop/cmodules.html) which are a
|
||||||
|
mechanism to add additional out-of-tree modules into the firmware build.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
This directory contains several examples of writing dynamic native modules, in
|
||||||
|
two main categories:
|
||||||
|
|
||||||
|
1. Feature examples.
|
||||||
|
|
||||||
|
* `features0` - A module containing a single "factorial" function which
|
||||||
|
demonstrates working with integers.
|
||||||
|
|
||||||
|
* `features1` - A module that demonstrates some common tasks:
|
||||||
|
- 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
|
||||||
|
|
||||||
|
* `features2` - This is a hybrid module containing both Python and C code,
|
||||||
|
and additionally the C code is spread over multiple files. It also
|
||||||
|
demonstrates using floating point (only when the target supports
|
||||||
|
hardware floating point).
|
||||||
|
|
||||||
|
* `features3` - A module that shows how to use types, constant objects,
|
||||||
|
and creating dictionary instances.
|
||||||
|
|
||||||
|
* `features4` - A module that demonstrates how to define a class.
|
||||||
|
|
||||||
|
2. Dynamic version of existing built-ins.
|
||||||
|
|
||||||
|
This provides a way to add missing functionality to firmware that doesn't
|
||||||
|
include certain built-in modules. See the `heapq`, `random`, `re`,
|
||||||
|
`deflate`, `btree`, and `framebuf` directories.
|
||||||
|
|
||||||
|
So for example, if your firmware was compiled with `MICROPY_PY_FRAMEBUF`
|
||||||
|
disabled (e.g. to save flash space), then it would not include the
|
||||||
|
`framebuf` module. The `framebuf` native module provides a way to add the
|
||||||
|
`framebuf` module dynamically.
|
||||||
|
|
||||||
|
The way these work is they define a dynamic native module which
|
||||||
|
`#include`'s the original module and then does the necessary
|
||||||
|
initialisation of the module's globals dict.
|
||||||
|
|
||||||
|
## Build instructions
|
||||||
|
|
||||||
|
To compile an example, you need to have the same toolchain available as
|
||||||
|
required for your target port. e.g. `arm-none-eabi-gcc` for any ARM Cortex M
|
||||||
|
target. See the port instructions for details.
|
||||||
|
|
||||||
|
You also need to have the `pyelftools` Python package available, either via
|
||||||
|
your system package manager or installed from PyPI in a virtual environment
|
||||||
|
with `pip`.
|
||||||
|
|
||||||
|
Each example provides a Makefile. You should specify the `ARCH` argument to
|
||||||
|
make (one of x86, x64, armv6m, armv7m, xtensa, xtensawin):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd features0
|
||||||
|
$ make ARCH=armv7m
|
||||||
|
$ mpremote cp features0.mpy :
|
||||||
|
```
|
14
examples/natmod/features4/Makefile
Normal file
14
examples/natmod/features4/Makefile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Location of top-level MicroPython directory
|
||||||
|
MPY_DIR = ../../..
|
||||||
|
|
||||||
|
# Name of module
|
||||||
|
MOD = features4
|
||||||
|
|
||||||
|
# Source files (.c or .py)
|
||||||
|
SRC = features4.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
|
73
examples/natmod/features4/features4.c
Normal file
73
examples/natmod/features4/features4.c
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
This example extends on features0 but demonstrates how to define a class.
|
||||||
|
|
||||||
|
The Factorial class constructor takes an integer, and then the calculate
|
||||||
|
method can be called to get the factorial.
|
||||||
|
|
||||||
|
>>> import features4
|
||||||
|
>>> f = features4.Factorial(4)
|
||||||
|
>>> f.calculate()
|
||||||
|
24
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include the header file to get access to the MicroPython API
|
||||||
|
#include "py/dynruntime.h"
|
||||||
|
|
||||||
|
// This is type(Factorial)
|
||||||
|
mp_obj_full_type_t mp_type_factorial;
|
||||||
|
|
||||||
|
// This is the internal state of a Factorial instance.
|
||||||
|
typedef struct {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
mp_int_t n;
|
||||||
|
} mp_obj_factorial_t;
|
||||||
|
|
||||||
|
// Essentially Factorial.__new__ (but also kind of __init__).
|
||||||
|
// Takes a single argument (the number to find the factorial of)
|
||||||
|
STATIC mp_obj_t factorial_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
|
||||||
|
mp_arg_check_num(n_args, n_kw, 1, 1, false);
|
||||||
|
|
||||||
|
mp_obj_factorial_t *o = mp_obj_malloc(mp_obj_factorial_t, type);
|
||||||
|
o->n = mp_obj_get_int(args_in[0]);
|
||||||
|
|
||||||
|
return MP_OBJ_FROM_PTR(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC mp_int_t factorial_helper(mp_int_t x) {
|
||||||
|
if (x == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return x * factorial_helper(x - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Factorial.calculate()
|
||||||
|
STATIC mp_obj_t factorial_calculate(mp_obj_t self_in) {
|
||||||
|
mp_obj_factorial_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_int(factorial_helper(self->n));
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_calculate_obj, factorial_calculate);
|
||||||
|
|
||||||
|
// Locals dict for the Factorial type (will have a single method, calculate,
|
||||||
|
// added in mpy_init).
|
||||||
|
mp_map_elem_t factorial_locals_dict_table[1];
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(factorial_locals_dict, factorial_locals_dict_table);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Initialise the type.
|
||||||
|
mp_type_factorial.base.type = (void*)&mp_type_type;
|
||||||
|
mp_type_factorial.flags = MP_TYPE_FLAG_NONE;
|
||||||
|
mp_type_factorial.name = MP_QSTR_Factorial;
|
||||||
|
MP_OBJ_TYPE_SET_SLOT(&mp_type_factorial, make_new, factorial_make_new, 0);
|
||||||
|
factorial_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_calculate), MP_OBJ_FROM_PTR(&factorial_calculate_obj) };
|
||||||
|
MP_OBJ_TYPE_SET_SLOT(&mp_type_factorial, locals_dict, (void*)&factorial_locals_dict, 1);
|
||||||
|
|
||||||
|
// Make the Factorial type available on the module.
|
||||||
|
mp_store_global(MP_QSTR_Factorial, MP_OBJ_FROM_PTR(&mp_type_factorial));
|
||||||
|
|
||||||
|
// This must be last, it restores the globals dict
|
||||||
|
MP_DYNRUNTIME_INIT_EXIT
|
||||||
|
}
|
@ -425,6 +425,7 @@ function ci_native_mpy_modules_build {
|
|||||||
make -C examples/natmod/features1 ARCH=$arch
|
make -C examples/natmod/features1 ARCH=$arch
|
||||||
make -C examples/natmod/features2 ARCH=$arch
|
make -C examples/natmod/features2 ARCH=$arch
|
||||||
make -C examples/natmod/features3 ARCH=$arch
|
make -C examples/natmod/features3 ARCH=$arch
|
||||||
|
make -C examples/natmod/features4 ARCH=$arch
|
||||||
make -C examples/natmod/btree ARCH=$arch
|
make -C examples/natmod/btree ARCH=$arch
|
||||||
make -C examples/natmod/deflate ARCH=$arch
|
make -C examples/natmod/deflate ARCH=$arch
|
||||||
make -C examples/natmod/framebuf ARCH=$arch
|
make -C examples/natmod/framebuf ARCH=$arch
|
||||||
|
Loading…
Reference in New Issue
Block a user