14.2.2.1. การแช่แข็งสคริปต์ลงในเฟิร์มแวร์#

โมดูล frozen คือไฟล์ .py ที่ถูกคอมไพล์เป็น bytecode และลิงก์เข้าไปในเฟิร์มแวร์ image ณ เวลาสร้าง (build time) รันไทม์จะนำเข้าโมดูล frozen โดยตรงจากแฟลช โดยไม่ต้องค้นหาจากระบบไฟล์บนดิสก์เลย สำหรับผลิตภัณฑ์ที่จัดส่งแล้ว นี่คือตำแหน่งที่เหมาะสมสำหรับโค้ดแอปพลิเคชัน: ผู้ใช้ปลายทางไม่สามารถลบออกได้ ไฟล์ .py เก่าบน SD card ไม่สามารถแทนที่ได้ และกล้องจะรันโค้ดเดิมทุกครั้งที่บูต ไม่ว่าไดรฟ์จะมีอะไรอยู่หรือไม่ก็ตาม

หน้านี้อธิบายลำดับการเริ่มต้น (startup sequence) ที่กล้องปฏิบัติตาม รวมถึงวิธีที่ manifest.py และคำสั่ง freeze ฝังแอปพลิเคชันเข้าสู่การสร้าง

14.2.2.1.1. ลำดับการเริ่มต้น#

สิ่งที่รันและเมื่อไหร่ เมื่อกล้องออกจาก reset:

  • บูตโหลดเดอร์ การเปิดเครื่องจะเข้าสู่ช่วง DFU สั้น ๆ ที่ IDE ใช้ในการส่งอัปเดตเฟิร์มแวร์ ช่วงนี้จะปิดลงหลังจากไม่กี่วินาที และบูตโหลดเดอร์จะส่งต่อให้ MicroPython สคริปต์ที่รันอยู่สามารถกลับเข้าสู่ช่วงนี้ได้ตามต้องการโดยเรียก machine.bootloader()

  • การเริ่มต้นระบบไฟล์ frozen ก่อนที่โค้ดแอปพลิเคชันใด ๆ จะรัน รันไทม์จะเตรียมระบบไฟล์ให้พร้อม โดยแฟลชภายในถูกเมาท์ที่ /flash (และฟอร์แมตว่างเปล่าหากไม่มีข้อมูลอยู่) หาก SD card มีอยู่ และ ไม่มีไฟล์เครื่องหมาย SKIPSD บนแฟลชภายใน SD card จะถูกเมาท์ที่ /sdcard ROMFS เมื่อการสร้างรวมไว้ จะถูกเมาท์อัตโนมัติที่ /rom ไดเรกทอรีทำงานถูกตั้งเป็นไดเรกทอรีบูต (/sdcard หาก card เมาท์แล้ว หรือ /flash หากไม่ได้เมาท์) และ sys.path จะถูกเติมด้วย /flash, /flash/lib, /sdcard, /sdcard/lib, /rom, และ /rom/lib การตั้งค่าที่อยู่ในแฟลชจัดการโดยโมดูล frozen ที่ชื่อ _boot.py -- เป็นโครงสร้างพื้นฐานของ port และ board ไม่ใช่ hook ของแอปพลิเคชัน แอปพลิเคชันไม่ควรปรับแต่ง _boot.py; การสร้างเป็นผู้ดูแล การวาง SKIPSD ลงบนแฟลชจาก IDE คือวิธีที่รองรับเพื่อให้กล้องบูตจากแฟลชภายในแทน SD card

  • การตั้งค่าก่อน REPL boot.py รันทุก soft reset -- การบูตเย็น, Ctrl-D จาก REPL, เมื่อสคริปต์ที่รันคืนค่า, และการกู้คืนจาก watchdog -- ก่อนที่ REPL จะสามารถเข้าถึงได้ หน้าที่คือเตรียมสภาพแวดล้อมที่ระบบส่วนที่เหลือใช้: การตั้งค่าประเภทที่ REPL, แอปพลิเคชัน, และเครื่องมือกู้คืนต้องการให้พร้อมใช้งาน ไม่ใช่ที่อยู่ของแอปพลิเคชันเอง main.py คือจุดเริ่มต้นของแอปพลิเคชัน

  • ลูปหลัก main.py คือลูปหลักของแอปพลิเคชัน รันครั้งเดียวเมื่อบูตเย็น หลังจาก boot.py ทันที ไม่ รันซ้ำเมื่อ soft reset ในภายหลัง -- กล้องจะตกลงสู่ REPL แทน ความไม่สมมาตรนี้มีความสำคัญในการพัฒนา (Ctrl-D จะตกลงสู่ REPL โดยไม่รันลูปซ้ำ ทำให้นักพัฒนาตรวจสอบสถานะได้) แต่ไม่สำคัญในการผลิต: กล้องที่ใช้งานจริงรับ power-on, watchdog, และ hard reset ซึ่งล้วนเป็น hardware reset ที่เข้าสู่เส้นทางบูตเย็นและรัน main.py อีกครั้ง

