use probe-run instead of dk-run

This commit is contained in:
Lotte Steenbrink 2020-08-17 10:58:10 +02:00
parent 7134ab798e
commit 529f5ea34c
12 changed files with 15 additions and 1089 deletions

View file

@ -1,5 +1,5 @@
[target.thumbv7em-none-eabi]
runner = "dk-run"
runner = "probe-run --chip nRF52840_xxAA"
rustflags = [
"-C", "link-arg=-Tlink.x",
]

View file

@ -1,5 +1,5 @@
[target.thumbv7em-none-eabi]
runner = "dk-run"
runner = "probe-run --chip nRF52840_xxAA"
rustflags = [
"-C", "link-arg=-Tlink.x",
]

View file

@ -301,7 +301,7 @@ fn RTC0() {
unsafe { core::mem::transmute::<_, RTC0>(()).events_ovrflw.reset() }
}
/// Exits the application and prints a backtrace when the program is executed through the `dk-run`
/// Exits the application and prints a backtrace when the program is executed through the `probe-run`
/// Cargo runner
pub fn exit() -> ! {
log::info!("`dk::exit() called; exiting ...`");

View file

@ -15,8 +15,8 @@ If you are not using VS code run the `cargo run --bin hello` command from the `a
> NOTE if you run into an error along the lines of "Debug power request failed" retry the operation and the error should disappear
The `firmware` workspace has been configured to cross-compile applications to the ARM Cortex-M architecture and then run them through the `dk-run` custom Cargo runner. The `dk-run` tool will load and run the embedded application on the microcontroller and collect logs from the microcontroller.
The `firmware` workspace has been configured to cross-compile applications to the ARM Cortex-M architecture and then run them using the `probe-run` custom Cargo runner. The `probe-run` tool will load and run the embedded application on the microcontroller and collect logs from the microcontroller.
The `dk-run` process will terminate when the microcontroller enters the "halted" state. From the embedded application, one can enter the "halted" state using the `asm::bkpt` function. For convenience, an `exit` function is provided in the `dk` Hardware Abstraction Layer (HAL). This function is divergent like `std::process::exit` (`fn() -> !`) and can be used to halt the device and terminate the `dk-run` process.
The `probe-run` process will terminate when the microcontroller enters the "halted" state. From the embedded application, one can enter the "halted" state using the `asm::bkpt` function. For convenience, an `exit` function is provided in the `dk` Hardware Abstraction Layer (HAL). This function is divergent like `std::process::exit` (`fn() -> !`) and can be used to halt the device and terminate the `probe-run` process.
Note that when the `dk-run` tool sees the device enter the halted state it will proceed to *reset-halt* the device. This is particularly important when writing USB applications because simply leaving the device in a halted state will make it appear as an unresponsive USB device to the host. Some OSes (e.g. Linux) will try to make an unresponsive device respond by power cycling the entire USB bus -- this will cause all other USB devices on the bus to be re-enumerated. Reset-halting the device will cause it to be electrically disconnected from the host USB bus and avoid the "power cycle the whole USB bus" scenario.
Note that when the `probe-run` tool sees the device enter the halted state it will proceed to *reset-halt* the device. This is particularly important when writing USB applications because simply leaving the device in a halted state will make it appear as an unresponsive USB device to the host. Some OSes (e.g. Linux) will try to make an unresponsive device respond by power cycling the entire USB bus -- this will cause all other USB devices on the bus to be re-enumerated. Reset-halting the device will cause it to be electrically disconnected from the host USB bus and avoid the "power cycle the whole USB bus" scenario.

View file

@ -147,6 +147,10 @@ Installed package `cargo-binutils v0.3.0` (..)
$ cargo install cargo-bloat
(..)
Installed package `cargo-bloat v0.9.3` (..)
$ cargo install probe-run
(..)
Installed package `probe-run v0.1.0` (..)
```
## Python

View file

@ -1,6 +1,6 @@
# Running the Program from VS Code
Both `cargo-embed` and `cargo-flash` are tools based on the `probe-rs` library. This library exposes an API to communicate with the J-Link and perform all the operations exposed by the JTAG protocol. For this workshop we have developed a small Cargo runner that uses the `probe-rs` library to streamline the process of running a program and printing logs, like `cargo-embed`, while also having better integration into VS code.
Both `cargo-embed` and `cargo-flash` are tools based on the `probe-rs` library. This library exposes an API to communicate with the J-Link and perform all the operations exposed by the JTAG protocol. We have developed a small Cargo runner called [probe-run](https://github.com/knurling-rs/probe-run) that uses the `probe-rs` library to streamline the process of running a program and printing logs, like `cargo-embed`, while also having better integration into VS code. We'll be using it in this workshop, and you can utilize it in your future projects too.
✅ Open the `src/bin/hello.rs` file and click the "Run" button that's hovering over the `main` function.
@ -21,6 +21,6 @@ stack backtrace:
3: 0x00001ba2 - Reset
```
`cargo run` will compile the application and then invoke the `dk-run` tool with its argument set to the path of the output ELF file.
`cargo run` will compile the application and then invoke `probe-run` with its argument set to the path of the output ELF file.
Unlike `cargo-embed`, `dk-run` will terminate when the program reaches a breakpoint (`asm::bkpt`) that halts the device. Before exiting `dk-run` will print a stack backtrace of the program starting from the breakpoint. This can be used to write small test programs that are meant to perform some work and then terminate.
Unlike `cargo-embed`, `probe-run` will terminate when the program reaches a breakpoint (`asm::bkpt`) that halts the device. Before exiting, `probe-run` will print a stack backtrace of the program starting from the breakpoint. This can be used to write small test programs that are meant to perform some work and then terminate.

View file

@ -28,7 +28,6 @@ nrfutil version 6.1.0
From the `tools` folder run these commands *from different terminals so they'll run in parallel*:
- `cargo install --path dk-run`
- `cargo install --path usb-list`
- `cargo install --path dongle-flash`
- `cargo install --path serial-term`
@ -40,7 +39,6 @@ Leave the processes running in the background.
From the `tools` folder run these commands *from different terminals so they'll run in parallel*:
- `cargo install --path dk-run`
- `cargo install --path usb-list`
Leave the processes running in the background.

View file

@ -7,7 +7,7 @@ You may get one of these errors:
``` console
$ cargo run --bin usb-4
Running `dk-run target/thumbv7em-none-eabi/debug/usb-4`
Running `probe-run target/thumbv7em-none-eabi/debug/usb-4`
Error: An error specific to a probe type occured: USB error while taking control over USB device: Access denied (insufficient permissions)
Caused by:
@ -16,7 +16,7 @@ Caused by:
``` console
$ cargo run --bin usb-4
Running `dk-run target/thumbv7em-none-eabi/debug/usb-4`
Running `probe-run target/thumbv7em-none-eabi/debug/usb-4`
Error: An error specific to a probe type occured: USB error while taking control over USB device: Resource busy
Caused by:

526
tools/Cargo.lock generated
View file

@ -42,41 +42,6 @@ version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "base64"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
[[package]]
name = "bit-set"
version = "0.5.2"
@ -92,24 +57,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
[[package]]
name = "bitfield"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cc"
version = "1.0.54"
@ -155,33 +108,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "derivative"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f"
dependencies = [
"proc-macro2",
"quote 1.0.7",
"syn 1.0.31",
]
[[package]]
name = "dk-run"
version = "0.0.0"
dependencies = [
"anyhow",
"arrayref",
"ctrlc",
"env_logger",
"gimli 0.21.0",
"log",
"probe-rs",
"probe-rs-rtt",
"rustc-demangle",
"xmas-elf",
]
[[package]]
name = "dongle-flash"
version = "0.0.0"
@ -193,42 +119,6 @@ dependencies = [
"xmas-elf",
]
[[package]]
name = "dtoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
[[package]]
name = "enum-primitive-derive"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd"
dependencies = [
"num-traits 0.1.43",
"quote 0.3.15",
"syn 0.11.11",
]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "filetime"
version = "0.2.10"
@ -241,18 +131,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "flate2"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "getrandom"
version = "0.1.14"
@ -264,51 +142,6 @@ dependencies = [
"wasi",
]
[[package]]
name = "gimli"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633"
dependencies = [
"arrayvec",
"byteorder",
"fallible-iterator",
"indexmap",
"smallvec",
"stable_deref_trait",
]
[[package]]
name = "gimli"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
dependencies = [
"fallible-iterator",
"indexmap",
"stable_deref_trait",
]
[[package]]
name = "goblin"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d20fd25aa456527ce4f544271ae4fea65d2eda4a6561ea56f39fb3ee4f7e3884"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]]
name = "hermit-abi"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
dependencies = [
"libc",
]
[[package]]
name = "hidapi"
version = "1.2.2"
@ -320,51 +153,12 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]]
name = "ihex"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9394ec1fbc3afbdfb357ba267a7fffdc39f6e3d8ba88c0af6d9476e6d3c889d5"
[[package]]
name = "indexmap"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
dependencies = [
"autocfg",
]
[[package]]
name = "jaylink"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7303af61073160fc72f7f56ae5bf39234ea85387441101229224a8d64b2057b"
dependencies = [
"bitflags",
"byteorder",
"log",
"rusb",
]
[[package]]
name = "jep106"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f57cd08ee4fbc8043949150a59e34ea5f2afeb172f875db9607689e48600c653"
dependencies = [
"serde",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -423,21 +217,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "linked-hash-map"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
dependencies = [
"cfg-if",
]
[[package]]
name = "mach"
version = "0.1.2"
@ -462,15 +241,6 @@ version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "nix"
version = "0.14.1"
@ -497,35 +267,6 @@ dependencies = [
"void",
]
[[package]]
name = "num-traits"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
dependencies = [
"num-traits 0.2.11",
]
[[package]]
name = "num-traits"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5666bbb90bc4d1e5bdcb26c0afda1822d25928341e9384ab187a9b37ab69e36"
dependencies = [
"flate2",
"target-lexicon",
"wasmparser",
]
[[package]]
name = "pids"
version = "0.0.0"
@ -536,101 +277,12 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "ppv-lite86"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "probe-rs"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4508248f016eb7f7a50751b9c3e8f9e25add169feac006aef5115dbb5e42be"
dependencies = [
"base64",
"bitfield",
"derivative",
"enum-primitive-derive",
"gimli 0.20.0",
"goblin",
"hidapi",
"ihex",
"jaylink",
"jep106",
"lazy_static",
"log",
"num-traits 0.2.11",
"object",
"probe-rs-t2rust",
"rusb",
"scroll",
"serde",
"serde_yaml",
"svg",
"thiserror",
]
[[package]]
name = "probe-rs-rtt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "823c43c9553ebea43672d38c4756fe360305225f2b649b0e0bf28319354ca01d"
dependencies = [
"probe-rs",
"scroll",
"thiserror",
]
[[package]]
name = "probe-rs-t2rust"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e0e2d177a33e8c8a46d98767fb01877f1c466826b9c25f88749f35140d0aae0"
dependencies = [
"base64",
"proc-macro2",
"quote 1.0.7",
"scroll",
"serde_yaml",
]
[[package]]
name = "proc-macro2"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [
"unicode-xid 0.2.0",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
@ -722,64 +374,6 @@ dependencies = [
"libusb1-sys",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "scroll"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9"
dependencies = [
"proc-macro2",
"quote 1.0.7",
"syn 1.0.31",
]
[[package]]
name = "serde"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
dependencies = [
"proc-macro2",
"quote 1.0.7",
"syn 1.0.31",
]
[[package]]
name = "serde_yaml"
version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
dependencies = [
"dtoa",
"linked-hash-map",
"serde",
"yaml-rust",
]
[[package]]
name = "serial-term"
version = "0.0.0"
@ -807,55 +401,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "smallvec"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "stable_deref_trait"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
[[package]]
name = "svg"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3685c82a045a6af0c488f0550b0f52b4c77d2a52b0ca8aba719f9d268fa96965"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
dependencies = [
"quote 0.3.15",
"synom",
"unicode-xid 0.0.4",
]
[[package]]
name = "syn"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
dependencies = [
"proc-macro2",
"quote 1.0.7",
"unicode-xid 0.2.0",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
dependencies = [
"unicode-xid 0.0.4",
]
[[package]]
name = "take_mut"
version = "0.2.2"
@ -874,12 +419,6 @@ dependencies = [
"xattr",
]
[[package]]
name = "target-lexicon"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
[[package]]
name = "tempfile"
version = "3.1.0"
@ -894,35 +433,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
"winapi-util",
]
[[package]]
name = "thiserror"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
dependencies = [
"proc-macro2",
"quote 1.0.7",
"syn 1.0.31",
]
[[package]]
name = "thread_local"
version = "1.0.1"
@ -932,18 +442,6 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "usb-list"
version = "0.0.0"
@ -971,12 +469,6 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasmparser"
version = "0.51.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a"
[[package]]
name = "winapi"
version = "0.3.8"
@ -993,15 +485,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
@ -1026,15 +509,6 @@ dependencies = [
"zero",
]
[[package]]
name = "yaml-rust"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "zero"
version = "0.1.2"

View file

@ -1,7 +1,6 @@
[workspace]
members = [
"change-channel",
"dk-run",
"dongle-flash",
"serial-term",
"usb-list",

View file

@ -1,18 +0,0 @@
[package]
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
name = "dk-run"
version = "0.0.0"
[dependencies]
anyhow = "1.0.31"
arrayref = "0.3.6"
ctrlc = "3.1.4"
env_logger = "0.7.1"
gimli = "0.21.0"
log = "0.4.8"
probe-rs = "0.6.2"
probe-rs-rtt = "0.1.0"
rustc-demangle = "0.1.16"
xmas-elf = "0.7.0"

View file

@ -1,531 +0,0 @@
use core::{
cmp,
convert::{TryFrom, TryInto},
mem,
ops::Range,
sync::atomic::{AtomicBool, Ordering},
};
use std::{
collections::{btree_map, BTreeMap},
env, fs,
io::{self, Write as _},
path::Path,
process,
rc::Rc,
};
use anyhow::{anyhow, bail};
use arrayref::array_ref;
use gimli::{
read::{CfaRule, DebugFrame, UnwindSection},
BaseAddresses, EndianSlice, LittleEndian, RegisterRule, UninitializedUnwindContext,
};
use probe_rs::{
flashing::{self, Format},
Core, CoreRegisterAddress, Probe,
};
use probe_rs_rtt::{Rtt, ScanRegion};
use xmas_elf::{program::Type, sections::SectionData, symbol_table::Entry, ElfFile};
fn main() -> Result<(), anyhow::Error> {
notmain().map(|code| process::exit(code))
}
fn notmain() -> Result<i32, anyhow::Error> {
env_logger::init();
let args = env::args().skip(1 /* program name */).collect::<Vec<_>>();
if args.len() != 1 {
bail!("expected exactly one argument")
}
let path = &args[0];
let bytes = fs::read(path)?;
let elf = ElfFile::new(&bytes).map_err(|s| anyhow!("{}", s))?;
// sections used in cortex-m-rt
// NOTE we won't load `.uninit` so it is not included here
// NOTE we don't load `.bss` because the app (cortex-m-rt) will zero it
let candidates = [".vector_table", ".text", ".rodata"];
let text = elf
.section_iter()
.zip(0..)
.filter_map(|(sect, shndx)| {
if sect.get_name(&elf) == Ok(".text") {
Some(shndx)
} else {
None
}
})
.next();
let mut debug_frame = None;
let mut range_names = None;
let mut rtt_addr = None;
let mut sections = vec![];
let mut dotdata = None;
let mut registers = None;
for sect in elf.section_iter() {
if let Ok(name) = sect.get_name(&elf) {
if name == ".debug_frame" {
debug_frame = Some(sect.raw_data(&elf));
continue;
}
if name == ".symtab" {
if let Ok(symtab) = sect.get_data(&elf) {
let (rn, rtt_addr_) = range_names_from(&elf, symtab, text)?;
range_names = Some(rn);
rtt_addr = rtt_addr_;
}
}
let size = sect.size();
// skip empty sections
if candidates.contains(&name) && size != 0 {
let start = sect.address();
if size % 4 != 0 || start % 4 != 0 {
// we could support unaligned sections but let's not do that now
bail!("section `{}` is not 4-byte aligned", name);
}
let start = start.try_into()?;
let data = sect
.raw_data(&elf)
.chunks_exact(4)
.map(|chunk| u32::from_le_bytes(*array_ref!(chunk, 0, 4)))
.collect::<Vec<_>>();
if name == ".vector_table" {
registers = Some(Registers {
vtor: start,
// Initial stack pointer
sp: data[0],
// Reset handler
pc: data[1],
});
} else if name == ".data" {
// don't put .data in the `sections` variable; it is specially handled
dotdata = Some(Data {
phys: start,
virt: start,
data,
});
continue;
}
sections.push(Section { start, data });
}
}
}
if let Some(data) = dotdata.as_mut() {
// patch up `.data` physical address
let mut patched = false;
for ph in elf.program_iter() {
if ph.get_type() == Ok(Type::Load) {
if u32::try_from(ph.virtual_addr())? == data.virt {
patched = true;
data.phys = ph.physical_addr().try_into()?;
break;
}
}
}
if !patched {
bail!("couldn't extract `.data` physical address from the ELF");
}
}
let registers = registers.ok_or_else(|| anyhow!("`.vector_table` section is missing"))?;
let probes = Probe::list_all();
if probes.is_empty() {
// TODO improve error message
bail!("nRF52840 Development Kit appears to not be connected")
}
log::debug!("found {} probes", probes.len());
let probe = probes[0].open()?;
log::info!("opened probe");
let sess = probe.attach("nRF52840_xxAA")?;
log::info!("started session");
let core = sess.attach_to_core(0)?;
log::info!("attached to core");
core.reset_and_halt()?;
log::info!("reset and halted the core");
eprintln!("flashing program ..");
// load program into memory
for section in &sections {
core.write_32(section.start, &section.data)?;
}
if let Some(section) = dotdata {
core.write_32(section.phys, &section.data)?;
}
// adjust registers
// this is the link register reset value; it indicates the end of the call stack
if registers.vtor >= 0x2000_0000 {
// program lives in RAM
core.write_core_reg(LR, LR_END)?;
core.write_core_reg(SP, registers.sp)?;
core.write_core_reg(PC, registers.pc)?;
core.write_word_32(VTOR, registers.vtor)?;
log::info!("loaded program into RAM");
eprintln!("DONE");
core.run()?;
} else {
// XXX the device may have already loaded SP and PC at this point in this case?
// program lives in Flash
flashing::download_file(&sess, Path::new(path), Format::Elf)?;
log::info!("flashed program");
eprintln!("DONE");
core.reset()?;
}
eprintln!("resetting device");
let core = Rc::new(core);
let rtt_addr_res = rtt_addr.ok_or_else(|| anyhow!("RTT control block not available"))?;
let mut rtt_res: Result<Rtt, probe_rs_rtt::Error> =
Err(probe_rs_rtt::Error::ControlBlockNotFound);
const NUM_RETRIES: usize = 5; // picked at random, increase if necessary
for try_index in 0..=NUM_RETRIES {
rtt_res = Rtt::attach_region(core.clone(), &sess, &ScanRegion::Exact(rtt_addr_res));
match rtt_res {
Ok(_) => {
log::info!("Successfully attached RTT");
break;
}
Err(probe_rs_rtt::Error::ControlBlockNotFound) => {
if try_index < NUM_RETRIES {
log::info!("Could not attach because the target's RTT control block isn't initialized (yet). retrying");
} else {
log::info!("Max number of RTT attach retries exceeded. Did you call dk::init() first thing in your program?");
return Err(anyhow!(probe_rs_rtt::Error::ControlBlockNotFound));
}
}
Err(e) => {
return Err(anyhow!(e));
}
}
}
let channel = rtt_res
.expect("unreachable") // this block is only executed when rtt was successfully attached before
.up_channels()
.take(0)
.ok_or_else(|| anyhow!("RTT up channel 0 not found"))?;
static CONTINUE: AtomicBool = AtomicBool::new(true);
ctrlc::set_handler(|| {
CONTINUE.store(false, Ordering::Relaxed);
})?;
// wait for breakpoint
let stdout = io::stdout();
let mut stdout = stdout.lock();
let mut read_buf = [0; 1024];
let mut was_halted = false;
while CONTINUE.load(Ordering::Relaxed) {
let n = channel.read(&mut read_buf)?;
if n != 0 {
stdout.write_all(&read_buf[..n])?;
}
let is_halted = core.core_halted()?;
if is_halted && was_halted {
break;
}
was_halted = is_halted;
}
drop(stdout);
// Ctrl-C was pressed; stop the microcontroller
if !CONTINUE.load(Ordering::Relaxed) {
core.halt()?;
}
let pc = core.read_core_reg(PC)?;
let debug_frame = debug_frame.ok_or_else(|| anyhow!("`.debug_frame` section not found"))?;
let range_names = range_names.ok_or_else(|| anyhow!("`.symtab` section not found"))?;
// print backtrace
backtrace(&core, pc, debug_frame, &range_names)?;
core.reset_and_halt()?;
Ok(0)
}
fn backtrace(
core: &Core,
mut pc: u32,
debug_frame: &[u8],
range_names: &RangeNames,
) -> Result<(), anyhow::Error> {
fn gimli2probe(reg: &gimli::Register) -> CoreRegisterAddress {
CoreRegisterAddress(reg.0)
}
struct Registers<'c> {
cache: BTreeMap<u16, u32>,
core: &'c Core,
}
impl<'c> Registers<'c> {
fn new(lr: u32, sp: u32, core: &'c Core) -> Self {
let mut cache = BTreeMap::new();
cache.insert(LR.0, lr);
cache.insert(SP.0, sp);
Self { cache, core }
}
fn get(&mut self, reg: CoreRegisterAddress) -> Result<u32, anyhow::Error> {
Ok(match self.cache.entry(reg.0) {
btree_map::Entry::Occupied(entry) => *entry.get(),
btree_map::Entry::Vacant(entry) => *entry.insert(self.core.read_core_reg(reg)?),
})
}
fn insert(&mut self, reg: CoreRegisterAddress, val: u32) {
self.cache.insert(reg.0, val);
}
fn update_cfa(
&mut self,
rule: &CfaRule<EndianSlice<LittleEndian>>,
) -> Result</* cfa_changed: */ bool, anyhow::Error> {
match rule {
CfaRule::RegisterAndOffset { register, offset } => {
let cfa = (i64::from(self.get(gimli2probe(register))?) + offset) as u32;
let ok = self.cache.get(&SP.0) != Some(&cfa);
self.cache.insert(SP.0, cfa);
Ok(ok)
}
// NOTE not encountered in practice so far
CfaRule::Expression(_) => todo!("CfaRule::Expression"),
}
}
fn update(
&mut self,
reg: &gimli::Register,
rule: &RegisterRule<EndianSlice<LittleEndian>>,
) -> Result<(), anyhow::Error> {
match rule {
RegisterRule::Undefined => unreachable!(),
RegisterRule::Offset(offset) => {
let cfa = self.get(SP)?;
let addr = (i64::from(cfa) + offset) as u32;
self.cache.insert(reg.0, self.core.read_word_32(addr)?);
}
_ => unimplemented!(),
}
Ok(())
}
}
let mut debug_frame = DebugFrame::new(debug_frame, LittleEndian);
// 32-bit ARM -- this defaults to the host's address size which is likely going to be 8
debug_frame.set_address_size(mem::size_of::<u32>() as u8);
let sp = core.read_core_reg(SP)?;
let lr = core.read_core_reg(LR)?;
// statically linked binary -- there are no relative addresses
let bases = &BaseAddresses::default();
let ctx = &mut UninitializedUnwindContext::new();
let mut frame = 0;
let mut registers = Registers::new(lr, sp, core);
println!("stack backtrace:");
loop {
println!(
"{:>4}: {:#010x} - {}",
frame,
pc,
range_names
.binary_search_by(|rn| if rn.0.contains(&pc) {
cmp::Ordering::Equal
} else if pc < rn.0.start {
cmp::Ordering::Greater
} else {
cmp::Ordering::Less
})
.map(|idx| &*range_names[idx].1)
.unwrap_or("<unknown>")
);
let fde = debug_frame.fde_for_address(bases, pc.into(), DebugFrame::cie_from_offset)?;
let uwt_row = fde.unwind_info_for_address(&debug_frame, bases, ctx, pc.into())?;
let cfa_changed = registers.update_cfa(uwt_row.cfa())?;
for (reg, rule) in uwt_row.registers() {
registers.update(reg, rule)?;
}
let lr = registers.get(LR)?;
if lr == LR_END {
break;
}
if !cfa_changed && lr == pc {
println!("error: the stack appears to be corrupted beyond this point");
return Ok(());
}
if lr > 0xffff_fff0 {
println!(" <exception entry>");
let sp = registers.get(SP)?;
let stacked = Stacked::read(core, sp)?;
registers.insert(LR, stacked.lr);
// adjust the stack pointer for stacked registers
registers.insert(SP, sp + mem::size_of::<Stacked>() as u32);
pc = stacked.pc;
} else {
if lr & 1 == 0 {
bail!("bug? LR ({:#010x}) didn't have the Thumb bit set", lr)
}
pc = lr & !1;
}
frame += 1;
}
Ok(())
}
/// Registers stacked on exception entry
// XXX assumes that the floating pointer registers are NOT stacked (which may not be the case for HF
// targets)
#[derive(Debug)]
struct Stacked {
r0: u32,
r1: u32,
r2: u32,
r3: u32,
r12: u32,
lr: u32,
pc: u32,
xpsr: u32,
}
impl Stacked {
fn read(core: &Core, sp: u32) -> Result<Self, anyhow::Error> {
let mut registers = [0; 8];
core.read_32(sp, &mut registers)?;
Ok(Stacked {
r0: registers[0],
r1: registers[1],
r2: registers[2],
r3: registers[3],
r12: registers[4],
lr: registers[5],
pc: registers[6],
xpsr: registers[7],
})
}
}
// FIXME this might already exist in the DWARF data; we should just use that
/// Map from PC ranges to demangled Rust names
type RangeNames = Vec<(Range<u32>, String)>;
type Shndx = u16;
fn range_names_from(
elf: &ElfFile,
sd: SectionData,
text: Option<Shndx>,
) -> Result<(RangeNames, Option<u32>), anyhow::Error> {
let mut range_names = vec![];
let mut rtt = None;
if let SectionData::SymbolTable32(entries) = sd {
for entry in entries {
if let Ok(name) = entry.get_name(elf) {
if name == "_SEGGER_RTT" {
rtt = Some(entry.value() as u32);
}
if Some(entry.shndx()) == text && entry.size() != 0 {
let mut name = rustc_demangle::demangle(name).to_string();
// clear the thumb bit
let start = entry.value() as u32 & !1;
// strip the hash (e.g. `::hd881d91ced85c2b0`)
let hash_len = "::hd881d91ced85c2b0".len();
if let Some(pos) = name.len().checked_sub(hash_len) {
let maybe_hash = &name[pos..];
if maybe_hash.starts_with("::h") {
// FIXME avoid this allocation
name = name[..pos].to_string();
}
}
range_names.push((start..start + entry.size() as u32, name));
}
}
}
}
range_names.sort_unstable_by(|a, b| a.0.start.cmp(&b.0.start));
Ok((range_names, rtt))
}
const LR: CoreRegisterAddress = CoreRegisterAddress(14);
const PC: CoreRegisterAddress = CoreRegisterAddress(15);
const SP: CoreRegisterAddress = CoreRegisterAddress(13);
const VTOR: u32 = 0xE000_ED08;
const LR_END: u32 = 0xFFFF_FFFF;
/// ELF section to be loaded onto the target
#[derive(Debug)]
struct Section {
start: u32,
data: Vec<u32>,
}
/// The .data section has a physical address different that its virtual address; we want to load the
/// data into the physical address (which would normally be in FLASH) so that cortex-m-rt doesn't
/// corrupt the variables in .data during initialization -- it would be best if we could disable
/// cortex-m-rt's `init_data` call but there's no such feature
#[derive(Debug)]
struct Data {
phys: u32,
virt: u32,
data: Vec<u32>,
}
/// Registers to update before running the program
struct Registers {
sp: u32,
pc: u32,
vtor: u32,
}