RISC-V Linux 实践课 01|环境与工具链 【讲义】

# 第一章 环境与工具链

> 本篇是纯文字版,不含配图与录像。想看带图、带终端录像和视频的完整版,去课程网站:

>

> - 完整讲义:第一章 环境与工具链 · 讲义 — RISC-V Linux 课程

> - 完整实验:第一章 · 本章实验 · 三个实验

> - 课程总览:Ruyi RISC-V Linux 课程 · 总览

## 讲义

### 开始之前

本章目标是把开发环境与上板通道真正打通:你能在电脑上安装并使用 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 发行版。

```text

RISC-V 指令集(规定 CPU 认哪些指令)

↓

荔枝派 4A(开发板:CPU + 内存 + 接口)

↓

RevyOS(板子上的 Linux 系统)

↓

RuyiSDK / ruyi(电脑上装工具链与镜像的入口)

```

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

| 项目 | 要求 | 检查方式 |

| — | — | — |

| 电脑系统 | Linux x86_64 | `uname -a` |

| 开发板 | 荔枝派 4A | 核对板卡丝印与包装 |

| USB 线 | 能传数据(不只充电) | 换已知数据线试 |

| 串口模块 | 3.3V USB-TTL | 看模块规格丝印 |

| 网络 | 能访问 RuyiSDK 软件源 | 浏览器或连通性测试 |

| 基础命令 | `curl` / `wget`、`sha256sum`、`file` | `command -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` |

| **用户态** | 用哪套库与 ABI | `gnu`(GNU / glibc) |

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

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

1. **记录电脑环境**:在 **x86 Linux** 上执行 `uname -m`,确认输出为 `x86_64`。

2. **安装 Ruyi 包管理器**:预编译二进制的下载与放置见官方文档。本课在已配置 Python 的 x86 Linux 上走 PyPI:

\`\`\`bash

python3 -m pip install --user ruyi

\`\`\`



若 \`pip\` 把可执行文件装到 \`\~/.local/bin\`,请确认该目录已在 \`PATH\` 中(可把 \`export PATH="$HOME/.local/bin:$PATH"\` 写入 \`\~/.bashrc\` 后 \`source \~/.bashrc\`)。其它发行版安装方式见官方文档。

3. **PATH 检查与版本验证**:`command -v ruyi` 应给出绝对路径(例如 `/home/你的用户名/.local/bin/ruyi`);`ruyi --version` 应打印版本号(本课验收样例主机为 `Ruyi 0.46.0`,你机器上的次版本可以更新,但必须能运行)。

4. **更新并安装 gnu-ruyisdk 工具链**:`ruyi update`,再 `ruyi install gnu-ruyisdk`。

5. **创建并激活虚拟环境**:(目录自定,下文以家目录为例):

\`\`\`bash

ruyi venv -t gnu-ruyisdk manual venv-gnu-ruyisdk

. ~/venv-gnu-ruyisdk/bin/ruyi-activate

\`\`\`



每次新开终端都要重新激活。磁盘紧张时可加 \`--without-sysroot\`(仍能交叉编译本课 CoreMark)。

6. **确认交叉编译器**:`riscv64-ruyisdk-linux-gnu-gcc -v`;`riscv64-ruyisdk-linux-gnu-gcc -dumpmachine` 应打印 `riscv64-ruyisdk-linux-gnu`。

```bash

# 以下均在 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` 会读出里面的架构字段。

```bash

$ 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 用户空间)。

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

- 硬件:Sipeed **LicheePi 4A 16G**(设备树 model 为 `Sipeed Lichee Pi 4A 16G`)

- 系统:**RevyOS 20251226**(板端 `/etc/revyos-release` 中 `RELEASE_ID=20251226`、`BOARD_NAME=lpi4a`)

- 主路径:用 RuyiSDK 的 `ruyi device provision` 交互式下载并写入(底层走 fastboot;包索引中对应 `revyos-sipeed-lpi4a` + `uboot-revyos-sipeed-lpi4a-16g`)

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

- RuyiSDK Support Matrix · LicheePi 4A / RevyOS(当前 good 记录即 20251226;含镜像目录与手工 fastboot 命令)

- Sipeed 官方烧录文档(BOOT 键进烧录模式、接线说明)

```text

主机:ruyi device provision

