Issues with ARM9 port and Link Time Optimization

I have a really specific problem and was wondering if some experts here who are more familiar
with the older compilers have an idea what might cause this issues.

I am using a port for the SAM9G20 core (which is already considered legacy)

There is a macro function to restore the port context which is written in inline assembly:

#define portRESTORE_CONTEXT()											\
{																		\
extern volatile void * volatile pxCurrentTCB;							\
extern volatile unsigned portLONG ulCriticalNesting;					\
	/* Set the LR to the task stack. */									\
	asm volatile (														\
	"LDR		R1, =pxCurrentTCB								\n\t"	\
	"LDR		R0, [R1]										\n\t"	\
	"LDR		LR, [R0]										\n\t"	\
	/* The critical nesting depth is the first item on the stack. */	\
	/* Load it into the ulCriticalNesting variable. */					\
	"LDR		R0, =ulCriticalNesting							\n\t"	\
	"LDMFD	LR!, {R1}											\n\t"	\
	"STR		R1, [R0]										\n\t"	\
	/* Get the SPSR from the stack. */									\
	"LDMFD	LR!, {R0}											\n\t"	\
	"MSR		SPSR, R0										\n\t"	\
	/* Restore all system mode registers for the task. */				\
	"LDMFD	LR, {R0-R14}^										\n\t"	\
	"NOP														\n\t"	\
	/* Restore the return address. */									\
	"LDR		LR, [LR, #+60]									\n\t"	\
	/* And return - correcting the offset in the LR to obtain the */	\
	/* correct address. */												\
	"SUBS	PC, LR, #4											\n\t"	\
	);																	\
	( void ) ulCriticalNesting;											\
	( void ) pxCurrentTCB;												\

When I flash the hardware using the J-Link, it works without issues. If I transfer the binary on the boot flash memory and restart the core (letting the bootloader do the job of loading the software), the program gets stuck in the macro when starting the task scheduler. This only happens for code optimized with Link Time Optimization (-flto flag).
The following change fixed the issue:

I removed

extern volatile void * volatile pxCurrentTCB;
extern volatile unsigned portLONG ulCriticalNesting;	     


( void ) ulCriticalNesting;
( void ) pxCurrentTCB;

and now the program does not hang up in the macro anymore but starts regularly (although I still need to run some advanced tests, everything seems to work up until now).

I am using the newest ARM GCC Toolchain available (9.3.1-1.1.1) so maybe it’s just the combination of the new toolchain and very old code but maybe someone also has an idea why
those 4 lines cause issues… Especially because functions like portSAVE_CONTEXT use similar code.

UPDATE: Alright, it worked for some time and now it is stuck again in the same function so the problem is propably unrelated…

Kind Regards

The externs were originally added to enable the code to link, and the casting to void added because the compiler cannot see the variables getting used and generated warnings - I suspect the problem with LTO is similar in that if the compiler thinks they are not being used it may just remove them or at least do something weird with their linkage. If the code compiles and links with those edits you should be fine.

I actually had to perform three additional steps… it works for now but at this point I am not sure whether the problem is completely unrelated…

  1. Turn off optimization for function to start scheduler

    portBASE_TYPE xPortStartScheduler( void ) attribute((optimize(“O0”)));

  2. Flag function to start first task as noinline

    static void vPortISRStartFirstTask( void ) attribute((noinline));

  3. Add -fno-omit-frame-pointer to compilation flags for FreeRTOS source files

And none of this is necessary when flashing with the J-Link… Really weird. Also, sometimes the code did crash (or whatever is happening, sometimes the hard fault handler is triggered, sometimes not…) even when Link Time Optimization was disabled.

Kind Regards

Flashing with J-Link makes no difference to the executable, but may to how the hardware boots post flashing.

I think I found out the issue a few weeks later and it was completely unrelated to FreeRTOS
(super evil bug). The processor architecture uses seven ARM vectors and I used the sixth one which is reserved and normally not used to store the binary size manually (this is sometimes required, but when it was not, I thought I could use it for own purposes. That was wrong apparently…). So a really stupid mistake by me.

When not doing that, the changes above are not necessary…

Kind Regards

Thanks for taking the time to report back.