MicroPython .mpy datoteke

MicroPython definira koncept .mpy datoteke, koja je binarni spremnički format datoteke koji sadrži prethodno prevedeni kod i koja se može uvesti poput uobičajenog .py modula. Datoteka foo.mpy može se uvesti pomoću import foo, sve dok mehanizam uvoza može pronaći foo.mpy na uobičajeni način. Obično se svaki direktorij naveden u sys.path pretražuje redom. Pri pretraživanju određenog direktorija prvo se traži foo.py, a ako se ne pronađe, tada se traži foo.mpy, te se pretraga nastavlja u sljedećem direktoriju ako se nijedna ne pronađe. Zbog toga foo.py ima prednost pred foo.mpy.

Te .mpy datoteke mogu sadržavati bytecode koji se obično generira iz Python izvornih datoteka (.py datoteka) pomoću programa mpy-cross. Za neke arhitekture .mpy datoteka može sadržavati i izvorni strojni kod, koji se može generirati na razne načine, najčešće iz C izvornog koda.

Prevoditelj mpy-cross

mpy-cross je unakrsni prevoditelj koji pretvara .py izvornu datoteku u .mpy binarni spremnik spreman za uvoz na kameru. Dio je MicroPython izvornog stabla (istog onog koje se koristi za izgradnju firmwarea kamere), a objavljen je i kao pip paket za korištenje na strani domaćina bez potpunog preuzimanja firmwarea:

$ pip install --user mpy-cross

Ili putem pipx:

$ pipx install mpy-cross

Nakon instalacije, pozovite ga na pojedinačnoj izvornoj datoteci:

$ mpy-cross foo.py

Time se stvara foo.mpy u trenutnom direktoriju, spreman za kopiranje na datotečni sustav kamere uz druge module ili za umetanje u ROMFS sliku.

Najkorisnije opcije naredbenog retka:

  • -o <path> – izlazna putanja za generirani .mpy (zadano je naziv ulazne datoteke s promijenjenom ekstenzijom; -o - zapisuje na stdout).

  • -O<n> – razina optimizacije od 0 do 3. Zadana vrijednost 0 zadržava tvrdnje i potpune lokacije izvornog koda; 3 uklanja tvrdnje i dokumentacijske nizove te prepisuje if __debug__ blokove. Razina upravlja istom micropython.opt_level površinom koju izlaže izvršno okruženje.

  • -march=<arch> – ciljna izvorna arhitektura za funkcije označene s @native i @viper. Obavezno kada izvorni kod koristi te dekoratore. Vrijednost mora odgovarati klasi MCU-a kamere: odaberite je s popisa koji ispisuje mpy-cross --help ili je pročitajte s kamere u izvođenju pomoću sys.implementation._mpy.

  • -s <path> – niz izvorne putanje ugrađen u informacije za otklanjanje pogrešaka u .mpy. Korisno kada se putanja na disku razlikuje od putanje uvoza pod kojom bi se datoteka trebala prikazivati u tragovima poziva.

  • -X emit=bytecode|native|viper – odabir zadanog emitera za cijeli modul (alternativa po funkciji dekoratorima @native / @viper).

  • --version – ispisuje verziju .mpy formata koju emitira ovaj binarni program. Taj se broj mora podudarati s verzijom koju podržava izvršno okruženje kamere (vidi tablicu izdanja u nastavku) ili će uvoz izazvati ValueError('incompatible .mpy file').

Pokrenite mpy-cross --help za potpuni popis zastavica.

Pip paket također izlaže mali Python modul API tako da skripte za izgradnju mogu pokretati prevoditelj unutar procesa umjesto ručnog grananja podprocesa:

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 i mpy_cross.mpy_version tri su ulazne točke; mpy_cross.CrossCompileError nosi stderr prevoditelja kada nešto pođe po zlu. Konstante arhitekture (NATIVE_ARCH_ARMV7EMSP, NATIVE_ARCH_ARMV7EMDP itd.) odgovaraju nizovima koje prihvaća zastavica -march.

Verzioniranje i kompatibilnost .mpy datoteka