├─ 选设备:Sipeed LicheePi 4A

├─ 选规格:16G RAM

└─ 选系统:RevyOS 20251226

      ↓ 下载镜像(含校验)

按住 BOOT → 插入 USB-C → fastboot 写入 eMMC

      ↓ 松开 BOOT,重新上电

串口 / SSH 出现登录提示(用户 debian)

```

##### 环境准备

- 主机已按 1.1 装好 `ruyi`,并能 `ruyi --version`。

- 主机安装 `fastboot`(Debian/Ubuntu 示例:`sudo apt install -y fastboot` 或包名 `android-tools-fastboot`,以发行版为准)。

- USB-C 数据线能传数据(仅充电线无法进烧录模式);另备 USB-UART 串口线(3.3V)便于看首次启动日志。

- 板子供电充足;烧录会覆盖 eMMC 上的系统,先备份需要保留的文件。

##### 用 ruyi device provision 烧录(主路径)

1. **更新索引并启动向导**:在主机执行 `ruyi update`,再 `ruyi device provision`。按提示确认后继续。

2. **选择设备与规格**:设备选 **Sipeed LicheePi 4A**;内存规格选 **16G**(8G 板不要选 16G 的 U-Boot 包)。

3. **选择系统镜像版本**:选 **RevyOS**,版本选 **20251226**(与 Support Matrix 当前 good 记录、本课验收样机一致)。向导会下载并校验对应镜像;下载目录以终端提示为准。

4. **进入烧录模式**:板子断电。按住板上 **BOOT** 键不放,再插入连向主机的 USB-C;主机应能枚举到 fastboot 设备。保持按住直至 `fastboot devices` 能看到设备(向导也会提示时机)。

5. **写入并观察预期输出**:按向导确认后开始写入。成功时终端会出现各分区 flash 完成、无 error 的提示;失败则根据报错检查线材、BOOT 时机与 `fastboot devices`。写入完成后按提示松开 BOOT、重新上电。

```bash

# 主机:启动一键准备(交互菜单以本机 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.bin`、`boot-lpi4a-20251225_175338.ext4.zst`、`root-lpi4a-20251225_175338.ext4.zst`,用 `zstd -d` 解压后,按 Support Matrix 中的 `fastboot flash` 顺序写入。细节以该 README 与 Sipeed 文档为准。

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

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

```bash

$ 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/model` | 含 `Lichee Pi 4A 16G` |

| RevyOS 发行标识 | `cat /etc/revyos-release` | `RELEASE_ID=20251226`、`BOARD_NAME=lpi4a` |

| 架构 | `uname -m` | `riscv64` |

| 内存规格 | `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 连接 | 是 | 是 | 电脑与板子之间传文件 |

```bash

# 板端启用 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’

```

```bash

# 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 提供两类自动化能力:

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

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

```bash

# 以本机 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 error`。`file` 读的正是同一个字段——所以 `file` 说它是 RISC-V,就预判了它在 x86 电脑上跑不了;说它是 x86-64,就预判了它在板子上跑不了。

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

```bash

# 把两种产物都拷到板子 /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` 可以走出三种产物。分清「编给谁、能在哪跑」:

```text

同一份 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)。

```text

交叉编译内部(都在电脑上执行)

main.c

↓  编译器:C → RISC-V 汇编

main.s

↓  汇编器:汇编 → 目标文件

main.o

↓  链接器:.o + RISC-V C 库 → 可执行文件

app-riscv(ELF,riscv64)

```

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

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

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

```bash

# 交叉编译 → RISC-V 反汇编

$ riscv64-ruyisdk-linux-gnu-objdump -d mini-riscv | sed -n ‘/:/,/^$/p’

0000000000000614 :

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 ‘/:/,/^$/p’

0000000000001129 :

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, 0` 加 `mv a0, a5` 两条指令,x86 用 `mov $0x0, %eax` 一条——助记符、寄存器、机器码编码全不一样。这就是为什么两边产物不能互换:内核检查 ELF 头时直接拒收。

#### Makefile 跟做:本机 vs 交叉

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

```bash

# 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):

```bash

$ 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 可执行文件,再传到板端并跑出分数。

```bash

# 在 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 可执行文件(尚未上板)。

## 实验

