The DATAFLOW Runtime for Intel NIOS2 Soft Core provides a preemptive scheduler. It has been designed to execute active parts in the same way as a prioritized interrupt controller using a single stack.
The implementation of the non-blocking, single-stack scheduler of the DATAFLOW Runtime works as follows:
- DATAFLOW Runtime uses only the Main Stack Pointer (DATAFLOW is a single stack runtime).
- The first line of code in each Interrupt Service Routine (ISR) must call the
RuntimeInterrupts::applicationIsrEntrymethod. The last line must call the
When the DATAFLOW Code Generator is used, this is already the case in the generated
OsIsrHandlerwill be called with interrupts disabled. It will enable interrupt nesting (mask same and lower priority interrupts and enable interrupts) and call the specific ISR method. After the call returns the interrupt nesting is disabled again. Then the scheduler will be called, but only when there is no nested interrupt.
In order to ensure that a high priority active part is not blocked by a long running low priority one, the low priority active part is preempted whenever a message is sent to an active part with higher priority. This can happen in synchronous way (sending a message from application code) and asynchronous way (sending a message from an ISR).
The "synchronous preemption" occurs when one (low-priority) active part is preempted by another (high-priority) active part. DATAFLOW Runtime handles this case as a regular function call. This function call happens inside the
The "asynchronous preemption" occurs when an interrupt sends a message to an active part with a higher priority as the current one. In NIOS2, this preemption is handled in the
- The timeline begins with the DATAFLOW Runtime executing the idle loop.
- At some point an interrupt occurs and the CPU immediately suspends the idle loop, pushes the interrupt stack frame to the Main Stack and starts executing the OsIsrHandler.
- The OsIsrHandler enabled nested interrupts and calls the actual ISR method.
- The ISR method performs its work. At the end, the interrupt nesting is disabled again.
runtimeSchedule()method, is called by function call.
runtimeSchedule()method enables interrupts and calls the execute method of the Low-priority active part.
- Some time later a low-priority interrupt occurs. The Low-priority active part is suspended and the CPU pushes the interrupt stack frame to the Main Stack and starts executing the ISR.
- Before the Low-priority ISR completes, it too gets preempted by a High-priority ISR. The CPU pushes another interrupt stack frame and starts executing the High-priority ISR.
- The High-priority ISR does not call the scheduler because there is a nested interrupt. The stack frame of the high priority ISR is removed upon exit of the ISR.
- Before exit from the Low-priority ISR, the scheduler is called as previously described. The scheduler detects that the High-priority active part has a pending message and calls its execute method. The High-priority active part runs to completion and returns to the scheduler.
- The scheduler does not find any more higher-priority active parts to execute and returns to the low priority ISR. This is also exited and returns to the preempted active part. This removes the low priority ISR stack frame.
- The Low-priority active part, which has been preempted all that time, resumes and finally runs to completion and returns to the scheduler. The scheduler does not find any more active parts to execute and returns to the idle loop.