We’ve been using the GCC v7.2.0 toolchain for compiling our code with FreeRTOS 10.2.0 for RISC-V.
I recently experimented GCC v12.2.0 and found that FreeRTOS was not running any of my tasks, not even the IDLE task was given an opportunity to run.
Digging into it, I determined that the issue is that the following variable in port.c has a 0 in it:
const uint32_t ulTimerIncrementsForOneTick = ( uint32_t ) ( configRTC_CLOCK_HZ / configTICK_RATE_HZ ); /* Assumes increment won't go over 32-bits. */
This means that the machine timer (MTIME) is effectively stuck in an interrupt. As soon as MTIMECMP is updated, it is time to service the interrupt again.
What I found, was that if I removed the
const keyword from
ulTimerIncrementsForOneTick, then the MTIME timer works again and the system works. I cannot rationalise why the removal of the “const” keyword means that the correct value gets loaded into the variable during the device’s
configTICK_RATE_HZ are fixed values that the compiler has access to.
The same code works with GCC 7.2.0 and not in GCC 12.2.0. Can someone help me shed some light? I don’t want to just remove the
const, it doesn’t feel like the right thing to do.
If the constant is only used in assembly code then it’s possible the compiler removes it and you need to use a “keep” directive to prevent that happening. However I can’t find that constant in the code anywhere so don’t know if that is the case. Grateful for a github link to the line of code.
It looks like the type of the variable has changed from a
uint32_t to a
size_t in a newer version of the kernel.
Here’s a link to the constant.
The variable is used when the scheduler starts in vPortSetupTimerInterrupt(). My understanding of this is that it sets up the first MTIME interrupt by loading in the number of timer increments for one RTOS tick into the MTIMECMP register. After the first timer interrupt fires, the assembly takes over to constantly update the MTIMECMP register to schedule the next RTOS tick.
portASM.S does look slightly different from the version I am using (v10.2.0), maybe I will try update the kernel to the latest to see if the GCC compiler treats it any differently.
Is it possible for you to share the generated assembly of
Here are some links to GitHub gists. The only different between the two is whether or not
ulTimerIncrementsForOneTick in port.c has the const keyword.
- With const keyword
** FreeRTOS v10.2.0, GCC 12.2.0 (with const) · GitHub
** The compiler has chosen to directly load in the value 306 (no reference to memory). This looks OK, but it only run when FreeRTOS starts. After the first MTIME interrupt, the MTIMECMP needs to be updated, this is done in postASM.S,
handle_asynchronous in my gist. Here, it references a memory location 0x80000124, which has never been initialised and contains the value 0.
- Without const keyword
** FreeRTOS v10.2.0, GCC 12.2.0 (without const) · GitHub
handle_asynchronous() reference a memory location (0x80000124) which has been pre-initialised with the value 306.
Maybe the compiler is getting confused when optimising a const variable which has been externed from port.c into portASM.S?
Yes, that seems to be the case. Can you try the following:
- Add the following line to portmacro.h:
#define portDONT_DISCARD __attribute__( ( used ) )
- Change the declaration of the variable to the following:
portDONT_DISCARD const size_t uxTimerIncrementsForOneTick = ( size_t ) ( ( configCPU_CLOCK_HZ ) / ( configTICK_RATE_HZ ) ); /* Assumes increment won't go over 32-bits. */
This relates to a KNOWN BUG in GCC that whole program optimization doesn’t take into account non-C/C++ code that references items.
The attribute( (used) ) is the defined fix for this issue.
Hi Gaurav, Richard,
I tried using
__attribute__( ( used ) ) as specified, but unfortunately, this did not work.
I searched for GCC Variable Attributes to understand
used a bit better and found this on gnu.org. It states that
used can only be used on variables with static storage.
uxTimerIncrementsForOneTick is not static, it is externed into
This lead me to a very insightful Stack Overflow thread. The topic is becoming more difficult for me to comprehend, however, the takeaway from the thread (right at the bottom) is to add
--undefined=ulTimerIncrementsForOneTick to my linker flags. This compiles and works.
For reference, my full linker flags is as follows:
-Wl,--cref -mabi=ilp32 -g3 -gdwarf-2 -march=rv32imac_zicsr_zifencei -mcmodel=medlow -std=c99 -Werror -nostartfiles -static -nostdlib -L. -Wl,--start-group -Wl,--end-group -Wl,-gc-sections,--print-memory-usage,--undefined=ulTimerIncrementsForOneTick -lc -static-libgcc -lgcc
I’m not sure I would call this a solution, what do you think? It feels a bit dirty to put this exception in the linker flags, however, I don’t want to go around modifying bits of the FreeRTOS kernel by removing a const keyword. My other alternative is just to stay put and continue to use GCC 7.2.0.
There are some dirty hacks whereby we reference variables after the scheduler started, so in code that won’t run, but the compiler doesn’t know that. In your case that would have to be done in the portable layer, rather than the core code.
So maybe this is actually an issue with the linker garbage collection of unused sections (and compiling with -fdata-sections, assuming you are doing that). Would be interested to see the linker output with option -Wl,-print-gc-sections to prove it. That option prints to stderr any section dropped due to being “unused”. Then search the stderr output for
ulTimerIncrementsForOneTick. Maybe this is not be related to whole-program optimization at all.
By any chance, are you compiling FreeRTOS as a static archive and then linking? Is it possible for you to share a minimal project showing the problem that I can use to repro it?
Thanks for the help and feedback so far.
I will get the output from
--print-gc-sections later today.
I am compiling FreeRTOS as a static library and linking it into my executable.
I will start on getting a minimal project together that demonstrates the issue, however, this is for an ASIC we have developed and I am checking with the team that I would be OK to share the header files. It might be that I create a private repo and share access as needed.
That is likely the cause. The following stack overflow response explains the issue - c - What does the GNU ld --undefined option do? - Stack Overflow
Can you try compiling FreeRTOS sources along with the executable instead of building FreeRTOS as a static library?