**本章实验 · 三个实验** —— 实验一 板端原生跑通 probe · 实验二 架构陷阱(编错就上不了板)· 实验三 上板通道验收(CoreMark)

### 本章三个实验一览

本章全部围绕「程序怎么到板子上跑起来」这一件事,拆成三个递进的实验。实验一、实验二只需要软件(板端 gcc),没有硬件器材;实验三是正式的通道验收。三个实验都必须在板上跑通、留下可复查证据才算过关。

| 实验 | 层次 | 一句话目标 | 跑在哪 |

| — | — | — | — |

| **实验一**
板端原生:自己编、自己跑 | 软件 | 最小程序 `probe` 在板端用 `make` 编出 RISC-V 产物并跑通 | 荔枝派(原生) |

| **实验二**
架构陷阱:编错就上不了板 | 软件 | 把主机 gcc 编出的 x86 产物传上板,亲眼看到 `Exec format error`,理解为何要「对架构编译」 | 主机 + 荔枝派 |

| **实验三**
上板通道验收(CoreMark) | 通道验收 | 交叉编 CoreMark → SCP 上传 → 板端跑分,归档整套通道证据 | 主机交叉 + 荔枝派 |

**器材:**实验一、实验二只需要「已能 SSH 登录的荔枝派 4A + 板端自带 gcc/make」(讲义 1.2 之后已具备),**不需要任何外设或接线**。

### 实验一 · 板端原生:自己编、自己跑(probe)

荔枝派跑的是一套完整的 Linux,板子上自带 `gcc` 与 `make`(讲义 1.3「三条开发路径」中的板端原生路径)。本实验让板子「自己编、自己跑」一个最小程序,并用 `file` / `readelf` 认一认产物确实是 RISC-V。

#### 步骤

源码在仓库 `chapters/ch01/code/probe/`(一个 `probe.c` + 一个 `Makefile`)。先把它传到板子:

```bash

# 主机上执行:把 probe 目录整个传到板端家目录

$ scp -r chapters/ch01/code/probe user@board-ip:~/

```

然后登录板子,编译、认架构、运行:

```bash

# 板端 shell

$ cd ~/probe

$ make

$ file probe

# 期望输出里含 UCB RISC-V;若没有,说明 make 用的不是板端 gcc

$ readelf -h probe | grep Machine

# 期望:Machine: RISC-V

$ ./probe

# 期望:

# arch = riscv

# ptr size = 8 bytes

# probe done.

```

**本实验成功判据:**`file probe` 出现 RISC-V;`readelf -h` 的 Machine 为 RISC-V;`./probe` 正常打印三行并退出。把三处输出抄进下表。

| 记录字段 | 你的记录 |

| — | — |

| 板端 `uname -m` | |

| `file probe` 一行摘要 | |

| `readelf -h probe \| grep Machine` 输出 | |

| `./probe` 输出(三行) | |

**验收:**上表字段齐全、值正确,即通过实验一。

(配图说明:板端证据(SSH → probe):原生 `make`、`file` 为 RISC-V、`./probe` 跑通)

### 实验二 · 架构陷阱:编错就上不了板

实验一证明「板端原生 gcc 编的能跑」。现在反过来看:**主机 gcc 编的程序**,架构是 x86-64,直接传上板子会怎样?本实验把同一份 `probe.c` 在主机编一次、在板端编一次,两份都拿到板子上跑,亲自确认「对架构编译」这件事。

#### 步骤

```bash

# 主机上执行(这台是 x86 Linux):make 得到 x86-64 产物

$ cd chapters/ch01/code/probe

$ make

$ file probe

# 期望:x86-64 / Advanced Micro Devices X86-64,而不是 RISC-V

$ readelf -h probe | grep Machine

# 期望:Machine: Advanced Micro Devices X86-64

$ scp probe user@board-ip:/tmp/probe.x86

```

```bash

# 板端 shell:先确保自己有 riscv 版,再试跑传上来的 x86 版

$ cd ~/probe && make # 若实验一已编过,这步会跳过

$ file probe

# 期望:UCB RISC-V

$ chmod +x /tmp/probe.x86

$ /tmp/probe.x86

# 期望:cannot execute binary file: Exec format error(退出码 126)

# —— x86 程序在 RISC-V 板上跑不起来,而不是「程序崩溃」

$ ./probe

# 期望:正常打印 arch = riscv —— 板端自己编的就能跑

```

