MicroPython .mpy -tiedostot

MicroPython määrittelee .mpy-tiedoston käsitteen. Kyseessä on binäärinen säiliötiedostomuoto, joka sisältää esikäännettyä koodia ja jonka voi tuoda kuten tavallisen .py-moduulin. Tiedosto foo.mpy voidaan tuoda komennolla import foo, kunhan foo.mpy löytyy tuontikoneiston tavanomaisesta hakupaikasta. Yleensä jokainen sys.path-listassa lueteltu hakemisto käydään läpi järjestyksessä. Tiettyä hakemistoa tutkittaessa etsitään ensin tiedostoa foo.py, ja jos sitä ei löydy, etsitään tiedostoa foo.mpy. Mikäli kumpaakaan ei löydy, haku jatkuu seuraavasta hakemistosta. Näin ollen foo.py on etusijalla tiedostoon foo.mpy nähden.

Nämä .mpy-tiedostot voivat sisältää bytecode-koodia, joka tavallisesti tuotetaan Python-lähdekooditiedostoista (.py-tiedostoista) mpy-cross-ohjelmalla. Joillakin arkkitehtuureilla .mpy-tiedosto voi sisältää myös natiivia konekoodia, joka voidaan tuottaa monin eri tavoin, erityisesti C-lähdekoodista.

mpy-cross-kääntäjä

mpy-cross on ristikääntäjä, joka muuntaa .py-lähdekooditiedoston .mpy-binäärisäiliöksi, joka on valmis tuotavaksi kameralle. Se on osa MicroPythonin lähdekoodipuuta (samaa, jota käytetään kameran laiteohjelmiston rakentamiseen) ja se julkaistaan myös pip-pakettina isäntäkoneella käytettäväksi ilman täydellistä laiteohjelmiston kloonausta:

$ pip install --user mpy-cross

Tai pipx-työkalun kautta:

$ pipx install mpy-cross

Kun se on asennettu, suorita se yksittäiselle lähdekooditiedostolle:

$ mpy-cross foo.py

Tämä tuottaa tiedoston foo.mpy nykyiseen hakemistoon valmiina kopioitavaksi kameran tiedostojärjestelmään muiden moduulien rinnalle tai syötettäväksi ROMFS-kuvaan.

Hyödyllisimmät komentorivivalitsimet:

  • -o <path> – tulostuspolku luodulle .mpy-tiedostolle (oletuksena syötetiedoston nimi, jonka tarkennin on korvattu; -o - kirjoittaa vakiotulosteeseen).

  • -O<n> – optimointitaso 03. Oletustaso 0 säilyttää assertiot ja täydet lähdekoodisijainnit; taso 3 poistaa assertiot ja docstringit sekä uudelleenkirjoittaa if __debug__ -lohkot. Taso ohjaa samaa micropython.opt_level-rajapintaa, jonka ajoympäristö tarjoaa.

  • -march=<arch> – kohdenatiiviarkkitehtuuri @native- ja @viper-koristelluille funktioille. Vaaditaan, kun lähdekoodi käyttää näitä koristelijoita. Arvon on vastattava kameran MCU-luokkaa: valitse se listasta, jonka mpy-cross --help tulostaa, tai lue se kameralta ajonaikaisesti komennolla sys.implementation._mpy.

  • -s <path>.mpy-tiedoston virheenkorjaustietoihin upotettava lähdepolkumerkkijono. Hyödyllinen, kun levyllä oleva polku eroaa siitä tuontipolusta, jonka alla tiedoston tulisi näkyä jäljityksissä.

  • -X emit=bytecode|native|viper – valitse oletusemitteri koko moduulille (funktiokohtainen vaihtoehto @native- / @viper-koristelijoille).

  • --version – tulostaa .mpy-muodon version, jonka tämä binääri tuottaa. Tämän numeron on vastattava versiota, jota kameran ajoympäristö tukee (katso julkaisutaulukko alla), tai tuonti aiheuttaa virheen ValueError('incompatible .mpy file').

Suorita mpy-cross --help saadaksesi täydellisen valitsinlistan.

