Least restrictive critical section?

monkeybranch
Posts: 2
Joined: Fri Dec 06, 2019 10:57 pm

Least restrictive critical section?

Postby monkeybranch » Fri Dec 06, 2019 11:17 pm

I have set up an external interrupt on a pin successfully, I use a FreeRTOS queue to queue some data which is fetched in the main loop(). This works well but I also have to access a variable from both the loop() and the ISR.

It is my understanding that my only option is a critical section using portENTER_CRITICAL and portEXIT_CRITICAL. When inside the critical section interrupts are disabled. This is what I want since I don't want the code in the loop() that modifies the shared variable to be interrupted by the ISR which also want to modify the share resources.

Two questions:
  1. It seems disabling ALL interrupts during the critical section is somewhat overkill? I just don't want the scheduler to be able to interrupt and the ISR for the external interrupt. All other interrupts should be fine since they don't touch the data? Is there a way to be more specific or is this an all or nothing thing?
  2. I read in another post on this forum that portENTER_CRITICAL_ISR is not required inside the ISR. Why is it safe to not call portENTER_CRITICAL_ISR? Could'nt the FreeRTOS scheduler ISR interrupt my ISR and start scheduling the loop task again somehow? Or will the scheduler never pre-empt an interrupt?
These are pretty specific questions but I hope somebody can come up with a reply so I can fully understand what's going.

ESP_Sprite
Posts: 9051
Joined: Thu Nov 26, 2015 4:08 am

Re: Least restrictive critical section?

Postby ESP_Sprite » Sat Dec 07, 2019 4:20 am

Disabling all interrupts indeed is a bit overkill, however critical sections are intended to only run for a short while, that is, while you write or read the variable. As this action is only one or a few clock cycles, it's usually no issue that some interrupts are delayed by that.

When in an ISR, no task can interrupt you; the CPU is in interrupt context and will only go back to running tasks when your ISR ends.

Do note that critical sections on the ESP32, unlike in 'vanilla' FreeRTOS, are implemented using spinlocks instead of just disabling interrupts. Disabling interrupts does not stop the other core from accessing the variables within the critical region; a spinlock does. Aside from that detail, you can use critical sections in the same way.

monkeybranch
Posts: 2
Joined: Fri Dec 06, 2019 10:57 pm

Re: Least restrictive critical section?

Postby monkeybranch » Sat Dec 07, 2019 4:26 pm

ESP_Sprite wrote:
Sat Dec 07, 2019 4:20 am
When in an ISR, no task can interrupt you; the CPU is in interrupt context and will only go back to running tasks when your ISR ends.
I understand no actual FreeRTOS task can interrupt my ISR but seeing how the ESP32 supports interrupt levels/priorities could my ISR be interrupted by a higher priority interrupt? Not that it matters if it does in my case, just want to understand the intricacies of the system.

So let me summarize to see if I understand correctly:
portENTER/EXIT_CRITICAL_SECTION disables all interrupts for the current CPU and uses a spinlock, burning CPU cycles. This ensures that on the current CPU no interrupts will take place. This critical section is only needed in my loop() task since accessing the shared resource from within the ISR guarantees that the loop() task won't be running anyway.

The use case for portENTER_CRITICAL_ISR seems to be synchronizing two ISRs that could potentially fire simultaneously?

ESP_Sprite
Posts: 9051
Joined: Thu Nov 26, 2015 4:08 am

Re: Least restrictive critical section?

Postby ESP_Sprite » Sun Dec 08, 2019 2:59 am

Yes, a higher-prio int could theoretically interrupt your ISR.

Note that the spinlock will only actually use CPU cycles if there actually is a second bit of code trying to access the resource at the same time. This normally is a worst-case scenario that is almost never hit.

Also, you seem to forget that the ESP32 is a dual-core machine. Even if it's not that common on Arduino, it's possible that interrupts and tasks running *on the other core* want to access the resource. Disabling the interrupts on 'your' core isn't enough to stop the other core from messing with the resource you're accessing, hence spinlocks.

In general, while there's nothing wrong with trying to understand what happens, I'd say you should just use critical regions in FreeRTOS to protect whatever resource you're trying to access. That's what they're there for, and whatever implementation is used 'under the hood' is probably going to be pretty efficient, as it's used all over the place within FreeRTOS anyway. Trying to find something that may be a tad faster than a critical section smells like premature optimization, and it makes your code a lot less robust when circumstances (toolchain, your codebase, FreeRTOS, processor used) change.

Who is online

Users browsing this forum: No registered users and 156 guests