**理解:**`Exec format error` 不是程序写错,而是**产物架构和 CPU 对不上**。要让板子跑,程序必须编成 RISC-V:要么在板端原生编,要么在主机用交叉工具链编(实验三用后者)。讲义 1.3 的反汇编对比,让你看到同一份 C 在不同架构下生成的指令根本不同。

| 记录字段 | 你的记录 |

| — | — |

| 主机 `file probe` 一行摘要(x86-64) | |

| 板端 `file probe` 一行摘要(RISC-V) | |

| 板端跑 `/tmp/probe.x86` 的错误行(`cannot execute binary file: Exec format error`) | |

| 板端跑 `./probe` 的输出首行 | |

| 一句话结论(为什么 x86 产物上不了板) | |

**验收:**两张 `file` 摘要架构不同;x86 产物在板上报 `Exec format error`;riscv 产物正常跑;结论句写对。

(配图说明:板端证据(SSH → 架构陷阱):`/tmp/probe.x86` → Exec format error;板端 `./probe` 正常)

### 实验三 · 上板通道验收(CoreMark)

实验一、实验二已经证明:板端能原生编、能跑;主机默认 gcc 的产物上不了板。实验三把整条「主机交叉编译 → 传输 → 板端运行」通道用到正式程序 CoreMark 上,并留下可复查的分数与证据。

### 本实验要做什么

本实验要证明一件事:你在主机上用交叉工具链编出的 RISC-V 程序,能够完整传到荔枝派 4A,并在板端真正跑起来。我们用 CoreMark 作为这份程序:你将留下编译产物、传输核对与板端运行的完整证据,使整条通道可以被复查。分数用来确认程序确实执行成功;可复查的通道证据,才是本章实验要交付的成果。

开始之前,请确认讲义 1.1 至 1.3 已经做完:交叉工具链可用,板子可以登录,主机上已经交叉编出 CoreMark,并且用 `file` 确认产物架构是 RISC-V。交叉编译的完整命令以讲义为准;本页从「核对已有产物」开始,完成传输、跑分与归档。

你将依次完成这些事:在主机上再次用 `file` 与 `sha256sum` 核对产物;用 SCP 把文件传到板子并确认两端哈希一致;在板端加上执行权限并跑出 CoreMark 分数;把分数、日志与通道自证输出写入记录表。

**通过标准:**记录表与验收表中的项目全部齐全。本章实验采用主机交叉编译得到的可执行文件完成上板;讲义中介绍的板端原生编译可作为对照理解,但本章实验的通过条件以交叉编译产物为准。

### 前提自检

请先确认下表每一项都已具备。若有缺项,回到对应讲义小节补齐后再继续本页步骤。

| 前提 | 你怎么确认「已经具备」 | 对应讲义 | □ |

| — | — | — | — |

| ruyi 与交叉工具链已安装 | 在 **x86 Linux** 上激活 `venv-gnu-ruyisdk` 后执行 `riscv64-ruyisdk-linux-gnu-gcc -dumpmachine`,输出为 `riscv64-ruyisdk-linux-gnu` | 1.1 | |

| 荔枝派 4A 已烧录并可启动 | 上电能登录;`cat /etc/revyos-release` 中 `RELEASE_ID=20251226`;`uname -m` 为 `riscv64`(讲义 1.2) | 1.2 | |

| 串口或 SSH 至少一种可登录板端 | 能进入板端 shell,并执行简单命令(如 `hostname`) | 1.2 | |

| 主机上已有 RISC-V 架构的 CoreMark 可执行文件 | 对产物执行 `file`,输出中含 `RISC-V` 或 `UCB RISC-V`(架构为 x86-64 则需回到讲义 1.3 重编) | 1.3 | |

### 步骤

下面是本实验的主体步骤。若交叉编译命令需要重做,请回到讲义 1.3。

#### 1. 在主机确认 CoreMark 产物架构

在 **x86 Linux** 上进入 `chapters/ch01/code`(或板上同步目录 `~/ruyi-riscv-embedded/ch01-lab` 的主机交叉产物):

```bash

$ file coremark/coremark.exe

# 成功:一行摘要里应出现 UCB RISC-V 或 RISC-V 字样

# 失败:若出现 x86-64 / Intel 80386,说明没用交叉工具链,回讲义 1.3 重编

$ sha256sum coremark/coremark.exe

# 记下这串哈希,稍后与板端对照,证明 SCP 传的是同一份文件

```

