RISC-V Linux 嵌入式编程技术 · 第一章

环境与工具链

荔枝派 4A · RuyiSDK · 烧录与开发通道
交叉编译 · Makefile · CoreMark
— 1.1 / 1.2 / 1.3 —

目标板:荔枝派 4A 系统:RevyOS 工具:RuyiSDK

开始之前

本章目标是把开发环境与上板通道真正打通:你能在电脑上安装并使用 RuyiSDK 交叉工具链,把 RevyOS 烧到荔枝派 4A,用串口或 SSH 登录板子,并理解为什么必须用交叉编译才能生成板子可执行的程序。学完本章,你应当能在主机上交叉编出一份 RISC-V 架构的 CoreMark,并用 file 确认产物架构正确。

整章可以看成一条链:先认清板子与工具(1.1),再把系统烧进去并打通登录与传文件的通道(1.2),最后弄清交叉编译并用 Makefile 编出 CoreMark(1.3)。

阅读与跟做顺序:按 1.1 → 1.2 → 1.3 进行。每一节都含跟做命令;建议边做边保存关键终端输出。每节约 90–120 分钟。

你需要准备什么

电脑

Linux x86_64(推荐 Ubuntu 22.04+)。可联网,磁盘至少留 10GB。

荔枝派 4A

一块开发板。配套:电源适配器、USB 数据线(能传数据)、3.3V USB-TTL 串口模块、网线或 Wi-Fi。

软件

课程指定的 RevyOS 镜像、RuyiSDK(命令入口是 ruyi)、RISC-V 交叉编译工具链。安装步骤在 1.1–1.2。

本章你会学到什么

1.1 荔枝派 4A 与 RuyiSDK

认清板子接口;安装 ruyi;验证交叉编译器能编出 RISC-V 程序。

1.2 烧录、开发通道与一键准备/一键编译

把系统写到板子上;打通串口、SSH、SCP;掌握一键准备与一键编译的用法。

1.3 交叉编译、Makefile 与 CoreMark

弄清本机编译与交叉编译的差别;用 Makefile 交叉编出 CoreMark;用 file 确认产物是 RISC-V。

学习建议:每一步都对照「你应该看到…」核对输出;对不上就先停在当前命令查路径与工具链。
术语表

本章术语:先说清,再往下

下面这些词会在本章反复出现。先扫一遍,知道「它是什么、为什么需要它」;读到再见到就不慌,也可以随时翻回这一页。它们不是堆砌——每引入一个词,都是因为我们到了必须说清那一步的关口。

术语是什么为什么需要它
指令集CPU 认识的机器指令清单决定一种 CPU 能执行哪些操作
x86_64 / riscv64两种不同指令集电脑和板子各用一套,程序不能互换
RISC-V开放的指令集架构本课板子的 CPU 用它
RevyOS面向 RISC-V 的 Linux 发行版烧进板子、跑在板子上的系统
交叉编译在一台机器上编出另一架构的程序电脑编出板子可运行的 RISC-V 程序
工具链编译器 + 链接器 + 库的集合把源码一步步变成可执行文件
三元组(target triple)「CPU-厂商-系统」的简写告诉编译器「给谁编」
ELF可执行文件的一种容器格式file 靠它读出「这是什么架构」
镜像整套系统的打包烧录的对象
烧录把镜像按分区写入板子存储给板子装系统
UART / 串口逐字节收发的物理接口本地调试、看开机日志
SSH加密的远程登录通道日常登录板子敲命令
SCP复用 SSH 传文件电脑与板子之间拷程序
虚拟环境ruyi venv 建的开发环境自动配好工具链路径与 sysroot
sysroot目标系统的运行库目录链接时找 RISC-V 版 C 库
CoreMark测处理器算力的基准程序用一份真程序验证整条工具链通没通
Makefile描述编译规则的文本敲一个 make 就按规则编译
1.1

荔枝派 4A 与 RuyiSDK

认识荔枝派 4A

荔枝派 4A 是本课用的 RISC-V 开发板:一块带 CPU、内存、存储接口和排针的电路板,用来跑 Linux、接传感器和执行器。

