diff --git a/README.md b/README.md index 29d71cd8bae60de3aa80ba1af187d7a9a4c9093a..e3d110bdca3c71517c5dcc4e3df21bb93efeda0c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ - -Are We Embedded Yet +Are We Embedded Yet ===================== ## D7018E - Special Studies in Embedded Systems @@ -29,8 +28,8 @@ The course will be given as a self study course with a set of introductional sem 7. Macros - Macro_rules/matching and expansion. Procedural macros (macros 2.0). Overview of the Rust RTFM implementation. - + Macro_rules/matching and expansion. Procedural macros (macros 2.0). Overview of the Rust RTFM implementation. + 8. Building and debugging embedded code in Rust Hardware abstractions using svd2rust (autogenerated from vendor-provided SVD specifications). Compiling using xargo. Setting up openocd and gdb. @@ -45,7 +44,7 @@ Please fill out the [doodle](http://doodle.com) so we can arrange for a first sc Each topic will be accompanied by a set of small illustrative examples and assignments that each student should master (learning goals). On the following session, each student should prepare to discuss and demonstrate their solutions. -Lectures and assignments will be covered over a 4 week period, leaving time for a larger assignment/project. +Lectures and assignments will be covered over a 4 week period, leaving time for a larger assignment/project. Each project should be reported in terms of a git Rust crate, with sufficient documentation (readme.md) to build, and recreate the result. @@ -54,19 +53,19 @@ There will be two presentation rounds (at end of LP2 and LP3). Students taking ( Projects should be related to embedded programming, either on the target side (some application using the RTFM-core or CRC model), or on the host side, communicating with an embedded target running Rust RTFM. E.g., two groups can work together with building a system, e.g., with back-end processing of data collected by the embedded system, or by providing a front-end to the embedded system. Alternatively, host side project could relate the development of the RTFM-core/ RTFM-CRC frameworks or related tools (e.g. LLWM-KLEE as a backend for analysis of Rust code). ## Resources -Students will carry out the assignments on their personal laptops (in case you don't have a working laptop we will try to lend you one). Tools used are available for Linux and OSX, but with a bit of trickery windows based installations should be possible (but you are on your own here). In case you don't run OSX/Linux native, virtual box or VMware is possible, though debugging of target MCUs are feasible it is a bit more tricky. +Students will carry out the assignments on their personal laptops (in case you don't have a working laptop we will try to lend you one). Tools used are available for Linux and OSX, but with a bit of trickery windows based installations should be possible (but you are on your own here). In case you don't run OSX/Linux native, virtual box or VMware is possible, though debugging of target MCUs are feasible it is a bit more tricky. Embedded targets STM32F401RE/411RE (Nucleo-64) will be made available to all students. You will need to get a mini-USB cable yourselves. If you prefer to work with some other ARM Cortex M processor, let us know and we will have a look at available support. -Teaching material will be made available through git (this project), build and refined throughout the course. +Teaching material will be made available through git (this project), build and refined throughout the course. We encourage all sorts of collaborations in between the students. Rust is not the easiest language to learn, and we will cover a lot of ground. Use the seminars to try to get as much as possible by being well prepared in order to succeed. We will use telegram as the main means of communication and support from teachers as well as other fellow students, (this way information will reach everyone, keeping support as efficient as possible). # Course in detail Seminars -1. Welcome. +1. Welcome. * Preparation Please bring your laptop, and we will install the Rust tools required in order to compile and run your first application. @@ -75,10 +74,10 @@ Seminars About the course. Scheduling further seminars. Introduction to the Rust [ecosystem](doc/Ecosystem.md), and basics of Rust programming. - We will cover the Rust book [Rustbook Second Edition](https://doc.rust-lang.org/book/second-edition/) sections + We will cover the Rust book [Rustbook Second Edition](https://doc.rust-lang.org/book/second-edition/) sections - * [1 - Introduction](https://doc.rust-lang.org/book/second-edition/ch01-00-introduction.html), - * [2 - Guessing Game Tutorial](https://doc.rust-lang.org/book/second-edition/ch02-00-guessing-game-tutorial.html), and + * [1 - Introduction](https://doc.rust-lang.org/book/second-edition/ch01-00-introduction.html), + * [2 - Guessing Game Tutorial](https://doc.rust-lang.org/book/second-edition/ch02-00-guessing-game-tutorial.html), and * [3 - Common Programming Concepts](https://doc.rust-lang.org/book/second-edition/ch03-00-common-programming-concepts.html). * Assignment 1 @@ -86,7 +85,7 @@ Seminars Make a GitHub account (if you don't have it). Make a GitHub project with your code, along with a README.md for usage instructions. - Prepare to present your development for the next seminar. + Prepare to present your development for the next seminar. 2. Basic Programming * Preparation @@ -94,7 +93,7 @@ Seminars Solve and be prepared to present Assignment 1. * Assessment - We will in the class install, test and comment on one of Your solutions. That is, make sure your `crate` on git actually works, and that the accompanying `readme.md` is sufficient for your fellow students in order to recreate the result. + We will in the class install, test and comment on one of Your solutions. That is, make sure your `crate` on git actually works, and that the accompanying `readme.md` is sufficient for your fellow students in order to recreate the result. Later in the course, you will assess each others assignments by creating an `issue` to their development. More on that later. * Topic @@ -102,10 +101,10 @@ Seminars Using the Rust ownership model. Data structures, structs, enums, String (relation to slices) etc. Containers, Vec, and HashSets/Maps. Iterators. Using the Rust module system. Handling errors. - We will cover the Rust book [Rustbook Second Edition](https://doc.rust-lang.org/book/second-edition/) sections + We will cover the Rust book [Rustbook Second Edition](https://doc.rust-lang.org/book/second-edition/) sections - * [4 - Understanding Ownership](https://doc.rust-lang.org/book/second-edition/ch04-00-understanding-ownership.html), - * [5 - Using Structs to Structure Related Data](https://doc.rust-lang.org/book/second-edition/ch05-00-structs.html), + * [4 - Understanding Ownership](https://doc.rust-lang.org/book/second-edition/ch04-00-understanding-ownership.html), + * [5 - Using Structs to Structure Related Data](https://doc.rust-lang.org/book/second-edition/ch05-00-structs.html), * [6 - Enums and Pattern Matchings](https://doc.rust-lang.org/book/second-edition/ch06-00-enums.html), * [7 - Modules](https://doc.rust-lang.org/book/second-edition/ch07-00-modules.html), @@ -124,7 +123,7 @@ Seminars e. Instead of vector use a 'HashMap', with a key `u32` and a value `String`. Again iterate (`for`) to print all entries at exiting. (Explain the result.) - f. Optional, find a way to print the last 3 entries of the `HashMap` (output similar to d). + f. Optional, find a way to print the last 3 entries of the `HashMap` (output similar to d). Make new branches (`2a, 2b, ..., 2f`) with your solutions, along with a README.md for usage instructions, and expected behavior. @@ -138,12 +137,12 @@ Seminars Finish Assignment 2. * Topic [Memory](doc/Memory.md) - In-depth discussion of underlying theory, linear types (relation to functional programming). The *Affine* type system of Rust, requirements on the programmer, and guarantees offered by the compiler. Lifetimes, of stack allocated and global variables. Relation to C++ `unique pointers`. + In-depth discussion of underlying theory, linear types (relation to functional programming). The *Affine* type system of Rust, requirements on the programmer, and guarantees offered by the compiler. Lifetimes, of stack allocated and global variables. Relation to C++ `unique pointers`. * Assignment a. Recall the D0013E course lab2/4, where you decrypted a message in assembler (lab2) and C (lab 4). Now, let's re-implement the lab in Rust (base your development on group number [1's](http://www.sm.luth.se/csee/courses/smd/D0013E/labs/lab1underlag/grupp_01.lab1_underlag.s ) lab assignment). - You have to be careful about the signed/unsigned operations and use `wrapping` arithmetics to avoid panics due to unsigned *carry* and signed *overflow*. + You have to be careful about the signed/unsigned operations and use `wrapping` arithmetics to avoid panics due to unsigned *carry* and signed *overflow*. Use borrowed array slices as arguments to `decode`. @@ -161,12 +160,13 @@ Seminars Finish assignment 3. Bring a USB mini cable, and/or your Cortex M dev board of choice. We will provide Nucleo 64s (STM32f401re/STM32f411re if you do not have a board.) * Topic - Embedded programming in Rust. + Embedded programming in Rust. Check this [document](doc/Quickstart.md) * xargo for building non-`std` (bare metal) systems - * [cortex-m-quickstart] - * [cortex-m] - * [bluepill/nucleo] crates + * `cortex-m-quickstart`, project template + * `cortex-m`, crate common to all Cortex-M devices + * `stm32f103xx` and `stm32f40x`, device crates + * `blue-pill` and `nucleo` board support crates * Building and debugging your first application. * Assignment @@ -200,13 +200,13 @@ Seminars * Finish lab 3 and be prepared to show your solution. * Topic - * UnsafeCell, and synchronization in the RTFM model. + * UnsafeCell, and synchronization in the RTFM model. * Requirements for Stack Resource Policy (SRP) based single-core scheduling. * [cortex-m-rtfm](https://github.com/japaric/cortex-m-rtfm) The RTFM-core (task and resource model) in Rust for the Cortex-M family - * [svd2rust](https://github.com/japaric/svd2rust) Generating + * [svd2rust](https://github.com/japaric/svd2rust) Generating * Assignment 4 Implement a simple system with two tasks @@ -254,7 +254,7 @@ Seminars Write a project specification including individual grading assessment criteria. -10. Wrap-up +10. Wrap-up * Preparation * Be prepared to present assignment 5. diff --git a/assets/cortex-m-layers.png b/assets/cortex-m-layers.png new file mode 100644 index 0000000000000000000000000000000000000000..f5bcfbb9a184d3ce473b56bb0325b3fea05245f6 Binary files /dev/null and b/assets/cortex-m-layers.png differ diff --git a/assets/vscode-build.png b/assets/vscode-build.png new file mode 100644 index 0000000000000000000000000000000000000000..0e1ad1db0fbeb5fc2578bcaf373fb08964112c36 Binary files /dev/null and b/assets/vscode-build.png differ diff --git a/assets/vscode-debug.png b/assets/vscode-debug.png new file mode 100644 index 0000000000000000000000000000000000000000..d76847733ee38fa0ca5cc8d34e0777d633806f21 Binary files /dev/null and b/assets/vscode-debug.png differ diff --git a/doc/Quickstart.md b/doc/Quickstart.md new file mode 100644 index 0000000000000000000000000000000000000000..276306c340899aed2498074d8ea12720362bcca8 --- /dev/null +++ b/doc/Quickstart.md @@ -0,0 +1,686 @@ +# Quickstart: a template for Cortex-M development + +## Abstraction layers + + + +- `cortex-m` is a crate that provides an API to use functionality common to all Cortex-M + microcontrollers. + +- `stm32f30x` is a *device crate*. It provides an API to access the hardware of a device. This crate + is automatically generated from a [SVD file][svd] and provides a low level API to manipulate + registers. + +[svd]: https://github.com/posborne/cmsis-svd/blob/master/data/STMicro/STM32F103xx.svd + +- `f3` is a *board support crate*. It provides a higher level API (`Serial`, `I2C`, etc.) tailored + to a specific development board. + +- `cortex-m-rt` is a minimal "runtime" that handles initialization of RAM and provides the default + exception handling behavior. It is also gives your program the required memory layout. + +- `???` is a concurrency framework that we'll introduce in a later lecture. + +## Dependencies for development + +- `arm-none-eabi-binutils`, linker + +- `arm-none-eabi-gdb`, debugger + +- `openocd`, for flashing / debugging the device + +- `xargo`, for compiling the `core` crate. Xargo is a Cargo wrapper -- it has the exact same UI. + Xargo takes care of building the `core` crate and linking it to your program / library. + +- And other handy Cargo subcommands + +### Linux + +- Arch Linux + +``` console +$ sudo pacman -Sy arm-none-eabi-{binutils,gdb} openocd +``` + +For hardware-association and pre-packaged UDEV-rules also install: +``` +$ sudo pacman -S stlink +``` + +### macOS + +``` console +$ brew cask install gcc-arm-embedded +$ brew install openocd +``` + +If the brew cask command doesn't work (Error: Unknown command: cask), then run `brew tap +Caskroom/tap` first and try again. + +### Windows + +Installers below + +- [`arm-none-eabi` toolchain](https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update/+download/gcc-arm-none-eabi-5_4-2016q3-20160926-win32.exe) + +- [OpenOCD](http://sysprogs.com/files/gnutoolchains/arm-eabi/openocd/OpenOCD-20170821.7z). Unzip to + your C (system) drive + +## All + +``` console +$ # we have to use the nightly channel for embedded development +$ rustup default nightly + +$ cargo install cargo-clone xargo +``` + +> **NOTE** If the `cargo install` fails you may need to install `pkg-config`. In Arch this can be +> accomplished with the `pacman -S pkg-config` command. + +## Demo + +In the first part of the demo we'll use command line tools in the terminal then we'll transition to +the Visual Studio Code IDE. It's a good idea to get familiar with the command line tools. The IDE is +nice because it calls these tools with the right arguments for you but when things go south it pays +off to understand what the IDE is doing under the hood. + +### Creating a new project + +These steps will give you a minimal Cortex-M project. If you run into any problem running these +commands check out the [troubleshooting guide][troubleshoot]. + +[troubleshoot]: https://docs.rs/cortex-m-quickstart/0.2.1/cortex_m_quickstart/#troubleshooting + +``` console +$ # fetch the Cargo project template +$ cargo clone cortex-m-quickstart + +$ # rename it as you wish (remember this name! you'll use it later) +$ mv cortex-m-quickstart app + +$ cd app + +$ # Cargo.toml.orig has a nicer format so let's use that instead of the reformatted one +$ mv Cargo.toml{.orig,} + +$ # update the crate name and author +$ $EDITOR Cargo.toml +$ cat Cargo.toml +[package] +authors = ["Jorge Aparicio <jorge@japaric.io>"] +name = "app" +version = "0.1.0" + +[dependencies] +cortex-m = "0.3.0" +cortex-m-semihosting = "0.2.0" + +[dependencies.cortex-m-rt] +features = ["abort-on-panic"] +version = "0.3.3" + +[profile.release] +debug = true +lto = true + +$ # we need to specify the memory layout of the device +$ $EDITOR memory.x + +$ # for the blue-pill you should have +$ cat memory.x +MEMORY +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x08000000, LENGTH = 64K + RAM : ORIGIN = 0x20000000, LENGTH = 20K +} + +$ # for the NUCLEO-F401RE you should have +$ cat memory.x +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x08000000, LENGTH = 512K + RAM : ORIGIN = 0x20000000, LENGTH = 96K +} +``` + +### Hello world + +Let's start with the hello world example: + +``` console +$ rm -rf src +$ mkdir src +$ cp examples/hello.rs src/main.rs +``` + +This is the hello world program. You can ignore the `INTERRUPTS` + `default_handler` part -- that's +a generic interrupt table that we'll remove later. + +``` rust +#![feature(used)] +#![no_std] + +extern crate cortex_m; +extern crate cortex_m_rt; +extern crate cortex_m_semihosting; + +use core::fmt::Write; + +use cortex_m::asm; +use cortex_m_semihosting::hio; + +fn main() { + // get a handle to the *host* standard output + let mut stdout = hio::hstdout().unwrap(); + + // write "Hello, world!" to it + writeln!(stdout, "Hello, world!").unwrap(); +} + +// As we are not using interrupts, we just register a dummy catch all handler +#[link_section = ".vector_table.interrupts"] +#[used] +static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; + +extern "C" fn default_handler() { + asm::bkpt(); +} +``` + +The new thing here is the `#![no_std]` attribute. This indicates that this program will *not* link +to the `std`, standard, crate. Instead it will link to the `core` crate. The `core` crate is a +subset of the `std` crate that has no dependencies to OS mechanisms like threads, dynamic memory +allocation, sockets, etc. `core` provides the minimal amount of support to run Rust on a bare metal +system. + +### Build and analyze + +Let's build this: + +``` console +$ # NOTE use `thumbv7m-none-eabi` for the blue-pill, and `thumbv7em-none-eabihf` for the nucleo +$ xargo build --target thumbv7m-none-eabi +``` + +> The `thumbv7m-none-eabi` target corresponds to the Cortex-M3 architecture. The +> `thumbv7em-none-eabihf` target corresponds to the Cortex-M4F architecture -- note the "F": this +> means that the architecture has hardware support for floating point operations). + +This produces an unoptimized binary. + +``` console +$ # mind the target name (use thumbv7em-none-eabihf for the nucleo) +$ arm-none-eabi-size target/thumbv7m-none-eabi/debug/app + text data bss dec hex filename + 14596 0 0 14596 3904 target/thumbv7m-none-eabi/debug/app +``` + +Let's rebuild in release mode. To avoid repeating myself I'll create a `$TARGET` variable that +contains the name of the target. + +``` console +$ TARGET=thumbv7m-none-eabi + +$ xargo build --target $TARGET --release +``` + +Now the binary is much smaller. + +``` console +$ arm-none-eabi-size target/$TARGET/release/app + text data bss dec hex filename + 3646 0 0 3646 e3e target/thumbv7m-none-eabi/release/app +``` + +You can get a breakdown of the memory usage by passing the `-Ax` flag: + +``` console +$ arm-none-eabi-size -Ax target/$TARGET/debug/app +section size addr +.vector_table 0x400 0x8000000 +.text 0x27f8 0x8000400 +.rodata 0xd0c 0x8002c00 +.bss 0x0 0x20000000 +.data 0x0 0x20000000 +``` + +`.bss` and `.data` are statically allocated (`static`) variables; there are none in this program. +`.text` holds the program code. `.rodata` are constants, usually you'll find strings like our +"Hello, world!" in this section. `.vector_table` is a region of memory that holds the vector table. + +> **Exercise** Do you remember the start address of the Flash memory and RAM? (hint: memory.x) Which +> sections are located in Flash memory? Which sections are located in RAM? + +Another interesting thing to do here is to look at the disassembly of the program: + +``` console +$ arm-none-eabi-objdump -CD target/$TARGET/release/app +Disassembly of section .vector_table: + +08000000 <_svector_table>: + 8000000: 20005000 andcs r5, r0, r0 + +08000004 <cortex_m_rt::RESET_VECTOR>: + 8000004: 08000401 stmdaeq r0, {r0, sl} + +08000008 <EXCEPTIONS>: + 8000008: 08000639 stmdaeq r0, {r0, r3, r4, r5, r9, sl} + (..) + +Disassembly of section .text: + +08000400 <cortex_m_rt::reset_handler>: + 8000400: b580 push {r7, lr} + 8000402: 466f mov r7, sp + 8000404: b088 sub sp, #32 + 8000406: f240 0000 movw r0, #0 + 800040a: f240 0100 movw r1, #0 + 800040e: f2c2 0000 movt r0, #8192 ; 0x2000 + 8000412: f2c2 0100 movt r1, #8192 ; 0x2000 + 8000416: 4281 cmp r1, r0 + (..) + +08000638 <BUS_FAULT>: + 8000638: f3ef 8008 mrs r0, MSP + 800063c: f7ff bffa b.w 8000634 <cortex_m_rt::default_handler> + (..) + +Disassembly of section .rodata: + +08000d44 <vtable.8>: + 8000d44: 080004cb stmdaeq r0, {r0, r1, r3, r6, r7, sl} + 8000d48: 00000004 andeq r0, r0, r4 + 8000d4c: 00000004 andeq r0, r0, r4 + 8000d50: 080005e9 stmdaeq r0, {r0, r3, r5, r6, r7, r8, sl} + (..) +``` + +> **Exercise** Compare the contents of the `.vector_table` linker section, see above (or look at +> your local output), to the diagram of the vector table in the [ARM documentation][vector_table]. +> What are the values of the "Initial SP value", "Reset", "NMI", "Hard fault" entries according to +> the disassembly? What do these values mean? Investigate how these values are used in the boot +> process and the exception handling mechanism. + +[vector_table]: https://developer.arm.com/docs/dui0552/latest/the-cortex-m3-processor/exception-model/vector-table + +If you are curious about how the program ended with this particular memory layout look at the linker +scripts in the `target` directory -- these scripts instruct the linker where to place things. + +``` console +$ # list of linker scripts +$ find target -name '*.x' +target/thumbv7m-none-eabi/release/build/app-4c6a87e0e5f739ae/out/memory.x +target/thumbv7m-none-eabi/release/build/cortex-m-rt-4f13cf879b7980df/out/link.x +``` + +You can also visualize the exact linker command `rustc` used to link the binary by running the +following command: + +``` console +$ xargo rustc --target $TARGET --release -- -Z print-link-args +"arm-none-eabi-ld" "-L" (..) +``` + +### Flash and debug + +To flash the program into the microcontroller we must first connect the device to our laptop. If you +are using a NUCLEO-F401RE you only to connect a USB cable. If you are using the blue-pill you'll +have to connect a external SWD programmer. The pinout of the blue-pill is shown below ; +you'll have to at least connect the GND, SWDIO and SWCLK pins. If you want to power the blue-pill +using the SWD programmer then also connect the 3V3 *or* the 5V pin. + + + +Then we have to start OpenOCD. OpenOCD will connect to the SWD programmer (the NUCLEO-F401RE board +has a built-in one) and start a GDB server. + +``` console +$ # for the blue-pill +$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg + +$ # for the NUCLE-F401RE +$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f4x.cfg +``` + +> **NOTE(Linux)** If you get a permission error when running OpenOCD then you'll need to change the +> udev rules for the SWD programmer you are using. To do that create the following file at +> `/etc/udev/rules.d`. +> +> ``` console +> $ cat /etc/udev/rules.d/99-st-link.rules +> # ST-LINK v2 +> SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE:="0666" +> +> # ST-LINK v2-1 +> SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE:="0666" +> +> # NUCLEO-F401RE +> SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE:="0666" +> ``` +> +> With that file in place call the command `sudo udevadm control --reload-rules`. Then unplug and +> re-plug your SWD programmer. That should fix the permission problem. + +You should see some output like this: + +``` console +Open On-Chip Debugger 0.10.0 +Licensed under GNU GPL v2 +For bug reports, read + http://openocd.org/doc/doxygen/bugs.html +Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'. +Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD +adapter speed: 2000 kHz +adapter_nsrst_delay: 100 +none separate +Info : Unable to match requested speed 2000 kHz, using 1800 kHz +Info : Unable to match requested speed 2000 kHz, using 1800 kHz +Info : clock speed 1800 kHz +Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B +Info : using stlink api v2 +Info : Target voltage: 3.268993 +Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints +``` + +You should definitively get the last line -- maybe with some different numbers -- if you don't that +indicates a problem: it could be a connection problem, or you could have used the wrong +configuration file. + +The program will block. That's OK. Leave it running. + +Apart from the GDB server OpenOCD also starts a telnet server. You can connect to this server and +issue commands to the SWD programmer. + +``` console +$ telnet localhost 4444 + +> # this is the telnet propmt + +> # the following command will reset the microcontroller and halt the processor + +> reset halt +adapter speed: 1800 kHz +target halted due to debug-request, current mode: Thread +xPSR: 0x01000000 pc: 0x08000188 msp: 0x20018000 + +> exit +``` + +The documentation of these commands is [here][openocd-commands]. + +[openocd-commands]: http://openocd.org/doc/html/General-Commands.html + +With OpenOCD working now we can flash and debug the program using GDB. + +``` console +$ # enable .gdbinit files +$ echo 'add-auto-load-safe-path /' >> ~/.gdbinit + +$ arm-none-eabi-gdb target/$TARGET/debug/app + +(gdb) # this is the GDB shell +``` + +The processor will be halted at the entry point. You can print the source code that the processor is +about to execute using the `list` command: + +``` console +(gdb) # source code +(gdb) list +331 /// +332 /// This is the entry point of all programs +333 #[cfg(target_arch = "arm")] +334 #[link_section = ".reset_handler"] +335 unsafe extern "C" fn reset_handler() -> ! { +336 r0::zero_bss(&mut _sbss, &mut _ebss); +337 r0::init_data(&mut _sdata, &mut _edata, &_sidata); +338 +339 match () { +340 #[cfg(not(has_fpu))] +``` + +And you can print the machine code that the processor is about to execute using the `disassemble` +command. + +``` console +(gdb) disassemble +Dump of assembler code for function cortex_m_rt::reset_handler: + 0x08000130 <+0>: push {r7, lr} + 0x08000132 <+2>: mov r7, sp + 0x08000134 <+4>: sub sp, #32 +=> 0x08000136 <+6>: movw r0, #0 + 0x0800013a <+10>: movw r1, #0 + 0x0800013e <+14>: movt r0, #8192 ; 0x2000 + 0x08000142 <+18>: movt r1, #8192 ; 0x2000 +``` + +We can skip to our program `main` by creating a breakpoint and then calling `continue`. + +``` console +(gdb) break app::main +Breakpoint 1 at 0x800045c: file src/main.rs, line 18. + +(gdb) continue +Continuing. +Note: automatically using hardware breakpoints for read-only addresses. + +Breakpoint 1, app::main () at src/main.rs:18 +18 let mut stdout = hio::hstdout().unwrap(); +``` + +We are now in `main` we can execute each line of code in this function be repeatedly calling the +`next` command. + +``` console +(gdb) next +19 writeln!(stdout, "Hello, world!").unwrap(); + +(gdb) next +20 } +``` + +After executing `writeln!` you should see "Hello, world!" printed *on the OpenOCD console*. + +``` console +$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg +(..) +Info : halted: PC: 0x08000aee +Hello, world! +Info : halted: PC: 0x0800049c +(..) +``` + +One more thing we can do here is to reset the microcontroller using the `monitor` command. `monitor` +will forward the command to the telnet server. + +``` console +(gdb) monitor reset halt +target halted due to debug-request, current mode: Thread +xPSR: 0x01000000 pc: 0x08000400 msp: 0x20005000, semihosting +``` + +This is the same command we ran before from the `telnet` prompt. + +Tip: You can get list of all the GDB commands by entering `help all` in the GDB prompt. + +Note that semihosting is *very slow*. Each write operation takes *hundreds* of milliseconds; the +processor will be in a halted state for the duration of the write operation. Semihosting is nice +because it requires no extra wiring or stream but it's only appropriate for simple programs where +timing is not a concern. + +### Device specific program + +Let's replace that weird `INTERRUPTS` + `default_handler` with something proper. Change +`src/main.rs` to: + +``` rust +#![no_std] + +extern crate cortex_m_semihosting; +extern crate stm32f103xx; // heads up! use `stm32f40x` for the NUCLEO-401RE + +use core::fmt::Write; + +use cortex_m_semihosting::hio; + +fn main() { + // get a handle to the *host* standard output + let mut stdout = hio::hstdout().unwrap(); + + // write "Hello, world!" to it + writeln!(stdout, "Hello, world!").unwrap(); +} +``` + +What we have done here is replace the *generic* vector table with one tailored for the device we +are targeting. Note that `cortex-m-rt` is gone; that crate is now provided by the device crate. + +Before we can compile this we have to tell Cargo where to get the device crate from. This info goes +in the Cargo.toml file: + +``` console +$ $EDITOR Cargo.toml + +$ cat Cargo.toml +# .. + +# for the blue-pill (NOTE use this dependency or the other but not both) +[dependencies.stm32f103xx] +features = ["rt"] # this feature indicates that the device crate will provide the vector table +version = "0.7.5" + +# for the blue-pill (NOTE use this dependency or the other but not both) +[dependencies.stm32f40x] +features = ["rt"] # see comment above +git = "https://gitlab.henriktjader.com/pln/STM32F40x" + +# .. +``` + +You should now be able to compile the program again. + +``` console +$ xargo build --target $TARGET --release + +$ arm-none-eabi-size -Ax target/$TARGET/release/app +section size addr +.vector_table 0x130 0x8000000 +.text 0x944 0x8000130 +.rodata 0xfc 0x8000a74 +.bss 0x0 0x20000000 +.data 0x0 0x20000000 +``` + +If you are careful observer you probably have noticed that the `.vector_table` is now smaller. +All the interrupts of a device are listed in the vector table and each device has a different number +of interrupts thus the size of the vector table will vary according to the device. + +In the original program we were using a "generic" vector table that assumed that the device had 240 +interrupts -- that's the maximum number of interrupts a device can have but devices usually have way +less interrupts. + +### Bonus: setting a default target + +So far we have always been calling Xargo with the `--target` flag. We can skip that by setting a +default target in `.cargo/config`. + +``` console +$ cat >>.cargo/config <<EOF +[build] +target = "$TARGET" +EOF +``` + +Now you can build your program by simply calling `xargo build` or `xargo build --release`. + +## Transitioning to Visual Code Studio + +> **NOTE** Here I assume that you have already installed the [vscode-rust] plugin. + +[vscode-rust]: https://github.com/editor-rs/vscode-rust + +First some cleanup: + +- Terminate any open GDB clients connected to the OpenOCD GDB server. + +- Remove, or rename, the local `.gdbinit` file. + +Now open the `app` folder with VSCode. + +``` console +$ code . +``` + +### Formatting + +You can enable format on save by adding `"editor.formatOnSave": true` to the User settings which you +can open hitting `Ctrl + ,` + +### Build task + +You make the build task work with Cortex-M projects you'll have to tweak the default build task. +Pick from the menu: `Tasks > Configure Tasks...` then pick `Rust: cargo build`. In `tasks.json` +write: + +``` js +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "taskName": "xargo build", + "command": "xargo", + "args": [ + "build" + ], + "problemMatcher": [ + "$rustc" + ] + } + ] +} +``` + +Now pick from the menu: `Tasks > Configure Default Build Task...` and pick `xargo build`. + +Now you should be able to build your project by picking `Tasks > Run Build Task...` from the menu or +by hitting the shorcut `Ctrl + Shift + B`. + + + +### Debugging + +You'll need to configure Native Debug to work with embedded projects. Pick `Debug > Open +Configurations` from the menu, pick `GDB` from the drop down menu and then write this into +`launch.json`. + +``` js +{ + "configurations": [ + { + "autorun": [ + "monitor arm semihosting enable", + "load", + "break app::main" // Heads up: crate name + ], + "cwd": "${workspaceRoot}", + "gdbpath": "arm-none-eabi-gdb", + "executable": "./target/thumbv7m-none-eabi/debug/app", // Heads up: target name + "name": "Debug", + "remote": true, + "request": "attach", + "target": ":3333", + "type": "gdb" + } + ], + "version": "0.2.0" +} +``` + +Now you should be able to debug your program by pressing `F5`. Note that (a) you have to build the +program first (e.g. by pressing `Ctrl + Shift + B`) and that (b) the debugger will execute your +program right after flashing the device so you'll always need at least one breakpoint. + +