lobaro / FreeRTOS-rust

Rust crate for FreeRTOS
MIT License
354 stars 56 forks source link

Sending message from ISR does not work #63

Closed koija closed 10 months ago

koija commented 10 months ago

Sending a message from an ISR through send_from_isr method does not work, but instead it causes panic:

(gdb) bt

0 panic_halt::panic (_info=0x2001f8ac) at src/lib.rs:32

1 0x0800bf94 in core::panicking::panic_fmt () at library/core/src/panicking.rs:64

2 0x0800c0be in core::result::unwrap_failed () at library/core/src/result.rs:1750

3 0x0800bb7e in core::result::Result<alloc::string::String, freertos_rust::base::FreeRtosError>::unwrap<alloc::string::String, freertos_rust::base::FreeRtosError> (self=...)

at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/core/src/result.rs:1090

4 0x0800abf0 in freertos_rust::hooks::vAssertCalled (file_name_ptr=0x1cd, line=134272464)

at src/hooks.rs:31

5 0x08003f24 in vPortEnterCritical () at FreeRTOS-Kernel/portable/GCC/ARM_CM4F/port.c:461

6 0x08002d0c in xTaskResumeAll () at FreeRTOS-Kernel/tasks.c:2193

7 0x08003a52 in pvPortMalloc (xWantedSize=16) at FreeRTOS-Kernel/portable/MemMang/heap_4.c:287

8 0x080026c6 in freertos_rs_pvPortMalloc (xWantedSize=8)

at /home/chkolli/.cargo/registry/src/index.crates.io-6f17d22bba15001f/freertos-rust-0.1.2/src/freertos/shim.c:24

