Chibios thirdparty IRQ support

Discussions and support about ChibiOS/RT, the free embedded RTOS.
rogtalha
Posts: 11
Joined: Mon Apr 30, 2018 1:56 pm

Chibios thirdparty IRQ support

Post by rogtalha »

Hello,

we are currently using Chibios on STM32F429 board and have recently migrated to Chibios. A thirdparty c++ library stm32plus is being used for peripherals. And Chibios/RT stand-alone has been successfully integrated into the system. My question is how do we use the irq subsystem in this context. The c++ peripheral library in use provides a signal/delegate (FastDelegate Library) mechanism to handle IRQ's. This allows us to use IRQ callbacks as regular class member functions.

Code: Select all

void __attribute__ ((interrupt("IRQ"))) DMA1_Stream1_IRQHandler() {

      if(DMA_GetITStatus(DMA1_Stream1,DMA_IT_TCIF1)!=RESET) {
        DmaInterruptFeature<1,1>::_dmaInstance->DmaInterruptEventSender.raiseEvent(DmaEventType::EVENT_COMPLETE);
        DMA_ClearITPendingBit(DMA1_Stream1,DMA_IT_TCIF1);
      }
}


This is how the control is passed from the IRQ to the class member callback function subscribed to this particular event. This is how subscription is done

Code: Select all

_DMA.DmaInterruptEventSender.insertSubscriber( DmaInterruptEventSourceSlot::bind(this,&SerialObj::Handle_dmaIRQ));


Looking at the stm32f429 linker and stm32_isr.h i figured out that the DMA1_Stream1_IRQn has been mapped to Vector70 and thought the following define's would suffice.

Code: Select all

#define USART3_IRQHandler                  VectorDC
#define DMA1_Stream1_IRQHandler            Vector70


But the Program ends up in the unhandled exception loop at the first instance of an Interrupt. Is there an elegant way to handle IRQ's like these with in the context of Chibios and be able to assign priorities to them as well.

Best Regards
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Chibios thirdparty IRQ support

Post by Giovanni »

Hi,

ChibiOS/RT requires IRQ handlers to use its prefix/postfix macros, the reschedule check and context switch is done in there. You may declare a normal ISR with macros then call your ISR from there.

Note that the attribute "IRQ" is not required for Cortex-M devices, ISRs are normal C functions.

You may omit the macros but then you should call no RT function from that ISR.

Giovanni
rogtalha
Posts: 11
Joined: Mon Apr 30, 2018 1:56 pm

Re: Chibios thirdparty IRQ support

Post by rogtalha »

Thank you for your response Giovanni.

Code: Select all

#pragma once
#include<ch.h>


#define STM32_MOTOR_USART_HANDLER       VectorDC
#define STM32_USART_DMA_HANDLER         Vector70

#define STM32_IRQ_HANDLER(num)      CH_IRQ_HANDLER(num)

STM32_IRQ_HANDLER(STM32_MOTOR_USART_HANDLER)
{
  CH_IRQ_PROLOGUE();
  USART3_IRQHandler();
  CH_IRQ_EPILOGUE();
}

STM32_IRQ_HANDLER(STM32_USART_DMA_HANDLER)
{
  CH_IRQ_PROLOGUE();
  DMA1_Stream1_IRQHandler();
  CH_IRQ_EPILOGUE();
}



I implemented this, but ran into another problem where the system was just freezing inside the idle_thread loop. A bit of digging and i realised i need to enable the systick timer and its corresponding interrupt.

Code: Select all

CH_IRQ_HANDLER(SysTick_Handler)
{
  CH_IRQ_PROLOGUE();           
     
  chSysLockFromISR();         
  chSysTimerHandlerI();
  chSysUnlockFromISR();       
     
  CH_IRQ_EPILOGUE();           
}   


This ought to have solved the issue. But now the SysTick interrupt causes the system to end up in unhandled exception. Below are the values of the registers:

Code: Select all

r0             0x200011c8       536875464
r1             0x20001348       536875848
r2             0x20001296       536875670
r3             0x20001348       536875848
r4             0x80001c4        134218180
r5             0x80001c4        134218180
r6             0x0      0
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x0      0
sp             0x20000400       0x20000400
lr              0xffffffed          -19
pc             0x8001ba8        0x8001ba8 <_unhandled_exception>
xPSR          0x8100000f       -2130706417


xPSR register points to Systick Interrupt (15). I know i must be missing something trivial here.
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Chibios thirdparty IRQ support

Post by Giovanni »

Apparently your SysTick ISR is not linked to the SysTick vector, look at the map file to confirm it, _unhandled_exception is reached by unassigned IRQ vectors.

Giovanni
rogtalha
Posts: 11
Joined: Mon Apr 30, 2018 1:56 pm

Re: Chibios thirdparty IRQ support

Post by rogtalha »

Yup. Since the entire project is c++ based. I forgot to disable name mangling around ISR definitions, hence the linker was unable to link to the ISR's. Thank you for your help giovanni.

