nrf-rs / nrf-hal

A Rust HAL for the nRF family of devices
Apache License 2.0
503 stars 139 forks source link

UARTE: turning uarte off and on again on nrf9160 hangs indefinitely #381

Open folkertdev opened 2 years ago

folkertdev commented 2 years ago

Problem

To save power, we want to disable the uarte peripheral inbetween writes. We have recently upgraded the hal, and this has stopped working. Only the first write to uart works, any subsequent write hangs indefinitely.

Code

Full runnable example is available here. Note that the branch name is uarte-on-off-bug. A simple cargo run --bin hello should flash and run the whole program (SPM is included).

use nrf9160_hal as hal;

use hal::gpio::Level;
use hal::uarte;

use core::fmt::Write;

#[cortex_m_rt::entry]
fn main() -> ! {
    let p = hal::pac::Peripherals::take().unwrap();
    let pins0 = hal::gpio::p0::Parts::new(p.P0_NS);
    let pins = hal::uarte::Pins {
        rxd: pins0.p0_05.into_floating_input().degrade(),
        txd: pins0.p0_06.into_push_pull_output(Level::High).degrade(),
        cts: None,
        rts: None,
    };

    let device = p.UARTE0_NS;

    // init 1

    let mut uart = hal::Uarte::new(
        device,
        pins,
        uarte::Parity::EXCLUDED,
        uarte::Baudrate::BAUD115200,
    );

    write!(uart, "write 1\r\n").unwrap();
    write!(uart, "write 2\r\n").unwrap();

    // deinit 1
    let (device, pins) = uart.free();
    device.events_txstopped.reset();
    device.tasks_stoptx.write(|w| w.tasks_stoptx().trigger());

    while device.events_txstopped.read().bits() == 0 {
        cortex_m::asm::nop();
    }

    device.enable.write(|w| w.enable().disabled());

    // init 2
    let mut uart = hal::Uarte::new(
        device,
        pins,
        uarte::Parity::EXCLUDED,
        uarte::Baudrate::BAUD115200,
    );

    write!(uart, "write 3\r\n").unwrap();

    nrf9160_rust_starter::exit()
}

Actual output

terminal

folkertdev@folkertdev ~/t/d/nrf9160-rust-starter (uarte-on-off-bug)> cargo run --bin hello
    Finished dev [optimized + debuginfo] target(s) in 0.01s
     Running `probe-run --chip nRF9160_xxAA target/thumbv8m.main-none-eabi/debug/hello`
(HOST) INFO  flashing program (9 pages / 36.00 KiB)
(HOST) INFO  success!
────────────────────────────────────────────────────────────────────────────────
^C────────────────────────────────────────────────────────────────────────────────
stack backtrace:
   0: core::ptr::read_volatile
        at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/ptr/mod.rs:1054:14
   1: vcell::VolatileCell<T>::get
        at /home/folkertdev/.cargo/registry/src/github.com-1ecc6299db9ec823/vcell-0.1.3/src/lib.rs:33:18
   2: nrf9160_pac::generic::Reg<REG>::read
        at /home/folkertdev/.cargo/registry/src/github.com-1ecc6299db9ec823/nrf9160-pac-0.11.0/src/generic.rs:65:19
   3: nrf_hal_common::uarte::Uarte<T>::write
        at /home/folkertdev/.cargo/registry/src/github.com-1ecc6299db9ec823/nrf-hal-common-0.15.0/src/uarte.rs:185:15
   4: <nrf_hal_common::uarte::Uarte<T> as core::fmt::Write>::write_str
        at /home/folkertdev/.cargo/registry/src/github.com-1ecc6299db9ec823/nrf-hal-common-0.15.0/src/uarte.rs:460:13
   5: <&mut W as core::fmt::Write>::write_str
        at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/fmt/mod.rs:193:9
   6: core::fmt::write
        at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/fmt/mod.rs:1187:9
   7: core::fmt::Write::write_fmt
        at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/fmt/mod.rs:187:6
   8: hello::__cortex_m_rt_main
        at src/bin/hello.rs:57:5
   9: main
        at src/bin/hello.rs:13:1
  10: Reset
(HOST) INFO  device halted by user

uart output:

write 1
write 2

Expected output

write 1
write 2
write 3

Extra info

We suspect this is a problem with the workaround for nRF9160 - anomaly 23. Specifically, our code works when we comment out this block in apply_workaround_for_enable_anomaly (uarte.rs).

        // NB Safety: This is taken from Nordic's driver -
        // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
        if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 {
            self.0.enable.write(|w| w.enable().enabled());
            self.0.tasks_stoprx.write(|w| unsafe { w.bits(1) });

            let mut workaround_succeded = false;
            // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered.
            // On lowest supported baud rate (1200 baud), with parity bit and two stop bits configured
            // (resulting in 12 bits per data byte sent), this may take up to 40 ms.
            for _ in 0..40000 {
                // NB Safety: This is taken from Nordic's driver -
                // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197
                if unsafe { core::ptr::read_volatile(rxenable_reg) } == 0 {
                    workaround_succeded = true;
                    break;
                } else {
                    // Need to sleep for 1us here
                }
            }

            if !workaround_succeded {
                // panic!("Failed to apply workaround for UART");
            }

            let errors = self.0.errorsrc.read().bits();
            // NB Safety: safe to write back the bits we just read to clear them
            self.0.errorsrc.write(|w| unsafe { w.bits(errors) });
            // self.0.enable.write(|w| w.enable().disabled());
        }

cc @diondokter