Pip-paketti tarjoaa myös pienen Python-moduulirajapinnan, jotta rakennusskriptit voivat ajaa kääntäjää prosessin sisällä sen sijaan, että aliprosessi haarautettaisiin käsin:

import mpy_cross

mpy_cross.compile('foo.py', dest='build/foo.mpy', opt=3,
                  march=mpy_cross.NATIVE_ARCH_ARMV7EMSP)

mpy_cross.compile, mpy_cross.run ja mpy_cross.mpy_version ovat ne kolme aloituspistettä; mpy_cross.CrossCompileError välittää kääntäjän stderr-tulosteen, kun jokin menee vikaan. Arkkitehtuurivakiot (NATIVE_ARCH_ARMV7EMSP, NATIVE_ARCH_ARMV7EMDP jne.) vastaavat merkkijonoja, jotka -march-valitsin hyväksyy.

.mpy-tiedostojen versiointi ja yhteensopivuus

Tietty .mpy-tiedosto voi olla yhteensopiva tietyn MicroPython-järjestelmän kanssa tai sitten ei. Yhteensopivuus perustuu seuraaviin tekijöihin:

  • .mpy-tiedoston versio: tiedoston version on vastattava versiota, jota sitä lataava järjestelmä tukee.

  • .mpy-tiedoston aliversio: jos .mpy-tiedosto sisältää natiivia konekoodia, tiedoston aliversion on vastattava versiota, jota sitä lataava järjestelmä tukee. Muussa tapauksessa, jos .mpy-tiedostossa ei ole natiivia konekoodia, aliversio jätetään huomiotta latauksen aikana.

  • Pienen kokonaisluvun bitit: .mpy-tiedosto vaatii vähimmäismäärän bittejä small integer-tyypiltä, ja sitä lataavan järjestelmän on tuettava vähintään tätä bittimäärää.

  • Natiiviarkkitehtuuri: jos .mpy-tiedosto sisältää natiivia konekoodia, se määrittää kyseisen konekoodin arkkitehtuurin, ja sitä lataavan järjestelmän on tuettava tämän arkkitehtuurin koodin suoritusta.

Jos MicroPython-järjestelmä tukee .mpy-tiedostojen tuontia, kenttä sys.implementation._mpy on olemassa ja palauttaa kokonaisluvun, joka koodaa version (alemmat 8 bittiä), ominaisuudet ja natiiviarkkitehtuurin.

Sellaisen .mpy-tiedoston tuontiyritys, joka ei läpäise jotakin neljästä ensimmäisestä testistä, aiheuttaa virheen ValueError('incompatible .mpy file'). Sellaisen .mpy-tiedoston tuontiyritys, joka ei läpäise natiiviarkkitehtuuritestiä (jos se sisältää natiivia konekoodia), aiheuttaa virheen ValueError('incompatible .mpy arch').

Jos .mpy-tiedoston tuonti epäonnistuu, kokeile seuraavaa:

  • Selvitä MicroPython-järjestelmäsi tukema .mpy-versio ja liput suorittamalla:

    import sys
    sys_mpy = sys.implementation._mpy
    arch = [None, 'x86', 'x64',
        'armv6', 'armv6m', 'armv7m', 'armv7em', 'armv7emsp', 'armv7emdp',
        'xtensa', 'xtensawin', 'rv32imc', 'rv64imc'][(sys_mpy >> 10) & 0x0F]
    print('mpy version:', sys_mpy & 0xff)
    print('mpy sub-version:', sys_mpy >> 8 & 3)
    print('mpy flags:', end='')
    if arch:
        print(' -march=' + arch, end='')
    if (sys_mpy >> 16) != 0:
        print(' -march-flags=' + (sys_mpy >> 16), end='')
    print()
    
  • Tarkista .mpy-tiedoston kelpoisuus tutkimalla tiedoston kaksi ensimmäistä tavua. Ensimmäisen tavun tulisi olla iso ’M’-kirjain ja toisen tavun versionumero, jonka tulisi vastata edellä saatua järjestelmän versiota. Jos se ei täsmää, rakenna .mpy-tiedosto uudelleen.

  • Tarkista, vastaako järjestelmän .mpy-versio versiota, jonka tuotti se mpy-cross, jota käytettiin .mpy-tiedoston rakentamiseen; tämän saa selville komennolla mpy-cross --version. Jos se ei täsmää, käännä mpy-cross uudelleen Git-tietovarastosta, joka on otettu ulos siitä tagista (tai hajautusarvosta), jonka mpy-cross --version raportoi.

  • Varmista, että käytät oikeita mpy-cross-lippuja, jotka selviävät yllä olevalla koodilla tai tutkimalla käyttämäsi portin MPY_CROSS_FLAGS-Makefile-muuttujaa.

  • Jos .mpy-tiedoston kolmannen tavun bitti #6 on asetettu, tarkista, onko koodattu arkkitehtuurikohtainen lippubittien vuint yhteensopiva sen kohteen kanssa, jolle tuot tiedostoa.

