Odd behavior in assembly busy wait cycle
I am preparing the labs for the undergraduate embedded systems course at the university where I work (the University of Milano-Bicocca in Italy). This year we will change the hardware and we will start using the STM32 Nucleo F767ZI boards. I am a total beginner, so please bear me. I am preparing the usual "blinky" example, so I started STM32CubeIDE, I created a fresh project, did not touch the default pinout configuration, and proudly wrote my first code for a Nucleo board ever:
/* main.c: */
...
int main(void)
{
...
while (1)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_14, GPIO_PIN_SET);
PAUSE_CIRCA_ONE_SECOND;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_14, GPIO_PIN_RESET);
PAUSE_CIRCA_ONE_SECOND;
}
}
...To spice the things up a bit I wrote the pause code in ARM inline assembly:
#define PAUSE_CIRCA_ONE_SECOND \
asm volatile("mov r0, #0x4400000\n" \
"sub r0, r0, #1\n" \
"cmp r0, #0\n" \
"bne .-6" : : : "cc", "r0");where I considered the 216MHz clock and I guessed that the CPU would execute approximately one instruction per clock cycle - hence 0x4400000. It turns out that the resulting behavior is almost as I would expect: the leds stay on for about one second, and this is great, but they stay off much longer, that is, for about 12 seconds. The interesting thing is that if I unroll the loop once:
while (1)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_14, GPIO_PIN_SET);
PAUSE_CIRCA_ONE_SECOND;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_14, GPIO_PIN_RESET);
PAUSE_CIRCA_ONE_SECOND;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_14, GPIO_PIN_SET);
PAUSE_CIRCA_ONE_SECOND;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0 | GPIO_PIN_7 | GPIO_PIN_14, GPIO_PIN_RESET);
PAUSE_CIRCA_ONE_SECOND;
}the leds always stay on for about one second, and stay off for about 12 seconds on the odd cycles (starting to count from 1) and for about 1 second on the even cycles. This behavior remains the same if I unroll the loop more than once. I would like to understand the reason of such a strange behavior.
I did some small investigations to exclude the most trivial causes. I don't think there is any difference between the many instances of the PAUSE_CIRCA_ONE_SECOND code: I disassembled the main() and compared them without finding any difference. Also, I used PC-relative offsets and cross-checked the branch target in the disassembled code to be sure that one instance does not jump into the other. The slow part is not the call to HAL_GPIO_WritePin: I debugged the code and setting/resetting the GPIO pins always takes a negligible amount of time when compared to PAUSE_CIRCA_ONE_SECOND.
Any explanation, any suggestion on how I can further investigate the causes, or any fix to the above code, is welcome.
Thank you in advance
Pietro Braione
