Выбрать главу

-cpu cortex-m3 \

-machine lm3s6965evb \

-nographic \

-semihosting-config enable=on,target=native \

-kernel target/thumbv7m-none-eabi/debug/examples/hello

Hello, world!

The command should successfully exit (exit code = 0) after printing the text. On *nix you can check that with the following command:

echo $?

0

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 hence the name.

   • -cpu cortex-m3. This tells QEMU to emulate a Cortex-M3 CPU. Specifying the CPU model lets us catch some miscompilation errors: for example, running a program compiled for the Cortex-M4F, which has a hardware FPU, will make QEMU error during its execution.

   • -machine lm3s6965evb. This tells QEMU to emulate the LM3S6965EVB, a evaluation board that contains a LM3S6965 microcontroller.

   • -nographic. This tells QEMU to not launch its GUI.

   • -semihosting-config (..). This tells QEMU to enable semihosting. Semihosting lets the emulated device, among other things, use the host stdout, stderr and stdin and create files on the host.

   • -kernel $file. This tells QEMU which binary to load and run on the emulated machine.

Typing out that long QEMU command is too much work! We can set a custom runner to simplify the process. .cargo/config.toml has a commented out runner that invokes QEMU; let's uncomment it:

head -n3 .cargo/config.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"

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:

cargo run --example hello --release

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`

Hello, world!

Debugging is critical to embedded development. Let's see how it's done.

Debugging an embedded device involves remote debugging as the program that we want to debug won't be running on the machine that's running the debugger program (GDB or LLDB).

Remote debugging involves a client and a server. In a QEMU setup, the client will be a GDB (or LLDB) process and the server will be the QEMU process that's also running the embedded program.

In this section we'll use the hello example we already compiled.

The first debugging step is to launch QEMU in debugging mode:

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 have passed two extra flags this time:

   • -gdb tcp::3333. This tells QEMU to wait for a GDB connection on TCP port 3333.

   • -S. This tells QEMU to freeze the machine at startup. Without this the program would have reached the end of main before we had a chance to launch the debugger!

Next we launch GDB in another terminal and tell it to load the debug symbols of the example:

gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello

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.

target remote :3333

Remote debugging using :3333

Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473

473 pub unsafe extern "C" fn Reset() -> ! {

You'll see that the process is halted and that the program counter is pointing to a function named Reset. That is the reset handler: what Cortex-M cores execute upon booting.

Note that on some setup, instead of displaying the line Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 as shown above, gdb may print some warnings like :

core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum.rs:254 src/libcore/num/bignum.rs: No such file or directory.

That's a known glitch. You can safely ignore those warnings, you're most likely at Reset().

This reset handler will eventually call our main function. Let's skip all the way there using a breakpoint and the continue command. To set the breakpoint, let's first take a look where we would like to break in our code, with the list command.

list main

This will show the source code, from the file examples/hello.rs.

6 use panic_halt as _;

7

8 use cortex_m_rt::entry;

9 use cortex_m_semihosting::{debug, hprintln};

10

11 #[entry]

12 fn main() -> ! {

13 hprintln!("Hello, world!").unwrap();

14

15 // exit QEMU

We would like to add a breakpoint just before the "Hello, world!", which is on line 13. We do that with the break command:

break 13

We can now instruct gdb to run up to our main function, with the continue command:

continue

Continuing.

Breakpoint 1, hello::__cortex_m_rt_main () at examples\hello.rs:13

13 hprintln!("Hello, world!").unwrap();

We are now close to the code that prints "Hello, world!". Let's move forward using the next command.

next

16 debug::exit(debug::EXIT_SUCCESS);

At this point you should see "Hello, world!" printed on the terminal that's running qemu-system-arm.

$ qemu-system-arm (..)

Hello, world!

Calling next again will terminate the QEMU process.

next

[Inferior 1 (Remote target) exited normally]

You can now exit the GDB session.

quit

By now you should be somewhat familiar with the tooling and the development process. In this section we'll switch to real hardware; the process will remain largely the same. Let's dive in.

Before we begin you need to identify some characteristics of the target device as these will be used to configure the project:

   • The ARM core. e.g. Cortex-M3.

   • Does the ARM core include an FPU? Cortex-M4F and Cortex-M7F cores do.

   • How much Flash memory and RAM does the target device have? e.g. 256 KiB of Flash and 32 KiB of RAM.

   • Where are Flash memory and RAM mapped in the address space? e.g. RAM is commonly located at address 0x2000_0000.