Interrupts / FreeRTOS on standard SDK

Hi.
I’m running an app that uses some high priority interrupts (I2C slave, and external GPIO interrupt)
I’m finding that sometimes the interrupts get missed. The interrupt doesn’t fire for the event.

Usually FreeRTOS critical sections on Cortex M3 are done by configuring interrupt base priority to configMAX_SYSCALL_INTERRUPT_PRIORITY.
This leaves higher priority interrupts (that don’t use FreeRTOS) to still execute.

I can see the Ameba SDK uses this:
portDISABLE_INTERRUPTS() __asm volatile ( " cpsid i " ::: “memory” )
portENABLE_INTERRUPTS() __asm volatile ( " cpsie i " ::: “memory” )

This looks to be a global interrupt disable?
So what happens if another interrupt fires during a FreeRTOS critical section?
Is it queued to run afterwards, or is it missed?
Thanks.

Hi @Smythbuilt

Please let us know what board/IC you are using, and the version of the SDK so we can understand the context better.

For Ameba IoT solutions, we manage interrupt with NVIC, to register and enable interrupt is not enough, you have to set the interrupt mask to enable each individual interrupt bit so they can trigger once the event occurs, an example of this is to look at this line,

Note that, even before this step, interrupt has already been registered and enabled at


Long story short-- it disables all interrupts including systick and pendsv so that even FreeRTOS can NOT switch task during execution of your Interrupt Service Routine, other interrupt also can not trigger

Hope I have answerd your questions and look forward to your replies~

Thanks for the response.
I’m using the BW16 module, and Standard SDK V6.2.

I have configured an I2C slave interface using:
InterruptRegister((IRQ_FUN)I2CISRHandle, I2C0_IRQ_LP, (u32)(obj), 3);
InterruptEn(I2C0_IRQ_LP, 3);
I2C_INTConfig(i2cslave.I2Cint.I2Cx, (BIT_IC_INTR_MASK_M_ADDR_1_MATCH | BIT_IC_INTR_MASK_M_STOP_DET | BIT_IC_INTR_MASK_M_RX_FULL | BIT_IC_INTR_MASK_M_RX_OVER | BIT_IC_INTR_MASK_M_RX_UNDER | BIT_IC_INTR_MASK_M_RD_REQ | BIT_IC_INTR_MASK_M_RX_DONE), ENABLE);

It works well most of the time, but in some cases I do a Write from the host, and the Slave device (BW13) does not appear to receive it.

With the external GPIO interrupt:
GPIO_INTMode(ARBITER_REQUEST, ENABLE, GPIO_INT_Trigger_BOTHEDGE, GPIO_INT_POLARITY_ACTIVE_LOW | GPIO_INT_POLARITY_ACTIVE_HIGH, GPIO_INT_DEBOUNCE_DISABLE);

InterruptRegister(GPIO_INTHandler, GPIOA_IRQ, (u32)GPIOA_BASE, GPIO_INTERRUPT_PRIORITY);
InterruptEn(GPIOA_IRQ, GPIO_INTERRUPT_PRIORITY);

GPIO_UserRegIrq(ARBITER_REQUEST, (void *)arbiter_irq_handler, &GPIO_InitStruct);
GPIO_INTConfig(ARBITER_REQUEST, ENABLE);

Similarly, it works fine most of the time, but in some cases the interrupt does not fire when the GPIO changes state. I had to add some code to also poll the pin to see if it changed state without the interrupt firing.

I am only guessing about the FreeRTOS interrupt disable causing this.
What else could cause interrupts to not fire when the event occurs?
Thanks.

Hi @Smythbuilt

As discussed eariler, calling taskENTER_CRITICAL() will disable ALL interrupt including I2C’s (at least for the case of V6.2 SDK), so it’s up to your program to speed up the processing after calling taskENTER_CRITICAL() so that it won’t miss important interrupt.

From what I can see here, there are 2 solutions,

  1. You may change the included FreeRTOS port to enable taskENTER_CRITICAL() only disable interrupt up to an user-defined priority level, see here for more details

  2. If your program have to process a lot of data in critical sections, then do not rely on the time-critical interrupt such as BIT_IC_INTR_MASK_M_STOP_DET , instead, you can query the I2C status register for RX/TX FIFO status, for example,