指令集是 CPU 认识的机器指令清单——规定「加、跳、读内存」等操作怎么编码成二进制。电脑常见 x86_64 指令集;荔枝派 4A 跑的是 riscv64 指令集。两边指令编码不同,所以电脑上用普通 gcc 编好的程序,不能直接丢到板子上跑。

RISC-V 是一种开放的指令集架构:规范公开,任何人可以做兼容芯片。本课板子上的 Linux 系统是 RevyOS——面向 RISC-V 板子的 Linux 发行版。

RISC-V 指令集(规定 CPU 认哪些指令) ↓ 荔枝派 4A(开发板:CPU + 内存 + 接口) ↓ RevyOS(板子上的 Linux 系统) ↓ RuyiSDK / ruyi(电脑上装工具链与镜像的入口)

动手前先在板子上标出:电源口、串口排针、网口、启动介质(eMMC / SD)。串口模块必须是 3.3V 电平——电平是引脚高低电压的约定;5V 可能损坏板子。

项目要求检查方式
电脑系统Linux x86_64uname -a
开发板荔枝派 4A核对板卡丝印与包装
USB 线能传数据(不只充电)换已知数据线试
串口模块3.3V USB-TTL看模块规格丝印
网络能访问 RuyiSDK 软件源浏览器或连通性测试
基础命令curl / wgetsha256sumfilecommand -v curl
常见陷阱
· 部分 USB-C 线只供电、不传数据 → 电脑认不出设备时先换线
· 串口电平必须 3.3V → 5V 可能损坏板卡
· 工具链前缀选 linux-gnu(跑 Linux 的程序)→ unknown-elf 是裸机工具链,本课不用

安装 RuyiSDK

RuyiSDK 是面向 RISC-V 开发的工具包集合;命令入口叫 ruyi,用来查包、装工具链、查设备支持。真正把 C 代码编成机器码的是里面的 GCC(GNU 编译器)。关系可以记成:ruyi 管「装什么」,GCC 管「怎么编」。

三元组(target triple)是编译器目标平台的简写,格式大致是「CPU-厂商-系统」。本课统一用 riscv64-ruyisdk-linux-gnu(经 RuyiSDK 的 gnu-ruyisdk 提供):意思是「为 64 位 RISC-V CPU、跑 Linux、用 GNU 用户态约定」生成程序。

把三元组从左到右拆开看,每一段都在说一件事:

字段含义本课取值
CPU给哪种指令集生成代码riscv64(64 位 RISC-V)
厂商/来源谁提供的这套约定ruyisdk
系统跑在什么系统上linux
用户态用哪套库与 ABIgnu(GNU / glibc)

把用户态一段换成 elf(如 riscv64-unknown-elf),就变成裸机工具链——没有 Linux 的加载与运行库,编出的程序跑不了本课这样的 Linux 用户程序。

安装包管理器本身请以 RuyiSDK 官方安装文档 为准。官方推荐使用预编译二进制;对于已配置 Python 环境的 x86 Linux 主机,也可以通过 PyPI 安装。下面按 PyPI 路径写出本课步骤。

  1. 1
    记录电脑环境
    x86 Linux 上执行 uname -m,确认输出为 x86_64
  2. 2
    安装 Ruyi 包管理器
    预编译二进制的下载与放置见官方文档。本课在已配置 Python 的 x86 Linux 上走 PyPI:
    python3 -m pip install --user ruyi
    pip 把可执行文件装到 ~/.local/bin,请确认该目录已在 PATH 中(可把 export PATH="$HOME/.local/bin:$PATH" 写入 ~/.bashrcsource ~/.bashrc)。其它发行版安装方式见官方文档。
  3. 3
    PATH 检查与版本验证
    command -v ruyi 应给出绝对路径(例如 /home/你的用户名/.local/bin/ruyi);ruyi --version 应打印版本号(本课验收样例主机为 Ruyi 0.46.0,你机器上的次版本可以更新,但必须能运行)。
  4. 4
    更新并安装 gnu-ruyisdk 工具链
    ruyi update,再 ruyi install gnu-ruyisdk
  5. 5
    创建并激活虚拟环境
    (目录自定,下文以家目录为例):
    ruyi venv -t gnu-ruyisdk manual venv-gnu-ruyisdk
    . ~/venv-gnu-ruyisdk/bin/ruyi-activate
    每次新开终端都要重新激活。磁盘紧张时可加 --without-sysroot(仍能交叉编译本课 CoreMark)。
  6. 6
    确认交叉编译器
    riscv64-ruyisdk-linux-gnu-gcc -vriscv64-ruyisdk-linux-gnu-gcc -dumpmachine 应打印 riscv64-ruyisdk-linux-gnu
