新闻详情

新闻详情

首页 / 资讯中心 / 详情

从 NEMU 到 AbstractMachine:程序、运行时环境与 AM 完全解析

发布时间:2026/10/2 2:33:06来源:尧图网络
从 NEMU 到 AbstractMachine:程序、运行时环境与 AM 完全解析
本文结合ysyx-workbench的实际代码讲解 PA 讲义《程序、运行时环境与 AM》的核心内容。重点回答NEMU 已经能执行指令了为什么程序还是不能直接运行AM 到底提供了什么摘要NEMU 提供了指令执行 物理内存 PC 通用寄存器 MMIO 设备但是一个程序真正运行还需要程序入口 栈 堆 main 参数 标准输出 程序退出 异常处理 设备访问这些内容不属于单条 CPU 指令而属于运行时环境。AbstractMachine 就是 ysyx 中为程序提供的统一运行时环境应用程序 / kernel | v klib | v AbstractMachine | v riscv32-nemu 平台实现 | v NEMU | v Linux 主机关键词NEMU、AbstractMachine、AM、TRM、IOE、CTE、riscv32、运行时环境、PA一、为什么需要运行时环境NEMU 已经实现了很多指令但只有指令并不能直接运行一个完整的程序。一个程序运行至少需要这些条件程序加载到哪里 第一条指令是什么 栈指针 sp 初始化成什么 堆从哪里开始 main 的参数从哪里来 printf 最后的输出由谁完成 程序结束时如何通知 NEMU 中断和异常由谁处理这些问题分别对应问题所属模块程序入口start.S栈和堆linker.ldmain 参数trm.cinsert-arg.py输出putch停机haltnemu_trap设备访问IOE异常处理CTE硬件模拟NEMU二、AM 与 NEMU 的分工NEMUNEMU 模拟硬件CPU 指令 物理内存 MMIO 设备 中断异常AbstractMachineAM 提供运行时环境程序入口 栈 堆 参数 I/O 接口 异常接口 退出接口对应关系AM 负责“程序怎么运行” NEMU 负责“硬件怎么工作”三、构建阶段ARCH 决定 ISA 和平台以 hello 为例am-kernels/kernels/hello/ ├── hello.c └── MakefileMakefileNAME hello SRCS hello.c include $(AM_HOME)/Makefile执行make ARCHriscv32-nemuAM Makefile 拆出ARCH_SPLIT $(subst -, ,$(ARCH)) ISA $(word 1,$(ARCH_SPLIT)) PLATFORM $(word 2,$(ARCH_SPLIT))所以ARCHriscv32-nemu ISAriscv32 PLATFORMnemu接着包含-include $(AM_HOME)/scripts/$(ARCH).mk也就是abstract-machine/scripts/riscv32-nemu.mk代码include $(AM_HOME)/scripts/isa/riscv.mk include $(AM_HOME)/scripts/platform/nemu.mk CFLAGS -DISA_H\riscv/riscv.h\ COMMON_CFLAGS -marchrv32im_zicsr -mabiilp32 LDFLAGS -melf32lriscv AM_SRCS riscv/nemu/start.S \ riscv/nemu/cte.c \ riscv/nemu/trap.S \ riscv/nemu/vme.c因此AM 的 ISA 和平台选择由 ARCH 决定。四、运行时内存布局linker.ld文件abstract-machine/scripts/linker.ld代码ENTRY(_start) SECTIONS { . _pmem_start _entry_offset; .text : { *(entry) *(.text*) } .rodata : { *(.rodata*) } .data : { *(.data*) } .bss : { _bss_start .; *(.bss*) *(.sbss*) *(.scommon) } _stack_top ALIGN(0x1000); . _stack_top 0x8000; _stack_pointer .; end .; _end .; _heap_start ALIGN(0x1000); }它决定_start 程序入口 .text 代码 .rodata 只读数据 .data 有初值数据 .bss 零初始化数据 _stack_pointer 栈顶 _heap_start 堆起点其中_pmem_start 0x80000000 _entry_offset 0所以程序从0x80000000开始。五、程序入口start.S文件abstract-machine/am/src/riscv/nemu/start.S代码.section entry, ax .globl _start .type _start, function _start: mv s0, zero la sp, _stack_pointer call _trm_init .size _start, . - _start它完成初始化 s0 设置 sp _stack_pointer 调用 _trm_init这就是运行时环境建立的第一步。六、TRM最基础的运行时环境文件abstract-machine/am/src/platform/nemu/trm.c标准形式#include am.h #include nemu.h extern char _heap_start; int main(const char *args); Area heap RANGE(_heap_start, PMEM_END); static const char mainargs[MAINARGS_MAX_LEN] TOSTRING(MAINARGS_PLACEHOLDER); void putch(char ch) { outb(SERIAL_PORT, ch); } void halt(int code) { nemu_trap(code); while (1); } void _trm_init() { int ret main(mainargs); halt(ret); }它提供heap 堆区信息 mainargs main 参数 putch 输出字符 halt 结束程序 _trm_init 启动入口七、mainargs 从哪里来trm.c中static const char mainargs[MAINARGS_MAX_LEN] TOSTRING(MAINARGS_PLACEHOLDER);编译时定义CFLAGS -DMAINARGS_MAX_LEN$(MAINARGS_MAX_LEN) CFLAGS -DMAINARGS_PLACEHOLDER$(MAINARGS_PLACEHOLDER)运行时make ARCHriscv32-nemu run mainargshelloAM 调用insert-arg: image python $(AM_HOME)/tools/insert-arg.py ...脚本在二进制中寻找占位字符串并替换成真正的hello所以运行时 main(args) 中的 args 来自 insert-arg.py八、putchAM 的输出接口void putch(char ch) { outb(SERIAL_PORT, ch); }SERIAL_PORT来自abstract-machine/am/src/platform/nemu/include/nemu.h最终对应 NEMU 的串口 MMIO0xa00003f8NEMU 中初始化void init_serial() { serial_base new_space(8); add_mmio_map(serial, CONFIG_SERIAL_MMIO, serial_base, 8, serial_io_handler); }调用链AM putch - outb(SERIAL_PORT) - NEMU paddr_write - mmio_write - serial_io_handler - 主机终端九、haltAM 的退出接口void halt(int code) { nemu_trap(code); while (1); }nemu_trap在 riscv32 下#define nemu_trap(code) \ asm volatile(mv a0, %0; ebreak : : r(code))因此生成mv a0, code ebreakNEMU 解码到ebreakINSTPAT(000000000001 00000 000 00000 11100 11, ebreak, I, { s-dnpc isa_raise_intr(3, s-pc); });最终AM halt - nemu_trap - ebreak - NEMU_END十、AM 接口am.h文件abstract-machine/am/include/am.h定义了运行时接口extern Area heap; void putch(char ch); void halt(int code); bool ioe_init(void); void ioe_read(int reg, void *buf); void ioe_write(int reg, void *buf); bool cte_init(Context *(*handler)(Event ev, Context *ctx)); void yield(void); bool ienabled(void); void iset(bool enable); Context *kcontext(Area kstack, void (*entry)(void *), void *arg);对应TRM Turing Machine IOE I/O Extension CTE Context Extension VME Virtual Memory Extension MPE Multi-Processing Extension十一、IOEAM 如何访问设备例如定时器io_read(AM_TIMER_UPTIME);io_read()在abstract-machine/am/src/platform/nemu/ioe/ioe.c中查表typedef void (*handler_t)(void *buf); static void *lut[128] { [AM_TIMER_CONFIG] __am_timer_config, [AM_TIMER_RTC] __am_timer_rtc, [AM_TIMER_UPTIME] __am_timer_uptime, [AM_INPUT_CONFIG] __am_input_config, [AM_INPUT_KEYBRD] __am_input_keybrd, ... };调用链io_read(AM_TIMER_UPTIME) - lut[AM_TIMER_UPTIME] - __am_timer_uptime()最终读取 NEMU 的 RTC MMIO 寄存器。十二、NEMU 如何加载 AM 程序文件nemu/src/monitor/monitor.c代码static long load_img() { FILE *fp fopen(img_file, rb); fseek(fp, 0, SEEK_END); long size ftell(fp); fseek(fp, 0, SEEK_SET); int ret fread(guest_to_host(RESET_VECTOR), size, 1, fp); assert(ret 1); fclose(fp); return size; }RESET_VECTOR0x80000000所以AM 生成的 .bin - NEMU pmem[0x80000000]CPU 启动cpu.pc RESET_VECTOR;然后从0x80000000取第一条指令执行_start。十三、hello 程序的完整运行链路代码#include am.h #include klib-macros.h int main(const char *args) { const char *fmt Hello, AbstractMachine!\n mainargs %.\n; for (const char *p fmt; *p; p) { (*p %) ? putstr(args) : putch(*p); } return 0; }执行链NEMU 从 0x80000000 取指 - _start - 设置 sp _stack_pointer - _trm_init - main(mainargs) - putstr / putch - outb(SERIAL_PORT, ch) - NEMU serial - 终端输出 - main 返回 0 - halt(0) - nemu_trap(0) - ebreak - NEMU_END十四、AM 为什么是运行时环境AM 隐藏了不同 ISA 和平台的差异不同 ISA 不同平台 不同设备地址 不同启动代码 不同中断方式上层程序只看到putch() getch() io_read() io_write() yield() halt()具体实现由 AM 完成。例如riscv32-nemu scripts/riscv32-nemu.mk am/src/riscv/nemu/ am/src/platform/nemu/ x86_64-qemu scripts/x86_64-qemu.mk am/src/x86/qemu/ am/src/platform/qemu/所以AM 是程序与硬件之间的一层运行时环境。十五、常见问题1.mainargs没有正确替换检查tools/insert-arg.py scripts/platform/nemu.mk trm.c 中 TOSTRING(MAINARGS_PLACEHOLDER)2.halt没有使用nemu_trap如果写成串口0xFF后死循环NEMU 不会停止。正确void halt(int code) { nemu_trap(code); while (1); }3.ioe_init()没有调用如果程序使用 IOE需要调用ioe_init();否则io_read/io_write使用未初始化设备。4.linker.ld和 NEMU 地址不一致必须保持_pmem_start 0x80000000 NEMU RESET_VECTOR 0x800000005.__am_*函数没有实现例如__am_timer_uptime() __am_gpu_fbdraw() __am_input_keybrd()可能表现为AM Panic: access nonexist register十六、总结这份讲义的核心是NEMU 提供硬件模拟 AM 提供运行时环境 程序运行在 AM 之上完整链路AM 源代码 - riscv32-nemu 编译 - linker.ld 布局 - start.S 建立栈 - _trm_init - main - putch / io_read / io_write - nemu_trap - NEMU 停机对应代码运行环境组成代码位置程序入口am/src/riscv/nemu/start.S栈指针scripts/linker.ld堆信息trm.clinker.ldmain 参数trm.cinsert-arg.py输出putch NEMU serial退出haltnemu_trapebreak设备访问am/src/platform/nemu/ioe/异常/中断am/src/riscv/nemu/cte.ctrap.S硬件模拟nemu/一句话总结AM 不是硬件也不是普通库而是运行在 NEMU 模拟硬件之上的运行时环境。它负责程序入口、栈、堆、参数、I/O、异常和退出让上层程序不必直接面对 ISA 和平台差异
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