Dana .mpy datoteka može, ali i ne mora biti kompatibilna s danim MicroPython sustavom. Kompatibilnost se temelji na sljedećem:

  • Verzija .mpy datoteke: verzija datoteke mora odgovarati verziji koju podržava sustav koji je učitava.

  • Pod-verzija .mpy datoteke: ako .mpy datoteka sadrži izvorni strojni kod, tada pod-verzija datoteke mora odgovarati verziji koju podržava sustav koji je učitava. U suprotnom, ako u .mpy datoteci nema izvornog strojnog koda, tada se pod-verzija pri učitavanju zanemaruje.

  • Bitovi malog cijelog broja: .mpy datoteka zahtijevat će minimalni broj bitova u small integer, a sustav koji je učitava mora podržavati barem toliko bitova.

  • Izvorna arhitektura: ako .mpy datoteka sadrži izvorni strojni kod, tada će odrediti arhitekturu tog strojnog koda, a sustav koji je učitava mora podržavati izvršavanje koda te arhitekture.

Ako MicroPython sustav podržava uvoz .mpy datoteka, tada će polje sys.implementation._mpy postojati i vraćati cijeli broj koji kodira verziju (donjih 8 bitova), značajke i izvornu arhitekturu.

Pokušaj uvoza .mpy datoteke koja ne prođe jedan od prva četiri testa izazvat će ValueError('incompatible .mpy file'). Pokušaj uvoza .mpy datoteke koja ne prođe test izvorne arhitekture (ako sadrži izvorni strojni kod) izazvat će ValueError('incompatible .mpy arch').

Ako uvoz .mpy datoteke ne uspije, pokušajte sljedeće:

  • Utvrdite .mpy verziju i zastavice koje podržava vaš MicroPython sustav izvođenjem:

    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()
    
  • Provjerite valjanost .mpy datoteke pregledom prva dva bajta datoteke. Prvi bajt trebao bi biti veliko slovo ‘M’, a drugi bajt bit će broj verzije, koji bi se trebao podudarati s gornjom verzijom sustava. Ako se ne podudara, ponovno izgradite .mpy datoteku.

  • Provjerite podudara li se .mpy verzija sustava s verzijom koju emitira mpy-cross korišten za izgradnju .mpy datoteke, koja se pronalazi pomoću mpy-cross --version. Ako se ne podudara, ponovno prevedite mpy-cross iz Git repozitorija preuzetog na oznaci (ili hashu) koju prijavljuje mpy-cross --version.

  • Provjerite koristite li ispravne mpy-cross zastavice, pronađene pomoću gornjeg koda ili pregledom Makefile varijable MPY_CROSS_FLAGS za port koji koristite.

  • Ako treći bajt .mpy datoteke ima postavljen bit #6, tada provjerite je li kodirani vuint zastavica specifičnih za arhitekturu kompatibilan s ciljem na koji uvozite datoteku.

Sljedeća tablica prikazuje korespondenciju između MicroPython izdanja i .mpy verzije.

MicroPython izdanje

.mpy verzija

v1.23.0 i novije

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

Radi potpunosti, sljedeća tablica prikazuje Git commit glavnog MicroPython repozitorija u kojem je promijenjena .mpy verzija.

promjena .mpy verzije

Git commit

6.2 na 6.3

bdbc869f9ea200c0d28b2bc7bfb60acd9d884e1b

6.1 na 6.2

6967ff3c581a66f73e9f3d78975f47528db39980

6 na 6.1

d94141e1473aebae0d3c63aeaa8397651ad6fa01

5 na 6

f2040bfc7ee033e48acef9f289790f3b4e6b74e5

4 na 5

5716c5cf65e9b2cb46c2906f40302401bdd27517

3 na 4

9a5f92ea72754c01cc03e5efcdfe94021120531e

2 na 3

ff93fd4f50321c6190e1659b19e64fef3045a484

1 na 2

dd11af209d226b7d18d5148b239662e30ed60bad

0 na 1

6a11048af1d01c78bdacddadd1b72dc7ba7c6478

početna verzija 0

d8c834c95d506db979ec871417de90b7951edc30

Binarno kodiranje .mpy datoteka

MicroPython .mpy datoteke binarni su spremnički format s objektima koda (bytecode i izvorni strojni kod) pohranjenima interno u ugniježđenoj hijerarhiji. Kod za vanjski modul pohranjuje se prvi, a zatim slijede njegova djeca. Svako dijete može imati daljnju djecu, na primjer u slučaju klase koja ima metode ili funkcije koja definira lambdu ili razumijevanje. Kako bi datoteke ostale male, a istovremeno omogućile širok raspon mogućih vrijednosti, na mnogim mjestima koristi koncept varijabilno kodiranog nepredznačenog cijelog broja (vuint). Slično UTF-8 kodiranju, ovo kodiranje pohranjuje 7 bitova po bajtu, pri čemu je 8. bit (MSB) postavljen ako slijedi jedan ili više bajtova. Bitovi nepredznačenog cijelog broja pohranjuju se u vuint u LSB obliku.

