]> git.proxmox.com Git - rustc.git/blob - src/doc/embedded-book/src/start/hardware.md
New upstream version 1.64.0+dfsg1
[rustc.git] / src / doc / embedded-book / src / start / hardware.md
1 # Hardware
2
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.
6
7 ## Know your hardware
8
9 Before we begin you need to identify some characteristics of the target device
10 as these will be used to configure the project:
11
12 - The ARM core. e.g. Cortex-M3.
13
14 - Does the ARM core include an FPU? Cortex-M4**F** and Cortex-M7**F** cores do.
15
16 - How much Flash memory and RAM does the target device have? e.g. 256 KiB of
17 Flash and 32 KiB of RAM.
18
19 - Where are Flash memory and RAM mapped in the address space? e.g. RAM is
20 commonly located at address `0x2000_0000`.
21
22 You can find this information in the data sheet or the reference manual of your
23 device.
24
25 In this section we'll be using our reference hardware, the STM32F3DISCOVERY.
26 This board contains an STM32F303VCT6 microcontroller. This microcontroller has:
27
28 - A Cortex-M4F core that includes a single precision FPU
29
30 - 256 KiB of Flash located at address 0x0800_0000.
31
32 - 40 KiB of RAM located at address 0x2000_0000. (There's another RAM region but
33 for simplicity we'll ignore it).
34
35 ## Configuring
36
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
39 `cargo-generate`.
40
41 [previous section on QEMU]: qemu.md
42
43 ``` text
44 $ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
45 Project Name: app
46 Creating project called `app`...
47 Done! New project created /tmp/app
48
49 $ cd app
50 ```
51
52 Step number one is to set a default compilation target in `.cargo/config.toml`.
53
54 ``` console
55 tail -n5 .cargo/config.toml
56 ```
57
58 ``` 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)
64 ```
65
66 We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core.
67
68 The second step is to enter the memory region information into the `memory.x`
69 file.
70
71 ``` text
72 $ cat memory.x
73 /* Linker script for the STM32F303VCT6 */
74 MEMORY
75 {
76 /* NOTE 1 K = 1 KiBi = 1024 bytes */
77 FLASH : ORIGIN = 0x08000000, LENGTH = 256K
78 RAM : ORIGIN = 0x20000000, LENGTH = 40K
79 }
80 ```
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`.
84
85 We'll start with the hello example again, but first we have to make a small
86 change.
87
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.
90
91 ```rust,ignore
92 #[entry]
93 fn main() -> ! {
94 hprintln!("Hello, world!").unwrap();
95
96 // exit QEMU
97 // NOTE do not run this on hardware; it can corrupt OpenOCD state
98 // debug::exit(debug::EXIT_SUCCESS);
99
100 loop {}
101 }
102 ```
103
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.
108
109 ``` console
110 cargo build --example hello
111 ```
112
113 ## Debugging
114
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).
120
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.
123
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.
126
127 [verify]: ../intro/install/verify.md
128
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.
132
133 ``` console
134 cat openocd.cfg
135 ```
136
137 ``` text
138 # Sample OpenOCD configuration for the STM32F3DISCOVERY development board
139
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.
142
143 # Revision C (newer revision)
144 source [find interface/stlink.cfg]
145
146 # Revision A and B (older revisions)
147 # source [find interface/stlink-v2.cfg]
148
149 source [find target/stm32f3x.cfg]
150 ```
151
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`.
155
156 ``` text
157 $ openocd
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
166 none separate
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
174 ```
175
176 On another terminal run GDB, also from the root of the template.
177
178 ``` text
179 gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello
180 ```
181
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`.
185
186 Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333.
187
188 ``` console
189 (gdb) target remote :3333
190 Remote debugging using :3333
191 0x00000000 in ?? ()
192 ```
193
194 Now proceed to *flash* (load) the program onto the microcontroller using the
195 `load` command.
196
197 ``` console
198 (gdb) load
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.
204 ```
205
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.
209
210 ``` console
211 (gdb) monitor arm semihosting enable
212 semihosting is enabled
213 ```
214
215 > You can see all the OpenOCD commands by invoking the `monitor help` command.
216
217 Like before we can skip all the way to `main` using a breakpoint and the
218 `continue` command.
219
220 ``` console
221 (gdb) break main
222 Breakpoint 1 at 0x8000490: file examples/hello.rs, line 11.
223 Note: automatically using hardware breakpoints for read-only addresses.
224
225 (gdb) continue
226 Continuing.
227
228 Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11
229 11 #[entry]
230 ```
231
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).
236
237 Step into the main function with `step`.
238
239 ``` console
240 (gdb) step
241 halted: PC: 0x08000496
242 hello::__cortex_m_rt_main () at examples/hello.rs:13
243 13 hprintln!("Hello, world!").unwrap();
244 ```
245
246 After advancing the program with `next` you should see "Hello, world!" printed on the OpenOCD console,
247 among other stuff.
248
249 ``` console
250 $ openocd
251 (..)
252 Info : halted: PC: 0x08000502
253 Hello, world!
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
260 ```
261 The message is only displayed once as the program is about to enter the infinite loop defined in line 19: `loop {}`
262
263 You can now exit GDB using the `quit` command.
264
265 ``` console
266 (gdb) quit
267 A debugging session is active.
268
269 Inferior 1 [Remote target] will be detached.
270
271 Quit anyway? (y or n)
272 ```
273
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:
276
277 ``` console
278 cat openocd.gdb
279 ```
280
281 ``` text
282 target extended-remote :3333
283
284 # print demangled symbols
285 set print asm-demangle on
286
287 # detect unhandled exceptions, hard faults and panics
288 break DefaultHandler
289 break HardFault
290 break rust_begin_unwind
291
292 monitor arm semihosting enable
293
294 load
295
296 # start the process but immediately halt the processor
297 stepi
298 ```
299
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.
302
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.
306
307 ``` console
308 head -n10 .cargo/config.toml
309 ```
310
311 ``` 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"
315
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"
322 ```
323
324 ``` text
325 $ cargo run --example hello
326 (..)
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.
332 (gdb)
333 ```