Best Regards.
rogtalha
Posts: 11
Joined: Mon Apr 30, 2018 1:56 pm

Re: Chibios thirdparty IRQ support

Post by rogtalha »

The IRQ's seem to work only when the supporting OS code is removed (CH_IRQ_PROLOGUE & CH_IRQ_EPILOGUE). I have tested the code by disabling the interrupts and it seems to work fine without any issues. The moment i enable the interrupts with the supporting os code it causes a HardFault. i have tried increasing the PORT_INT_REQUIRED STACK macro to 128. To ensure that there is no stack overflow, i enabled the thread fill option and checked the entire stack at the instance of HardFault and only about 1/3rd of the allotted stack was being used. Both the stack pointed to by msp and psp do not overflow. I will give you a complete run down of the registers so that you may be able to help me zero in on the issue. Right now i'm using a dummy ISR which just clears the interrupt flag and does nothing else. This is the result of "info reg" at the instance of HardFault.

Code: Select all

r0             0x20001370       536875888
r1             0x2000172c       536876844
r2             0x1c094710       470370064
r3             0x0      0
r4             0x0      0
r5             0x0      0
r6             0xffffffcb       -53
r7             0x8      8
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x8000000        134217728
sp             0x20000800       0x20000800
lr             0xffffffed       -19
pc             0x1c094710       0x1c094710
xPSR           0x3      3


Weirdly PC gets set to an out of bound address. I suppose this happens during the exception exit sequence. Since the value in r0 is a reference to a thread

Code: Select all

0x20001370 <_ZN2AX12motor_threadE+864>: 0x00000000


Now if i set the breakpoint at the HardFault_Handler code, i get the following output

Code: Select all

r0             0x0      0
r1             0x0      0
r2             0x0      0
r3             0x0      0
r4             0x0      0
r5             0x0      0
r6             0xffffffbc       -68
r7             0x8      8
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x8000000        134217728
sp             0x20000800       0x20000800
lr             0xffffffed       -19
pc             0x8001bee        0x8001bee <HardFault_Handlerimpl>
xPSR           0x41000003       1090519043


The Process and stack sizes are 0x800 each.
I dont understand what is happening. Initially i thought its my code with all the delegates and IRQ callback trickery, but even a simple ISR such as below is causing issues.

Code: Select all

void USART3_IRQHandlertemp(void)
{
  if(USART_GetITStatus(USART3,USART_IT_IDLE))                                                                                                                 
  {
    USART_ClearITPendingBit(USART3,USART_IT_IDLE);                                                                                                           
  }   
} 


I thought maybe interrupts have high priority than the OS SVC_Call interrupt. But setting the priority to above 4 doesnt seem to make any difference whatsoever.

Here is how my main function looks like.

Code: Select all

int main()
{     
board::init();

chibios_rt::System::init();
AX::init();
chibios_rt::BaseThread::setPriority(LOWPRIO);

while(1)
{
  GPIOG->ODR ^= GPIO_Pin_14;
  chThdSleep(MS2ST(1000));     
}


}


Here is the board::init function and the AX::init refers to the thread init function defined in the last code block at the end.

Code: Select all

namespace board
{
  void init()
  {
    //Initializing the Systick Timer
    SysTick->LOAD = SYSTEM_CLOCK / CH_CFG_ST_FREQUENCY - (systime_t)1;
    SysTick->VAL = (uint32_t)0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk |
                  SysTick_CTRL_TICKINT_Msk;
  }
}


Here is the uart and dma thread.

Code: Select all

class : public chibios_rt::BaseStaticThread<1024>
{
  protected:
    using LEDgreen=GpioG<DefaultDigitalOutputFeature<13>>;
    using LEDred=GpioG<DefaultDigitalOutputFeature<14>>;
    using serial_parameters=
    UsartPeripheral<Usart3DefaultPinPackage,PERIPHERAL_USART3>::Parameters;
    using dma_inst=Dma1Channel4Stream1<Usart3RxDmaChannelInterruptFeature,
    UsartDmaReaderFeature<Usart3PeripheralTraits>>;
    using uart=Usart3<Usart3InterruptFeature>;
  public:
    void main() override
    {
      serial_parameters params; 
      //default baudrate is 9600
      params.usart_parity=USART_Parity_Even;
     
      SerialObj<uart,dma_inst> serial_obj(params);
      AX2550<SerialObj<uart,dma_inst>> driver(serial_obj);
     
      while(true)
      {
        GPIOG->ODR ^= GPIO_Pin_13;
        chThdSleep(MS2ST(1000));
      }
    }
   
}motor_thread;

void init()
{
  motor_thread.start(HIGHPRIO);
}



The above is just some sample code that i'm trying to test with. It works perfectly without the OS calls in the ISR but hangs when the ISR is enabled with the supporting os calls. I enabled all the debug checks in chconf and the ch.dbg.panic_msg doesnt report anything it just stays at 0x0. I presume something fishy is happening during the exception return sequence. Because when i set the breakpoint at the beginning of the HardFault_Handler shown below all the registers are zero except r6 which is loaded with "0xffffffbc" and upon continuing the execution from this point gdb reports this error "0x1c094710 in ?? ()". Could it be that the PC is being loaded with a wrong value instead of the exc_return.

