news 2026/9/21 3:36:54

MicroPython QEMU 移植(ports/qemu)完全指南:无硬件驱动的跨架构仿真、CI 与调试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MicroPython QEMU 移植(ports/qemu)完全指南:无硬件驱动的跨架构仿真、CI 与调试
  • 嵌入式
  • 语言运行时
  • 编程语言
  • 解释器
  • 编译器
  • 物联网
  • 系统编程

【免费下载链接】micropython

MicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems

项目地址:https://gitcode.com/gh_mirrors/mi/micropython
点击查看免费下载

本指南以 ports/qemu/README.md 为骨架,结合 ports/qemu/Makefile、ports/qemu/main.c 等仓库源码,系统讲解 MicroPython 官方 QEMU 移植的用途、工具链依赖、构建方式、REPL 与测试运行方法及全部可调 make 选项。读完你将掌握:无需任何真实 MCU 与 JTAG,即可在 QEMU 上为 ARM Cortex-M、RISC-V RV32/RV64、PowerPC 64 架构构建并运行 MicroPython 固件,跑通官方测试套件与原生模块(natmod)测试,并接入 gdb 进行无硬件调试。

一、这个移植是做什么的

ports/qemu是 MicroPython 仓库中一个实验性、由社区维护的移植,其目标平台不是某块真实开发板,而是 QEMU(Quick Emulator)模拟的虚拟机器。它覆盖三类处理器架构:

  • ARM(Cortex-M 系列,如 Cortex-M3)
  • RISC-V(RV32IMC / RV64IMC)
  • PowerPC 64(little-endian)

README 明确给出了这一移植的三个核心用途,这也是它与其它端口最本质的区别:

  1. 持续集成(CI)——在虚拟机上直接运行针对代码库中架构相关部分的测试。仓库的 tools/ci.sh 中可以看到完整的 CI 编排:ci_qemu_build_arm_sabreliteci_qemu_build_rv32ci_qemu_build_rv64等函数分别对SABRELITEVIRT_RV32VIRT_RV64板执行test_fulltesttest_natmod,覆盖了大小端(MP_ENDIANNESS_BIG)、硬浮点/软浮点(armv6m/armv7m/armv7emsp/armv7emdp)等多种组合。
  2. 实验与原型验证——任何包含架构相关代码的部分都可以先仿真验证,例如研究某条指令集特性对 MicroPython 或某个模块的优化影响。
  3. 轻量化调试——不需要 JTAG,不需要真实 MCU 芯片,也不需要 OpenOCD,省去了接线、按键等一切硬件调试的繁琐环节。

二、构建前的工具链依赖

不同架构的板卡需要对应的交叉编译工具链。

