目录
2.1 QEMU
2.1.1 创建非标准的Rust程序
2.1.2 代码简介
2.1.3 交叉编译
2.1.4 二进制文件分析工具
2.1.5 运行
2.1.6 调试
在本节中,我们将引导您完成编写,构建,刷新和调试嵌入式程序的过程。您将能够在没有任何特殊硬件的情况下尝试大多数示例,因为我们将向您展示使用QEMU(一种流行的开源硬件仿真器)的基础知识。唯一需要硬件的部分自然就是硬件部分,我们使用OpenOCD对自己的开发板进行编程。
我们将开始为LM3S6965编写一个程序,这是一个Cortex-M3微控制器。我们选择这个作为我们的初始目标,因为它可以使用QEMU进行仿真,因此您不需要在本节中使用硬件,我们可以专注于工具和开发过程。
重要 我们将在本教程中使用名称“app”作为项目名称。每当您看到“app”一词时,您应该将其替换为您为项目选择的名称。或者,您也可以将项目命名为“app”并避免替换。
我们将从cortex-m-quickstart项目模板中生成一个新项目。
使用cargo-generate
首先安装cargo-generate
cargo install cargo-generate
然后生成新项目
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
Project Name: app
Creating project called `app`...
Done! New project created /tmp/app
cd app
使用git
克隆repository
git clone https://github.com/rust-embedded/cortex-m-quickstart app
cd app
然后在Cargo.toml文件中填写占位符
[package]
authors = ["{{authors}}"] # "{{authors}}" -> "John Smith"
edition = "2018"
name = "{{project-name}}" # "{{project-name}}" -> "awesome-app"
version = "0.1.0"
# ..
[[bin]]
name = "{{project-name}}" # "{{project-name}}" -> "awesome-app"
test = false
bench = false
以上两者都不用
获取cortex-m-quickstart模板的最新快照并将其解压缩。
curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip
unzip master.zip
mv cortex-m-quickstart-master app
cd app
或者您可以浏览到cortex-m-quickstart,单击绿色的“克隆或下载”按钮,然后单击“下载ZIP”。然后在“使用git”版本的第二部分中填写Cargo.toml文件中的占位符。
为方便起见,这里是src / main.rs中源代码最重要的部分:
#![no_std]
#![no_main]
extern crate panic_halt;
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
loop {
// your code goes here
}
}
这个程序与标准的Rust程序有点不同,所以让我们仔细看看。
core
crate。fn main() -> !。我们的程序将是目标硬件上运行的唯一进程,因此我们不希望它结束!我们使用一个 divergent function(函数签名中的 - >!位)来确保在编译时就是这种情况。
下一步是交叉编译Cortex-M3架构的程序。这就像运行cargo build --target $TRIPLE一样简单 - 如果您知道编译目标($triple)应该是什么。幸运的是,模板中的.cargo / config有答案:
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
要为Cortex-M3架构进行交叉编译,我们必须使用thumbv7m-none-eabi。此编译目标已设置为默认值,因此下面的两个命令执行相同的操作:
cargo build --target thumbv7m-none-eabi
cargo build
现在我们在target / thumbv7m-none-eabi / debug / app中有一个非本机ELF二进制文件。我们可以使用cargo-binutils进行检查。使用cargo-readobj,我们可以打印ELF头以确认这是一个ARM二进制文件。
注意:
cargo-size可以打印二进制文件的链接器部分的大小。
注:该输出假设rust-embedded/cortex-m-rt#111已合并。
cargo size --bin app --release -- -A
我们使用--release检查优化版本
关于ELF链接器部分的扩展:
重要:ELF文件包含调试信息之类的元数据,因此它们在磁盘上的大小无法准确反映程序拷贝到设备时占用的空间。始终使用cargo-size来检查二进制文件的实际大小。
cargo-objdump可用于反汇编二进制文件。
cargo objdump --bin app --release -- -disassemble -no-show-raw-insn -print-imm-hex
注意此输出可能因系统而异。新版本的rustc,LLVM和库可以生成不同的汇编语言。 我们截取了一些指令以使代码片段保持较小。
接下来,让我们看看如何在QEMU上运行嵌入式程序!这一次我们将使用Hello示例,它实际上可以做一些事情。为了方便起见,这里是examples/hello.rs的源代码:
//! Prints "Hello, world!" on the host console using semihosting
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};
#[entry]
fn main() -> ! {
hprintln!("Hello, world!").unwrap();
// exit QEMU
// NOTE do not run this on hardware; it can corrupt OpenOCD state
debug::exit(debug::EXIT_SUCCESS);
loop {}
}
该程序使用称为semihosting的东西将文本打印到主机控制台。当使用真正的硬件时,这需要一个调试会话,但当使用QEMU时,这才有效。让我们从编译示例开始:
cargo build --example hello
输出二进制文件将位于target/thumbv7m-none-eabi/debug/examples/hello。要在QEMU上运行此二进制文件,请运行以下命令:
qemu-system-arm \
-cpu cortex-m3 \
-machine lm3s6965evb \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/thumbv7m-none-eabi/debug/examples/hello
打印文本后,该命令应成功退出(退出代码= 0)。在*nix上,您可以使用以下命令检查:
echo $?
0
让我们分解一下QEMU命令:
输入这么长的qemu命令太费事了!我们可以设置一个自定义的运行程序来简化这个过程。.cargo/config有一个引用qemu的注释掉的运行程序;让我们取消注释:
[target.thumbv7m-none-eabi]
# 取消注释,'cargo run'使程序在QEMU上执行
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
此运行程序仅适用于thumbv7m-none-eabi目标,这是我们的默认编译目标。 cargo run将编译程序并在qemu上运行:
cargo run --example hello --release
调试对嵌入式开发至关重要。让我们看看是怎么做到的。
调试嵌入式设备涉及远程调试,因为我们要调试的程序不会在运行调试程序(gdb或lldb)的计算机上运行。
远程调试涉及客户机和服务器。在QEMU设置中,客户机将是一个gdb(或lldb)进程,服务器将是运行嵌入式程序的QEMU进程。在本节中,我们将使用已经编译的hello示例。第一个调试步骤是在调试模式下启动QEMU:
qemu-system-arm \
-cpu cortex-m3 \
-machine lm3s6965evb \
-nographic \
-semihosting-config enable=on,target=native \
-gdb tcp::3333 \
-S \
-kernel target/thumbv7m-none-eabi/debug/examples/hello
此命令不会将任何内容打印到控制台,并且会阻止终端。这次我们又传递了两个标志:
接下来,我们在另一个终端启动GDB并告诉它加载示例的调试符号:
gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello
注意:您可能需要另一个版本的gdb而不是gdb multiarch,这取决于您在安装章节中安装了哪个版本。这也可以是 arm-none-eabi-gdb
或只是gdb。然后在GDB shell中我们连接到QEMU,它正在等待TCP端口3333上的连接。
target remote :3333
您将看到进程停止,程序计数器指向一个名为reset的函数。这是重置处理程序:启动时Cortex-M核心执行的操作。这个重置处理程序最终将调用我们的主函数。让我们使用断点和continue命令一路跳过:
我们现在已经接近打印“Hello, world!”的代码了。 让我们使用next命令继续前进:
到这里,你应该看到“Hello, world!”打印在运行qemu-system-arm的终端上。再次调用Next将终止QEMU进程。
现在可以退出gdb会话。