I'm using Keil uVision5 and an LPC1768 on an MCB1700 dev board.
I am writing a simple RTOS, and I have implemented two methods of context switching - one co-operative using a yield() function and one pre-emptive using SysTick. I just spent a while debugging a hardfault and long story short the SysTick context switcher would periodically fire in the middle of a cooperative context switch, which messed up my various stacks and led to the hard fault.
I have tried using __disable_irq() at the top of my yield function then re-enabling IRQ just before I do the switch (which happens in handler mode for various reasons), but that didn't fix anything. I have currently kludged together a solution where I have a global boolean that the yield function sets to false while it's doing its thing. If that boolean is false, the SysTick switcher just returns.
I would like to disable the SysTick interrupt during a yield, but it seems that __disable_irq isn't the way to do it. Does anyone have any suggestions?
Yes, it does disable the systick interrupt, but as soon as you re-enable interrupts the systick will fire.
You really only want 1 set of code to Save the Current User Contex and Restore the New Context. It should not matter if it was from Yield or Systick or possibly some other interrupt that allows a blocked task to become ready again.
SysTick should be your lowest priority interrupt.
SVC should be 1 higher then the lowest priority interrupt.
[If you allow interrupts to cause tasks to unblock, you will want to set PendSV to also be your lowest priority interrupt.]
When you want to yeild, you need to call the SVC and execute the Yield function.
1) If there is no context switch, you will just return.
2) If there is a context switch, you need to save the rest of the unsaved registers on the current stack, change the USP, restore the previously saved registers from USP and update the USP. Do a return to thread mode. This will be done at a priority level that SysTick will not interrupt until after the SVC returns
When you get a SysTick.
1) Do your SysTick processing.
2) If there is no context switch, just return.
3) If there is a context switch Do what you did in step 2 above (it should be the same code)
This requires 1 Master Stack of appropriate size, and 1 seperate User Stack for every Thread/Task.
In this case, SVC can only be called from thread mode. While the SVC is running, the SysTick cannot actually run. IF a SysTick happens during a Yield Context Switch, the SVC will still fully restore the task that should run because of the Yield.
When the pending SyTick Runs after the yield, IF a task switch is required, the currently restored task will be saved again and the new task will be retored. If there is no task switch required when SysTick Returns, it will just return to the Task that yield had set up to run.
Ah, thank you, that helps me a lot. I think I have some redesign to do :)