14.2.2.1.2. การแช่แข็งเข้าสู่เฟิร์มแวร์#

ชุดโมดูล frozen ของบอร์ดถูกประกาศใน boards/<TARGET>/manifest.py ในต้นไม้เฟิร์มแวร์ manifest คือไฟล์ Python ขนาดเล็กที่เรียกใช้คำสั่งไม่กี่อย่าง:

  • freeze("$(OMV_LIB_DIR)/", "foo.py") -- ฝัง foo.py ไฟล์เดียวเข้าสู่การสร้าง

  • package("mylib", base_path="...") -- ฝัง Python package หลายไฟล์โดยรักษาโครงสร้างไดเรกทอรีภายใต้ base path ที่กำหนด

  • include("...") -- ดึงไฟล์ manifest อื่นเข้ามา บอร์ด manifests ใช้สิ่งนี้เพื่อแบ่งปันชุดโมดูลร่วมกัน

  • require("logging") -- ดึงโมดูล micropython-lib upstream ที่มีชื่อมาตามชื่อ

manifest แอปพลิเคชันขั้นต่ำจะเพิ่มบรรทัด freeze หนึ่งบรรทัดต่อสคริปต์ระดับบนสุด และบรรทัด package หนึ่งบรรทัดต่อ package ที่แอปพลิเคชันพึ่งพา

14.2.2.1.2.1. ตำแหน่งของซอร์สโค้ด#

ซอร์สโค้ดแอปพลิเคชันอยู่ใน scripts/libraries/ ในต้นไม้เฟิร์มแวร์ ข้างๆ โมดูลที่การสร้างแช่แข็งไว้แล้ว ตัวแปร manifest $(OMV_LIB_DIR) ขยายไปยังเส้นทางนั้น ทำให้รายการ manifest สั้น การแก้ไข manifest เป็นการดำเนินการภายใน tree อยู่แล้ว ดังนั้นการเก็บซอร์สใน tree ช่วยหลีกเลี่ยงการจัดการ repo โปรเจกต์แยกต่างหากในการแก้ไขเส้นทาง

โครงสร้างทั่วไปสำหรับแอปพลิเคชันที่จัดส่ง main.py ไฟล์เดียวพร้อม package สนับสนุน:

scripts/libraries/
    main.py
    my_lib/
        __init__.py
        helpers.py

และใน boards/<TARGET>/manifest.py ของบอร์ด บรรทัด freeze หนึ่งบรรทัดสำหรับสคริปต์ และบรรทัด package หนึ่งบรรทัดสำหรับ package:

freeze("$(OMV_LIB_DIR)/", "main.py")
package("my_lib", base_path="$(OMV_LIB_DIR)/my_lib")

สคริปต์ไฟล์เดียว -- main.py ที่นี่ แต่กฎเดียวกันใช้กับ boot.py หรือ helper แบบ standalone ใด ๆ -- ใช้ freeze Package หลายไฟล์ใช้ package การเพิ่มสคริปต์อื่นคือบรรทัด freeze อีกหนึ่งบรรทัด; การเพิ่ม package อื่นคือบรรทัด package อีกหนึ่งบรรทัด