Thanks.
I don’t do anything in critical sections in my app. FreeRTOS itself calls it at times though (vPortEnterCritical) which I have no control over.

I will look solution 1 (same solution that ST Micro use.)
Thanks for the help.

Then it’s very strange.

Could you elaborate on how you found FreeRTOS critical section to be the cuase of this issue? bcos from what I know, FreeRTOS uses SysTick and PendSV to schedule tasks, and only if you are manuplating data shared across task, then will FreeRTOS enter critical section( e.g. send queue, semaphore, mutex etc.) to protect those global scope data

If you program never uses queue/sema then it should not enter critical section that often…

I don’t know for sure that FreeRTOS critical section is the cause. It’s just a theory.
Maybe I should say, FreeRTOS disabling the interrupts.

In the current Ameba port the interrupts are disabled for every task context switch:
select_next_task:
cpsid i
bl vTaskSwitchContext
cpsie i

Also, portDISABLE_INTERRUPTS() is called at various times during FreeRTOS operation.
E.g. xQueueIsQueueFullFromISR(), prvCheckPendingReadyList()

I tried to port over the IAR Cortex M33 port from FreeRTOS:

For “disabling” interrupts, this port uses ulSetInterruptMask to change the basepri register, leaving higher priority interrupts able to run still.

I got it to compile but so far it crashes on boot.

I dont know about IAR, but the Github v6.2 SDK is based on GCC +GNU make, so I would strongly suggest that you start looking from there,

Some of the register address will be different, so you have to really PORT the generic FreeRTOS implementation to the SDK you are using

I’m using IAR Embeded Workbench myself, which is why I picked that one.
I’ll spend some more time on this next week.

FreeRTOS 10.3.1 is the version that changed the M33 ports from disabling all interrupts, to just masking and letting high priority interrupts fire.

I’ve tried these approaches:

  • change just the port.c/portasm.s changes for portENABLE_INTERRUPTS/portDISABLE_INTERRUPTS to Ameba

  • use the generic M33 NTZ port on Ameba

  • use the entire FreeRTOS 10.3.1 code on Ameba

In all cases, it ends up the same way. Not actually crashed, but it seems to get stuck looping in vPortSuppressTicksAndSleep(). None of my tasks execute at all, so my app doesn’t boot fully.

I’m not super familiar with the porting side, so I’m stuck.
Not having responsive interrupts is going to cause me future problems on the next few apps that I need to do.

Is there anyone who can help? It seems like something that would benefit everyone.

1 Like

Hi, up to now, the standard SDK only supports up to FreeRTOS v10.2.0.

As RTOS is one of the most critical part of the software, changing it without testing and making sure its stable is not a good idea. However, newer version FreeRTOS might be added in the future.

For now, we have both established that

  • The I2C on BW16 has to be configured as a slave receiving device in your applicaiton and the FreeRTOS scheduler is the cause of your data-lost issue
  • Standard SDK only supports up to FreeRTOS v10.2.0

What I suggest here are,

  • Method 1: Change the I2C RX FIFO Threshold level from 0(default) to a bigger value, this way, the Hardware I2C RX FIFO will take in and store the incoming data no matter if interrupts are disabled. and only trigger ISR when the expected amount of data is received in the FIFO.

  • Method 2: since I2C is a rather slow protocol(default 100KHz) as compared to the CPU clock(200MHz), you may try poll the IC_STATUS register for RX/TX FIFO status every now and then.

  • Method 3: You may change how fast FreeRTOS triggers the scheduler by changing the tick time at ambd_sdk/FreeRTOSConfig.h at 4e10ff98fe2e2f89c6fa681d1004d8112f74b558 · ambiot/ambd_sdk · GitHub

Thanks for the suggestions.
I don’t think I can use method 1, as some of my registers are an odd number of bytes.
I tried method 3 to change from 1 msec tick to 2 and 4 msec. I didn’t appear to improve, and causes me some other application issues.

I added code to the I2C master host to read back the register written, and retry writing until that passes. Somehow this also didn’t help, which makes me think its some other issue.
I will investigate further.

1 Like