**本步成功判据:**`file` 确认是 RISC-V ELF;你已把 `sha256sum` 结果抄进下方「必记字段」表。

(配图说明:图 1 在 x86 Linux 上激活 `venv-gnu-ruyisdk` 后 `make`:产物为 RISC-V ELF)

#### 2. SCP 上传到荔枝派 4A 并核对完整性

**SCP**(Secure Copy)通过 SSH 通道把文件拷到远端。把下面命令里的 `user` 和 `board-ip` 换成你板子的真实用户名与局域网 IP(可先在板端执行 `ip a` 查看)。

```bash

$ scp coremark/coremark.exe user@board-ip:/tmp/

$ ssh user@board-ip ‘sha256sum /tmp/coremark.exe’

# 板端打印的哈希必须与主机上一步完全一致

```

**本步成功判据:**两端哈希一致;板端路径 `/tmp/coremark.exe`(或你实际选用的路径)可被列出。

#### 3. 板端赋予执行权限并跑分

```bash

$ ssh user@board-ip

# 已进入板端 shell 后:

$ chmod +x /tmp/coremark.exe

$ /tmp/coremark.exe

```

程序会运行一段时间后打印结果。请从输出中抄录 CoreMark 分数(常见字段为 `Iterations/Sec`,或汇总行里的 CoreMark 总分),并把完整终端输出另存为日志文件(截图或重定向均可),同时写入下方「必记字段」表。

**本步成功判据:**程序正常结束并打出分数;你已把分数与运行日期写入「必记字段」表,并保存完整日志。

(配图说明:图 2 板端证据(SSH → CoreMark):产物为 RISC-V,跑分通过校验(并列原生路径;验收仍以交叉+SCP 为准))

本实验验收走步骤 2–3 的 SCP 路径。图 2 是板上自己编译/跑分的对照,两条都会即可。

**排错:**板端报 `Exec format error` 时,先回主机用 `file` 核对是否误用了主机 GCC;再核对 SCP 目标路径与 `chmod +x` 是否做过。若 SSH 连不上,回讲义 1.2 检查网线、IP 与防火墙。命令细节以讲义 1.2 / 1.3 为准。

#### 必记字段

| 字段 | 你的记录 |

| — | — |

| 产物文件名 | |

| 主机 `file` 一行摘要 | |

| 主机与板端 `sha256sum`(须一致) | |

| 板端可执行文件路径 | |

| CoreMark 分数(含字段名) | |

| 运行日期 / 板卡型号 | |

#### 4. 通道与工具链自证

除跑分外,还要留下三份「通道还活着」的证据。每条保留一行真实输出(可粘贴到实验报告)。

```bash

# 板端:证明 SSH 能登录,且架构是 riscv64

$ ssh user@board-ip ‘hostname; uname -m’

# uname -m 的期望输出:riscv64

# 主机:证明交叉工具链三元组正确

$ riscv64-ruyisdk-linux-gnu-gcc -dumpmachine

# 期望输出:riscv64-ruyisdk-linux-gnu

```

**本步成功判据:**三行输出均已保存;`uname -m` 为 `riscv64`;`-dumpmachine` 为 `riscv64-ruyisdk-linux-gnu`。

### 验收表

全部勾选且材料齐全,即完成本章实验。缺证据视为未通过。

| 验收项 | 证据 | □ |

| — | — | — |

| 烧录后荔枝派 4A 可启动 | 启动日志或串口/SSH 登录截图 | |

| SSH 可登录板端 | `ssh … ‘hostname; uname -m’` 输出 | |

| `uname -m` 为 `riscv64` | 同上日志中的架构行 | |

| 工具链三元组正确 | `-dumpmachine` → `riscv64-ruyisdk-linux-gnu` | |

| CoreMark 交叉编译产物为 RISC-V | 主机 `file` 输出 | |

| SCP 上板成功且文件完整 | 两端 `sha256sum` 一致 | |

| 板端跑出 CoreMark 分数并已记录 | 完整运行日志 + 「必记字段」表 | |

**完成标志:**验收表全部勾选;CoreMark 分数与三项自证输出已归档。通道打通后进入第二章。