Commit efde24ba authored by schneider's avatar schneider
Browse files

Update pycardium_modules.md

parent 5630a992
Pipeline #1267 passed with stages
in 35 seconds
......@@ -2,133 +2,5 @@
title: Pycardium Modules Development
---
Pycardium is our micropython port. You can find it [here](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/pycardium).
This page gives an overview on adding new modules to pycardium. There are two kinds of modules:
#### Modules written in Python
Python modules are written in ... Python. They live in [`pycardium/modules/py`](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/pycardium/modules/py) and are pre-compiled and packed into pycardium at build-time. Python modules are meant to be used for creating abstractions and helpers based on lower level functionality. The rationale is that it is easier to write pythonic modules in Python instead of having to wrap all that in C code. Though care has to be taken as these modules get quite big.
#### Modules written in C
C modules allow MicroPython code to interface with the rest of the system or allow a much faster implementation of performance-critical code. For interfacing with card10, these modules are mostly meant to make use of the [Epicardium API](../epicardium_api_development).
## Table Of Contents
- [Python Modules](#python-modules)
- [Adding a new Python module](#adding-a-new-python-module)
- [Size Considerations](#size-considerations)
- [C Modules](#c-modules)
- [Basic Structure](#basic-structure)
- [Module Source](#module-source)
- [Build System](#build-system)
- [QSTR Definitions](#qstr-definitions)
- [Enable Module](#enable-module)
- [Wrapping Epicardium API](#wrapping-epicardium-api)
- [QSTR](#qstr)
## Python Modules
### Adding a new Python module
Add a file with the name of the module you want to [`pycardium/modules/py`](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/pycardium/modules/py). Make the build-system aware of the new Python module by adding it in [`pycardium/modules/py/meson.build`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/modules/py/meson.build). Rebuild and you should have your module available in MicroPython! You can list the available modules in MicroPython using the following command:
```python
help("modules")
```
### Size Considerations
Python modules can get quite big. If you want to get a feeling for how big, take a look at
```
ls -lh build/pycardium/*@@frozen.c@*/
```
If your module needs a lookup table or other kind of big data-file, consider pushing that out into the external flash. This is not yet possible, however (tracking [#11](https://git.card10.badge.events.ccc.de/card10/firmware/issues/11)).
## C Modules
### Basic Structure
#### Module Source
C modules live in [`pycardium/modules`](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/pycardium/modules). To add a new module, create a file in there containing the following example content. This module is named `example` but you should of course replace that with your own name:
```c
#include "py/obj.h"
/* Define a function in this module */
static mp_obj_t mp_example_hello(mp_obj_t number)
{
int num = mp_obj_get_int(number);
printf("Hello from example: %d\n", num);
return mp_const_none;
}
/* Create an object for this function */
static MP_DEFINE_CONST_FUN_OBJ_1(example_hello_obj, mp_example_hello);
/* The globals table for this module */
static const mp_rom_map_elem_t example_module_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example)},
{MP_ROM_QSTR(MP_QSTR_hello), MP_ROM_PTR(&example_hello_obj)},
};
static MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
const mp_obj_module_t example_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t*)&example_module_globals,
};
/* This is a special macro that will make MicroPython aware of this module */
MP_REGISTER_MODULE(MP_QSTR_example, example_module, MODULE_EXAMPLE_ENABLED);
```
#### Build System
Next, you need to add your module to [`pycardium/meson.build`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/meson.build). There is a list of module sources at the very top called `modsrc` where you need to add your c file (`modules/example.c`).
#### QSTR Definitions
If you now run `ninja -C build/`, you will hit a few errors regarding missing QSTR definitions. With the example module, those will look similar to
```text
../pycardium/modules/example.c:15:46: error: 'MP_QSTR_example' undeclared here (not in a function)
15 | {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example)},
```
To fix those, you need to add all of the missing QSTRs to [`pycardium/modules/qstrdefs.h`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/modules/qstrdefs.h). Add a section for your module with all the necessary definitions (ideally sorted):
```c
/* example */
Q(example)
Q(hello)
```
Each of these `Q(...)` will expand to a define for `MP_QSTR_...`. So `Q(example)` corresponds to `MP_QSTR_example`.
What are QSTRs? Take a look at the [QSTR](#qstr) section of this page.
#### Enable Module
Last step is to enable your module in the MicroPython config header [`pycardium/mpconfigport.h`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/mpconfigport.h). Search for the list of existing modules and add yours in there as well. The line should look similar to this:
```c
#define MODULE_EXAMPLE_ENABLED (1)
```
## Wrapping Epicardium API
Most modules will probably make use of the Epicardium API. Doing so does not require any extra work apart from adding
```c
#include "epicardium.h"
static mp_obj_t mp_example_hello(mp_obj_t number)
{
char buf[80];
snprintf(buf, "Hello from example: %d\n", num);
/* Call an Epicardium API function */
epic_uart_write_str(buf, strlen(buf));
return mp_const_none;
}
```
## QSTR
QSTRs are so called "interned strings". This means they are not allocated like normal Python objects but instead live in flash and are indexed. This allow MicroPython to very efficiently use them as identifiers. According to them, comparing two QSTR is as fast as comparing integers.
Unfortunately, the way these QSTRs are collected from the source files is quite weird. MicroPython comes with a few Python scripts (namely [`makeqstrdefs.py`](https://github.com/micropython/micropython/blob/master/py/makeqstrdefs.py) and [`makeqstrdata.py`](https://github.com/micropython/micropython/blob/master/py/makeqstrdata.py)) that parse the C source files and search for uses of `MP_QSTR_*`. These are then sorted and indexed into a header file called `qstrdefs.collected.h`. This is closely tied in with their Makefiles.
As we use our own build system, we had to somehow wrap this generation to work for us as well. This is done using a few scripts in [`lib/micropython`](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/lib/micropython).
Currently, our build system does **not** parse the module sources to search for QSTRs. Instead all QSTRs needed by modules need to be defined in the header [`pycardium/modules/qstrdefs.h`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/modules/qstrdefs.h).
## Obsolete
Please tend to https://firmware.card10.badge.events.ccc.de/pycardium-guide.html
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment