MicroPython .mpy dosyaları

MicroPython, önceden derlenmiş kodu barındıran ve normal bir .py modülü gibi içe aktarılabilen ikili bir kapsayıcı dosya biçimi olan .mpy dosyası kavramını tanımlar. foo.mpy dosyası, import foo ile içe aktarılabilir; yeter ki foo.mpy içe aktarma mekanizması tarafından her zamanki gibi bulunabilsin. Genellikle sys.path içinde listelenen her dizin sırayla aranır. Belirli bir dizin aranırken önce foo.py aranır, bulunamazsa ardından foo.mpy aranır, ikisi de bulunamazsa arama bir sonraki dizinde devam eder. Bu nedenle foo.py, foo.mpy dosyasına göre önceliklidir.

Bu .mpy dosyaları, genellikle mpy-cross programı aracılığıyla Python kaynak dosyalarından (.py dosyaları) üretilen bytecode içerebilir. Bazı mimariler için bir .mpy dosyası, çeşitli yollarla, özellikle de C kaynak kodundan üretilebilen yerel makine kodu da içerebilir.

mpy-cross derleyicisi

mpy-cross, bir .py kaynak dosyasını kamera üzerinde içe aktarılmaya hazır bir .mpy ikili kapsayıcıya dönüştüren çapraz derleyicidir. MicroPython kaynak ağacının (kameranın aygıt yazılımını oluşturmak için kullanılan ağacın) bir parçasıdır ve ayrıca tam bir aygıt yazılımı kopyası olmadan ana makine tarafında kullanım için bir pip paketi olarak da yayımlanır:

$ pip install --user mpy-cross

Veya pipx aracılığıyla:

$ pipx install mpy-cross

Kurulduktan sonra, tek bir kaynak dosya üzerinde çalıştırın:

$ mpy-cross foo.py

Bu, geçerli dizinde foo.mpy dosyasını üretir; diğer modüllerin yanına kameranın dosya sistemine kopyalanmaya veya bir ROMFS imajına beslenmeye hazırdır.

En kullanışlı komut satırı seçenekleri:

  • -o <path> – üretilen .mpy için çıktı yolu (varsayılan olarak uzantısı değiştirilmiş giriş dosya adıdır; -o - stdout’a yazar).

  • -O<n>0 ile 3 arası optimizasyon düzeyi. Varsayılan 0 değeri assertion’ları ve tam kaynak konumlarını korur; 3 assertion’ları ve docstring’leri kaldırır ve if __debug__ bloklarını yeniden yazar. Düzey, çalışma zamanının sunduğu aynı micropython.opt_level yüzeyini denetler.

  • -march=<arch>@native ve @viper ile dekore edilmiş fonksiyonlar için hedef yerel mimari. Kaynak bu dekoratörleri kullandığında gereklidir. Değer kameranın MCU sınıfıyla eşleşmelidir: mpy-cross --help çıktısındaki listeden seçin veya çalışma zamanında sys.implementation._mpy ile kameradan okuyun.

  • -s <path>.mpy dosyasının hata ayıklama bilgisine gömülen kaynak yolu dizesi. Diskteki yol, dosyanın geri izlemelerde görünmesi gereken içe aktarma yolundan farklı olduğunda kullanışlıdır.

  • -X emit=bytecode|native|viper – tüm modül için varsayılan yayıcıyı seçer (@native / @viper dekoratörlerine fonksiyon başına bir alternatif).

  • --version – bu ikili dosyanın yaydığı .mpy biçim sürümünü yazdırır. Bu sayı, kameranın çalışma zamanının desteklediği sürümle (aşağıdaki sürüm tablosuna bakın) eşleşmelidir, aksi takdirde içe aktarma ValueError('incompatible .mpy file') hatası verir.

Tüm bayrak listesi için mpy-cross --help komutunu çalıştırın.

pip paketi ayrıca, derleme betiklerinin derleyiciyi elle bir alt süreç çatallamak yerine süreç içinde çalıştırabilmesi için küçük bir Python modül API’si de sunar:

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 ve mpy_cross.mpy_version üç giriş noktasıdır; bir şeyler ters gittiğinde mpy_cross.CrossCompileError derleyicinin stderr çıktısını taşır. Mimari sabitleri (NATIVE_ARCH_ARMV7EMSP, NATIVE_ARCH_ARMV7EMDP vb.), -march bayrağının kabul ettiği dizelerle eşleşir.

.mpy dosyalarının sürümlemesi ve uyumluluğu

Belirli bir .mpy dosyası, belirli bir MicroPython sistemiyle uyumlu olabilir veya olmayabilir. Uyumluluk aşağıdakilere dayanır:

  • .mpy dosyasının sürümü: dosyanın sürümü, onu yükleyen sistemin desteklediği sürümle eşleşmelidir.

  • .mpy dosyasının alt sürümü: .mpy dosyası yerel makine kodu içeriyorsa, dosyanın alt sürümü onu yükleyen sistemin desteklediği sürümle eşleşmelidir. Aksi takdirde, .mpy dosyasında yerel makine kodu yoksa, yükleme sırasında alt sürüm yok sayılır.

  • Küçük tamsayı bitleri: .mpy dosyası bir small integer içinde minimum sayıda bit gerektirir ve onu yükleyen sistem en az bu kadar biti desteklemelidir.

  • Yerel mimari: .mpy dosyası yerel makine kodu içeriyorsa, bu makine kodunun mimarisini belirtir ve onu yükleyen sistem o mimarinin kodunun yürütülmesini desteklemelidir.

Bir MicroPython sistemi .mpy dosyalarını içe aktarmayı destekliyorsa, sys.implementation._mpy alanı mevcut olur ve sürümü (alt 8 bit), özellikleri ve yerel mimariyi kodlayan bir tamsayı döndürür.

İlk dört testten birini geçemeyen bir .mpy dosyasını içe aktarmaya çalışmak ValueError('incompatible .mpy file') hatası verir. Yerel mimari testini geçemeyen (yerel makine kodu içeriyorsa) bir .mpy dosyasını içe aktarmaya çalışmak ValueError('incompatible .mpy arch') hatası verir.

Bir .mpy dosyasını içe aktarma başarısız olursa şunları deneyin:

  • MicroPython sisteminizin desteklediği .mpy sürümünü ve bayraklarını şunu çalıştırarak belirleyin:

    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()
    
  • .mpy dosyasının geçerliliğini, dosyanın ilk iki baytını inceleyerek kontrol edin. İlk bayt büyük harf ‘M’ olmalı, ikinci bayt ise yukarıdaki sistem sürümüyle eşleşmesi gereken sürüm numarası olmalıdır. Eşleşmiyorsa .mpy dosyasını yeniden oluşturun.

  • Sistem .mpy sürümünün, .mpy dosyasını oluşturmak için kullanılan mpy-cross tarafından yayılan sürümle (mpy-cross --version ile bulunur) eşleşip eşleşmediğini kontrol edin. Eşleşmiyorsa, mpy-cross --version tarafından bildirilen etikette (veya hash’te) çıkış yapılmış Git deposundan mpy-cross derleyicisini yeniden derleyin.

  • Yukarıdaki kod aracılığıyla veya kullandığınız port için MPY_CROSS_FLAGS Makefile değişkenini inceleyerek bulunan doğru mpy-cross bayraklarını kullandığınızdan emin olun.

  • .mpy dosyasının üçüncü baytında #6 bit ayarlıysa, kodlanmış mimariye özgü bayrak bitleri vuint’inin, dosyayı içe aktardığınız hedefle uyumlu olup olmadığını kontrol edin.

Aşağıdaki tablo, MicroPython sürümü ile .mpy sürümü arasındaki karşılığı gösterir.

MicroPython sürümü

.mpy sürümü

v1.23.0 ve üzeri

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

Bütünlük açısından, bir sonraki tablo .mpy sürümünün değiştirildiği ana MicroPython deposunun Git commit’ini gösterir.

.mpy sürüm değişikliği

Git commit

6.2’den 6.3’e

bdbc869f9ea200c0d28b2bc7bfb60acd9d884e1b

6.1’den 6.2’ye

6967ff3c581a66f73e9f3d78975f47528db39980

6’dan 6.1’e

d94141e1473aebae0d3c63aeaa8397651ad6fa01

5’ten 6’ya

f2040bfc7ee033e48acef9f289790f3b4e6b74e5

4’ten 5’e

5716c5cf65e9b2cb46c2906f40302401bdd27517

3’ten 4’e

9a5f92ea72754c01cc03e5efcdfe94021120531e

2’den 3’e

ff93fd4f50321c6190e1659b19e64fef3045a484

1’den 2’ye

dd11af209d226b7d18d5148b239662e30ed60bad

0’dan 1’e

6a11048af1d01c78bdacddadd1b72dc7ba7c6478

ilk sürüm 0

d8c834c95d506db979ec871417de90b7951edc30

.mpy dosyalarının ikili kodlaması

MicroPython .mpy dosyaları, kod nesnelerinin (bytecode ve yerel makine kodu) iç içe geçmiş bir hiyerarşide dahili olarak depolandığı ikili bir kapsayıcı biçimidir. Önce dış modülün kodu depolanır, ardından alt öğeleri gelir. Her alt öğenin başka alt öğeleri olabilir; örneğin metotları olan bir sınıf veya lambda ya da comprehension tanımlayan bir fonksiyon durumunda. Dosyaları küçük tutarken yine de geniş bir olası değer aralığı sağlamak için birçok yerde değişken-kodlanmış-işaretsiz-tamsayı (vuint) kavramını kullanır. UTF-8 kodlamasına benzer şekilde, bu kodlama bayt başına 7 bit depolar ve bir veya daha fazla bayt takip ediyorsa 8. bit (MSB) ayarlanır. İşaretsiz tamsayının bitleri vuint içinde LSB biçiminde depolanır.