ARM(QEMU_ARCH=arm

需要裸机 ARM 工具链,典型为arm-none-eabi-gcc。对应 Makefile 中CROSS_COMPILE ?= arm-none-eabi-的设置。

RISC-V 32(QEMU_ARCH=riscv32

  • 需要GCC 10 或更新版本的裸机 RISC-V 工具链;
  • 要求支持 multilib,或专门的 32 位工具链;
  • 必须支持M、C、Zicsr扩展,ABI 为ilp32
  • newlib 与 picolibc 均可,优先使用 picolibc(存在时自动选择)。

一个容易踩坑的细节:GCC 10 及更早版本不识别架构名中的Zicsr扩展,因此 Makefile 中会根据GCC_VERSION自动切换:

RV32_ARCH ?= rv32imac # GCC <= 10 RV32_ARCH ?= rv32imac_zicsr # 新版本 GCC

大多数发行版包管理器提供的预编译工具链(或 xPack 等独立分发的riscv-none-elf-gcc)开箱即用。

RISC-V 64(QEMU_ARCH=riscv64

要求与 RV32 类似,但 ABI 为lp64、扩展为M、C、Zicsr。README 给出了一个自检技巧:对交叉编译器执行gcc -v(仅此一个参数),如果输出中看不到-mcmodel=medany相关说明,则该工具链很可能无法用于本移植

原因在于链接地址的约束:QEMU 的VIRT_RV64板代码地址空间从0x80000000开始,而一些工具链自带的libclibmlibgcc无法被放置到超过0x7FFFF7FF的偏移处,一旦构建过程引入这些库代码就会失败。README 直言“市面上很多现成的 RISC-V 工具链在这方面都是坏的”。

针对这一痛点,仓库提供了Docker 容器镜像,内置与 MicroPython RV32/RV64 CI 任务相同、已知可用的编译器(见 Dockerfile.riscv,基于 Ubuntu 24.04,安装gcc-riscv64-unknown-elfpicolibc-riscv64-unknown-elfqemu-system及 Python 测试依赖)。构建并运行测试:

cd $MICROPYTHON_SOURCE_ROOT/ports/qemu docker build -t micropython/mpy-qemu-riscv -f Dockerfile.riscv .
cd $MICROPYTHON_SOURCE_ROOT docker container run -v .:/micropython --rm -it micropython/mpy-qemu-riscv make -C ports/qemu -- BOARD=VIRT_RV64 test

使用 Podman 时把docker换成podman即可。将VIRT_RV64换成VIRT_RV32即可运行 32 位测试。

PowerPC 64(QEMU_ARCH=ppc64

需要面向 Linux PowerPC 64、小端(little-endian)输出的工具链,典型为powerpc64le-linux-gnu-gcc,对应 Makefile 中的CROSS_COMPILE ?= powerpc64le-linux-gnu-

三、构建步骤

在 ports/qemu 目录下执行。第一步先构建 MicroPython 交叉编译器 mpy-cross(用于冻结模块与.mpy文件生成):

$ make -C ../../mpy-cross

然后构建固件:

$ make

默认板卡是mps2-an385(Cortex-M3)。选择其它板卡时通过BOARD参数指定:

$ make BOARD=SABRELITE

可用板卡一览

README 给出的官方板卡对照表如下(BOARD=值对应 boards 目录下的子目录,每个子目录含一个mpconfigboard.mk定义QEMU_ARCHQEMU_MACHINE等关键参数):

BOARD=名称架构对应的 QEMU 机器
MICROBITarmmicrobit
MPS2_AN385armmps2-an385
MPS2_AN500armmps2-an500
NETDUINO2armnetduino2
POWERNV9ppc64powernv9
SABRELITEarmsabrelite
VIRT_RV32riscv32virt
VIRT_RV64riscv64virt

从源码结构看,boards 目录下还额外包含MPS3_AN547(arm),README 表格虽未列出,但它同样可作为BOARD=MPS3_AN547使用,且 uart.c 中为其单独实现了 MPS3 外设寄存器映射(UART 基址0x49303000)。

各板卡的关键差异(源码佐证)

以默认板 boards/MPS2_AN385/mpconfigboard.mk 为例,它定义了:

  • QEMU_ARCH = armQEMU_MACHINE = mps2-an385
  • 编译参数-mthumb -mcpu=cortex-m3 -mfloat-abi=soft,并声明QEMU_SOC_MPS2
  • ROMFS 分区 0 起始地址0x21000000、大小0x00400000
  • 链接脚本mcu/arm/mps2.ld
  • 原生 GC helper 目标文件gchelper_native.o gchelper_thumb2.o
  • mpy-cross 架构参数-march=armv7m

RISC-V 板(如 boards/VIRT_RV64/mpconfigboard.mk)则把 ROMFS 放在0x80620000,并使用mcu/rv64/virt.ldgchelper_rv64i.o。注意其注释提醒:如果 ROMFS 分区大小不够,需要同步修改 mcu/rv64/virt.ld 中的 ROMFS 段大小。

四、运行与交互:REPL、串口与测试

固件启动后会在模拟硬件的 UART 上提供 MicroPython REPL

直接进入 REPL(UART 重定向到 stdio)

$ make repl

该命令会启动qemu-system-arm(或对应的qemu-system-riscv32等),并把 UART 重定向到终端标准输入输出。退出方式:在 REPL 中执行import machine; machine.reset(),或直接 Ctrl-C 终止命令。Makefile 中repl目标实现为-serial mon:stdio,并提示 “Use machine.reset() to exit”。

后台运行并连接外部串口终端

$ make run

该命令启动仿真,并把 UART 重定向到一个pty 伪终端设备,设备名会打印到 stdout。随后可用任何串口终端程序连接,例如 MicroPython 官方工具 mpremote:

$ mpremote connect /dev/pts/1

可以反复断开、重连该串口设备。结束时回到启动make run的终端按 Ctrl-C,或在 REPL 中执行import machine; machine.reset()

运行官方测试套件

一键运行全部测试:

$ make test

或手动方式:先make run启动仿真,再在 tests 目录下用run-tests.py针对串口设备执行:

$ cd ../../tests $ ./run-tests.py -t /dev/pts/1

make test的内部实现(Makefile)会把run-tests.py-t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) ... -serial pty -kernel ../ports/qemu/$(BUILD)/firmware.elf"组合起来,自动拉起 QEMU 实例,无需人工干预。

测试示例原生模块(natmod)

源码树自带的示例原生模块可用如下命令测试:

$ make test_natmod

手动方式的注意事项与上面相同,只是把run-tests.py换成run-natmodtests.py。默认会测试一组模块(Makefile 中TEST_NATMODS ?= btree deflate framebuf heapq random_basic re),可用TEST_NATMODS选项限定子集(见下一节)。

此外 Makefile 还提供了test_full目标,依次执行三次测试:普通运行、--via-mpy(通过.mpy交叉编译字节码运行)、--via-mpy --emit native(原生机器码执行),这正是 CI 中验证 mpy-cross 与各原生后端的关键路径。

五、全部可调 make 选项

以下是 README 列出的全部扩展选项,可在make命令行追加:

选项作用
CFLAGS_EXTRA向编译器传递额外编译标志
RUN_TESTS_EXTRAmake test/make test_natmod时,向run-tests.py/run-natmodtests.py传递额外参数(如 CI 中使用的--arch armv6m
QEMU_BASE指定 qemu 可执行文件的部分路径前缀,例如/opt/custom-directory/qemu/bin/qemu-system-,用法与交叉编译器名传给 MicroPython Makefile 的方式类似;默认从系统PATH中查找对应 qemu 二进制
QEMU_DEBUG=1运行 qemu(repl/run/test目标)时阻塞等待调试器连接;默认等待 gdb 连接 TCP 1234 端口
QEMU_DEBUG_ARGS默认-s(即 gdb 于 TCP 1234 端口),可改为其它 qemu gdb 参数
QEMU_DEBUG_EXTRAQEMU_DEBUG=1时额外传给 qemu 的选项
QEMU_ROMFS_IMG<n>传入由 qemu 加载的 romfs 镜像(若板卡启用)
TEST_NATMODS空格分隔的 natmod 名称列表,限定test_natmod只测试指定子集,例如make test_natmod TEST_NATMODS="btree heapq re"
MICROPY_HEAP_SIZE覆盖本移植的 GC 堆大小(字节)
MICROPY_STACK_SIZE覆盖本移植的解释器栈大小(字节)

选项背后的默认值(源码级)

  • 默认板卡BOARD ?= MPS2_AN385,构建目录按板卡命名:BUILD ?= build-$(BOARD)(即build-MPS2_AN385等)。
  • 堆与栈默认值:arm 与 riscv32 为堆143360字节、栈10240字节;riscv64 与 ppc64 为堆204800字节,其中 ppc64 栈为20480字节。这些值通过-DMICROPY_HEAP_SIZE=... -DMICROPY_STACK_SIZE=...编译进固件,且 main.c 在编译期强制校验二者必须为正整数。
  • QEMU_DEBUG 实现:当QEMU_DEBUG=1时,Makefile 会向 qemu 追加-S $(QEMU_DEBUG_ARGS) $(QEMU_DEBUG_EXTRA)-S使 CPU 在启动时暂停,等待调试器通过 gdb 协议接入后再继续执行。
  • ROMFS 加载:若配置了QEMU_ROMFS_IMG<n>,会通过-device loader,file=...,addr=<分区起始地址>,force-raw=on把镜像直接装入模拟内存;测试时还会自动挂载 tests/assets/random_romfs.bin 作为 ROMFS 测试镜像(见 Makefile 中ROMFS_TEST_IMAGE相关逻辑)。
  • picolibc 优先:构建时若检测到编译器的picolibc.specs,会自动追加--specs=...显式选用 picolibc(避免 Ubuntu 22.04 等发行版默认 nosys 导致的链接问题)。

六、源码视角:这个移植的内部结构

主循环与 REPL 分发

main.c 是整个移植的入口,其结构清晰地展示了“软复位”模型:

static uint32_t gc_heap[MICROPY_HEAP_SIZE / sizeof(uint32_t)]; int main(int argc, char **argv) { mp_cstack_init_with_sp_here(MICROPY_STACK_SIZE); gc_init(gc_heap, (char *)gc_heap + MICROPY_HEAP_SIZE); ... for (;;) { mp_init(); for (;;) { if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { if (pyexec_raw_repl() != 0) break; } else { if (pyexec_friendly_repl() != 0) break; } } mp_printf(&mp_plat_print, "MPY: soft reboot\n"); ... gc_sweep_all(); mp_deinit(); } }

要点:

  • 堆是一个静态数组,gc_init将其注册为 GC 堆,因此MICROPY_HEAP_SIZE必须在编译期确定;
  • 外层for(;;)实现无限软复位循环——这就是import machine; machine.reset()能“重启”而不退出仿真的原因;
  • 内层循环根据pyexec_mode_kind分发到 RAW REPL(测试与串口协议用)或友好 REPL(交互用)。

架构相关的配置开关

mpconfigport.h 根据编译时的架构宏自动启用对应的原生代码生成器(native emitter)

  • ARMv7(Thumb):MICROPY_EMIT_THUMBMICROPY_EMIT_INLINE_THUMBMPS2_AN385默认;QEMU_SOC_NRF51MICROBIT板除外);
  • RISC-V 32:MICROPY_EMIT_RV32,并启用ZBAZCMP扩展及内联汇编MICROPY_EMIT_INLINE_RV32
  • RISC-V 64:MICROPY_PERSISTENT_CODE_LOAD_NATIVE(64 位可加载原生.mpy);
  • PowerPC 64:通过MICROPY_MAKE_POINTER_CALLABLE处理函数指针。

同时该文件还启用了MICROPY_VFS_ROM(只读 ROM 文件系统,配合 ROMFS 分区使用)、MICROPY_PY_MACHINE(machine 模块)等特性,并将MICROPY_PY_SYS_PLATFORM设为"qemu"

UART 驱动:一套接口、多种 SoC

uart.c 是移植中最能体现“架构抽象”的文件:对外只暴露uart_init()uart_rx_chr()uart_rx_any()uart_tx_strn()四个函数,内部则按QEMU_SOC_*宏为不同模拟 SoC 提供不同的寄存器级实现:

  • QEMU_SOC_STM32(NETDUINO2):STM32 风格 UART,基址0x40011000
  • QEMU_SOC_NRF51(MICROBIT):nRF51 UART,基址0x40002000,通过事件寄存器(RXDRDY)轮询;
  • QEMU_SOC_MPS2/QEMU_SOC_MPS3(MPS2_AN385 / MPS2_AN500 / MPS3_AN547):Cortex-M 系统设计套件 UART,基址0x40004000/0x49303000,发送时轮询STATE.TXFULL位;
  • QEMU_SOC_IMX6(SABRELITE):i.MX6 UART,基址0x02020000
  • QEMU_SOC_VIRT(VIRT_RV32 / VIRT_RV64)与QEMU_SOC_POWERNV(POWERNV9):16550 兼容 UART,基址0x10000000/ PowerNV 特有地址。

网络支持(可选)

通过 lan9118.c、network_lan.c 与 mpnetworkport.c 实现 LAN9118 以太网控制器的模拟驱动。当板卡启用MICROPY_HW_ETH_LAN9118时(SABRELITE 等),会自动拉起 lwIP 协议栈(MICROPY_PY_LWIP),提供network.LAN接口与 socket 模块,默认主机名为"mpy-qemu"(见 mpconfigport.h)。

七、冻结模块与原生测试样本

构建时会通过FROZEN_MANIFEST冻结一组测试模块(Makefile 按架构区分):

  • ARM(除 MICROBIT):frozen_asm_thumb.pyfrozen_const.pyfrozen_viper.pynative_frozen_align.py
  • MICROBIT / riscv32 / riscv64 / ppc64:对应的frozen_asm_rv32.py或仅frozen_const.py

这些文件位于 test-frzmpy 目录,分别用于验证 Thumb 内联汇编、RV32 内联汇编、viper 模式、原生冻结对齐等架构相关特性在仿真环境中的正确性,是 CI 架构覆盖的重要一环。

八、适用前提与限制

  • 实验性质:README 明确标注本移植为 “experimental, community-supported”,适合测试、实验与调试,不代表对目标板卡硬件的完整支持;
  • 工具链门槛:RISC-V 64 工具链的-mcmodel=medany约束是构建失败的最常见原因,建议直接使用仓库提供的 Docker 镜像规避;
  • 外设覆盖有限:UART、可选以太网与 ROMFS 是主要外设抽象,真实芯片的中断、GPIO 等能力并不在仿真范围内;
  • 无 JTAG/OpenOCD 需求:这是本移植的卖点而非缺陷——调试通过QEMU_DEBUG=1配合 gdb(默认 TCP 1234)即可完成。

对希望验证 MicroPython 架构后端(Thumb/RV32/RV64 原生代码生成)、跑官方测试套件或在无硬件环境下做原型实验的开发者来说,ports/qemu是目前仓库中成本最低、覆盖最广的入口。

  • 嵌入式
  • 语言运行时
  • 编程语言
  • 解释器
  • 编译器
  • 物联网
  • 系统编程

【免费下载链接】micropython

MicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems

项目地址:https://gitcode.com/gh_mirrors/mi/micropython
点击查看免费下载

相关推荐

上一篇:终极SDAutoLayout布局约束复用指南:5个实用技巧提升iOS开发效率
下一篇:Claude Code 终端代理编程工具快速上手与避坑指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 3:22:35

上千篇笔记一键整理:Foam标签、Query查询与智能文件夹进阶指南

上千篇笔记一键整理&#xff1a;Foam标签、Query查询与智能文件夹进阶指南 【免费下载链接】foam A personal knowledge management and sharing system for VSCode 项目地址: https://gitcode.com/gh_mirrors/fo/foam Foam 是基于 VSCode 的个人知识管理与笔记分享系统…

作者头像 李华
网站建设 2026/9/21 3:21:34

STM32外部中断实战:ITR9606红外对管转速测量方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/21 3:19:54

MXNet Clojure KVStore API 实战:掌握多设备梯度聚合与键值对管理

MXNet Clojure KVStore API 实战&#xff1a;掌握多设备梯度聚合与键值对管理 【免费下载链接】mxnet Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript…

作者头像 李华