This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
I was thumbing through the FPU Primer (spraan9.pdf) and found the section 'Interrupt Context Save and Restore'. I then realized that I am performing context switches but not preserving the FPU registers during the context save and restore.
We have an in-house written task switcher that uses Timer 2 to switch between tasks. Several of these threads are using floating point calculations so I am very concerned.
In the spraam9.dpf document, there is a comment that says that the SAVE and RESTORE instructions should only be used within 'High Priority Interrupts'.
'RB must be saved if it is used in the interrupt and the FPU registers are copied to their shadow registers using the SAVE instruction. SAVE and RESTORE should only be used in high-priority interrupts.'
How do I know which interrupts are considered to be 'high' and 'low' priorities? I do not recall any documentation or chart that classifies interrupts sources as being catagorized as 'high' or 'low'. Can someone help clarify this?
Also, in the same document, it mentions a couple of pragmas that are used to identify your interrupts as 'high' and 'low' priority.
// Specify a High-Priority Interrupt:
#pragma INTERRUPT (function_name, HPI)
// Specify a Low-Priority Interrupt:
#pragma INTERRUPT (function_name, LPI)
Do I need to use these pragmas on all of my C interrupt routines so that the FPU state is properly saved and restored?
Here are the macros that I used to preserve and restore registers during a context save (does not include the FPU registers):
I$$SAVE .macro
;ASP
PUSH AR1H:AR0H ; Save remaining registers.
PUSH XAR2
PUSH XAR3
PUSH XAR4
PUSH XAR5
PUSH XAR6
PUSH XAR7
PUSH XT
PUSH RPC
.endm
I$$REST .macro
POP RPC
POP XT
POP XAR7
POP XAR6
POP XAR5
POP XAR4
POP XAR3
POP XAR2
POP AR1H:AR0H
;NASP
IRET
.endm
Here is the recommended context save and restore instructions:
High Priority Context save:
ASP ; Align stack
PUSH RB ; Save RB if used <-- New for FPU
PUSH AR1H:AR0H ; Save if used
PUSH XAR2
PUSH XAR3
PUSH XAR4
PUSH XAR5
PUSH XAR6
PUSH XAR7
PUSH XT
SPM 0 ; Set C28 modes
CLRC AMODE
CLRC PAGE0,OVM
SAVE RNDF32=1 ; FPU registers <--
High Priority Context restore:
RESTORE ; FPU registers ; <-- new for FPU
POP XT ; Restore registers
POP XAR7
POP XAR6
POP XAR5
POP XAR4
POP XAR3
POP XAR2
POP AR1H:AR0H
POP RB ; Restore RB ; <-- new for FPU
NASP ; Un-align stack
IRET ; Return
In this context a high priority interrupt is defined as an interrupt that cannot itself be interrupted. A low-priority interrupt is defined as an interrupt that allows itself to be interrupted.
The compiler assumes low-priority by default for C coded interrupts (which is the safest and will always work). If you have interrupts you know will not themselves be interrupted then you can improve performance by using the pragma to specify they are high priority.
-Lori
Lori,
Thank you for the quick response. After I posted I went back and found the definition in the FPU primer.
Since we are task switching between C threads performing floating point calculations, I believe that we should always use the low-priority context switching scheme.
The reason I believe that to be the case is that a thread might be in the middle of a floating point calculation when it is interrupted. Another thread might then use the FPU for more floating point calculations. The interrupted thread may need to retrieve FPU results after it resumes execution, the state of the FPU must be saved to the stack. If I am understanding the FPU shadow registers (using SAVE and RESTORE), that provides a single copy of the FPU and therefore the interrupt that executes these commands must itself not be interrupted.
If you have any other advice about multi-threading and working with the FPU it would be appreciated.
Thank you.