# 以下均在 x86 Linux 上执行
$ uname -m
x86_64
$ python3 -m pip install --user ruyi
$ export PATH="$HOME/.local/bin:$PATH"   # 若尚未写入 shell 配置
$ command -v ruyi
/home/你的用户名/.local/bin/ruyi
$ ruyi --version
Ruyi 0.46.0
# …版权与许可信息略…
$ ruyi update
$ ruyi install gnu-ruyisdk
$ ruyi venv -t gnu-ruyisdk manual venv-gnu-ruyisdk
$ . ~/venv-gnu-ruyisdk/bin/ruyi-activate
$ riscv64-ruyisdk-linux-gnu-gcc -dumpmachine
riscv64-ruyisdk-linux-gnu

最小编译验证

写一个空程序,用交叉编译器编一下,再用 file 看产物是不是 RISC-V。ELF 是 Linux 上可执行文件与目标文件的常见容器格式;file 会读出里面的架构字段。

$ printf 'int main(void) { return 0; }\n' > mini.c
$ riscv64-ruyisdk-linux-gnu-gcc mini.c -o mini-riscv
$ file mini-riscv
mini-riscv: ELF 64-bit LSB executable, UCB RISC-V, ...

在电脑上直接 ./mini-riscv 会失败——指令集不同。验收看 file 输出里有 RISC-V 字样即可。

本节成果:荔枝派 4A 接口标注 · 个人环境清单 · ruyi 已安装且 PATH 可用 · 版本记录 · 三元组 riscv64-ruyisdk-linux-gnu · 最小 RISC-V 可执行文件与 file 日志。
1.2

烧录、开发通道与一键准备/一键编译

烧录系统

烧录是把系统镜像按分区写入板子存储(本课用板载 eMMC)。镜像里通常有:引导程序(U-Boot)、启动分区(内核与设备树等)、根文件系统(RevyOS 用户空间)。

本课指定组合(与验收样机一致):

对照资料(排障与手工备用时查阅):

主机:ruyi device provision ├─ 选设备:Sipeed LicheePi 4A ├─ 选规格:16G RAM └─ 选系统:RevyOS 20251226 ↓ 下载镜像(含校验) 按住 BOOT → 插入 USB-C → fastboot 写入 eMMC ↓ 松开 BOOT,重新上电 串口 / SSH 出现登录提示(用户 debian)

环境准备

用 ruyi device provision 烧录(主路径)

  1. 1
    更新索引并启动向导
    在主机执行 ruyi update,再 ruyi device provision。按提示确认后继续。
  2. 2
    选择设备与规格
    设备选 Sipeed LicheePi 4A;内存规格选 16G(8G 板不要选 16G 的 U-Boot 包)。
  3. 3
    选择系统镜像版本
    RevyOS,版本选 20251226(与 Support Matrix 当前 good 记录、本课验收样机一致)。向导会下载并校验对应镜像;下载目录以终端提示为准。
  4. 4
    进入烧录模式
    板子断电。按住板上 BOOT 键不放,再插入连向主机的 USB-C;主机应能枚举到 fastboot 设备。保持按住直至 fastboot devices 能看到设备(向导也会提示时机)。
  5. 5
    写入并观察预期输出
    按向导确认后开始写入。成功时终端会出现各分区 flash 完成、无 error 的提示;失败则根据报错检查线材、BOOT 时机与 fastboot devices。写入完成后按提示松开 BOOT、重新上电。