14.2.2.1.2.2. การสร้างและการแฟลช#

เมื่อ manifest พร้อมแล้ว ให้สร้างเฟิร์มแวร์ตามที่ บทเฟิร์มแวร์ อธิบายไว้:

make -j$(nproc) -C lib/micropython/mpy-cross   # once, builds the cross-compiler
make -j$(nproc) TARGET=<TARGET>                # builds the firmware

ผลลัพธ์จะอยู่ใน build/<TARGET>/bin/

build/<TARGET>/bin/
    firmware.bin     # flash through the IDE
    romfs0.img       # flash through the IDE in a separate step

การแฟลช .bin และ .img ผ่าน IDE จะทำให้กล้องมีแอปพลิเคชันที่เป็นส่วนหนึ่งของการสร้าง

ลำดับการเริ่มต้นข้างต้นคือสิ่งที่ทำให้การฝังมีประสิทธิภาพ: รันไทม์แก้ไข boot.py และ main.py ไปยังสำเนา frozen ก่อนที่จะตรวจสอบระบบไฟล์ ดังนั้นกล้องที่จัดส่งจะรันโค้ดของการสร้างแม้ว่า SD card จะมี boot.py เก่าจากการพัฒนาก็ตาม

14.2.2.1.2.3. ลำดับการค้นหา#

ความหมายของการแทนที่ แตกต่างกัน สำหรับเส้นทางการรัน boot.py / main.py และสำหรับคำสั่ง import ทั่วไป การรู้ว่าสิ่งใดเป็นอะไรมีความสำคัญทั้งในการผลิตและการพัฒนา:

  • สำหรับ boot.py และ main.py: รันไทม์ค้นหาสำเนา frozen ก่อน แล้วจึงค้นหาระบบไฟล์ boot.py frozen ไม่สามารถถูกแทนที่โดยการวางบน SD card ได้ -- ผู้ที่ถือกล้องไม่สามารถเปลี่ยน entry point ได้โดยไม่ต้องแฟลชใหม่

  • สำหรับ import foo: รันไทม์ค้นหา sys.path ก่อน -- ซึ่งครอบคลุม /flash, /sdcard, /rom, และไดเรกทอรีย่อย lib ของแต่ละ -- จากนั้นค้นหาโมดูล frozen foo.py ที่มีชื่อเดียวกันบนแฟลชหรือ SD จะ แทนที่ foo frozen การนี้คือความสะดวกในการพัฒนา: วางโมดูลที่แก้ไขแล้วบนการ์ด soft-reset แล้วเห็นการเปลี่ยนแปลงโดยไม่ต้องแฟลชใหม่

ผลิตภัณฑ์ที่จัดส่งที่ต้องการปิดกั้นพฤติกรรมที่ระบบไฟล์แทนที่ frozen สำหรับ imports สามารถล้าง sys.path ตั้งแต่ต้นใน boot.py

import sys

sys.path.clear()

เมื่อ sys.path ว่างเปล่า imports ทั้งหมดจะแก้ไขจากโมดูล frozen เท่านั้น ไม่มีสิ่งใดบนแฟลช, SD, หรือ ROMFS สามารถบดบังได้

14.2.2.1.2.4. ปัญหา asset#

การแช่แข็งเหมาะสำหรับโค้ด แต่ ไม่เหมาะ สำหรับ asset ไบนารีขนาดใหญ่: ไฟล์โมเดล machine learning, ตารางป้ายกำกับ, การกำหนดค่า JSON, เทมเพลตภาพ การฝังสิ่งเหล่านี้เป็น Python literals จะทำให้ซอร์สบวมขึ้น คอมไพล์ช้า และเสียพื้นที่ bytecode container บนข้อมูลที่ interpreter จะต้องอ่านดิบอยู่ดี หน้า การสร้าง ROMFS image อธิบาย read-only flash filesystem ที่เติมเต็มช่องว่างนี้