Part Number: MSP432P401R
Dear Support,
I´m looking for an example program demonstrationg the usage of "FPU_IRQHandler" on CortexM4F.
The example should cover e.g. the detection of a NAN-result.
Best regards
Uwe C.
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.
Part Number: MSP432P401R
Dear Support,
I´m looking for an example program demonstrationg the usage of "FPU_IRQHandler" on CortexM4F.
The example should cover e.g. the detection of a NAN-result.
Best regards
Uwe C.
I'm not sure I've ever seen a non-trivial FPU exception handler for the Cortex-M. I think part of the problem is that the CPU/FPU only really gives you enough information to report the problem, not to fix anything.
The FPSCR/FPCAR/PC will tell you what went wrong with what instruction [Ref DDI0403E.d Sec B3.2.2], but it's up to you to decode the instruction to figure out its operands, and then deduce the cause. And as a practical matter, whatever fix-up you do will probably resemble the action that the FPU will take anyway (div0->Inf, e.g.) [Ref Table A2-3]
What did you have in mind to do in the case of an FPU exception?
[Edit: Fixed typo]
Dear Bruce,
the actual idea is to create a module that allows checking the FPU flags when needed, to decide higher-level if the program is working validly within the expected scope.
I copied my current "test program" together and attached it below.
All in all the function seems to be given in the meantime.However, it is not clear to me why I get so many IXC and why also IOC per loop is reported by the FPU. Also my assembler construct seems to be a bit clumsy.
I think I didn't quite understand how the underlying mechanism of accessing the status information works and how I operate the interrupts correctly.
A critical look from your side would be helpful!
- Translated with www.DeepL.com/Translator (free version)
/* DriverLib Includes */
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
/* Standard Includes */
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <fpu.h>
#define IOC_MASK (1U)
#define DZC_MASK (1U << 1U)
#define OFC_MASK (1U << 2U)
#define UFC_MASK (1U << 3U)
#define IXC_MASK (1U << 4U)
#define IDC_MASK (1U << 7U)
/* Statics */
volatile float fCalculate;
volatile float fDiv;
volatile uint32_t savedReg;
uint32_t uLoopCount;
uint32_t uIOC_MASK;
uint32_t uDZC_MASK;
uint32_t uOFC_MASK;
uint32_t uUFC_MASK;
uint32_t uIXC_MASK;
uint32_t uIDC_MASK;
//![Simple FPU Config]
int main(void)
{
MAP_WDT_A_holdTimer();
// required?
FPU_enableLazyStacking();
FPU_enableModule(); // MSP432: enabled by default
// correct ?
NVIC_ClearPendingIRQ(FPU_IRQn);
MAP_Interrupt_enableInterrupt(INT_FPU);
MAP_Interrupt_enableMaster();
while(1)
{
for(fDiv=0.0000000001f; fDiv < 20.0f; fDiv += 0.1f)
{
fCalculate = sinf(1.5f / fDiv) ;
}
uLoopCount++;
}
}
void FPU_IRQHandler (void)
{
// Prepare pointer to stack address with pushed FPSCR register
// (0x40 is FPSCR register offset in stacked data)
uint32_t * fpscr = (uint32_t *) (FPU->FPCAR + 0x40);
// Execute FPU instruction to activate lazy stacking <<===== Required: yes/no/why?
// (void) __get_FPSCR(); <<======== Code for CCS?
// __asm(" VMRS APSR_nzcv, FPSCR");
__asm(" PUSH {R10}\n"
" VMRS R10, FPSCR\n"
" POP {R10}");
// Check exception flags
// Critical FPU exceptions signaled:
// - IOC - Invalid Operation cumulative exception bit.
// - DZC - Division by Zero cumulative exception bit.
// - OFC - Overflow cumulative exception bit.
if (*fpscr & IOC_MASK)
uIOC_MASK++;
if (*fpscr & DZC_MASK)
uDZC_MASK++;
if (*fpscr & OFC_MASK)
uOFC_MASK++;
if (*fpscr & UFC_MASK)
uUFC_MASK++;
if (*fpscr & IXC_MASK)
uIXC_MASK++;
if (*fpscr & IDC_MASK)
uIDC_MASK++;
// Clear flags in stacked FPSCR register. To clear IDC, IXC, UFC, OFC, DZC and IOC flags, use 0x0000009F mask.
*fpscr = *fpscr & ~ (0x0000009F);
}
The code seems about right -- the right stack areas seem to update at the right time. It's unfortunate the CCS people forgot __get_FPSCR, but your code accomplishes the purpose.
Reading the description of IXC (DDI0403E.d p. A2-45) I suppose it could happen quite frequently; it might be interesting to disable it. The definition of FPProcessException() [p. A2-49] seems to suggest that FPSCR bits [15:8] are enable bits, but the register description claims they're Reserved [Sec A2.5.3] and they're always 0 in the saved FPSCR.
Arm App Note AN298 has a few more tidbits; for its purposes, it suggests using VSTM to both trigger the lazy save and save S16-S31 (but not in the stack, I guess). .
Dear Bruce,
OK, thanks for your support and reviewing my code!
Two points to mention
1) From TI LSAU356:
"The FPU sets the cumulative exception status flag in the FPSCR register as required for each instruction,
in accordance with the FPv4 architecture. The FPU does not support user-mode traps. The exception
enable bits in the FPSCR read-as-zero, and writes are ignored. The processor also has six output pins,
FPIXC, FPUFC, FPOFC, FPDZC, FPIDC, and FPIOC, that each reflect the status of one of the cumulative
exception flags. All these outputs are ORed and given on a single interrupt line in MSP432Pxx devices."
As I understand you, you think this can be circumvented?
2) Interestingly for ST dm00047230 they implemented this feature differently (and maybe better):
Exceptions cannot be trapped. They are managed through the interrupt controller.
Five exception flags (IDC, UFC, OFC, DZC, IOC) are ORed and connected to the interrupt
controller. There is no individual mask and the enable/disable of the FPU interrupt is done at
the interrupt controller level.
The IXC flag is not connected to the interrupt controller and cannot generate an interrupt as
its occurrence is very high. If needed, it must be managed by polling.
Best regards
Uwe Creutzburg
I tried a couple of ways to trick the FPU into accepting an FPSCR with bits [15:8] set (0x2000ff00), but it didn't read back and had no visible effect. Thanks for the TRM quote -- that explains why none of my tricks worked.
[For the archaeologists:]
3) I just noticed that the msp432e401y (really a Tiva) treats the FPIXC (et al) wires as first-class objects. They can be enabled/disabled (SYSEXCIM) and read (SYSEXCRIS) [Ref msp430e401y TRM (SLAU723A) Secs 5.2.2 and 5.2.1] I haven't tried it yet.
[Edit: It works. The IRQ is SYSEXC_IRQn. The FPSCR is presented as expected, but clearing the bits in the FPSCR doesn't clear the interrupt -- you also need to clear the condition(s) in the SYSEXC->IC register.]
Dear Bruce,
as I understand it, there is a reasonable solution for MSP432E401Y. However, when I look at the data sheet for MSP432P401R, I do not find the registers mentioned. This means that your elegant solution of MSP432E401Y is not transferable to MSP432P401R, right?
I think the short answer is: No, you can't do the same thing on the P-series.
Going back over DDI0403E, I think the keyword is "trap". None of P-series/E-series/STM32 support "user-mode traps". Those FPSCR bits are "trap" enables, not interrupt enables. The question "Why don't they?" leads to the MVFR0 register [DDI0403E.d Sec B4.7.2] where the "FP exception trapping" field has one possible value: 0=Not supported. So there are actually no trap enable bits in the FPSCR in the ARMv7/M FPU.
The MSP432P dealt with this by tying (logical-or) all the FPU exception wires into a single interrupt line to the NVIC. They are not individually maskable, but are (observed behavior) clearable in the FPSCR.
The STM32 tied all but FPIXC ("occurrence is very high" [Ref AN4044 Sec 3.3]) into a single interrupt line to the NVIC. They are not individually maskable, but are (I suspect) clearable in the FPSCR.
The MSP432E (Tiva) brought the exception wires individually into a bespoke controller (SYSEXC) which provide masking and clearing, and an interrupt line to the NVIC.