# 主机:启动一键准备(交互菜单以本机 ruyi 为准)
$ ruyi update
$ ruyi device provision
# 选择:Sipeed LicheePi 4A → 16G → RevyOS 20251226

# 进入烧录模式后,另开终端可自检(可选)
$ sudo fastboot devices
# 期望:出现一列设备序列号 +  fastboot
手工 fastboot 备用:若课堂要求对照 Support Matrix 手工写入,镜像目录为 20251226 镜像目录。 下载 u-boot-with-spl-lpi4a-16g.binboot-lpi4a-20251225_175338.ext4.zstroot-lpi4a-20251225_175338.ext4.zst,用 zstd -d 解压后,按 Support Matrix 中的 fastboot flash 顺序写入。细节以该 README 与 Sipeed 文档为准。

首次启动验证(验收样机实输出)

默认用户 / 密码多为 debian / debian(以镜像说明为准)。串口或 SSH 登录后执行:

$ cat /sys/firmware/devicetree/base/model; echo
Sipeed Lichee Pi 4A 16G

$ cat /etc/revyos-release
BUILD_ID=20251225_175338
BUILD_DATE=20251225
BOARD_NAME=lpi4a
RELEASE_ID=20251226
COMMIT_ID=65b79515309b7d77816def1e83f0d5a95178ab70
RUNNER_ID=20508594622

$ uname -m
riscv64

$ hostname
revyos-lpi4a

$ free -h | head -2
               total        used        free      shared  buff/cache   available
Mem:            15Gi       ...

$ ip -br addr
# 期望:除 lo 外,有网口(如 wlan0 / end0)拿到局域网 IP
检查项命令期望(本课样机)
板型cat /sys/firmware/devicetree/base/modelLichee Pi 4A 16G
RevyOS 发行标识cat /etc/revyos-releaseRELEASE_ID=20251226BOARD_NAME=lpi4a
架构uname -mriscv64
内存规格free -h总量约 15–16 Gi(16G 板)
账户id常见为用户 debian
IP 地址ip -br addr有有效局域网 IP
路由ip route有默认网关
时间timedatectl时间正确(错了会导致 TLS 失败)
软件源sudo apt update更新成功
说明:/etc/os-release 可能显示底层 Debian 代号(如 forky/sid),确认「是不是本课这套 RevyOS」请看 /etc/revyos-release,不要只看 PRETTY_NAME

三条开发通道

串口(UART)

本地调试线

UART 是按位串行收发的接口。接线:GND-GND、TX→RX、RX→TX,3.3V。不依赖网络,直接看启动日志。

SSH

远程主通道

SSH 是加密的远程终端。日常开发用它登录板子。本课用 Ed25519 密钥登录;首次连接须核对主机指纹。

SCP

文件传输

SCP 复用 SSH 连接拷文件。上传后在板端跑 sha256sum,与电脑端哈希一致即传输完整。

为什么需要三条:三条通道解决三类不同的问题,按场景挑:串口不依赖网络,板子起不来、SSH 还连不上时用它看开机日志;SSH 是日常的加密登录;SCP 专门在电脑与板子之间传文件。
通道怎么连要网络吗加密吗典型场景
串口 UART物理线直连板子排针开机引导日志、SSH 连不上时救急
SSH走网络连板子 IP日常登录敲命令
SCP复用 SSH 连接电脑与板子之间传文件
# 板端启用 SSH
$ sudo systemctl enable --now ssh

# 电脑生成密钥
$ ssh-keygen -t ed25519 -C "riscv-course"

# 首次连接——核对指纹后再输入 yes
$ ssh user@board-ip

# 部署公钥并免密验证
$ ssh-copy-id user@board-ip
$ ssh user@board-ip 'hostname; uname -m'
# SCP 上传并校验
$ printf 'transfer test\n' > test.txt
$ sha256sum test.txt
$ scp test.txt user@board-ip:/tmp/
$ ssh user@board-ip 'sha256sum /tmp/test.txt'
指纹变更:可能是板子重装系统,也可能连错了设备。先核对 IP 与串口里的主机指纹,再清理 known_hosts 旧记录。

一键准备与一键编译

手工烧录与手工 SSH/SCP 之外,ruyi 提供两类自动化能力:

一键准备

按设备支持列表自动下载镜像并写入板子存储,适合设备明确受支持、写入目标介质正确的场景。

一键编译

封装交叉工具链调用与常见构建参数,适合快速验证工具链或标准化构建。

# 以本机 ruyi 帮助为准
$ ruyi device --help
$ ruyi device provision --help
本课定位:烧录主路径是 ruyi device provision(选 LicheePi 4A / 16G / RevyOS 20251226)。一键编译与手工 SCP/SSH 也要掌握。Support Matrix 与 Sipeed 文档用于对照与排障。
本节成果:provision 选型记录 · 烧录成功日志或截图 · 首次启动检查表(含 revyos-release)· 串口接线记录 · SSH 密钥登录 · SCP 双向哈希一致。
1.3

交叉编译、Makefile 与 CoreMark

为何必须交叉编译

开发电脑的 CPU 执行 x86_64(少数机器是 ARM)机器码;荔枝派 4A 执行 riscv64 机器码。两边指令编码不同。

在电脑上用本机 gcc main.c -o app,得到的是本机架构的 ELF,只能在本机跑。拷到板端,Linux 内核会拒绝加载,常见报错是 Exec format error。板端编好的 RISC-V 程序拷回电脑,同样跑不了。

为什么内核会拒绝?可执行文件开头有一段 ELF 头,其中的 e_machine 字段写明这份文件属于哪种架构。内核加载程序时检查这个字段:和当前 CPU 架构不一致就拒绝加载,报 Exec format errorfile 读的正是同一个字段——所以 file 说它是 RISC-V,就预判了它在 x86 电脑上跑不了;说它是 x86-64,就预判了它在板子上跑不了。

在荔枝派上实测(本课真板):

# 把两种产物都拷到板子 /tmp 再运行
$ scp mini-riscv mini-host debian@板子IP:/tmp/
$ ssh debian@板子IP 'cd /tmp && ./mini-riscv; echo "exit=$?"'
# exit=0 —— RISC-V 产物在板端正常跑
$ ssh debian@板子IP 'cd /tmp && ./mini-host; echo "exit=$?"'
bash: line 1: ./mini-host: cannot execute binary file: Exec format error
# exit=126 —— x86 产物被内核拒绝,报 Exec format error

交叉工具链仍在电脑上运行,但专门生成 RISC-V 机器码,并链接面向 RISC-V Linux 的 C 库。本课经 RuyiSDK 安装,前缀是 riscv64-ruyisdk-linux-gnu-三元组 riscv64-ruyisdk-linux-gnu 标明:CPU 为 riscv64、系统为 Linux、用户态约定为 GNU。选成 unknown-elf 会得到裸机工具链,不适合本课跑 Linux 用户程序。

同一份源码,三条编译路径

同一份 main.c 可以走出三种产物。分清「编给谁、能在哪跑」:

同一份 main.c │ ├───────────────┬──────────────────┬─────────────── │ │ │ ▼ ▼ ▼ 电脑自带 gcc riscv64-ruyisdk- 板端自带 gcc (本机工具链) linux-gnu-gcc (板子上的 RISC-V gcc) │ │ │ │ 生成 x86_64 │ 生成 RISC-V │ 生成 RISC-V ▼ ▼ ▼ app-host app-riscv app-board 只能在电脑跑 架构正确,须传到板端 可在板端就地跑 上板 → Exec format 编给板子 (对照路径) error

抓住三点:① 编译器选错,架构就错;② 用 file 读 ELF 架构字段验收;③ 交叉编译只解决「编对架构」——把文件传到板端并运行,是传输与加载的另一步。

交叉工具链内部仍是编译 → 汇编 → 链接,目标一律是 RISC-V:产出 RISC-V 汇编与 .o,再链上 RISC-V 版 C 运行库(sysroot)。

交叉编译内部(都在电脑上执行) main.c ↓ 编译器:C → RISC-V 汇编 main.s ↓ 汇编器:汇编 → 目标文件 main.o ↓ 链接器:.o + RISC-V C 库 → 可执行文件 app-riscv(ELF,riscv64)

板端若已安装 gcc,可在板子上直接编译就地运行(上图第三条路)。本课后续章节会用到;板端原生编译可作对照,但不能代替主机交叉产物。

亲眼看到指令差异:反汇编

「指令集不同」光听概念记不牢。把同一份 main.c 用两种编译器各编一份,再用 objdump -d 把机器码反回汇编,两边从助记符到编码完全不同:

# 交叉编译 → RISC-V 反汇编
$ riscv64-ruyisdk-linux-gnu-objdump -d mini-riscv | sed -n '/<main>:/,/^$/p'
0000000000000614 <main>:
 614:  1141                addi  sp,sp,-16
 616:  e406                sd    ra,8(sp)
 61c:  4781                li    a5,0
 61e:  853e                mv    a0,a5

# 本机编译 → x86-64 反汇编
$ objdump -d mini-host | sed -n '/<main>:/,/^$/p'
0000000000001129 <main>:
 1129: f3 0f 1e fa           endbr64
 112d: 55                    push   %rbp
 1131: b8 00 00 00 00        mov    $0x0,%eax
 1137: c3                    ret

同样是「把 0 放进返回值」这一个动作,RISC-V 用 li a5, 0mv a0, a5 两条指令,x86 用 mov $0x0, %eax 一条——助记符、寄存器、机器码编码全不一样。这就是为什么两边产物不能互换:内核检查 ELF 头时直接拒收。

Makefile 跟做:本机 vs 交叉

Makefile 描述如何编译工程;敲 make 按规则调用编译器。把交叉前缀写进 Makefile,避免每次手敲冗长命令。

# Makefile — 交叉编译模板
CROSS_COMPILE := riscv64-ruyisdk-linux-gnu-
CC            := $(CROSS_COMPILE)gcc
CFLAGS        := -O2 -Wall
TARGET        := app

$(TARGET): main.c
	$(CC) $(CFLAGS) main.c -o $(TARGET)

clean:
	rm -f $(TARGET)

.PHONY: clean

用同一份源码对照两条路径(讲义跟做到 file 为止,不必 SCP):

$ printf 'int main(void) { return 0; }\n' > main.c

$ gcc main.c -o app-host
$ file app-host
# … ELF … x86-64 …(本机架构)
$ ./app-host
# 本机可执行

$ make
# 或:riscv64-ruyisdk-linux-gnu-gcc main.c -o app
$ file app
# … ELF … UCB RISC-V …
$ ./app
# 本机应失败(指令集不对)
常见陷阱:忘记设 CC / CROSS_COMPILE 会默默用本机 GCC,产物在板端报 Exec format error。先用 file 查架构。Makefile 配方行必须以 Tab 缩进。

构建 CoreMark(讲义收口:编出 + file)

CoreMark 是测处理器算力的小基准程序,输出可比较的分数。本章用它走通工具链:先在主机交叉编出 RISC-V 可执行文件,再传到板端并跑出分数。

# 在 x86 Linux 上:先激活虚拟环境,再进入课程仓库
$ . ~/venv-gnu-ruyisdk/bin/ruyi-activate
$ cd chapters/ch01/code
$ make
$ file coremark/coremark.exe
coremark/coremark.exe: ELF 64-bit LSB executable, UCB RISC-V, ...

源码已放在 chapters/ch01/code/coremark/(官方 CoreMark)。Makefile 在 x86 Linux 上自动选用交叉编译器;在荔枝派上则自动使用板载 gcc

本节收口:产物文件名因版本而异(常见 coremark.exe)。请确认 file 输出中含 RISC-V 或 UCB RISC-V 字样,并保存该可执行文件与对应终端日志。请打开 本章实验 继续。
本节成果:能口述指令集差异与交叉编译原因 · 能画三条路径并说明本章实验走哪条 · Makefile 交叉模板 · 本机/交叉 file 对照 · CoreMark 的 RISC-V 可执行文件(尚未上板)。