Code: Select all

void __attribute__((naked)) HardFault_Handlerimpl(void) {
  __asm volatile
  (
      " tst lr, #4                                                \n"
      " ite eq                                                    \n"
      " mrseq r0, msp                                             \n"
      " mrsne r0, psp                                             \n"
      " ldr r1, [r0, #24]                                         \n"
      " ldr r2, handler2_address_const                            \n"
      " bx r2                                                     \n"
      " handler2_address_const: .word prvGetRegistersFromStack    \n"
  );
}

void prvGetRegistersFromStack(uint32_t *pulFaultStackAddress) {

  volatile uint32_t r0 __attribute__((unused));
  volatile uint32_t r1 __attribute__((unused));
  volatile uint32_t r2 __attribute__((unused));
  volatile uint32_t r3 __attribute__((unused));
  volatile uint32_t r12 __attribute__((unused));
  volatile uint32_t lr __attribute__((unused));     /* Link register. */
  volatile uint32_t pc __attribute__((unused));     /* Program counter. */
  volatile uint32_t psr __attribute__((unused));    /* Program status register. */

  r0=pulFaultStackAddress[0];
  r1=pulFaultStackAddress[1];
  r2=pulFaultStackAddress[2];
  r3=pulFaultStackAddress[3];

  r12=pulFaultStackAddress[4];
  lr=pulFaultStackAddress[5];
  pc=pulFaultStackAddress[6];
  psr=pulFaultStackAddress[7];

  /* When the following line is hit, the variables contain the register values. */
  for(;;) ;
}


Bear in mind that i am unable to execute the HardFault handler code as well by single stepping through the routine. It just gets stuck the "__asm volatile" line.

Probably the kernel is being preempted by an IRQ? is it possible for an IRQ with NVIC priority 8 to preempt the kernel? Although the Systick interrupt runs fine without any issues for hours.
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Chibios thirdparty IRQ support

Post by Giovanni »

Only priorities 0 and 1 can preempt the kernel under default settings.

Do you have the debug options enabled in chconf.h? also try to increase the main stack size in Makefile, an overflow of that stack causes an hard fault by design because how it is placed. Note that IRQs can preempt each other so the total stack size required depends also on the number of IRQ priorities used. (try putting all IRQs to same priority for a test).

Are you using our startup files?

Giovanni
rogtalha
Posts: 11
Joined: Mon Apr 30, 2018 1:56 pm

Re: Chibios thirdparty IRQ support

Post by rogtalha »

yeah all the dbg checks have been enabled. I removed the hardfault handler code and linked it against the unhandled_exception routine again since the handler wasnt even getting executed. I have made the main stack size 4k now. Which i think is more than comfortable. I single stepped through the entire program and it seems to get stuck at

Code: Select all

thread_t *chSchReadyI(thread_t *tp) {
  thread_t *cp;

  chDbgCheckClassI();
  chDbgCheck(tp != NULL);
  chDbgAssert((tp->state != CH_STATE_READY) &&
              (tp->state != CH_STATE_FINAL),
              "invalid state");

  tp->state = CH_STATE_READY;
  cp = (thread_t *)&ch.rlist.queue;
  do {
    cp = cp->queue.next;
  } while (cp->prio >= tp->prio);
  /* Insertion on prev.*/
  tp->queue.next             = cp;
  tp->queue.prev             = cp->queue.prev;
  tp->queue.prev->queue.next = tp;
  cp->queue.prev             = tp;

  return tp;
}


the loop which is checking for the thread with lower priority than the current thread. it just keeps looping indefinitely within this code. Please let me know if i could provide you with any further info on this matter. Since i'm new to this OS, i dont really know what to look for. Regards
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Chibios thirdparty IRQ support

Post by Giovanni »

Hi,


If you are experiencing an infinite loop into the ready list then there is a problem elsewhere and there can be several possible causes, for example:
- Making the idle thread change state, for example by using chthdSleep(), it must be always ready.
- Calling chSchReadyI() or other scheduler functions directly, that can corrupt the list. Calling chSchReadyI() on a thread already in the ready list, for example, triggers exactly that problem.
- IRQs at priorities above kernel priority calling kernel code, priorities from 0 to 2 are forbidden from calling kernel code because the kernel can be preempted at those priorities. Prologue and epilogue macros must not be used for those IRQs too.
- Illegal use of I-Class or S-Class API.
- Stack overflow corrupting OS data structures.

Giovanni
rogtalha
Posts: 11
Joined: Mon Apr 30, 2018 1:56 pm

Re: Chibios thirdparty IRQ support

Post by rogtalha »

Disabling the SysTick_Handler fixes the issue with ISR's. But now the threads have no way of waking up after a sleep. Is their a way to incorporate tickless mode into a Kernel only build configuration?.
Post Reply