Merge branch 'master' into patch-10

This commit is contained in:
Vadim Kaushan
2019-05-01 21:36:31 +02:00
23 changed files with 228 additions and 231 deletions

View File

@@ -1,10 +1,10 @@
# [Work in Progress] - The Embedded Rust Book
# The Embedded Rust Book
> Documentation on how to use the Rust Programming Language to develop firmware for bare metal (microcontroller) devices
This project is developed and maintained by the [Resources team][team].
See [the issue tracker] for more details. This is a very early work in progress.
See [the issue tracker] for more details. This book is a living document, and is updated continuously.
[the issue tracker]: https://github.com/rust-embedded/book/issues
@@ -43,4 +43,4 @@ Conduct][CoC], the maintainer of this crate, the [Resources team][team], promise
to intervene to uphold that code of conduct.
[CoC]: CODE_OF_CONDUCT.md
[team]: https://github.com/rust-embedded/wg#the-cortex-m-team
[team]: https://github.com/rust-embedded/wg#the-resources-team

View File

@@ -2,6 +2,7 @@ set -euxo pipefail
main() {
mdbook build
mdbook test
# FIXME(rust-lang-nursery/mdbook#789) remove `--ignore-url` when that bug is fixed
linkchecker --ignore-url "print.html" book

View File

@@ -10,6 +10,7 @@ more information and coordination
-->
- [Introduction](./intro/index.md)
- [Hardware](./intro/hardware.md)
- [`no_std`](./intro/no-std.md)
- [Tooling](./intro/tooling.md)
- [Installation](./intro/install.md)
@@ -17,7 +18,6 @@ more information and coordination
- [MacOS](./intro/install/macos.md)
- [Windows](./intro/install/windows.md)
- [Verify Installation](./intro/install/verify.md)
- [Hardware](./intro/hardware.md)
- [Getting started](./start/index.md)
- [QEMU](./start/qemu.md)
- [Hardware](./start/hardware.md)

View File

@@ -79,7 +79,7 @@ for example:
const fn array_size() -> usize {
#[cfg(feature="use_more_ram")]
{ 1024 }
#[cfg(not(feature="use_more_ram")]
#[cfg(not(feature="use_more_ram"))]
{ 128 }
}
@@ -102,7 +102,7 @@ item, or pattern. Procedural macros are more complex but permit extremely
powerful additions to the Rust language: they can transform arbitrary Rust
syntax into new Rust syntax.
[macro system]: https://doc.rust-lang.org/book/second-edition/appendix-04-macros.html
[macro system]: https://doc.rust-lang.org/book/ch19-06-macros.html
In general, where you might have used a C preprocessor macro, you probably want
to see if a macro-by-example can do the job instead. They can be defined in
@@ -180,7 +180,7 @@ happily index outside the array.
Instead, use iterators:
```rust
```rust,ignore
let arr = [0u16; 16];
for element in arr.iter() {
process(*element);
@@ -194,7 +194,7 @@ data processing code.
See the [Iterators in the Book] and [Iterator documentation] for more details.
[Iterators in the Book]: https://doc.rust-lang.org/book/second-edition/ch13-02-iterators.html
[Iterators in the Book]: https://doc.rust-lang.org/book/ch13-02-iterators.html
[Iterator documentation]: https://doc.rust-lang.org/core/iter/trait.Iterator.html
## References vs Pointers
@@ -259,7 +259,7 @@ void driver() {
The equivalent in Rust would use volatile methods on each access:
```rust
```rust,ignore
static mut SIGNALLED: bool = false;
#[interrupt]

View File

@@ -118,7 +118,7 @@ fn on_oom(_layout: Layout) -> ! {
Once all that is in place, the user can finally use the collections in `alloc`.
``` rust
```rust,ignore
#[entry]
fn main() -> ! {
let mut xs = Vec::new();
@@ -140,7 +140,7 @@ as they are exact same implementation.
`heapless` requires no setup as its collections don't depend on a global memory
allocator. Just `use` its collections and proceed to instantiate them:
``` rust
```rust,ignore
extern crate heapless; // v0.4.x
use heapless::Vec;

View File

@@ -22,7 +22,7 @@ are no interrupts at all. Sometimes this is perfectly suited to the problem
at hand! Typically your loop will read some inputs, perform some processing,
and write some outputs.
```rust
```rust,ignore
#[entry]
fn main() {
let peripherals = setup_peripherals();
@@ -58,7 +58,7 @@ For an example of how this behaviour can cause subtle errors in your code,
consider an embedded program which counts rising edges of some input signal
in each one-second period (a frequency counter):
```rust
```rust,ignore
static mut COUNTER: u32 = 0;
#[entry]
@@ -99,7 +99,7 @@ sections_, a context where interrupts are disabled. By wrapping the access to
`COUNTER` in `main` in a critical section, we can be sure the timer interrupt
will not fire until we're finished incrementing `COUNTER`:
```rust
```rust,ignore
static mut COUNTER: u32 = 0;
#[entry]
@@ -160,7 +160,7 @@ of the time, but if it was interrupted it will automatically retry the entire
increment operation. These atomic operations are safe even across multiple
cores.
```rust
```rust,ignore
use core::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
@@ -215,7 +215,7 @@ We can abstract our counter into a safe interface which can be safely used
anywhere else in our code. For this example we'll use the critical-section
counter, but you could do something very similar with atomics.
```rust
```rust,ignore
use core::cell::UnsafeCell;
use cortex_m::interrupt;
@@ -340,7 +340,7 @@ the lock/unlock state of the mutex.
This is in fact done for us in the `cortex_m` crate! We could have written
our counter using it:
```rust
```rust,ignore
use core::cell::Cell;
use cortex_m::interrupt::Mutex;
@@ -410,7 +410,7 @@ the shared variable after it has been initialised in the main code. To do
this we can use the `Option` type, initialised to `None` and later set to
the instance of the peripheral.
```rust
```rust,ignore
use core::cell::RefCell;
use cortex_m::interrupt::{self, Mutex};
use stm32f4::stm32f405;

View File

@@ -120,7 +120,7 @@ For projects with limited dependencies or complexity, or for projects where it i
In the simplest case of compiling a single C file as a dependency to a static library, an example `build.rs` script using the [`cc` crate] would look like this:
```rust
```rust,ignore
extern crate cc;
fn main() {

View File

@@ -8,11 +8,9 @@ Let's get familiar with the hardware we'll be working with.
<img title="F3" src="../assets/f3.jpg">
</p>
We'll refer to this board as "F3" throughout this book.
What does this board contain?
- A STM32F303VCT6 microcontroller. This microcontroller has
- A [STM32F303VCT6](https://www.st.com/en/microcontrollers/stm32f303vc.html) microcontroller. This microcontroller has
- A single-core ARM Cortex-M4F processor with hardware support for single-precision floating point
operations and a maximum clock frequency of 72 MHz.
@@ -20,25 +18,22 @@ What does this board contain?
- 48 KiB of RAM.
- many "peripherals": timers, GPIO, I2C, SPI, USART, etc.
- A variety of integrated peripherals such as timers, I2C, SPI and USART.
- lots of "pins" that are exposed in the two lateral "headers".
- General purpose Input Output (GPIO) and other types of pins accessible through the two rows of headers along side the board.
- A USB interface accessible through the USB port labeled "USB USER".
- **IMPORTANT** This microcontroller operates at (around) 3.3V.
- An [accelerometer](https://en.wikipedia.org/wiki/Accelerometer) as part of the [LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html) chip.
- An [accelerometer] and a [magnetometer][] (in a single package).
- A [magnetometer](https://en.wikipedia.org/wiki/Magnetometer) as part of the [LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html) chip.
[accelerometer]: https://en.wikipedia.org/wiki/Accelerometer
[magnetometer]: https://en.wikipedia.org/wiki/Magnetometer
- A [gyroscope](https://en.wikipedia.org/wiki/Gyroscope) as part of the [L3GD20](https://www.pololu.com/file/0J563/L3GD20.pdf) chip.
- A [gyroscope].
- 8 user LEDs arranged in the shape of a compass.
[gyroscope]: https://en.wikipedia.org/wiki/Gyroscope
- A second microcontroller: a [STM32F103](https://www.st.com/en/microcontrollers/stm32f103cb.html). This microcontroller is actually part of an on-board programmer / debugger and is connected to the USB port named "USB ST-LINK".
- 8 user LEDs arranged in the shape of a compass
For a more detailed list of features and further specifications of the board take a look at the [STMicroelectronics](https://www.st.com/en/evaluation-tools/stm32f3discovery.html) website.
- A second microcontroller: a STM32F103CBT. This microcontroller is actually part of an on-board
programmer and debugger named ST-LINK and is connected to the USB port named "USB ST-LINK".
- There's a second USB port, labeled "USB USER" that is connected to the main microcontroller, the
STM32F303VCT6, and can be used in applications.
A word of caution: be careful if you want to apply external signals to the board. The microcontroller STM32F303VCT6 pins take a nominal voltage of 3.3 volts. For further information consult the [6.2 Absolute maximum ratings section in the manual](https://www.st.com/resource/en/datasheet/stm32f303vc.pdf)

View File

@@ -4,8 +4,8 @@ Welcome to The Embedded Rust Book: An introductory book about using the Rust
Programming Language on "Bare Metal" embedded systems, such as Microcontrollers.
## Who Embedded Rust is For
Embedded Rust is for everyone who wants to do embedded programming backed by the higher-level concepts and safety guarantees the Rust language provides.
(See also [Who Rust Is For](https://doc.rust-lang.org/book/2018-edition/ch00-00-introduction.html))
Embedded Rust is for everyone who wants to do embedded programming while taking advantage of the higher-level concepts and safety guarantees the Rust language provides.
(See also [Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-introduction.html))
## Scope
@@ -23,16 +23,16 @@ The goals of this book are:
This book tries to be as general as possible but to make things easier for both
the readers and the writers it uses the ARM Cortex-M architecture in all its
examples. However, the book assumes that the reader is not familiar with this
examples. However, the book doesn't assume that the reader is familiar with this
particular architecture and explains details particular to this architecture
where required.
## Who This Book is For
This book caters towards people with either some embedded background or some Rust background, however we assume
This book caters towards people with either some embedded background or some Rust background, however we believe
everybody curious about embedded Rust programming can get something out of this book. For those without any prior knowledge
we suggest you read the "Assumptions and Prerequisites" section and catch up on missing knowledge to get more out of the book
and improve your reading experience. You can check out the "Other Resources" section to find resources on topics
you want to catch up on.
you might want to catch up on.
### Assumptions and Prerequisites
@@ -41,7 +41,7 @@ you want to catch up on.
be familiar with the idioms of the [2018 edition] as this book targets
Rust 2018.
[2018 edition]: https://rust-lang-nursery.github.io/edition-guide/
[2018 edition]: https://doc.rust-lang.org/edition-guide/
* You are comfortable developing and debugging embedded systems in another
language such as C, C++, or Ada, and are familiar with concepts such as:
@@ -55,7 +55,7 @@ If you are unfamiliar with anything mentioned above or if you want more informat
| Topic | Resource | Description |
|--------------|----------|-------------|
| Rust | [Rust Book 2018 Edition](https://doc.rust-lang.org/book/2018-edition/index.html) | If you are not yet comfortable with Rust, we highly suggest reading the this book. |
| Rust | [Rust Book](https://doc.rust-lang.org/book/) | If you are not yet comfortable with Rust, we highly suggest reading this book. |
| Rust, Embedded | [Embedded Rust Bookshelf](https://docs.rust-embedded.org) | Here you can find several other resources provided by Rust's Embedded Working Group. |
| Rust, Embedded | [Embedonomicon](https://docs.rust-embedded.org/embedonomicon/) | The nitty gritty details when doing embedded programming in Rust. |
| Rust, Embedded | [embedded FAQ](https://docs.rust-embedded.org/faq.html) | Frequently asked questions about Rust in an embedded context. |
@@ -72,7 +72,7 @@ not dig into details on a topic, revisiting the topic in a later chapter.
This book will be using the [STM32F3DISCOVERY] development board from
STMicroelectronics for the majority of the examples contained within. This board
is based on the ARM Cortex-M architecture, and while basic functionality is
common across most CPUs based on this architecture, peripherals and other
the same across most CPUs based on this architecture, peripherals and other
implementation details of Microcontrollers are different between different
vendors, and often even different between Microcontroller families from the same
vendor.

View File

@@ -16,25 +16,25 @@ rustc 1.31.1 (b6c32da9b 2018-12-18)
For bandwidth and disk usage concerns the default installation only supports
native compilation. To add cross compilation support for the ARM Cortex-M
architecture choose one of the following compilation targets. Use the last one
for the STM32F3DISCOVERY board and follow along with the book.
architectures choose one of the following compilation targets. For the STM32F3DISCOVERY
board used for the examples in this book, use the final `thumbv7em-none-eabihf` target.
Cortex M0 M0+
Cortex-M0, M0+, and M1 (ARMv6-M architecture):
``` console
$ rustup target add thumbv6m-none-eabi
```
Cortex M3
Cortex-M3 (ARMv7-M architecture):
``` console
$ rustup target add thumbv7m-none-eabi
```
Cortex M4 M7 without FPU
Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture):
``` console
$ rustup target add thumbv7em-none-eabi
```
Cortex M4 M7 with FPU <-- STM32F3DISCOVERY
Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture):
``` console
$ rustup target add thumbv7em-none-eabihf
```

View File

@@ -57,7 +57,7 @@ sudo dnf install arm-none-eabi-gdb openocd qemu-system-arm
> Cortex-M programs
``` console
sudo pacman -S arm-none-eabi-gdb qemu-arch-extra
sudo pacman -S arm-none-eabi-gdb qemu-arch-extra openocd
```
Now install openocd from [AUR](https://aur.archlinux.org/packages/openocd/)

View File

@@ -6,7 +6,6 @@ All the tools can be install using [Homebrew]:
``` console
$ # GDB
$ brew tap armmbed/formulae
$ brew install armmbed/formulae/arm-none-eabi-gcc
$ # OpenOCD

View File

@@ -60,7 +60,7 @@ you'll need to configure things a bit differently later on. You can move to the
If neither command worked as a normal user then try to run them with root
permission (e.g. `sudo openocd ..`). If the commands do work with root
permission then check that the [udev rules] has been correctly set.
permission then check that the [udev rules] have been correctly set.
[udev rules]: linux.md#udev-rules

View File

@@ -11,7 +11,7 @@ There are two general Embedded Programming classifications:
## Hosted Environments
These kinds of environments are close to a normal PC environment.
What this means is you are provided with a System Interface [E.G. POSIX](https://en.wikipedia.org/wiki/POSIX)
What this means is that you are provided with a System Interface [E.G. POSIX](https://en.wikipedia.org/wiki/POSIX)
that provides you with primitives to interact with various systems, such as file systems, networking, memory management, threads, etc.
Standard libraries in turn usually depend on these primitives to implement their functionality.
You may also have some sort of sysroot and restrictions on RAM/ROM-usage, and perhaps some
@@ -61,5 +61,4 @@ bootstrapping (stage 0) code like bootloaders, firmware or kernels.
[alloc-cortex-m]: https://github.com/rust-embedded/alloc-cortex-m
## See Also
* [FAQ](https://www.rust-lang.org/en-US/faq.html#does-rust-work-without-the-standard-library)
* [RFC-1184](https://github.com/rust-lang/rfcs/blob/master/text/1184-stabilize-no_std.md)

View File

@@ -15,9 +15,8 @@ tested.
- OpenOCD >=0.8. Tested versions: v0.9.0 and v0.10.0
- GDB with ARM support. Version 7.12 or newer highly recommended. Tested
versions: 7.10, 7.11, 7.12 and 8.1
- [OPTIONAL] `git` OR
[`cargo-generate`](https://github.com/ashleygwilliams/cargo-generate). If you
have neither installed then don't worry about installing either.
- [`cargo-generate`](https://github.com/ashleygwilliams/cargo-generate) or `git`.
These tools are optional but will make it easier to follow along with the book.
The text below explains why we are using these tools. Installation instructions
can be found on the next page.
@@ -25,11 +24,11 @@ can be found on the next page.
## `cargo-generate` OR `git`
Bare metal programs are non-standard (`no_std`) Rust programs that require some
fiddling with the linking process to get the memory layout of the program
right. All this requires unusual files (like linker scripts) and unusual
settings (like linker flags). We have packaged all that for you in a template
so that you only need to fill in the blanks such as the project name and the
characteristics of your target hardware.
adjustments to the linking process in order to get the memory layout of the program
right. This requires some additional files (like linker scripts) and
settings (like linker flags). We have packaged those for you in a template
such that you only need to fill in the missing information (such as the project name and the
characteristics of your target hardware).
Our template is compatible with `cargo-generate`: a Cargo subcommand for
creating new Cargo projects from templates. You can also download the
@@ -58,7 +57,7 @@ can follow some parts of this book even if you don't have any hardware with you!
A debugger is a very important component of embedded development as you may not
always have the luxury to log stuff to the host console. In some cases, you may
not have LEDs to blink on your hardware!
not even have LEDs to blink on your hardware!
In general, LLDB works as well as GDB when it comes to debugging but we haven't
found an LLDB counterpart to GDB's `load` command, which uploads the program to

View File

@@ -11,13 +11,13 @@
We could make everything a public static, like this
```rust
```rust,ignore
static mut THE_SERIAL_PORT: SerialPort = SerialPort;
fn main() {
let _ = unsafe {
THE_SERIAL_PORT.read_speed();
}
};
}
```
@@ -44,7 +44,7 @@ static mut PERIPHERALS: Peripherals = Peripherals {
This structure allows us to obtain a single instance of our peripheral. If we try to call `take_serial()` more than once, our code will panic!
```rust
```rust,ignore
fn main() {
let serial_1 = unsafe { PERIPHERALS.take_serial() };
// This panics!
@@ -60,7 +60,7 @@ This has a small runtime overhead because we must wrap the `SerialPort` structur
Although we created our own `Peripherals` structure above, it is not necessary to do this for your code. the `cortex_m` crate contains a macro called `singleton!()` that will perform this action for you.
```rust
```rust,ignore
#[macro_use(singleton)]
extern crate cortex_m;
@@ -116,7 +116,7 @@ There are two important factors in play here:
These two factors put together means that it is only possible to access the hardware if we have appropriately satisfied the borrow checker, meaning that at no point do we have multiple mutable references to the same hardware!
```rust
```rust,ignore
fn main() {
// missing reference to `self`! Won't work.
// SerialPort::read_speed();

View File

@@ -57,7 +57,7 @@ times it has been called in the `COUNT` variable and then prints the value of
> **NOTE**: You can run this example on any Cortex-M device; you can also run it
> on QEMU
``` rust
```rust,ignore
#![deny(unsafe_code)]
#![no_main]
#![no_std]
@@ -185,7 +185,7 @@ memory location.
> `qemu-system-arm -machine lm3s6965evb` doesn't check memory loads and will
> happily return `0 `on reads to invalid memory.
``` rust
```rust,ignore
#![no_main]
#![no_std]

View File

@@ -82,7 +82,7 @@ MEMORY
Make sure the `debug::exit()` call is commented out or removed, it is used
only for running in QEMU.
``` rust
```rust,ignore
#[entry]
fn main() -> ! {
hprintln!("Hello, world!").unwrap();

View File

@@ -69,7 +69,7 @@ with the release profile (`cargo build --release`).
Here's an example that tries to index an array beyond its length. The operation
results in a panic.
``` rust
```rust,ignore
#![no_main]
#![no_std]

View File

@@ -7,41 +7,41 @@ the tooling and the development process.
[LM3S6965]: http://www.ti.com/product/LM3S6965
## A non standard Rust program
## Creating a non standard Rust program
We'll use the [`cortex-m-quickstart`] project template so go generate a new
We'll use the [`cortex-m-quickstart`] project template to generate a new
project from it.
[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart
- Using `cargo-generate`
``` console
$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
```console
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
```
```text
Project Name: app
Creating project called `app`...
Done! New project created /tmp/app
```
$ cd app
```console
cd app
```
- Using `git`
Clone the repository
``` console
$ git clone https://github.com/rust-embedded/cortex-m-quickstart app
$ cd app
```console
git clone https://github.com/rust-embedded/cortex-m-quickstart app
cd app
```
And then fill in the placeholders in the `Cargo.toml` file
``` console
$ cat Cargo.toml
```
``` toml
```toml
[package]
authors = ["{{authors}}"] # "{{authors}}" -> "John Smith"
edition = "2018"
@@ -62,18 +62,14 @@ Grab the latest snapshot of the `cortex-m-quickstart` template and extract it.
Using the command line:
``` console
$ # NOTE there's also a tarball available: archive/master.tar.gz
$ curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip
$ unzip master.zip
$ mv cortex-m-quickstart-master app
$ cd app
```console
curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip
unzip master.zip
mv cortex-m-quickstart-master app
cd app
```
OR you can browse to [`cortex-m-quickstart`], click the green "Clone or
Or you can browse to [`cortex-m-quickstart`], click the green "Clone or
download" button and then click "Download ZIP".
Then fill in the placeholders in the `Cargo.toml` file as done in the second
@@ -84,21 +80,13 @@ Whenever you see the word "app" you should replace it with the name you selected
for your project. Or, you could also name your project "app" and avoid the
substitutions.
For convenience here's the source code of `src/main.rs`:
For convenience here are the most important parts of the source code in `src/main.rs`:
``` console
$ cat src/main.rs
```
``` rust
```rust,ignore
#![no_std]
#![no_main]
// pick a panicking behavior
extern crate panic_halt; // you can put a breakpoint on `rust_begin_unwind` to catch panics
// extern crate panic_abort; // requires nightly
// extern crate panic_itm; // logs messages over ITM; requires ITM support
// extern crate panic_semihosting; // logs messages to the host stderr; requires a debugger
extern crate panic_halt;
use cortex_m_rt::entry;
@@ -122,7 +110,8 @@ with `no_main` is that using the `main` interface in `no_std` context requires
nightly.
`extern crate panic_halt;`. This crate provides a `panic_handler` that defines
the panicking behavior of the program. More on this later on.
the panicking behavior of the program. We will cover this in more detail in the
[Panicking](panicking.md) chapter of the book.
[`#[entry]`] is an attribute provided by the [`cortex-m-rt`] crate that's used
to mark the entry point of the program. As we are not using the standard `main`
@@ -143,11 +132,11 @@ That's as simple as running `cargo build --target $TRIPLE` if you know what the
compilation target (`$TRIPLE`) should be. Luckily, the `.cargo/config` in the
template has the answer:
``` console
$ tail -n6 .cargo/config
```console
tail -n6 .cargo/config
```
``` toml
```toml
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
@@ -160,10 +149,9 @@ To cross compile for the Cortex-M3 architecture we have to use
`thumbv7m-none-eabi`. This compilation target has been set as the default so the
two commands below do the same:
``` console
$ cargo build --target thumbv7m-none-eabi
$ cargo build
```console
cargo build --target thumbv7m-none-eabi
cargo build
```
### Inspecting
@@ -175,12 +163,14 @@ With `cargo-readobj` we can print the ELF headers to confirm that this is an ARM
binary.
``` console
$ # `--bin app` is sugar for inspect the binary at `target/$TRIPLE/debug/app`
$ # `--bin app` will also (re)compile the binary, if necessary
$ cargo readobj --bin app -- -file-headers
cargo readobj --bin app -- -file-headers
```
Note that:
* `--bin app` is sugar for inspect the binary at `target/$TRIPLE/debug/app`
* `--bin app` will also (re)compile the binary, if necessary
``` text
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
@@ -209,11 +199,10 @@ ELF Header:
> **NOTE** this output assumes that rust-embedded/cortex-m-rt#111 has been
> merged
``` console
$ # we use `--release` to inspect the optimized version
$ cargo size --bin app --release -- -A
```console
cargo size --bin app --release -- -A
```
we use `--release` to inspect the optimized version
``` text
app :
@@ -257,61 +246,51 @@ is.
`cargo-objdump` can be used to disassemble the binary.
``` console
$ cargo objdump --bin app --release -- -disassemble -no-show-raw-insn -print-imm-hex
```console
cargo objdump --bin app --release -- -disassemble -no-show-raw-insn -print-imm-hex
```
> **NOTE** this output assumes that rust-embedded/cortex-m-rt#111 has been
> merged
> **NOTE** this output can differ on your system. New versions of rustc, LLVM
> and libraries can generate different assembly. We truncated some of the instructions
> to keep the snippet small.
``` text
app: file format ELF32-arm-little
```text
app: file format ELF32-arm-little
Disassembly of section .text:
Reset:
400: bl #0x36
404: movw r0, #0x0
408: movw r1, #0x0
40c: movt r0, #0x2000
410: movt r1, #0x2000
414: bl #0x2c
418: movw r0, #0x0
41c: movw r1, #0x45c
420: movw r2, #0x0
424: movt r0, #0x2000
428: movt r1, #0x0
42c: movt r2, #0x2000
430: bl #0x1c
434: b #-0x4 <Reset+0x34>
main:
400: bl #0x256
404: b #-0x4 <main+0x4>
HardFault_:
436: b #-0x4 <HardFault_>
Reset:
406: bl #0x24e
40a: movw r0, #0x0
< .. truncated any more instructions .. >
DefaultHandler_:
656: b #-0x4 <DefaultHandler_>
UsageFault:
438: b #-0x4 <UsageFault>
657: strb r7, [r4, #0x3]
DefaultPreInit:
658: bx lr
__pre_init:
43a: bx lr
659: strb r7, [r0, #0x1]
__nop:
65a: bx lr
HardFaultTrampoline:
65c: mrs r0, msp
660: b #-0x2 <HardFault_>
HardFault_:
662: b #-0x4 <HardFault_>
HardFault:
43c: mrs r0, msp
440: bl #-0xe
__zero_bss:
444: movs r2, #0x0
446: b #0x0 <__zero_bss+0x6>
448: stm r0!, {r2}
44a: cmp r0, r1
44c: blo #-0x8 <__zero_bss+0x4>
44e: bx lr
__init_data:
450: b #0x2 <__init_data+0x6>
452: ldm r1!, {r3}
454: stm r0!, {r3}
456: cmp r0, r2
458: blo #-0xa <__init_data+0x2>
45a: bx lr
663: <unknown>
```
### Running
@@ -319,13 +298,9 @@ __init_data:
Next, let's see how to run an embedded program on QEMU! This time we'll use the
`hello` example which actually does something.
For convenience here's the source code of `src/main.rs`:
For convenience here's the source code of `examples/hello.rs`:
``` console
$ cat examples/hello.rs
```
``` rust
```rust,ignore
//! Prints "Hello, world!" on the host console using semihosting
#![no_main]
@@ -333,17 +308,15 @@ $ cat examples/hello.rs
extern crate panic_halt;
use core::fmt::Write;
use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hio};
use cortex_m_semihosting::{debug, hprintln};
#[entry]
fn main() -> ! {
let mut stdout = hio::hstdout().unwrap();
writeln!(stdout, "Hello, world!").unwrap();
hprintln!("Hello, world!").unwrap();
// exit QEMU or the debugger section
// exit QEMU
// NOTE do not run this on hardware; it can corrupt OpenOCD state
debug::exit(debug::EXIT_SUCCESS);
loop {}
@@ -356,8 +329,8 @@ QEMU this Just Works.
Let's start by compiling the example:
``` console
$ cargo build --example hello
```console
cargo build --example hello
```
The output binary will be located at
@@ -365,25 +338,31 @@ The output binary will be located at
To run this binary on QEMU run the following command:
``` console
$ qemu-system-arm \
-cpu cortex-m3 \
-machine lm3s6965evb \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/thumbv7m-none-eabi/debug/examples/hello
```console
qemu-system-arm \
-cpu cortex-m3 \
-machine lm3s6965evb \
-nographic \
-semihosting-config enable=on,target=native \
-kernel target/thumbv7m-none-eabi/debug/examples/hello
```
```text
Hello, world!
```
The command should successfully exit (exit code = 0) after printing the text. On
*nix you can check that with the following command:
``` console
$ echo $?
```console
echo $?
```
```text
0
```
Let me break down that long QEMU command for you:
Let's break down that QEMU command:
- `qemu-system-arm`. This is the QEMU emulator. There are a few variants of
these QEMU binaries; this one does full *system* emulation of *ARM* machines
@@ -410,11 +389,11 @@ Typing out that long QEMU command is too much work! We can set a custom runner
to simplify the process. `.cargo/config` has a commented out runner that invokes
QEMU; let's uncomment it:
``` console
$ head -n3 .cargo/config
```console
head -n3 .cargo/config
```
``` toml
```toml
[target.thumbv7m-none-eabi]
# uncomment this to make `cargo run` execute programs on QEMU
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
@@ -424,8 +403,11 @@ This runner only applies to the `thumbv7m-none-eabi` target, which is our
default compilation target. Now `cargo run` will compile the program and run it
on QEMU:
``` console
$ cargo run --example hello --release
```console
cargo run --example hello --release
```
```text
Compiling app v0.1.0 (file:///tmp/app)
Finished release [optimized + debuginfo] target(s) in 0.26s
Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel target/thumbv7m-none-eabi/release/examples/hello`
@@ -448,15 +430,15 @@ In this section we'll use the `hello` example we already compiled.
The first debugging step is to launch QEMU in debugging mode:
``` console
$ 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
```console
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
```
This command won't print anything to the console and will block the terminal. We
@@ -472,19 +454,22 @@ have passed two extra flags this time:
Next we launch GDB in another terminal and tell it to load the debug symbols of
the example:
``` console
$ <gdb> -q target/thumbv7m-none-eabi/debug/examples/hello
```console
gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello
```
**NOTE**: `<gdb>` represents a GDB program capable of debugging ARM binaries.
This could be `arm-none-eabi-gdb`, `gdb-multiarch` or `gdb` depending on your
system -- you may have to try all three.
**NOTE**: you might need another version of gdb instead of `gdb-multiarch` depending
on which one you installed in the installation chapter. This could also be
`arm-none-eabi-gdb` or just `gdb`.
Then within the GDB shell we connect to QEMU, which is waiting for a connection
on TCP port 3333.
``` console
(gdb) target remote :3333
```console
target remote :3333
```
```text
Remote debugging using :3333
Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473
473 pub unsafe extern "C" fn Reset() -> ! {
@@ -497,11 +482,19 @@ execute upon booting.
This reset handler will eventually call our main function. Let's skip all the
way there using a breakpoint and the `continue` command:
``` console
(gdb) break main
Breakpoint 1 at 0x400: file examples/panic.rs, line 29.
```console
break main
```
(gdb) continue
```text
Breakpoint 1 at 0x400: file examples/panic.rs, line 29.
```
```console
continue
```
```text
Continuing.
Breakpoint 1, main () at examples/hello.rs:17
@@ -512,30 +505,41 @@ We are now close to the code that prints "Hello, world!". Let's move forward
using the `next` command.
``` console
(gdb) next
18 writeln!(stdout, "Hello, world!").unwrap();
next
```
(gdb) next
```text
18 writeln!(stdout, "Hello, world!").unwrap();
```
```console
next
```
```text
20 debug::exit(debug::EXIT_SUCCESS);
```
At this point you should see "Hello, world!" printed on the terminal that's
running `qemu-system-arm`.
``` console
```text
$ qemu-system-arm (..)
Hello, world!
```
Calling `next` again will terminate the QEMU process.
``` console
(gdb) next
```console
next
```
```text
[Inferior 1 (Remote target) exited normally]
```
You can now exit the GDB session.
``` console
(gdb) quit
quit
```

View File

@@ -21,7 +21,7 @@ You may well find that the code you need to access the peripherals in your micro
Let's look at the SysTick peripheral that's common to all Cortex-M based micro-controllers. We can find a pretty low-level API in the [cortex-m] crate, and we can use it like this:
```rust
```rust,ignore
use cortex_m::peripheral::{syst, Peripherals};
use cortex_m_rt::entry;
@@ -125,7 +125,7 @@ The HAL crate for a chip typically works by implementing a custom Trait for the
Let's see an example:
```rust
```rust,ignore
#![no_std]
#![no_main]

View File

@@ -12,7 +12,7 @@ world!":
[`cortex-m-semihosting`]: https://crates.io/crates/cortex-m-semihosting
``` rust
```rust,ignore
#![no_main]
#![no_std]
@@ -63,7 +63,7 @@ QEMU process. Important: do **not** use `debug::exit` on hardware; this function
can corrupt your OpenOCD session and you will not be able to debug more programs
until you restart it.
``` rust
```rust,ignore
#![no_main]
#![no_std]
@@ -101,7 +101,7 @@ For convenience, the `panic-semihosting` crate has an "exit" feature that when
enabled invokes `exit(EXIT_FAILURE)` after logging the panic message to the host
stderr.
``` rust
```rust,ignore
#![no_main]
#![no_std]

View File

@@ -1,8 +1,8 @@
# Static Guarantees
It's Rust's type system what prevents data races at compile time (see [`Send`]
and [`Sync`] traits). The type system can also be used to check other properties
at compile time; reducing the need for runtime checks in some cases.
Rust's type system prevents data races at compile time (see [`Send`] and
[`Sync`] traits). The type system can also be used to check other properties at
compile time; reducing the need for runtime checks in some cases.
[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html