Speed up scheduler when only one task

I am using a 2 core esp32. The second core, which is suppose to run its own scheduler, has only 1 task, which is running a sensor/processing loop at high priority. It is the only thing being done on the second core. The sensor/processing loop must execute in 20us. Fortunately, the loop can be completed in 15us so from that respect all is fine. Unfortunately, every 10ms the tick timer goes off and starts the scheduler. The scheduler takes 6.9us to execute which violates the 20us specification.

Is there a way to shorten or eliminate that 6.9us processing time?

There will only be one task, so the scheduler will never need to determine if a context change needs to occur. Is there a way to set up the scheduler so it doesn’t waste time checking if there needs to be a context change (speaking from state of ignorance here)?

Any help would be appreciated.
Thanks

If you only every have one task running (not even switching to the idle task) you don’t need an RTOS, and can just run a bare-metal application.

If you still want FreeRTOS running because you want to block to wait for work (so you DO have the idle task running, and switch between the two tasks), but never need a timeout, just manualy disable the interrupt for the tick interrupt.

The tick timer is on core 0 and is used for the schedulers running on core 0 and core 1. I will need the RTOS on core 0 so I can’t really disable the timer because it is shared by the RTOS of both cores.

It was not entirely correct to say that I only have one task because it ignores the IDLE task. I may be looking for a way to tell the scheduler that there is no reason to go to the IDLE. I am assuming that when the tick timer expires that there will be a complete context switch to go to the IDLE task and back.

Thanks

No, the (lowest prio) idle task runs (i.e. is selected to run by the scheduler) only if all other, higher prioritized tasks are blocked/waiting for something. Hence IDLE task.

Excellent, thanks.

Is it correct to assume then that the 6.9us is caused entirely by the scheduler checking to see if there is another task that should be run, finding that there is no other task to run, then returning processing to the task, with no context switch ever being done?

You could try to use a cooperative scheduling scheme by setting

#define configUSE_PREEMPTION    0
#define configUSE_TIME_SLICING  0

to minimize the tick handler processing time if this still matches your requirements.
What about doing the sensor processing in a (HW timer triggered) ISR with a prio above FreeRTOS covered range ? Given the sensor processing doesn’t need any FreeRTOS API calls, of course.

1 Like

I have not done esp32, so I don’t know what happens when the tick timer interrupt goes off or what’s typical for a context switch. In most, if not all, of my FreeRTOS projects, there’s always a timer task that deals with dispatching the timer expiration events at thread rather than interrupt context. The scheduler should not be taking a significant amount of time when it’s called. If I remember correctly, the runnable tasks are kept in a priority ordered queue, and if the priority of the current task is higher than the head of the runnable queue, it shouldn’t do anything more; it does not have to look at anything else. If there’s a timer task, however, and the timer interrupt handler first makes the timer task runnable, the timer task is likely to be the highest priority, and when the scheduler is called at the end of the timer interrupt handler, the task context switch will occur, the timer task will deal with timeouts and software timers, then block until the next tick which causes the scheduler to perform a context switch back to the highest runnable priority task, which is your task.

I don’t have time to go back and look to be sure I’m correct in my above conjecture, but that may be what’s taking so long.

@fr_cone

Is it correct to assume then that the 6.9us is caused entirely by the scheduler checking to see if there is another task that should be run, finding that there is no other task to run, then returning processing to the task, with no context switch ever being done?

The time 6.9 us may consist of the followings:

  • The time to handle tick interrupt
  • The time for xTaskIncremenTick() call in the tick ISR. As you described above, if you have preemption and time slicing enabled, the scheduler will check if there are any tasks with equal priority that are ready to run in the xTaskIncrementTick() function.

As hs2 mentioned, disabling configUSE_PREEMPTION and configUSE_TIME_SLICING can minimize the time spent in the xTaskIncrementTick() call. It’s worth experimenting with these two configs disabled to find out the CPU time saved.

