In my continued quest to bring up the STM32G4 for the moteus controller r4.1 and other efforts, I get to discover and learn about the million and one ways in which things don’t work on my path to find the one or two ways in which they can work. I recently had an interesting enough problem that it is worth it to signal amplify google searches on the matter for the next unfortunate soul.
First, the symptom: snprintf of 32 bit floating point values gave seemingly random results. The diagnostics and configuration functions of the moteus controller can in some modes render values into ASCII for human readable interactions. In this particular instance, I was enumerating the full set of configurable values and saw that all of the floating point values read bogus values, like “0.00000”, “-0.00000”, “2.00001”, etc, no matter what actual value they were set to. Integral types seemed to work fine, and the debugger showed that parsing floating values when setting the configurable values was working just fine.
I had a similar problem not too long ago, when a linker script mis-configuration resulted in an overlap between the RAM based interrupt vector table and the first few global variables resulted in state necessary for C library functions being corrupted during early boot. Thus, I began investigating under that assumption. However, the linker script definitely had enough room (I allocated 0x200, where the datasheet shows that the final entry is only 0x1c4). Just to be sure it wasn’t some other global memory corruption issue, I stepped through the first 5,000 or so assembly instructions of an snprintf call, inspecting all RAM that was accessed and making sure that it wasn’t corrupted. I did notice that my 32 bit floating point value had been promoted to a 64 bit value when passed to snprintf, but didn’t think any more of it at that point.
Despite all that debugging, I found nothing of interest and turned to my choice of last resort, googling for the original symptom. Usually this doesn’t find much, because the set of root causes for any one particular failure is so vast and many problems are the result of simple misconfiguration. However, in this case, I hit the jackpot after digging through an hour worth of junk results.
The problem ultimately was one of stack alignment.
When I ported mbed to the stm32g4, I created the initial linker script from the STM32F446 that I had been using, but made the interrupt table bigger so that I wouldn’t have to worry about it at all. However, I managed to goof it up and ended up with the top of the stack aligned only to 4 bytes, not 8 bytes. Since 32 bit floats are promoted to doubles when passed through a variadic function, this resulted in the double being not 8 byte aligned when passed on the stack.
Once the problem was identified, the fix was easy enough: