mjhuslig wrote on Wednesday, August 08, 2018:
I have an ARM4 project working in cooperative mode. A bunch of priority 1 tasks, a higher priority task to handle USB, and two higher priority tasks to handle analog/DSP. The problem is I have to wait for a priority 1 task to finish before the DSP tasks can start. What I would like to do is have the DSP tasks be preemptive but leave the priority 1 tasks cooperative, because the priority 1 tasks share a lot of common memory. However, according to https://sourceforge.net/p/freertos/discussion/382005/thread/34689477/, when the DSP task preempts the priority 1 task, the priority 1 task is put back at the end of the queue such that when the DSP task is finished, a different priority 1 task starts.
To overcome this, I thought it might be possible for a priority 1 task to change its priority to 2 when it first starts and change itself back to 1 when it is ready to allow the next priority 1 task to start. This way when the DSP task ends, the same priority 1(now 2) task that was preempted will continue.
Before I start making wholesale changes, are my assumptions correct? Is there an easier way?
richarddamon wrote on Wednesday, August 08, 2018:
Changing priority would be one option, but in my mind would be a bit fragile. The issue being that if the task does anything to block for a moment, you will switch to the next task, and when the block is over, switch back to the first at an arbitary point.
Two better options (in my opinion) would be, first, to create a (or multiple) mutex, and when the tasks are using the common memory take the mutex, and then give it up when they are done. It could even be the existing code where you begin by taking the mutex, and then give it up when you are done with the operation.
A second option, if you really do have a set of tasks that just run in sequence, they could just be made one task running the operations in sequence (maybe checking which operation to do next).
mjhuslig wrote on Tuesday, August 21, 2018:
In my co-operative code, my ADC interrupt moves the output from the ADC, puts it in a buffer, and every 4th interrupt does:
In co-operative code, would the portEND_SWITCHING() cause problems?
I am not used to all this. This project is an update of several thousand lines of HC11 assembly language. To do a “deferred interrupt” all I had to do was just re-enable interrupts at the 4th sample and continue processing.
I hope Mr. Barry can continue with his e-books. But in his chapter on deferred interrupts, he doesn’t specifically say whether the examples are cooperative or pre-emptive. He also doesn’t show what happens when there are more than 1 task of the same priority.
richarddamon wrote on Tuesday, August 21, 2018:
If you don’t want preemption, then you do not want to call portEND_SWITCHING_ISR(), as that will possibly cause a preemptive task switch at that point.
In FreeRTOS there are several ways to perform a ‘deferred interrupt’, one is like you are doing where you send a message to a high priority task, the other is to use xTimerPendFunctionFromISR(). Both of these tend to require the use of preemption to get good response time (as if the system is non-preemptive/cooperative the continuation will wait for a system call to process).
Basically, you use a ‘cooperative’ system when you don’t want to need to worry (as much) about thinking about data sharing. It assumes that tasks yield often enough to meet the real time requirements. If that assumption is not true, you can’t use the non-preemption. FreeRTOS doesn’t support mixing preemption and non-preemption/cooperative tasks, except via the co-routine system which has significant limitations compared to simple cooperative tasks.
It sounds like you need to ‘bite the bullet’ and take control of data sharing and re-write the ‘cooperative’ tasks to either allow preemption with proper data sharing, or condense to a single task (so no need to share) with manual scheduling which ‘task’ to run.
mjhuslig wrote on Friday, August 24, 2018:
I can’t condense to a single task as each of the other priority one tasks have their own stacks and can yield is several different places. I could change all the priority one tasks, except for the main task, to priority two and have the main task resume and suspend them in turn, but that is also a little compicated. ( I saw your recent posts with Oliver. ) So I will try and bite the bullet.
One question off topic I have been wondering about for some time:
The version of malloc in heap4 and 5 unlinks a free block, carves out the requested block from the front of it, and then adds the remaining block to heap using the free routine. Wouldn’t be more efficient to not unlink the free block, carve out the requested block from the end, and simply adjust the size of original free block?
richarddamon wrote on Friday, August 24, 2018:
I can’t say I know the history of the FreeRTOS heap functions, so not sure why they are written the way they are. The ‘classical’ method works from the front because typically it got the space to build the heap from a function sbrk which would add to the end of the heap (often taking from the stack), and could also give back the end of the heap (and give back to the stack). By working from the front, you don’t need to worry about some leftover space at the beginning that may end up too small to ever be used, and then similar leftovers from every expansion. Since the FreeRTOS heap functions work off a fixed statically allocated buffer (except heap3 which just wraps the existing malloc) this isn’t a concern.
As to cooperative tasks, I would NOT try having one task suspend/resume tasks to control the cooperative tasks, that is likely tricky to get right. Assuming the purpose of being cooperative is to simplify the sharing process, one crude option is to create a mutex to ‘protect’ the shared resource, and then all the cooperative tasks take the mutex when they have something to do, and then give it just before doing a blocking action or yielding, and then taking it again after the return of that call. This lets you have other tasks that are preemptive, but this set will be acting ‘cooperative’. If you miss the give/take around one call that block, you don’t break the cooperation, just don’t allow the desired task switch.
mjhuslig wrote on Thursday, August 30, 2018:
Well, my first attempt crashed (only once so far) when assert caught my code trying to delete a NULL semaphore. It’s off to the races to track that down.
Maybe it would be advantageous to add another mode to FreeRTOS. Call it cooperative-deferred, for example. It would operate exactly like pre-emptive (no time slicing), but instead, when a priority x task is pre-empted by a higher priority y task, the priority x task is put on the front of the priority x ready queue instead of the back.