Seuraava taulukko näyttää vastaavuuden MicroPython-julkaisun ja .mpy-version välillä.

MicroPython-julkaisu

.mpy-versio

v1.23.0 ja uudemmat

6.3

v1.22.x

6.2

v1.20 - v1.21.0

6.1

v1.19.x

6

v1.12 - v1.18

5

v1.11

4

v1.9.3 - v1.10

3

v1.9 - v1.9.2

2

v1.5.1 - v1.8.7

0

Täydellisyyden vuoksi seuraava taulukko näyttää MicroPythonin pääaariston Git-commitin, jossa .mpy-versio muuttui.

.mpy-version muutos

Git-commit

6.2 -> 6.3

bdbc869f9ea200c0d28b2bc7bfb60acd9d884e1b

6.1 -> 6.2

6967ff3c581a66f73e9f3d78975f47528db39980

6 -> 6.1

d94141e1473aebae0d3c63aeaa8397651ad6fa01

5 -> 6

f2040bfc7ee033e48acef9f289790f3b4e6b74e5

4 -> 5

5716c5cf65e9b2cb46c2906f40302401bdd27517

3 -> 4

9a5f92ea72754c01cc03e5efcdfe94021120531e

2 -> 3

ff93fd4f50321c6190e1659b19e64fef3045a484

1 -> 2

dd11af209d226b7d18d5148b239662e30ed60bad

0 -> 1

6a11048af1d01c78bdacddadd1b72dc7ba7c6478

alkuperäinen versio 0

d8c834c95d506db979ec871417de90b7951edc30

.mpy-tiedostojen binäärikoodaus

MicroPython .mpy -tiedostot ovat binäärinen säiliömuoto, jossa koodiobjektit (bytecode ja natiivi konekoodi) tallennetaan sisäisesti sisäkkäiseen hierarkiaan. Uloimman moduulin koodi tallennetaan ensin, ja sitten sen lapset seuraavat. Kullakin lapsella voi olla edelleen lapsia, esimerkiksi kun luokalla on metodeja tai funktio määrittelee lambdan tai läpikäynnin. Pitääkseen tiedostot pieninä mutta tarjotakseen silti laajan mahdollisten arvojen alueen se käyttää monessa kohdassa muuttuvanmittaisesti koodatun etumerkittömän kokonaisluvun (vuint) käsitettä. UTF-8-koodauksen tapaan tämä koodaus tallentaa 7 bittiä tavua kohden siten, että 8. bitti (MSB) on asetettu, jos yksi tai useampi tavu seuraa. Etumerkittömän kokonaisluvun bitit tallennetaan vuint-muotoon LSB-muodossa.

.mpy-tiedoston ylin taso koostuu kolmesta osasta:

  • Otsake.

  • Globaalit qstr- ja vakiotaulukot.

  • Moduulin uloimman näkyvyysalueen raakakoodi. Tämä uloin näkyvyysalue suoritetaan, kun .mpy-tiedosto tuodaan.

.mpy-tiedoston sisältöä voi tutkia mpy-tool.py-työkalulla, esimerkiksi (suoritetaan MicroPythonin pääaariston juuresta):

$ ./tools/mpy-tool.py -xd myfile.mpy

Otsake

.mpy-otsake on:

koko

kenttä

tavu

arvo 0x4d (ASCII ’M’)

tavu

.mpy-pääversionumero

tavu