使用fastapi-mcp改造fastapi服务为MCP服务供智能体使用案例:把Base URL改到TaoToken 2026/10/2 14:57:59

使用fastapi-mcp改造fastapi服务为MCP服务供智能体使用案例:把Base URL改到TaoToken

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

阅读更多 →
ABAQUS轮胎仿真全流程:从过盈充气到滚动传涵实操 2026/10/2 14:57:53

ABAQUS轮胎仿真全流程:从过盈充气到滚动传涵实操

玩轮胎仿真不上手?说真的,这话我听了不下几十遍。但每次看到新人卡住,十有八九都不是软件操作不会,而是没搞懂轮胎仿真这套流程到底在算什么:从过盈充气到滚动传涵,中间每一步都环环相扣。今天我就用自家项…

阅读更多 →
VirtualBox 装 Win11 虚拟机:TPM 2.0、增强功能与避坑 2026/10/2 14:57:53

VirtualBox 装 Win11 虚拟机:TPM 2.0、增强功能与避坑

1. 先搞明白:为什么要在 VirtualBox 里装 Win11我平时干活的主力机是 Linux,但手头总有一些绕不开的 Windows 场景:帮朋友验证一个只在 Win11 上出问题的软件、跑某个银行客户端、测试一份文档在 Edge 下的排版、或者干脆想看看某个新版本系统…

