embassy-rs / nrf-softdevice

Apache License 2.0
264 stars 79 forks source link

Using with cortex-m-rtic #16

Closed kuon closed 2 years ago

kuon commented 4 years ago

I tried to use the softdevice with cortex-m-rtic, but if I specify "peripherals=true" and try to access CLOCK, I got the following crash

1879│                 None
1880│             } else {
1881│                 Some(unsafe { Peripherals::steal() })
1882│             }
1883│         })
1884│     }
1885│     #[doc = r"Unchecked version of `Peripherals::take`"]
1886│     #[inline]
1887│     pub unsafe fn steal() -> Self {
1888├───────> DEVICE_PERIPHERALS = true;
1889│         Peripherals {
1890│             FICR: FICR {
1891│                 _marker: PhantomData,
1892│             },
1893│             UICR: UICR {
1894│                 _marker: PhantomData,
1895│             },
1896│             CLOCK: CLOCK {
1897│                 _marker: PhantomData,
/home/kuon/.cargo/registry/src/github.com-1ecc6299db9ec823/nrf52840-pac-0.9.0/src/lib.rs
35                  hal::clocks::Clocks::new(cx.device.CLOCK).enable_ext_hfosc();
Breakpoint 1 at 0x2724c
Breakpoint 2 at 0x27264
Function "rust_begin_unwind" not defined.
Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal]
Breakpoint 3 at 0x271e4: file src/main.rs, line 22.
semihosting is enabled

Loading section .vector_table, size 0x100 lma 0x27000
Loading section .text, size 0x12c lma 0x27100
Start address 0x00027100, load size 556
Transfer rate: 1 KB/sec, 278 bytes/write.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 3, nrf52840_pac::Peripherals::steal () at /home/kuon/.cargo/registry/src/github.com-1ecc6299db9ec823/nrf52840-pac-0.9.0/src/lib.rs
:1888
1888            DEVICE_PERIPHERALS = true;
(gdb)

And if I try to use it with no peripherals, I got the following crash:

 4│
 5│ pub use bare_metal::{CriticalSection, Mutex, Nr};
 6│
 7│ /// Disables all interrupts
 8│ #[inline]
 9│ pub fn disable() {
10│     match () {
11│         #[cfg(all(cortex_m, feature = "inline-asm"))]
12│         () => unsafe {
13├───────────> llvm_asm!("cpsid i" ::: "memory" : "volatile");
14│         },
15│
16│         #[cfg(all(cortex_m, not(feature = "inline-asm")))]
17│         () => unsafe {
18│             extern "C" {
19│                 fn __cpsid();
20│             }
21│
22│             // XXX do we need a explicit compiler barrier here?
/home/kuon/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.6.3/src/interrupt.rs
13                  llvm_asm!("cpsid i" ::: "memory" : "volatile");
Breakpoint 1 at 0x271d0
Breakpoint 2 at 0x271e8
Function "rust_begin_unwind" not defined.
Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal]
Breakpoint 3 at 0x271b4: file src/main.rs, line 22.
semihosting is enabled

Loading section .vector_table, size 0x100 lma 0x27000
Loading section .text, size 0xec lma 0x27100
Start address 0x00027100, load size 492
Transfer rate: 1 KB/sec, 246 bytes/write.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 3, cortex_m::interrupt::disable () at /home/kuon/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.6.3/src/interrupt.rs:1
3
13                  llvm_asm!("cpsid i" ::: "memory" : "volatile");
(gdb)

Should I not try to use rtic and consider it incompatible with the softdevice?

Dirbaio commented 4 years ago

The softdevice has some reserved peripherals that you must not touch, or you'll get a crash. The forbidden peripherals are: POWER, CLOCK, RADIO, RTC0, TIMER0, RNG, ECB, CCM_AAR, TEMP, SWI5, EGU5

In your case you're lucky, you simply don't need to setup CLOCK. The softdevice does it for you (and you can configure it in the Config but the defaults are likely fine). You can simply enable the softdevice and then use TIMERs and RTCs etc directly.

This will become fully safe once #4 is implemented (it'll make it so it's impossible to use the peripherals after enabling softdevice without unsafe)

I don't know what the 2nd crash is about though. :( nrf-softdevice logs debug and panic messages with defmt. Can you set up defmt-rtt and run with probe-run so that we can check if there's a panic? here are some getting started docs, or you can just copy the config from the example dir in this repo.

I think RTIC should be usable if you don't use the softdevice's reserved priority levels and interrupts. It's somewhat unsafe because RTIC uses cortex_m's NVIC api directly instead of nrf_softdevice::interrupt, but as long as you don't touch them it should be OK. It'd be nice to have fully safe RTIC+softdevice but I think that requires changes to RTIC code.

kuon commented 4 years ago

Apparently before even running my code, cortex-m-rtic try to disable all interrupt, but this doesn't play well with the softdevice.

https://github.com/rtic-rs/cortex-m-rtic/blob/7506bd8ae0ba335fc058c2138438fab5f20f6dab/macros/src/codegen/pre_init.rs#L12

Is this to be expected? I mean at this point, the softdevice is not enable, but it was called as bootloader.

Dirbaio commented 4 years ago

That shouldn't crash if the softdevice is disabled. (In fact disabling interrupts when it's enabled doesn't crash either, it only crashes if you have them disabled for more than a few uS).

From reading your output it looks like you have a breakpoint at that address, and are simply hitting it (ie, it's not a crash). Maybe try removing all breakpoints?

(BTW breakpoints are not usable with the softdevice. The softdevice crashes if the CPU is stopped for a while, for the same reason it crashes if you disable interrupts for a long time)

kuon commented 4 years ago

Actually I had a breakpoint on main, but because of what you mentioned (breakpoints not usable with softdevice) I realized that breakpoint was causing the softdevice to crash.

Now I have another issue (I migrated to probe-run and defmt):

  (HOST) INFO  flashing program
  (HOST) INFO  success!
────────────────────────────────────────────────────────────────────────────────
0.000000 DEBUG Starting up
└─ playground::init @ src/main.rs:40
0.000000 DEBUG Config created
└─ playground::init @ src/main.rs:74
stack backtrace:
   0: 0x00027dfe - HardFaultTrampoline
      <exception entry>
   1: 0x000273ec - nrf_softdevice::softdevice::Softdevice::enable
   2: 0x000272d8 - playground::init
   3: 0x00027358 - main
   4: 0x0002783a - ResetTrampoline
   5: 0x000271a8 - Reset
   6: 0x00025f74 - <unknown>
Error: debug information is missing. Likely fixes:
1. compile the Rust code with `debug = 1` or higher. This is configured in the `profile.*` section of Cargo.toml
2. use a recent version of the `cortex-m` crates (e.g. cortex-m 0.6.3 or newer). Check versions in Cargo.lock
3. if linking to C code, compile the C code with the `-g` flag

Caused by:
    Do not have unwind info for the given address.

My init code is executed properly, then I try to enable the softdevice, and I have this crash.

I update my example repo with current code: https://github.com/kuon/nrf-playground

Dirbaio commented 4 years ago

If you want to see logs from nrf-softdevice, you have to enable the feature defmt-trace, otherwise you're only seeing logs from your app.

Turns out the issue is RTIC runs init with interrupts disabled and sd_softdevice_enable hardfaults if interrupts are disabled. :( I checked it by adding unsafe { cortex_m::interrupt::enable(); } before enabling, but that's not the right fix, it'll probably break RTIC.

Try enabling it from a task instead of init, hope it helps.

kuon commented 4 years ago

I did hit the following issue (do not put an empty idle function): https://github.com/rtic-rs/cortex-m-rtic/issues/122

Once, I fixed this. Running softdevice::enable frrom a task would just hang forever. I enabled defmt-trace but I see no output from the softdevice.

I updated my example repo.

Dirbaio commented 4 years ago

Just tried your latest repo and it's panicking for me (not hanging)

0.000000 DEBUG Starting up
└─ playground::init @ src/main.rs:38
0.000000 DEBUG Start init
└─ playground::init @ src/main.rs:60
0.000000 DEBUG End init
└─ playground::init @ src/main.rs:75
0.000000 DEBUG Config created
└─ playground::softdevice @ src/main.rs:123
0.000000 ERROR too little RAM for softdevice. Change your app's RAM start address to 536925320
└─ nrf_softdevice::softdevice::{{impl}}::enable @ /home/dirbaio/.cargo/git/checkouts/nrf-softdevice-9b99539d60cc72a7/7567bff/nrf-softdevice/src/util/macros.rs:6
stack backtrace:
   0: 0x0002998a - HardFaultTrampoline
      <exception entry>
   1: 0x00028d0e - __udf
   2: 0x0002987c - cortex_m::asm::udf
   3: 0x000298b2 - rust_begin_unwind
   4: 0x00028e16 - core::panicking::panic_fmt
   5: 0x00028dae - core::panicking::panic
   6: 0x000280e6 - nrf_softdevice::softdevice::Softdevice::enable
   7: 0x000276b2 - SWI0_EGU0
      <exception entry>
   8: 0x00027a9c - main
   9: 0x00028888 - ResetTrampoline
  10: 0x000271a8 - Reset
  11: 0x00025f74 - <unknown>

After changing memory.x RAM origin to 0x2000d488, it works fine (it's 536925320 in hex, unfortunately defmt doesn't support printing in hex yet knurling-rs/defmt#145

kuon commented 4 years ago

I tried changing the ram, but it still hangs with no output. I'm using an nrf52840 on a laird dev kit, but I don't see what problem this could cause.

Dirbaio commented 4 years ago

I've added an RTIC example here: https://github.com/akiles/nrf-softdevice/blob/master/examples/src/bin/rtic.rs

It enables the softdevice, starts advertising and accepts connections. Tested working on both nrf52840-dk and a custom board with the nrf52840. You just have to cd examples; cargo run --bin rtic --features cortex-m-rtic.

Please check if it works for you. If it doesn't it must be something related to your setup, your Rust version...

kuon commented 4 years ago

Your example also hangs. I will investigate on my side and maybe try with a different board.

BTW: I do ask quite a many questions in the issues, but I hope it will help for documentation and serve a bit as an FAQ. Anyway, thanks a lot for the support.

kuon commented 4 years ago

Ok, I found the issue. The laird board comes with an external crystal, but I read the doc a bit too fast and thought it was enabled by default, but it is not. To use the external crystal on the laird device, I had to remove two resistors and short 2 smd pads.

I think having a note about this behavior (enable will hang if clock source is not available) is a good way to address this issue.

I will leave this open while I work on a more complete rtic example.

Dirbaio commented 4 years ago

Glad you found the issue! In the softdevice's clock config you can choose whether to use external or internal clock. The examples use the external clock: source: raw::NRF_CLOCK_LF_SRC_XTAL as u8,. Using internal one would've probably worked without modifying the hw

I'll make sure to note that in the docs when we do the new config struct (#7)

kuon commented 4 years ago

Yeah I noticed the option in the config afterward, but that's good, I hope it will prevent other from falling into this pit.

badrbouslikhin commented 2 years ago

The rtic example has been deleted in commit https://github.com/embassy-rs/nrf-softdevice/commit/74d6f78cd3085a9de36cd72fdb4059302677eae7, why is it? Is there a reason not to use nrf-softdevice and rtic together anymore?

Dirbaio commented 2 years ago

@badrbouslikhin Yes, it should work. The example was removed because it was broken and I didn't have brain juices to fix it at the time, but there should be nothing fundamental preventing it from working.

badrbouslikhin commented 2 years ago

Thanks! I was able to get a proof of concept working on a Thingy:52. The next step is to figure out how to trigger an RTIC interrupt from a GATT characteristic change.