Najviša razina .mpy datoteke sastoji se od tri dijela:

  • Zaglavlje.

  • Globalne tablice qstr i konstanti.

  • Sirovi kod za vanjski opseg modula. Taj se vanjski opseg izvršava kada se .mpy datoteka uveze.

Sadržaj .mpy datoteke možete pregledati pomoću mpy-tool.py, na primjer (pokrenuto iz korijena glavnog MicroPython repozitorija):

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

Zaglavlje

.mpy zaglavlje je:

veličina

polje

bajt

vrijednost 0x4d (ASCII ‘M’)

bajt

.mpy broj glavne verzije

bajt

zastavice značajki, izvorna arhitektura, broj sporedne verzije (u starijim verzijama bile su zastavice značajki)

bajt

broj bitova u malom cijelom broju

Treći bajt podijeljen je kako slijedi (prvo MSB):

bit

značenje

7

rezervirano, mora biti 0

6

vuint zastavica specifičnih za arhitekturu slijedi nakon zaglavlja

5..2

broj izvorne arhitekture

1..0

broj sporedne verzije

Zastavice specifične za arhitekturu

Ako je postavljen bit #6 bajta zastavica značajki u zaglavlju, tada će nakon zaglavlja slijediti vuint koji sadrži opcionalne informacije specifične za arhitekturu. Sadržaj ovog cijelog broja ovisi o tome za koju je izvornu arhitekturu datoteka namijenjena.

Ovo se trenutno koristi za pohranu toga koja RISC-V procesorska proširenja MPY datoteka treba za ispravan rad osim I, M, C i Zicsr. Različite varijante ArmV7 identificiraju se svojim brojem izvorne arhitekture, ali ponovno korištenje tog mehanizma zakompliciralo bi stvari za RV32 i RV64.

MPY datoteke namijenjene za RV32 ili RV64 koje ne trebaju nikakva posebna procesorska proširenja ne moraju pružati cijeli broj zastavica (uz postavljanje odgovarajućeg bita u zaglavlju). Nepostojanje vrijednosti zastavica za RV32 i RV64 MPY datoteke koristi se za označavanje da nisu potrebna nikakva posebna proširenja te štedi jedan bajt u konačnom izlaznom binarnom programu.

Vidi također opciju naredbenog retka -march-flags u mpy-tool.py i mpy-cross, te opciju naredbenog retka --arch-flags u mpy_ld.py za postavljanje ove vrijednosti pri stvaranju MPY datoteka.

Globalne tablice qstr i konstanti

.mpy datoteka sadrži jednu qstr tablicu i jednu tablicu konstantnih objekata. One su globalne za .mpy datoteku, referenciraju ih svi ugniježđeni objekti sirovog koda. qstr tablica preslikava interni qstr broj (interni za .mpy datoteku) na razriješeni qstr broj izvršnog okruženja u koje se .mpy datoteka uvozi. Time se .mpy datoteka povezuje s ostatkom sustava unutar kojeg se izvršava. Tablica konstantnih objekata popunjava se referencama na sve konstantne objekte koji su .mpy datoteci potrebni.

veličina

polje

vuint

broj qstr-ova

vuint

broj konstantnih objekata

qstr podaci

kodirani konstantni objekti

Elementi sirovog koda

Element sirovog koda sadrži kod, bilo bytecode ili izvorni strojni kod. Njegov sadržaj je:

veličina

polje

vuint

tip, veličina i postoje li podelementi sirovog koda

kod (bytecode ili strojni kod)

vuint

broj podelemenata sirovog koda (samo ako je različit od nule)

podelementi sirovog koda

Prvi vuint u elementu sirovog koda kodira tip koda pohranjenog u ovom elementu (dva najmanje značajna bita), ima li ovaj sirovi kod djece (treći najmanje značajan bit) i duljinu koda koji slijedi (količinu RAM-a koju treba dodijeliti za njega).

Nakon vuinta dolazi sam kod. Osim ako je tip koda viper kod s relokacijama, taj je kod konstantni podatak i ne treba ga mijenjati.

Ako ovaj sirovi kod ima djece (kako je naznačeno bitom u prvom vuintu), nakon koda dolazi vuint koji broji podelemente sirovog koda.

Konačno se rekurzivno pohranjuju svi podelementi sirovog koda.