The other question I would like to know is whether you are using an AMP or SMP configuration since your ESP32 platform has 2 cores.

Thanks for your explanation. I haven’t created a timer task but I do have a bare metal timer running. If I turn off the bare metal timer the 6.9us still occurs. There is an inter processor communication task that the ESP32 environment creates so maybe that is being called and doing something.

Thanks again.

Thanks for your feedback
.
The documentation says it is SMP, but it also says that each core has its own scheduler. I’m not sure both can be true. Maybe one instance of FreeRTOS with 2 schedulers, one for each core (insert my dumbfounded facial expression here)??? When I do a vTaskListTasks it lists an IDLE, and InterProcessorComunication task for each core.

There is only one configUSE_PREEMPTION and configUSE_TIME_SLICING so I’m thinking this means there is only one FreeRTOS.

A Dual core processor can run either SMP with one copy of FreeRTOS that controls and schedules both core or AMP with a separate copy of FreeRTOS, one for each core, which sounds like what you want.

The AMP structure should end up generating two independent program files that each get loaded in its own location of memory, one for each processor. Normally you have two build scripts, one for each processor, and that should allow having separate FreeRTOSConfig files for the two processors, but they might not actually implement that capability in the default build structure (in my mind they should, but they might not).

If one call to vTaskListTasks lists two Idle processes, one for each core, the the system is running SMP, but then I don’t understand the need for an ā€œInterProcessorComunicationā€ task, as the processors are just sharing. (Unless they have a not-very-symmetric SMP where FreeRTOS sits in the shared memory, but a lot of the tasks run in private memory for one of the cores.)

I tried turning off Preemption and Time Slicing but it made no difference.

Thanks for all the input. I have a greater understanding of FreeRTOS now and have you all to thank for that.

Did you also try @hs2’s suggestion of doing this processing in a HW timer ISR which runs at a priority above FreeRTOS covered range?

No I didn’t. But I’m not too keen about having the processor running exclusively as an ISR in an infinite loop.

Thanks for the reminder though.

The idea was a timer based sensor processing instead of a polling loop (if applicable).

It still wouldn’t make a difference. The sensor processing must run once every 20us and is initiated by a timer. The sensor processing will take 15us regardless of whether some of it is running in an ISR or not. That leaves 5us of free time. The FreeRTOS process takes 9.6us which is longer than the free time. Running some, or all, in an ISR would restrict the 9.6us from starting during the ISR, but it would not prevent the 9.6us from running after returning from the interrupt, so the sensor process and 9.6us would still not complete in 20us.

Ok. You didn’t mention or I missed that it’s already timer triggered.
Good luck !

When I had a task similar to that, I put the timer interrupt higher in priority then could use FreeRTOS, so it did not get masked by critical sections, and did everything in the ISR. If it needs to interface with FreeRTOS at times, it needs to trigger a lower priority interrupt and that can do that part of the actions.

If the ESP32 can’t do that, it may just be the wrong processor for the task.

Thanks for sticking with me.

Just for clarification, are you saying that you entered the ISR once, and only once, and never returned from the interrupt?

That wouldn’t make much sense I guess. But e.g. on Cortex-M MCUs FreeRTOS covers only a certain range of interrupt priorities. Interrupts with a logically higher prio outside this range are not disabled by critical sections etc. but on the other hand the corresponding ISRs can’t call FreeRTOS API functions.
This might be used for harder (much less jitter) realtime interrupt processing as seemingly required in your case. So the plan was or could be to have a periodic 20 us HW timer interrupt with a higher prio than covered by FreeRTOS, do the needed sensor processing in the ISR and return. If you have to use FreeRTOS API in addition you would’ve to use the mechanism described by @richard-damon and set a lower, FreeRTOS covered prio interrupt in the RT ISR (by SW) and do the FreeRTOS API calls in the corresponding ā€˜trampoline’ ISR.