3 By now you should be somewhat familiar with the tooling and the development
4 process. In this section we'll switch to real hardware; the process will remain
5 largely the same. Let's dive in.
9 Before we begin you need to identify some characteristics of the target device
10 as these will be used to configure the project:
12 - The ARM core. e.g. Cortex-M3.
14 - Does the ARM core include an FPU? Cortex-M4**F** and Cortex-M7**F** cores do.
16 - How much Flash memory and RAM does the target device have? e.g. 256 KiB of
17 Flash and 32 KiB of RAM.
19 - Where are Flash memory and RAM mapped in the address space? e.g. RAM is
20 commonly located at address `0x2000_0000`.
22 You can find this information in the data sheet or the reference manual of your
25 In this section we'll be using our reference hardware, the STM32F3DISCOVERY.
26 This board contains an STM32F303VCT6 microcontroller. This microcontroller has:
28 - A Cortex-M4F core that includes a single precision FPU
30 - 256 KiB of Flash located at address 0x0800_0000.
32 - 40 KiB of RAM located at address 0x2000_0000. (There's another RAM region but
33 for simplicity we'll ignore it).
37 We'll start from scratch with a fresh template instance. Refer to the
38 [previous section on QEMU] for a refresher on how to do this without
41 [previous section on QEMU]: qemu.md
44 $ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
46 Creating project called `app`...
47 Done! New project created /tmp/app
52 Step number one is to set a default compilation target in `.cargo/config.toml`.
55 tail -n5 .cargo/config.toml
59 # Pick ONE of these compilation targets
60 # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
61 # target = "thumbv7m-none-eabi" # Cortex-M3
62 # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
63 target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
66 We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core.
68 The second step is to enter the memory region information into the `memory.x`
73 /* Linker script for the STM32F303VCT6 */
76 /* NOTE 1 K = 1 KiBi = 1024 bytes */
77 FLASH : ORIGIN = 0x08000000, LENGTH = 256K
78 RAM : ORIGIN = 0x20000000, LENGTH = 40K
81 > **NOTE**: If you for some reason changed the `memory.x` file after you had made
82 > the first build of a specific build target, then do `cargo clean` before
83 > `cargo build`, because `cargo build` may not track updates of `memory.x`.
85 We'll start with the hello example again, but first we have to make a small
88 In `examples/hello.rs`, make sure the `debug::exit()` call is commented out or
89 removed. It is used only for running in QEMU.
94 hprintln!("Hello, world!").unwrap();
97 // NOTE do not run this on hardware; it can corrupt OpenOCD state
98 // debug::exit(debug::EXIT_SUCCESS);
104 You can now cross compile programs using `cargo build`
105 and inspect the binaries using `cargo-binutils` as you did before. The
106 `cortex-m-rt` crate handles all the magic required to get your chip running,
107 as helpfully, pretty much all Cortex-M CPUs boot in the same fashion.
110 cargo build --example hello
115 Debugging will look a bit different. In fact, the first steps can look different
116 depending on the target device. In this section we'll show the steps required to
117 debug a program running on the STM32F3DISCOVERY. This is meant to serve as a
118 reference; for device specific information about debugging check out [the
119 Debugonomicon](https://github.com/rust-embedded/debugonomicon).
121 As before we'll do remote debugging and the client will be a GDB process. This
122 time, however, the server will be OpenOCD.
124 As done during the [verify] section connect the discovery board to your laptop /
125 PC and check that the ST-LINK header is populated.
127 [verify]: ../intro/install/verify.md
129 On a terminal run `openocd` to connect to the ST-LINK on the discovery board.
130 Run this command from the root of the template; `openocd` will pick up the
131 `openocd.cfg` file which indicates which interface file and target file to use.
138 # Sample OpenOCD configuration for the STM32F3DISCOVERY development board
140 # Depending on the hardware revision you got you'll have to pick ONE of these
141 # interfaces. At any time only one interface should be commented out.
143 # Revision C (newer revision)
144 source [find interface/stlink.cfg]
146 # Revision A and B (older revisions)
147 # source [find interface/stlink-v2.cfg]
149 source [find target/stm32f3x.cfg]
152 > **NOTE** If you found out that you have an older revision of the discovery
153 > board during the [verify] section then you should modify the `openocd.cfg`
154 > file at this point to use `interface/stlink-v2.cfg`.
158 Open On-Chip Debugger 0.10.0
159 Licensed under GNU GPL v2
160 For bug reports, read
161 http://openocd.org/doc/doxygen/bugs.html
162 Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
163 adapter speed: 1000 kHz
164 adapter_nsrst_delay: 100
165 Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
167 Info : Unable to match requested speed 1000 kHz, using 950 kHz
168 Info : Unable to match requested speed 1000 kHz, using 950 kHz
169 Info : clock speed 950 kHz
170 Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
171 Info : using stlink api v2
172 Info : Target voltage: 2.913879
173 Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
176 On another terminal run GDB, also from the root of the template.
179 gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello
182 **NOTE**: like before you might need another version of gdb instead of `gdb-multiarch` depending
183 on which one you installed in the installation chapter. This could also be
184 `arm-none-eabi-gdb` or just `gdb`.
186 Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333.
189 (gdb) target remote :3333
190 Remote debugging using :3333
194 Now proceed to *flash* (load) the program onto the microcontroller using the
199 Loading section .vector_table, size 0x400 lma 0x8000000
200 Loading section .text, size 0x1518 lma 0x8000400
201 Loading section .rodata, size 0x414 lma 0x8001918
202 Start address 0x08000400, load size 7468
203 Transfer rate: 13 KB/sec, 2489 bytes/write.
206 The program is now loaded. This program uses semihosting so before we do any
207 semihosting call we have to tell OpenOCD to enable semihosting. You can send
208 commands to OpenOCD using the `monitor` command.
211 (gdb) monitor arm semihosting enable
212 semihosting is enabled
215 > You can see all the OpenOCD commands by invoking the `monitor help` command.
217 Like before we can skip all the way to `main` using a breakpoint and the
222 Breakpoint 1 at 0x8000490: file examples/hello.rs, line 11.
223 Note: automatically using hardware breakpoints for read-only addresses.
228 Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11
232 > **NOTE** If GDB blocks the terminal instead of hitting the breakpoint after
233 > you issue the `continue` command above, you might want to double check that
234 > the memory region information in the `memory.x` file is correctly set up
235 > for your device (both the starts *and* lengths).
237 Step into the main function with `step`.
241 halted: PC: 0x08000496
242 hello::__cortex_m_rt_main () at examples/hello.rs:13
243 13 hprintln!("Hello, world!").unwrap();
246 After advancing the program with `next` you should see "Hello, world!" printed on the OpenOCD console,
252 Info : halted: PC: 0x08000502
254 Info : halted: PC: 0x080004ac
255 Info : halted: PC: 0x080004ae
256 Info : halted: PC: 0x080004b0
257 Info : halted: PC: 0x080004b4
258 Info : halted: PC: 0x080004b8
259 Info : halted: PC: 0x080004bc
261 The message is only displayed once as the program is about to enter the infinite loop defined in line 19: `loop {}`
263 You can now exit GDB using the `quit` command.
267 A debugging session is active.
269 Inferior 1 [Remote target] will be detached.
271 Quit anyway? (y or n)
274 Debugging now requires a few more steps so we have packed all those steps into a
275 single GDB script named `openocd.gdb`. The file was created during the `cargo generate` step, and should work without any modifications. Let's have a peak:
282 target extended-remote :3333
284 # print demangled symbols
285 set print asm-demangle on
287 # detect unhandled exceptions, hard faults and panics
290 break rust_begin_unwind
292 monitor arm semihosting enable
296 # start the process but immediately halt the processor
300 Now running `<gdb> -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` will immediately connect GDB to
301 OpenOCD, enable semihosting, load the program and start the process.
303 Alternatively, you can turn `<gdb> -x openocd.gdb` into a custom runner to make
304 `cargo run` build a program *and* start a GDB session. This runner is included
305 in `.cargo/config.toml` but it's commented out.
308 head -n10 .cargo/config.toml
312 [target.thumbv7m-none-eabi]
313 # uncomment this to make `cargo run` execute programs on QEMU
314 # runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
316 [target.'cfg(all(target_arch = "arm", target_os = "none"))']
317 # uncomment ONE of these three option to make `cargo run` start a GDB session
318 # which option to pick depends on your system
319 runner = "arm-none-eabi-gdb -x openocd.gdb"
320 # runner = "gdb-multiarch -x openocd.gdb"
321 # runner = "gdb -x openocd.gdb"
325 $ cargo run --example hello
327 Loading section .vector_table, size 0x400 lma 0x8000000
328 Loading section .text, size 0x1e70 lma 0x8000400
329 Loading section .rodata, size 0x61c lma 0x8002270
330 Start address 0x800144e, load size 10380
331 Transfer rate: 17 KB/sec, 3460 bytes/write.