bbcmicrobit / bbcmicrobit/micropython
Freezing the byte-code of a module and include it in the firmware doesn't work
- Dominant language
- C
- Stars
- 646
- Forks
- 290
- PR merge metrics
- No merged PRs in 30d
Description
I'm working on reducing the RAM usage of a module I'm writing in micropython and I've hit a roadblock - after just a couple hundred of lines the microbit stops working.
---
So, I started looking into converting the python script into a `.mpy` bytecode and then convert it to a `.c` file to have it compiled within the firmware. These are the steps I have followed (with support from https://github.com/bbcmicrobit/micropython/issues/347 & https://github.com/bbcmicrobit/micropython/issues/530):
1. Cloned https://github.com/bbcmicrobit/micropython repo on an Ubuntu machine and then used the following hash https://github.com/bbcmicrobit/micropython/blob/master/inc/genhdr/mpversion.h#L3 to clone the official https://github.com/micropython/micropython repo.
1. Created `perf.py` module of which implementation can be seen down below.
1. Built `mpy-cross` program from the official version of micropython and then I placed it system-wide so I can use it everywhere.
1. Ran `mpy-cross perf.py` to generate the `perf.mpy` bytecode.
1. Placed `perf.mpy` in an empty folder inside the `tools` folder in the official repo clone and ran `python make-frozen.py generic-folder-name > frozen_module.c`.
1. Copied `frozen_module.c` file in the microbit/micropython repo clone in `source/py` and then added the `#define MICROPY_MODULE_FROZEN (1)` and `#define MICROPY_MODULE_FROZEN_STR (1)` instructions in `inc/microbit/mpconfigport.h` header on line 30.
1. `cd`ed to the root directory of the microbit/micropython repo clone and ran `make all` as `yt build` doesn't work - probably due to having had to install `gcc-arm-embedded` instead of `gcc-arm-none-eabi` package. That's because the `pmiller-opensource/ppa` repo is not accessible and it gives me a 404.
1. Copied the ` build/bbc-microbit-classic-gcc-nosd/source/microbit-micropython.hex` binary over the microbit and waited for it to flash it.
---
Upon REPLing into the microbit, I tried to import the module called `perf` and I got an ImportError telling me it doesn't exist. Then I typed `help('modules')` and there was a perf module, but it was called `perf.`, which doesn't make any sense. I took the bait and I tried to import `perf.` and I got a syntax error, which I think it's the expectable behavior. Here's the response I got from `help('modules')`:
```
Type "help()" for more information.
>>> help('modules')
__main__ love os this
antigravity machine perf. time
array math radio ucollections
audio microbit random ustruct
builtins micropython speech utime
collections music struct
gc neopixel sys
Plus any modules on the filesystem
>>>
```
Here's a preview of `freeze_module.c`:
```
#include
const char mp_frozen_str_names[] = {
"perf.mpy\0"
"\0"};
const uint32_t mp_frozen_str_sizes[] = {
602,
};
const char mp_frozen_str_content[] = {
"M\x02\x02\x1f" "=\x02\x00\x00\x00\x00\x00\x0e\n\x00\xf7\x00" ")Hg\x85\x0e\x85\x08\x00\x00\xff\x80\x11" "h\xf8\x00" "$\xf8\x00\x80\x11" "h\xf9\x00" "$\xf9\x00\x14\x80\xce\x10" "$\xfa\x00" "`\x00" "$\xfb\x00" "`\x01" "$\x00\x01" "`\x02" "$\x08\x01\x11" "[\x08" "\x07" "perf.py\x04" "time\x04" "time\x08" "microbit\x08" "microbit\x01" "N\x0b" "read_button\x07" "time_it\x07" "call_it\x00\x03" "b\x07\x00\x00\x01\x00\x00\x14\xfb\x00\xf7\x00\x81\x07" "*)$$$$$$$$$\x00\x00\xff\x1c\xf9\x00\x1d\xfe\x00\x1d\xfd\x00\xc1\xb0\x8a\xe0\x80" "5,\x80" "0\xc2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\xb1" "d\x00" "2\x81\xe9" "13\xf0" "6\xce\x7f" "22\x11" "[\x0b" "read_button\x07" "perf.py\x08" "microbit\x08" "button_a\nis_pressed\x00\x00\x01" "nU\r\x00\x00\x02\x00\x00\x0e\x00\x01\xf7\x00\x81\x15" ")%)+#\x00\x00\xff\x1c\xf8\x00\x1e\x03\x01" "f\x00\xc2\xb0\xb1" "d\x01" "2\x1c\xf8\x00\x1e\x03\x01" "f\x00\xc3\x1c\xf8\x00\x1e\x06\x01\xb3\xb2" "f\x02\xc4\x17\x02\xc5\x1c\xc7\x00\xb5\x1e\x8c\x00\xb4\x17\x03\xde\xb4\xb1\xe0\xb1\xb4\xe0\x17\x04\xde" "f\x03" "d\x01" "2\x11" "[\x07" "time_it\x07" "perf.py\x04" "time\x08" "ticks_us\x04" "time\x08" "ticks_us\x04" "time\nticks_diff\x05" "print\x06" "format\x03\x00\x01" "f\x01" "ns6{:5.3f} sec, {:6.3} usec/button : {:8.2f} kbuttons/secf\x05" "1e-06f\x06" "1000.0\x1e\x03\x00\x00\x00\x00\x00\x09\x08\x01\xf7\x00\x81\x1d\x00\x00\xff\x1c\x00\x01\x1c\xfb\x00\x1c\xfa\x00" "d\x02" "2\x11" "[\x07" "call_it\x07" "perf.py\x07" "time_it\x0b" "read_button\x01" "N\x00\x00\0"
};
```
And here's the source code of `perf.py`. And yeah, I know, I used your example program from one of your @dpgeorge conferences :) :
```
import time
import microbit
N = 10000
# read the button n times
def read_button(n):
is_pressed = microbit.button_a.is_pressed
for i in range(n/10):
is_pressed()
is_pressed()
is_pressed()
is_pressed()
is_pressed()
is_pressed()
is_pressed()
is_pressed()
is_pressed()
is_pressed()
def time_it(f, n):
t0 = time.ticks_us()
f(n)
t1 = time.ticks_us()
dt = time.ticks_diff(t1, t0)
fmt = '{:5.3f} sec, {:6.3} usec/button : {:8.2f} kbuttons/sec'
print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))
def call_it():
time_it(read_button, N)
# call_it()
```
---
Now, if I just skip the byte-compiling stuff (that is skipping `mpy-cross`) and just do the freezing stuff to generate the `c` file, the `perf` module appears when calling `help('modules')` and I can import it and even run whatever it's in there just fine. But it seems to stop working when I try to freeze the byte-code.
Does anyone know what's the problem here? Am I missing something? I truly hope it's not something obvious.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.