这里以 Ubuntu 18.04 作为宿主环境,其它 Linux 发行版类似。Windows 环境待更新。
wget https://cdn.jsdelivr.net/gh/rust-lang-nursery/rustup.rs/rustup-init.sh
export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
sh rustup-init.sh
按默认选项安装即可,安装完成后检查 Rust 编译器:
source $HOME/.cargo/env
rustc -V
如果能打印出类似rustc 1.48.0 (7eac88abb 2020-11-16)
的版本信息,说明安装成功了。
source $HOME/.cargo/env
将 rustc 所在的目录加入到 PATH 环境变量,系统重启后会自动添加,不需要再执行这条命令。
RUSTUP_DIST_SERVER
和 RUSTUP_UPDATE_ROOT
这两个环境变量的设置可以放到 .profile
文件内,这样不用每次使用 rustup
都重新设置环境变量。
echo "export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static" >> ~/.profile
echo "export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup" >> ~/.profile
按照官方的文档是这么安装 Rust 的:
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
,不过 sh.rustup.rs 是国外网站,速度很慢,而且经常超时,这里使用国内镜像安装。
为了让 Rust 编译器产生 RISC-V 的指令集,必须安装 RISC-V 编译器后端。
查看 Rust 支持的后端:
rustup target list
可以看到 Rust 支持很多后端,RISC-V 的后端有:
riscv32gc-unknown-linux-gnu
riscv32i-unknown-none-elf
riscv32imac-unknown-none-elf
riscv32imc-unknown-none-elf
riscv64gc-unknown-linux-gnu
riscv64gc-unknown-none-elf
riscv64imac-unknown-none-elf
我们需要安装 riscv32imac-unknown-none-elf 后端:
rustup target add riscv32imac-unknown-none-elf
下载 riscv-none-embed-gcc ,要用里面的 riscv-none-embed-gdb 作为调试器。
下载 tgz 类型的文件,解压,然后将 /path/to/gnu-mcu-eclipse/riscv-none-gcc/x.y.z-xxx/bin 添加到 PATH 路径。
gdb-multiarch 目前似乎不支持 RISC-V。
cargo install cargo-binutils
rustup component add llvm-tools-preview
sudo apt install pkg-config libssl-dev
cargo install cargo-generate
run pkg_config fail
错误,如果没有 libssl-dev 会导致 Package openssl was not found in the pkg-config search path
错误。
- Cargo 是 Rust 的构建系统和软件包管理器。
- Rustup 是 Rust 的工具链安装和管理工具。
cargo generate --git https://github.com/riscv-rust/riscv-rust-quickstart
通过模板创建工程,根据提示输入工程名称,这里假设输入 app
。进入新建的工程目录:
cd app
cargo build
新建工程第一次构建,cargo 会自动下载依赖包,然后开始编译程序及依赖包。默认构建 debug 版本,编译好的程序:/path/to/app/target/riscv32imac-unknown-none-elf/debug/app
构建 release 版本:
cargo build --release
release 版本的程序:/path/to/app/target/riscv32imac-unknown-none-elf/release/app
检查文件头:
cargo readobj --bin app -- -file-headers
cargo readobj --bin app --release -- -file-headers
检查程序大小:
cargo size --bin app
cargo size --bin app --release
反汇编程序:
cargo objdump --bin app -- -d
cargo objdump --bin app --release -- -d
下载 Renode Linux portable 版本,这是个免安装版,解压后即可使用。
解压:
mkdir renode_portable
tar xf renode-*.linux-portable.tar.gz -C renode_portable --strip-components=1
将 renode 目录添加到 PATH 环境变量。
参考 Renode - Installation - Using the Linux portable release。
Renode 是开源的模拟器,可以模拟 Cortex-M、RISC-V 等微控制器,不仅可以模拟 CPU 指令,还可以模拟外设,甚至可以模拟板载的外设。它与 QEMU 的关注点不同,QEMU 主要用来模拟标准化的高性能 PC,Renode 主要来用模拟物联网设备,Renode 可以同时模拟多个设备,而且这些设备可以互联互通。这里将用 Renode 模拟 HiFive1 开发板。
:name: HiFive1
$name?="HiFive1"
using sysbus
mach create $name
machine LoadPlatformDescription @platforms/cpus/sifive-fe310.repl
machine StartGdbServer 3333
showAnalyzer uart0
sysbus Tag <0x10008000 4> "PRCI_HFROSCCFG" 0xFFFFFFFF
sysbus Tag <0x10008004 4> "PRCI_HFXOSCCFG" 0xFFFFFFFF
sysbus Tag <0x10008008 4> "PRCI_PLLCFG" 0xFFFFFFFF
cpu PC 0x20010000
cpu PerformanceInMips 320
将上述内容保存到文件 hifive1.resc。
上述文件根据 renode 安装目录下 /path/to/renode/scripts/single-node/sifive_fe310.resc 修改。
renode
启动Renode模拟器,Renode 启动后会开启新的命令窗口用作 Renode 命令输入,原来的命令行窗口作为 Renode 的日志输出窗口使用。下图所示的窗口是 Renode 命令窗口,在该窗口输入 Renode 命令。
(monitor) i @/path/to/hifive1.resc
请根据 hifive1.resc 的存储路径修改上面的命令。
执行了这个命令后,还会弹出一个 HiFive1:sysbus.uart0 窗口,这个是串口数据监视窗口,暂时不去理会它。
启动 GDB 调试器:
riscv-none-embed-gdb /path/to/app/target/riscv32imac-unknown-none-elf/release/app
GDB 连接到 Renode 模拟器,Renode 通过 machine StartGdbServer 3333
开启了 GDB Server,监听端口 3333 :
(gdb) target remote :3333
装载程序:
(gdb) load
设置断点:
(gdb) b main
运行程序:
(gdb) monitor start
(gdb) c
monitor start 指令是传达给 Renode 的,告知 Renode 可以开始执行了。
接下来,该怎么调试就怎么调试了。
调试完成后,在 Renode 控制窗口输入 quit 推出模拟器:
(monitor) quit
推荐使用 Eclipse IDE for Rust Developers,原因有二:一是 Eclipse 几乎是嵌入式开发领域的事实标准,国内国外的半导体厂商以及开发工具厂商推出的嵌入式开发工具几乎都是基于 Eclipse 平台,甚至有些厂商废弃原来自有平台转向 Eclipse 平台。二是 Eclipse 的调试功能比较完备,支持各种类型的断点、反汇编、指令单步、函数调用栈、局部变量、全局变量、处理器寄存器、外设寄存器等等。
除了 Eclipse 外,更多的开发工具见 Rust Tools。
首先安装 RLS(Rust 语言服务器),Eclipse 需要这个:
rustup component add rls rust-analysis rust-src
RLS 提供了一个在后台运行的服务器,提供了Rust编程的相关信息,包括IDE,编辑器和其它工具。参考 Rust 语言服务器 (RLS) ,Rust Language Server (RLS)。如果没有安装 RLS,打开 Eclipse 会报告 Rust Support Not Found 错误。
前往 Eclipse 下载中心 下载 Eclipse IDE for Rust Developers。
tar -xf /path/to/eclipse-rust-2020-09-R-linux-gtk-x86_64.tar.gz
解压后打开 eclipse:
cd eclipse
./eclipse
选择菜单 Help >> Eclipse Marketplace … 打开应用市场,搜索安装 Eclipse Embedded C/C++ 插件,后面的调试需要用到这个插件。
选择菜单 File >> Open Projects form File System … 打开 Import Projects from File System or Archive 对话框,Import source 填入前面用 cargo generate
创建的app工程目录,点击 Finish 打开工程。
Eclipse 2020-09 还 不支持 通过菜单 Project >> Build Project 构建工程,因此还是只能在命令行中用 cargo 命令来构建。
可以在 Eclipse 中打开 Terminal 视图,在 Terminal 视图输入 cargo 命令,这样不需要来回切换窗口。
选择菜单 Window >> Show View >> Others … ,找到 Terminal 打开视图。点击 Terminal 工具栏最左边的按钮启动一个终端:
选择 Local Terminal 。然后就可以在新建的终端内输入 cargo 命令。
首先启动 Renode 模拟器,假如还没有启动的话:
renode -e "i @/path/to/hifive1.resc" --hide-analyzers
这次直接在 renode 命令行就指定了要在 (monitor) 输入的命令
i @/path/to/hifive1.resc
,--hide-analyzers
将隐藏串口输出窗口,现在暂时用不到。
选择菜单 Run >> Debug Configurations … 打开调试配置对话框。左边类型栏选择 GDB Hardware Debugging,点击右键弹出菜单选择 New Configuration。
Main 选项页,Project 填入工程名,C/C++ Application 填入要调试的程序,即 cargo build 生成的程序。
Debugger 选项页,GDB Command 填入 riscv-none-embed-gdb
,JTAG Device 选择 Generic TCP/IP,GDB Connection String 填入 localhost:3333
。
Startup 选项页,勾上 Set breakpoint at,后面输入框填入 main
,下方输入框填入 monitor start
,如果不希望停在 _start 的位置,勾上Resume。
完成配置后,点击 Debug 按钮进入调试模式。
在 Renode 模拟器中运行时,GDB 连接到模拟器开启的 GDB Server,在 HiFive1 开发板中运行时,GDB 连接到 OpenOCD 开启的 GDB Server。OpenOCD 通过 JTAG 和物理上的处理器进行交互。
这里下载 OpenOCD。
解压:
tar -xf /path/to/xpack-openocd-0.10.0-15-linux-x64.tar.gz
将 openocd 所在目录加入 PATH 环境变量。
拷贝 openocd.rules 文件到系统目录:
sudo cp xpack-openocd-0.10.0-15/contrib/60-openocd.rules /etc/udev/rules.d/
重启系统,让 openocd-rules 生效。
将 HiFive1 开发板通过数据线连接到计算机。
打开 OpenOCD :
openocd -f board/sifive-hifive1.cfg
调整调试配置 Debugger 选项页的 GDB Connection String,OpenOCD 默认端口为 3333。
Startup 选项页删除 monitor start
。
调整好了之后,点击 Debug 按钮开始调试。
通过 riscv-rust-quickstart 模板创建的工程,在 examples 目录下包含了多个示例程序,其中就有闪灯的 led_gpio 示例、串口打印的 hello_world 示例等。 以 hello_world 示例为例。
构建 hello_world 示例:
cargo build --example hello_world
renode -e "i @/path/to/hifive1.resc"
创建 Eclipse 调试配置项,Main 选项页的 C/C++ Application 填入刚生成的 hello_world:/path/to/app/target/riscv32imac-unknown-none-elf/debug/examples/hello_world,其它选项与之前的一样。
编译好的示例程序位于 target/riscv32imac-unknown-none-elf/debug/examples 目录下。发布版位于 target/riscv32imac-unknown-none-elf/release/examples 目录下
配置完成后点击 Debug 开始调试。执行到 sprintln!("hello world!");
这一句可以看到 HiFive1:sysbus.uart0
串口打印出了字符串 hello world!
。
闪灯的示例在模拟器里执行看不到 LED 灯,按照 在 HiFive1 开发板中执行 跑在 HiFive1 开发板就可以看到 LED 闪烁了。
Eclipse 调试 leds_blink 会失去响应?调试 hello_world 和 led_gpio 这两个示例不会有这个问题。这是何故?