阅读更多 →
Pytorch Unet医学图像分割实战:一键训练脚本与预测全流程解析 2026/10/2 14:57:53

Pytorch Unet医学图像分割实战:一键训练脚本与预测全流程解析

简介:一个基于Pytorch与Unet的医学图像分割实战项目,面向有一定深度学习基础的开发者、医学影像研究者,以及需要快速落地分割任务的技术人员,适用于病灶区域提取、器官结构分割等实际场景。项目完整覆盖数据加载、模型搭建、模型训…

阅读更多 →
从零手搓AI工程:数据管道、实验管理与推理服务实战 2026/10/2 14:57:53

从零手搓AI工程:数据管道、实验管理与推理服务实战

1. 从零手搓AI工程:为什么我不建议你直接调包很多人一听到“AI工程”这四个字,第一反应就是打开某个云平台,拖几个组件,调一下API,跑通一个Demo,然后发个朋友圈说“今天又搞定了一个AI项目”。我刚开始也是…

阅读更多 →
Jetson Nano 无显示器远程桌面:网线直连 NoMachine 实战 2026/10/2 14:57:47

Jetson Nano 无显示器远程桌面:网线直连 NoMachine 实战

把 Jetson Nano 从盒子里翻出来的第一天,我干的事情是给它插上 HDMI 线、USB 键盘、USB 鼠标,再拖一个显示器过去。折腾半小时后我意识到一个问题:这块板子最终是要放在设备柜里跑推理任务的,我不可能每次都把整套外设搬过去。于是…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