Bir .mpy dosyasının en üst düzeyi üç bölümden oluşur:

  • Başlık.

  • Genel qstr ve sabit tabloları.

  • Modülün dış kapsamı için ham kod. Bu dış kapsam, .mpy dosyası içe aktarıldığında yürütülür.

Bir .mpy dosyasının içeriğini mpy-tool.py kullanarak inceleyebilirsiniz; örneğin (ana MicroPython deposunun kök dizininden çalıştırılır):

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

Başlık

.mpy başlığı şöyledir:

boyut

alan

bayt

değer 0x4d (ASCII ‘M’)

bayt

.mpy ana sürüm numarası

bayt

özellik bayrakları, yerel mimari, alt sürüm numarası (eski sürümlerde özellik bayraklarıydı)

bayt

küçük bir int’teki bit sayısı

Üçüncü bayt şu şekilde bölünür (önce MSB):

bit

anlam

7

ayrılmış, 0 olmalı

6

başlığın ardından mimariye özgü bir bayraklar vuint’i gelir

5..2

yerel mimari numarası

1..0

alt sürüm numarası

Mimariye özgü bayraklar

Başlığın özellik bayrakları baytının #6 biti ayarlıysa, başlığın ardından isteğe bağlı mimariye özgü bilgi içeren bir vuint gelir. Bu tamsayının içeriği, dosyanın hangi yerel mimari için tasarlandığına bağlıdır.

Bu şu anda, MPY dosyasının I, M, C ve Zicsr dışında doğru çalışmak için hangi RISC-V işlemci uzantılarına ihtiyaç duyduğunu depolamak için kullanılır. ArmV7’nin farklı türevleri yerel mimari numaralarıyla tanımlanır, ancak bu mekanizmayı yeniden kullanmak RV32 ve RV64 için işleri karmaşıklaştırır.

Herhangi bir özel işlemci uzantısına ihtiyaç duymayan RV32 veya RV64 hedefli MPY dosyalarının bir bayraklar tamsayısı sağlaması gerekmez (başlıkta uygun biti ayarlamanın yanı sıra). RV32 ve RV64 MPY dosyaları için bir bayraklar değerinin bulunmaması, belirli uzantıların gerekmediğini belirtmek için kullanılır ve son çıktı ikili dosyasında bir bayt tasarruf sağlar.

MPY dosyaları oluştururken bu değeri ayarlamak için hem mpy-tool.py hem de mpy-cross içindeki -march-flags komut satırı seçeneğine ve mpy_ld.py içindeki --arch-flags komut satırı seçeneğine de bakın.

Genel qstr ve sabit tabloları

Bir .mpy dosyası tek bir qstr tablosu ve tek bir sabit nesne tablosu içerir. Bunlar .mpy dosyasına geneldir ve tüm iç içe geçmiş ham kod nesneleri tarafından başvurulur. qstr tablosu, dahili qstr numarasını (.mpy dosyasına dahili) .mpy dosyasının içe aktarıldığı çalışma zamanının çözümlenmiş qstr numarasıyla eşler. Bu, .mpy dosyasını içinde yürütüldüğü sistemin geri kalanıyla bağlar. Sabit nesne tablosu, .mpy dosyasının ihtiyaç duyduğu tüm sabit nesnelere yapılan başvurularla doldurulur.

boyut

alan

vuint

qstr sayısı

vuint

sabit nesne sayısı

qstr verisi

kodlanmış sabit nesneler

Ham kod öğeleri

Bir ham kod öğesi, bytecode veya yerel makine kodu olmak üzere kod içerir. İçeriği şöyledir:

boyut

alan

vuint

tür, boyut ve alt ham kod öğelerinin olup olmadığı

kod (bytecode veya makine kodu)

vuint

alt ham kod öğelerinin sayısı (yalnızca sıfır değilse)

alt ham kod öğeleri

Bir ham kod öğesindeki ilk vuint, bu öğede depolanan kodun türünü (en düşük anlamlı iki bit), bu ham kodun herhangi bir alt öğesi olup olmadığını (en düşük anlamlı üçüncü bit) ve ardından gelen kodun uzunluğunu (bunun için ayrılacak RAM miktarı) kodlar.

vuint’in ardından kodun kendisi gelir. Kod türü yer değiştirmeli (relocation) viper kodu olmadığı sürece, bu kod sabit veridir ve değiştirilmesi gerekmez.

Bu ham kodun herhangi bir alt öğesi varsa (ilk vuint’teki bir bitle belirtildiği gibi), kodun ardından alt ham kod öğelerinin sayısını sayan bir vuint gelir.

Son olarak, varsa tüm alt ham kod öğeleri özyinelemeli olarak depolanır.