ominaisuusliput, natiiviarkkitehtuuri, aliversionumero (vanhemmissa versioissa olivat ominaisuusliput)

tavu

pienen kokonaisluvun bittien määrä

Kolmas tavu jakautuu seuraavasti (MSB ensin):

bitti

merkitys

7

varattu, oltava 0

6

arkkitehtuurikohtainen lippujen vuint seuraa otsaketta

5..2

natiiviarkkitehtuurin numero

1..0

aliversionumero

Arkkitehtuurikohtaiset liput

Jos otsakkeen ominaisuuslippujen tavun bitti #6 on asetettu, otsaketta seuraa vuint, joka sisältää valinnaista arkkitehtuurikohtaista tietoa. Tämän kokonaisluvun sisältö riippuu siitä, mille natiiviarkkitehtuurille tiedosto on tarkoitettu.

Tätä käytetään tällä hetkellä tallentamaan, mitä RISC-V-suoritinlaajennuksia MPY-tiedosto tarvitsee toimiakseen oikein laajennusten I, M, C ja Zicsr lisäksi. Eri ArmV7-variantit tunnistetaan niiden natiiviarkkitehtuurinumeron perusteella, mutta tämän mekanismin uudelleenkäyttö mutkistaisi asioita RV32:n ja RV64:n osalta.

RV32:lle tai RV64:lle tarkoitettujen MPY-tiedostojen, jotka eivät tarvitse mitään erityisiä suoritinlaajennuksia, ei tarvitse tarjota lippukokonaislukua (otsakkeen asianmukaisen bitin asettamisen ohella). Lippuarvon puuttumista RV32- ja RV64-MPY-tiedostoissa käytetään osoittamaan, ettei erityisiä laajennuksia tarvita, ja se säästää yhden tavun lopullisessa tulostebinäärissä.

Katso myös -march-flags-komentorivivalitsin sekä työkalussa mpy-tool.py että mpy-cross-ohjelmassa, ja --arch-flags-komentorivivalitsin työkalussa mpy_ld.py tämän arvon asettamiseksi MPY-tiedostoja luotaessa.

Globaalit qstr- ja vakiotaulukot

.mpy-tiedosto sisältää yhden qstr-taulukon ja yhden vakio-objektitaulukon. Nämä ovat .mpy-tiedostolle globaaleja, ja kaikki sisäkkäiset raakakoodiobjektit viittaavat niihin. qstr-taulukko kuvaa sisäisen qstr-numeron (.mpy-tiedoston sisäisen) sen ajoympäristön ratkaistuun qstr-numeroon, johon .mpy-tiedosto tuodaan. Tämä linkittää .mpy-tiedoston muuhun järjestelmään, jonka sisällä se suoritetaan. Vakio-objektitaulukko täytetään viittauksilla kaikkiin vakio-objekteihin, joita .mpy-tiedosto tarvitsee.

koko

kenttä

vuint

qstr-jonojen määrä

vuint

vakio-objektien määrä

qstr-data

koodatut vakio-objektit

Raakakoodielementit

Raakakoodielementti sisältää koodia, joko bytecode- tai natiivia konekoodia. Sen sisältö on:

koko

kenttä

vuint

tyyppi, koko ja se, onko ali-raakakoodielementtejä

koodi (bytecode tai konekoodi)

vuint

ali-raakakoodielementtien määrä (vain jos nollasta poikkeava)

ali-raakakoodielementit

Raakakoodielementin ensimmäinen vuint koodaa tähän elementtiin tallennetun koodin tyypin (kaksi vähiten merkitsevää bittiä), onko tällä raakakoodilla lapsia (kolmas vähiten merkitsevä bitti), ja seuraavan koodin pituuden (sille varattavan RAM-muistin määrän).

vuintin jälkeen tulee itse koodi. Ellei koodityyppi ole viper-koodi uudelleensijoituksin, tämä koodi on vakiodataa eikä sitä tarvitse muokata.

Jos tällä raakakoodilla on lapsia (kuten ensimmäisen vuintin bitti osoittaa), koodin jälkeen tulee vuint, joka laskee ali-raakakoodielementtien määrän.

Lopuksi tallennetaan kaikki ali-raakakoodielementit rekursiivisesti.