Natiivi konekoodi .mpy-tiedostoissa¶
Tässä osiossa kuvataan, miten rakennetaan ja käsitellään .mpy-tiedostoja, jotka sisältävät natiivia konekoodia jollakin muulla kielellä kuin Pythonilla. Tämän avulla voit kirjoittaa koodia esimerkiksi C-kielellä, kääntää ja linkittää sen .mpy-tiedostoon ja tuoda kyseisen tiedoston aivan kuten tavallisen Python-moduulin. Tätä voidaan käyttää suorituskyvyn kannalta kriittisten toimintojen toteuttamiseen tai jollakin toisella kielellä kirjoitetun olemassa olevan kirjaston sisällyttämiseen.
Yksi natiivien .mpy-tiedostojen käytön tärkeimmistä eduista on se, että skripti voi tuoda natiivin konekoodin dynaamisesti ilman tarvetta kääntää MicroPython-päälaiteohjelmistoa uudelleen. Tämä eroaa MicroPythonin ulkoiset C-moduulit-mekanismista, joka myös mahdollistaa mukautettujen moduulien määrittelyn C-kielellä, mutta ne on käännettävä mukaan päälaiteohjelmiston levykuvaan.
Painopiste on tässä C-kielen käytössä natiivien moduulien rakentamiseen, mutta periaatteessa mikä tahansa kieli, joka voidaan kääntää itsenäiseksi konekoodiksi, voidaan sijoittaa .mpy-tiedostoon.
Natiivi .mpy-moduuli rakennetaan mpy_ld.py-työkalulla, joka löytyy projektin tools/-hakemistosta. Tämä työkalu ottaa joukon objektitiedostoja (.o-tiedostoja) ja linkittää ne yhteen luodakseen natiivin .mpy-tiedoston. Se vaatii CPython 3:n ja pyelftools-kirjaston version 0.25 tai uudemman.
Tuetut ominaisuudet ja rajoitukset¶
.mpy-tiedosto voi sisältää MicroPython-tavukoodia ja/tai natiivia konekoodia. Jos se sisältää natiivia konekoodia, .mpy-tiedostoon liittyy tietty arkkitehtuuri. Tällä hetkellä tuetut arkkitehtuurit ovat (nämä ovat ARCH-muuttujan kelvolliset vaihtoehdot, katso alla):
x86(32-bittinen)x64(64-bittinen x86)armv6m(ARM Thumb, esim. Cortex-M0)armv7m(ARM Thumb 2, esim. Cortex-M3)armv7emsp(ARM Thumb 2, yksinkertainen tarkkuus liukuluku, esim. Cortex-M4F, Cortex-M7)armv7emdp(ARM Thumb 2, kaksinkertainen tarkkuus liukuluku, esim. Cortex-M7)xtensa(ei-ikkunoitu, esim. ESP8266)xtensawin(ikkunoitu ikkunakoolla 8, esim. ESP32, ESP32S3)rv32imc(32-bittinen RISC-V pakatuilla käskyillä, esim. ESP32C3, ESP32C6)rv64imc(64-bittinen RISC-V pakatuilla käskyillä)
Jos valittu alusta tukee eksplisiittisiä arkkitehtuurilippuja ja haluat, että tuloksena syntyvä .mpy-tiedosto kantaa näiden lippujen arvon, sinun on välitettävä ne ARCH_FLAGS-lippumuuttujaan .mpy-tiedostoa rakennettaessa.
Natiivia .mpy-tiedostoa käännettäessä ja linkitettäessä on valittava arkkitehtuuri, ja vastaavan tiedoston voi tuoda vain kyseisellä arkkitehtuurilla (ja jos arkkitehtuurilippuja on, vain jos ne vastaavat kohteen ominaisuuksia). Lisätietoja .mpy-tiedostoista on kohdassa MicroPython .mpy -tiedostot.
Natiivi koodi on käännettävä paikkariippumattomaksi koodiksi (PIC) ja sen on käytettävä globaalia siirtymätaulua (GOT), vaikka tämän yksityiskohdat vaihtelevat arkkitehtuurista toiseen. Kun .mpy-tiedostoja, joissa on natiivia koodia, tuodaan, tuontikoneisto pystyy tekemään joitakin natiivin koodin perussijoituksia (relocation). Tämä sisältää text-, rodata- ja BSS-osioiden uudelleensijoittamisen.
Linkkerin ja dynaamisen latauskoneiston tukemia ominaisuuksia ovat:
suoritettava koodi (text)
vain luettava data (rodata), mukaan lukien merkkijonot ja vakiodata (taulukot, tietueet jne.)
nollattu data (BSS)
osoittimet text-osiossa text-, rodata- ja BSS-osioihin
osoittimet rodata-osiossa text-, rodata- ja BSS-osioihin
Tunnetut rajoitukset ovat:
data-osioita ei tueta; kiertotapa: käytä BSS-dataa ja alusta data-arvot eksplisiittisesti
staattisia BSS-muuttujia ei tueta; kiertotapa: käytä globaaleja BSS-muuttujia
säiekohtaisen tallennuksen (thread-local storage) muuttujia ei tueta rv32imc-arkkitehtuurilla; kiertotapa: käytä globaaleja BSS-muuttujia tai varaa niiden tallentamiseksi tilaa keosta
Jos siis C-koodissasi on kirjoitettavaa dataa, varmista, että data määritellään globaalisti ilman alustinta ja että siihen kirjoitetaan vain funktioiden sisällä.
Natiivia moduulia ei linkitetä automaattisesti standardien staattisten kirjastojen, kuten libm.a ja libgcc.a, kanssa, mikä voi johtaa undefined symbol -virheisiin. Voit linkittää ajonaikaiset kirjastot asettamalla LINK_RUNTIME = 1 Makefile-tiedostossasi. Mukautettuja staattisia kirjastoja voi myös linkittää lisäämällä MPY_LD_FLAGS += -l path/to/library.a. Huomaa, että nämä linkitetään natiiviin moduuliin, eikä niitä jaeta muiden moduulien tai järjestelmän kanssa.
Linkkerin rajoitus: natiivia moduulia ei linkitetä koko MicroPython-laiteohjelmiston symbolitaulun kanssa. Sen sijaan se linkitetään eksplisiittiseen vietyjen symbolien tauluun, joka löytyy kohteesta mp_fun_table (tiedostossa py/nativeglue.h) ja joka on kiinteä laiteohjelmiston rakennusvaiheessa. Näin ollen ei ole mahdollista yksinkertaisesti kutsua jotakin mielivaltaista HAL/OS/RTOS/järjestelmäfunktiota, ellei se sijaitse kiinteässä osoitteessa. Siinä tapauksessa polun linkkeriskriptiin, joka sisältää joukon symbolinimiä ja niiden kiinteät osoitteet, voi välittää mpy_ld.py-työkalulle komentoriviargumentilla --externs. Näin linkkeriskriptissä esiintyvät symbolit ovat etusijalla objektitiedostojen tarjoamiin nähden, mutta tällä hetkellä objektitiedostojen toteutus jää silti lopulliseen MPY-tiedostoon. Linkkeriskriptin jäsennin on ominaisuuksiltaan rajoitettu, ja sitä käytetään tällä hetkellä vain ESP8266-portin ROM-symbolilistan jäsentämiseen (katso ports/esp8266/boards/eagle.rom.addr.v6.ld).
Uusia symboleja voi lisätä taulun loppuun ja rakentaa laiteohjelmiston uudelleen. Symbolit on myös lisättävä tools/mpy_ld.py-tiedoston fun_table-sanakirjaan samaan kohtaan. Näin mpy_ld.py pystyy poimimaan uudet symbolit ja tarjoamaan niille uudelleensijoitukset, kun mpy tuodaan. Lopuksi, jos symboli on funktio, kannattaa lisätä makro tai tynkä (stub) tiedostoon py/dynruntime.h funktion kutsumisen helpottamiseksi.
Natiivin moduulin määrittely¶
Natiivi .mpy-moduuli määritellään joukolla tiedostoja, joita käytetään .mpy:n rakentamiseen. Tiedostojärjestelmän asettelu koostuu kahdesta pääosasta, lähdetiedostoista ja Makefile-tiedostosta:
Yksinkertaisimmassa tapauksessa tarvitaan vain yksi C-lähdetiedosto, joka sisältää kaiken koodin, joka käännetään .mpy-moduuliin. Tämän C-lähdekoodin on sisällettävä tiedosto
py/dynruntime.h, jotta MicroPythonin dynaamiseen API:in pääsee käsiksi, ja siinä on määriteltävä ainakin funktio nimeltämpy_init. Tämä funktio on moduulin sisääntulopiste, jota kutsutaan, kun moduuli tuodaan.Moduuli voidaan haluttaessa jakaa useampaan C-lähdetiedostoon. Osia moduulista voidaan myös toteuttaa Pythonilla. Kaikki lähdetiedostot tulisi luetella Makefile-tiedostossa lisäämällä ne
SRC-muuttujaan (katso alla). Tämä koskee sekä C-lähdetiedostoja että kaikkia Python-tiedostoja, jotka sisällytetään tuloksena syntyvään .mpy-tiedostoon.Makefilesisältää moduulin rakennusasetukset ja luettelee lähdetiedostot, joita käytetään .mpy-moduulin rakentamiseen. Sen tulisi määritelläMPY_DIRMicroPython-repositorion sijaintina (otsikkotiedostojen, asiaankuuluvan Makefile-fragmentin jampy_ld.py-työkalun löytämiseksi),MODmoduulin nimenä,SRClähdetiedostojen luettelona, valinnaisesti määrittää konearkkitehtuurinARCH-muuttujalla sekä valinnaiset konearkkitehtuurin liputARCH_FLAGS-muuttujalla, ja sitten sisällyttääpy/dynruntime.mk.
Minimaalinen esimerkki¶
Tässä osiossa annetaan täysin toimiva esimerkki yksinkertaisesta moduulista nimeltä factorial. Tämä moduuli tarjoaa yhden funktion factorial.factorial(x), joka laskee syötteen kertoman ja palauttaa tuloksen.
Hakemistorakenne:
factorial/
├── factorial.c
└── Makefile
Tiedosto factorial.c sisältää:
// Include the header file to get access to the MicroPython API
#include "py/dynruntime.h"
// Helper function to compute factorial
static mp_int_t factorial_helper(mp_int_t x) {
if (x == 0) {
return 1;
}
return x * factorial_helper(x - 1);
}
// This is the function which will be called from Python, as factorial(x)
static mp_obj_t factorial(mp_obj_t x_obj) {
// Extract the integer from the MicroPython input object
mp_int_t x = mp_obj_get_int(x_obj);
// Calculate the factorial
mp_int_t result = factorial_helper(x);
// Convert the result to a MicroPython integer object and return it
return mp_obj_new_int(result);
}
// Define a Python reference to the function above
static MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial);
// 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 function available in the module's namespace
mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj));
// This must be last, it restores the globals dict
MP_DYNRUNTIME_INIT_EXIT
}
Tiedosto Makefile sisältää:
# Location of top-level MicroPython directory
MPY_DIR = ../../..
# Name of module
MOD = factorial
# Source files (.c or .py)
SRC = factorial.c
# Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
# Include to get the rules for compiling and linking the module
include $(MPY_DIR)/py/dynruntime.mk
Moduulin kääntäminen¶
Natiivin .mpy-tiedoston rakentamiseen tarvittavat esiehdolliset työkalut ovat:
MicroPython-repositorio (vähintään
py/- jatools/-hakemistot).CPython 3 ja pyelftools-kirjasto (esim.
pip install 'pyelftools>=0.25').GNU make.
C-kääntäjä kohdearkkitehtuurille (jos C-lähdekoodia käytetään).
Valinnaisesti
mpy-cross, joka on rakennettu MicroPython-repositoriosta (jos .py-lähdekoodia käytetään).
Varmista, että valitset oikean ARCH-arvon kohteelle, jolla aiot ajaa. Rakenna sitten komennolla:
$ make
Makefilea muokkaamatta voit määrittää kohdearkkitehtuurin näin:
$ make ARCH=armv7m
Sama pätee valinnaisiin arkkitehtuurilippuihin:
$ make ARCH=rv32imc ARCH_FLAGS=zba
Moduulin käyttö MicroPythonissa¶
Kun moduuli on rakennettu, pitäisi olla tiedosto nimeltä factorial.mpy. Kopioi se niin, että se on käytettävissä MicroPython-järjestelmäsi tiedostojärjestelmässä ja löytyy tuontipolusta. Moduuliin pääsee nyt käsiksi Pythonissa aivan kuten mihin tahansa muuhun moduuliin, esimerkiksi:
import factorial
print(factorial.factorial(10))
# should display 3628800
Picolibcin käyttö moduuleja rakennettaessa¶
Picolibc-kirjaston käyttöä C-standardikirjastonasi ei vain tueta, vaan se on itse asiassa oletus rv32imc- ja rv64imc-alustoilla. On kuitenkin pari mainitsemisen arvoista seikkaa, joilla varmistat, ettet törmää ongelmiin myöhemmin koodia rakentaessasi.
Jotkin esirakennetut Picolibc-versiot (esimerkiksi Ubuntu Linuxin tarjoamat paketit picolibc-arm-none-eabi, picolibc-riscv64-unknown-elf ja picolibc-xtensa-lx106-elf) olettavat säiekohtaisen tallennuksen (TLS) olevan käytettävissä ajonaikaisesti, mutta valitettavasti MicroPython-moduulit eivät tue sitä joillakin arkkitehtuureilla (nimittäin rv32imc ja rv64imc). Tämä tarkoittaa, että jotkin Picolibcin tarjoamat toiminnot käyttävät oletuksena TLS:ää ja palauttavat virheen joko käännöksen tai linkityksen aikana.
Esimerkki siitä, miten tämä voi vaikuttaa sinuun: examples/natmod/btree-esimerkkimoduuli sisältää kiertotavan, jolla varmistetaan errno:n toimivuus (etsi __PICOLIBC_ERRNO_FUNCTION Makefile-tiedostosta ja seuraa jälkiä siitä eteenpäin).
Lisää esimerkkejä¶
Katso examples/natmod/ lisäesimerkkejä varten, jotka esittelevät monia natiivien .mpy-moduulien käytettävissä olevia ominaisuuksia. Tällaisia ominaisuuksia ovat:
useiden C-lähdetiedostojen käyttö
Python-koodin sisällyttäminen C-koodin rinnalle
rodata- ja BSS-data
muistin varaaminen
liukulukujen käyttö
poikkeusten käsittely
ulkoisten C-kirjastojen sisällyttäminen