9 0x0800ac78 in freertos_rust::allocator::{impl#0}::alloc (self=0x800ce70, layout=...)

at src/allocator.rs:16

10 0x08000228 in ::_::__rg_alloc (arg0=8, arg1=1) at src/main.rs:25

11 0x0800ad7a in alloc::alloc::alloc ()

at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/alloc/src/alloc.rs:95

12 alloc::alloc::Global::alloc_impl (self=0x2001fe1c, layout=..., zeroed=false)

at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/alloc/src/alloc.rs:177

13 0x0800b156 in alloc::alloc::{impl#1}::allocate (self=0x2001fe1c, layout=...)

at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/alloc/src/alloc.rs:237

14 0x0800b318 in alloc::raw_vec::finish_grow (new_layout=...,

current_memory=..., alloc=0x2001fe1c)
at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/alloc/src/raw_vec.rs:476

15 0x0800b69a in alloc::raw_vec::RawVec<u8, alloc::alloc::Global>::grow_amortized<u8, alloc::alloc::Global> (self=0x2001fe1c, len=0, additional=1)

at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/alloc/src/raw_vec.rs:405

16 0x0800b752 in alloc::raw_vec::RawVec<u8, alloc::alloc::Global>::reserve_for_push<u8, alloc::alloc::Global> (self=0x2001fe1c, len=0)

at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/alloc/src/raw_vec.rs:303

17 0x0800b83e in alloc::vec::Vec<u8, alloc::alloc::Global>::push<u8, alloc::alloc::Global> (

self=0x2001fe1c, value=35)
at /rustc/44f5180584404d18058cbbf224c55255db4fdcbb/library/alloc/src/vec/mod.rs:1840

18 0x0800a7d6 in freertos_rust::utils::str_from_c_string (str=0x358) at src/utils.rs:59

19 0x0800abe0 in freertos_rust::hooks::vAssertCalled (file_name_ptr=0x358, line=134272464)

at src/hooks.rs:31

20 0x080040ac in vPortValidateInterruptPriority ()

at FreeRTOS-Kernel/portable/GCC/ARM_CM4F/port.c:856

21 0x08004996 in xQueueGenericSendFromISR (xQueue=0x20000140 <ucHeap+8>, pvItemToQueue=0x2001ff5e,

pxHigherPriorityTaskWoken=0x0, xCopyPosition=0) at FreeRTOS-Kernel/queue.c:1119

22 0x080027da in freertos_rs_queue_send_isr (queue=0x20000140 <ucHeap+8>, item=0x2001ff5e,

xHigherPriorityTaskWoken=0x0)
at /home/chkolli/.cargo/registry/src/index.crates.io-6f17d22bba15001f/freertos-rust-0.1.2/src/freertos/shim.c:244

23 0x08001af6 in freertos_rust::queue::Queue::send_from_isr (self=0x200001a8 <ucHeap+112>,

context=0x2001ff98, item=1)
at /home/chkolli/.cargo/registry/src/index.crates.io-6f17d22bba15001f/freertos-rust-0.1.2/src/queue.rs:54

The reason for this is vAssertCalled function that attempts to allocate memory to create a String.

If I remove the corresponding lines from vAssertCalled, my program does not panic but the waiting thread is never woken up. The reason for this is that listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) call in xQueueGenericSendFromISR always returns pdTRUE.

My code (STM32F411RE):

#![no_std]
#![no_main]

////////////////////////////////////////////////////////////////////////////////
use core::cell::RefCell;
use core::ops::DerefMut;
use core::ops::Deref;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry;
use freertos_rust::*;
use panic_halt as _;
use stm32f4xx_hal;
use stm32f4xx_hal::gpio::{GpioExt};
use stm32f4xx_hal::interrupt;
use stm32f4xx_hal::pac::USART1;
use stm32f4xx_hal::prelude::*;
use stm32f4xx_hal::serial::{Config, Event, Serial};
extern crate alloc;
use alloc::sync::Arc;

////////////////////////////////////////////////////////////////////////////////

// Global allocator for memory allocations.
#[global_allocator]
static GLOBAL: FreeRtosAllocator = FreeRtosAllocator;

// Required by USART1 interrupt handler
static UART_SERVICE_CHANNEL: Mutex<RefCell<Option<Serial<USART1>>>> =
    Mutex::new(RefCell::new(None));
static UART_MESSAGE_QUEUE: Mutex<RefCell<Option<Arc<Queue<u8>>>>> = Mutex::new(RefCell::new(None));

////////////////////////////////////////////////////////////////////////////////
#[entry]
fn main() -> ! {
    let dp = stm32f4xx_hal::pac::Peripherals::take().unwrap();

    let rcc = dp.RCC.constrain();
    let clocks = rcc
        .cfgr
        .sysclk(100.MHz())
        .hclk(100.MHz())
        .pclk1(50.MHz())
        .pclk2(100.MHz())
        .freeze();

    let gpioa = dp.GPIOA.split();

    let pin_service_channel_tx = gpioa.pa9.into_alternate();
    let pin_service_channel_rx = gpioa.pa10.into_alternate();

    let service_channel_uart = dp
        .USART1
        .serial(
            (pin_service_channel_tx, pin_service_channel_rx),
            Config::default()
                .baudrate(115200.bps())
                .wordlength_8()
                .parity_none(),
            &clocks,
        )
        .unwrap()
        .with_u8_data();

    cortex_m::interrupt::free(|cs| {
        UART_SERVICE_CHANNEL
            .borrow(cs)
            .replace(Some(service_channel_uart))
    });

    let uart_message_queue = Arc::new(Queue::<u8>::new(4).unwrap());

    // To be used from the uart interrupt handler.
    cortex_m::interrupt::free(|cs| {
        UART_MESSAGE_QUEUE
            .borrow(cs)
            .replace(Some(uart_message_queue.clone()))
    });

    Task::new()
        .name("uart_rx")
        .stack_size(1024)
        .priority(TaskPriority(5))
        .start(move || {
            // Enable service channel uart and then proceed to wait for data.
            cortex_m::interrupt::free(|cs| {
                if let Some(ref mut uart) = UART_SERVICE_CHANNEL.borrow(cs).borrow_mut().deref_mut()
                {
                    uart.listen(Event::Rxne);

                    unsafe {
                        // Enable USART1 interrupt in NVIC
                        cortex_m::peripheral::NVIC::unmask(stm32f4xx_hal::interrupt::USART1);
                    }

                }
            });

            loop {
                let _ = uart_message_queue.receive(Duration::infinite());

                cortex_m::interrupt::free(|cs| {
                    if let Some(ref mut uart) =
                        UART_SERVICE_CHANNEL.borrow(cs).borrow_mut().deref_mut()
                    {
                        if let Ok(byte) = uart.read() {
                            // Do something with the received byte.
                        }
                    }
                });
            }
        })
        .unwrap();

    FreeRtosUtils::start_scheduler();
}

#[interrupt]
fn USART1() {
    cortex_m::interrupt::free(|cs| {
        if let Some(ref mut uart) = UART_SERVICE_CHANNEL.borrow(cs).borrow_mut().deref_mut() {
            if uart.is_rx_not_empty() {
                let _ = UART_MESSAGE_QUEUE
                    .borrow(cs)
                    .borrow()
                    .deref()
                    .as_ref()
                    .unwrap()
                    .send_from_isr(&mut InterruptContext::new(), 1);
            }
        }
    });
}
koija commented 10 months ago

Closed as invalid.