Hard faults due to (gcc) compiler computing incorrect addresses
I've encountered a number of compiler problems in a project to port freemodbus on an STM32G4 nucleo board, which is available on github. There's a demo application specifically for the NUCLEO-G431RB:
https://github.com/alammertink/FreeModbusDemo
This uses the library ported to STM32 using the cmake toolchain:
https://github.com/alammertink/freemodbus
https://github.com/alammertink/freemodbus/blob/master/demo/STM32_CMAKE/README.md
I've tested STM32CubeCLT versions 1.16 to 1.18 on Windows, which all produce the same problem: Hard fault interrupts occurring because of some error in the address calculation of function pointers and even byte arrays, which does not have anything to do with alignment issues.
I've worked around these errors using inline assembly, where the workarounds are conditionally compiled using the
#ifdef STM32_CMAKE // work around nasty gcc compiler bug
{
uint32_t* srcPtr = NULL;
uint32_t* destPtr = NULL;
__asm__ volatile ("ldr %0, =eMBRTUStart" : "=r" (pvMBFrameStartCur));
__asm__ volatile ("ldr %0, =eMBRTUStop" : "=r" (pvMBFrameStopCur));
__asm__ volatile ("ldr %0, =eMBRTUSend" : "=r" (peMBFrameSendCur));
__asm__ volatile ("ldr %0, =eMBRTUReceive" : "=r" (peMBFrameReceiveCur));
pvMBFrameCloseCur = NULL;
// Assign pxMBFrameCBByteReceived
__asm__ volatile ("ldr %0, =xMBRTUReceiveFSM" : "=r" (srcPtr));
__asm__ volatile ("ldr %0, =pxMBFrameCBByteReceived" : "=r" (destPtr));
*destPtr = (uint32_t)srcPtr;
// Assign pxMBFrameCBTransmitterEmpty
__asm__ volatile ("ldr %0, =xMBRTUTransmitFSM" : "=r" (srcPtr));
__asm__ volatile ("ldr %0, =pxMBFrameCBTransmitterEmpty" : "=r" (destPtr));
*destPtr = (uint32_t)srcPtr;
// Assign pxMBPortCBTimerExpired
__asm__ volatile ("ldr %0, =xMBRTUTimerT35Expired" : "=r" (srcPtr));
__asm__ volatile ("ldr %0, =pxMBPortCBTimerExpired" : "=r" (destPtr));
*destPtr = (uint32_t)srcPtr;
}
#else
pvMBFrameStartCur = eMBRTUStart;
pvMBFrameStopCur = eMBRTUStop;
peMBFrameSendCur = eMBRTUSend;
peMBFrameReceiveCur = eMBRTUReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
#endifAnd in mbrtu.c , where even the address of a byte buffer array was not computed correctly:
https://github.com/alammertink/freemodbus/blob/master/modbus/rtu/mbrtu.c
#ifdef STM32_CMAKE // work around nasty gcc compiler bug
volatile UCHAR* destPtr;
__asm__ volatile ("ldr %0, =ucRTUBuf" : "=r" (destPtr));
destPtr += usRcvBufferPos;
*destPtr = ucByte;
usRcvBufferPos++;
#else
ucRTUBuf[usRcvBufferPos++] = ucByte;
#endif
There are more problems in these two files, all worked around using inline assembly and conditionally compiled using the STM32_CMAKE macro, set in:
https://github.com/alammertink/freemodbus/blob/master/CMakeLists.txt
The problems can be reproduced with just a Nucleo-G431RB board by un-setting the STM32_CMAKE macro. The function pointers are already set in the initialization, which will be hit without any further action. The errors in mbrtu.c require at least some data to be sent over the (virtual) com port, which can be done using modpoll as described in the readme:
https://github.com/alammertink/freemodbus/blob/master/demo/STM32_CMAKE/README.md
As far as I can tell, this is a gcc compiler bug, since I've seen no warning nor errors, while the code in question has been running without problems on various platforms for years.
