)
从零开始在Ubuntu 22.04上用QEMU运行玄铁E902的Hello World第一次接触RISC-V架构的嵌入式开发很多人会被各种专业术语和复杂的工具链吓退。但事实上只要掌握正确的方法即使是新手也能在半小时内完成从环境搭建到程序运行的完整流程。本文将带你用最直接的方式在Ubuntu 22.04系统上体验平头哥玄铁E902处理器的开发过程。玄铁E902作为一款兼容RISC-V指令集的嵌入式处理器核以其极低的功耗和成本优势在智能卡、物联网设备等领域广受欢迎。通过QEMU模拟器我们无需购买开发板就能体验其开发环境。下面就从最基本的Hello World程序开始逐步拆解每个操作步骤。1. 环境准备与依赖安装在开始之前请确保你使用的是Ubuntu 22.04系统其他版本可能需调整依赖包。打开终端我们先更新软件包列表sudo apt update接下来安装QEMU模拟器和基础编译工具链。这些工具将帮助我们构建和运行RISC-V架构的程序sudo apt install -y qemu-system-riscv32 build-essential git wget玄铁E902需要一些特定的依赖库才能正常运行。以下命令将安装所有必要的依赖项sudo apt install -y libsnappy-dev libpixman-1-dev libjpeg-dev \ libdaxctl-dev libvdeplug-dev libpmem-dev libgbm-dev \ libepoxy-dev libgtk-3-0 libaio1 libslirp-dev \ libcapstone-dev libspice-server-dev libsdl2-2.0-0 \ libsdl2-image-2.0-0 libvirglrenderer-dev libcacard-dev \ libusbredirparser1 libfuse3-3 libiscsi7 librbd1注意如果在安装过程中遇到无法定位软件包的错误请先执行sudo apt update刷新软件源。验证QEMU是否安装成功qemu-system-riscv32 --version如果看到版本信息输出说明基础环境已经准备就绪。2. 获取玄铁E902 SDK与示例代码平头哥官方提供了完整的SDK开发包其中包含了工具链、示例代码和QEMU配置。我们通过以下步骤获取# 创建项目目录 mkdir -p ~/e902_project cd ~/e902_project # 下载SDK约500MB下载速度取决于网络状况 wget https://www.xrvm.cn/community/download?id658699855023046656 -O e902_sdk.zip # 解压SDK unzip e902_sdk.zip -d e902_sdk解压完成后目录结构应如下所示e902_sdk/ ├── SmartL_E902-R2S2-V1.7.11 │ ├── projects │ │ ├── examples │ │ │ └── hello_world │ │ │ ├── main.c │ │ │ └── Makefile │ ├── tools │ └── ...进入Hello World示例目录cd e902_sdk/SmartL_E902-R2S2-V1.7.11/projects/examples/hello_world查看main.c文件内容这是一个简单的Hello World程序#include stdio.h int main(void) { printf(Hello World!\n); printf(Hello_World runs successfully!\n); return 0; }3. 编译Hello World程序玄铁E902使用RISC-V 32位架构我们需要使用特定的工具链进行交叉编译。SDK中已经包含了配置好的Makefile编译过程非常简单make clean make编译过程会输出类似以下信息riscv32-unknown-elf-gcc -marchrv32em -mabiilp32e -O2 -c main.c -o main.o riscv32-unknown-elf-gcc -marchrv32em -mabiilp32e -O2 -T link.ld -nostartfiles main.o -o out/smartl_e902_evb.elf编译成功后会在out目录下生成smartl_e902_evb.elf文件这就是能在玄铁E902上运行的可执行文件。常见问题如果遇到riscv32-unknown-elf-gcc: command not found错误说明SDK中的工具链路径未正确设置。解决方法是在Makefile开头添加工具链路径或执行export PATH$PATH:~/e902_sdk/SmartL_E902-R2S2-V1.7.11/tools/riscv/bin4. 在QEMU中运行程序现在到了最激动人心的时刻——在模拟器中运行我们编译的程序。玄铁E902有两种版本E902和E902M分别对应不同的指令集扩展。我们先尝试E902版本qemu-system-riscv32 -M smartl -cpu e902 -kernel out/smartl_e902_evb.elf -nographic -m 128M如果一切顺利你将看到以下输出Hello World! Hello_World runs successfully!对于E902M版本只需将cpu参数改为e902mqemu-system-riscv32 -M smartl -cpu e902m -kernel out/smartl_e902_evb.elf -nographic -m 128M提示参数说明-M smartl指定机器类型为平头哥SmartL-cpu e902指定CPU型号-kernel指定要运行的ELF文件-nographic禁用图形界面直接输出到终端-m 128M设置内存大小为128MB5. 调试与问题排查初次尝试时可能会遇到各种问题。以下是几个常见错误及解决方法问题1缺少动态链接库error while loading shared libraries: libspice-server.so.1: cannot open shared object file解决方法安装缺失的库sudo apt install -y libspice-server1问题2QEMU启动失败qemu-system-riscv32: Unable to load the RISC-V firmware opensbi-riscv32-smartl-e902-fw_jump.bin解决方法指定正确的机器类型确保使用-M smartl参数问题3程序无输出如果QEMU启动后没有看到Hello World输出可能是以下原因编译时使用了错误的架构参数确保Makefile中的-marchrv32emELF文件路径错误确认-kernel参数指向正确的文件终端设置问题尝试去掉-nographic参数看看是否有图形界面6. 深入理解玄铁E902开发环境成功运行Hello World后你可能想进一步了解这个开发环境的构成。让我们分析几个关键组件工具链组成玄铁E902 SDK包含完整的RISC-V工具链组件用途所在路径riscv32-unknown-elf-gcc交叉编译器tools/riscv/binriscv32-unknown-elf-objdump反汇编工具tools/riscv/binqemu-system-riscv32模拟器系统安装或SDK提供Makefile关键配置Hello World项目的Makefile中几个重要参数ARCH rv32em ABI ilp32e CFLAGS -march$(ARCH) -mabi$(ABI) -O2 LDFLAGS -T link.ld -nostartfiles这些参数确保了代码能正确编译为玄铁E902可执行的格式。内存布局link.ld链接脚本定义了程序的内存布局MEMORY { ROM (rx) : ORIGIN 0x00000000, LENGTH 0x10000 RAM (rwx) : ORIGIN 0x20000000, LENGTH 0x4000 }这表示代码将从0地址开始执行数据段位于0x20000000处。7. 扩展实践修改与调试程序现在你已经掌握了基础流程可以尝试修改程序并观察变化。例如修改main.c#include stdio.h int main(void) { printf(Welcome to Xuantie E902!\n); for(int i0; i5; i) { printf(Counting: %d\n, i); } return 0; }重新编译并运行make clean make qemu-system-riscv32 -M smartl -cpu e902 -kernel out/smartl_e902_evb.elf -nographic -m 128M如果想深入了解程序运行细节可以使用QEMU的调试功能。首先在一个终端启动QEMU并等待GDB连接qemu-system-riscv32 -M smartl -cpu e902 -kernel out/smartl_e902_evb.elf -nographic -m 128M -S -s然后在另一个终端启动GDBriscv32-unknown-elf-gdb out/smartl_e902_evb.elf在GDB中连接QEMU并设置断点(gdb) target remote :1234 (gdb) break main (gdb) continue这样就能单步调试程序观察寄存器变化和程序执行流程了。