1

I am new to embedded C, and I recently watched some videos about volatile qualifier. They all mention about the same things. The scenarios for the use of a volatile qualifier :

  1. when reading or writing a variable in ISR (interrupt service routine)
  2. RTOS application or multi thread (which is not my case)
  3. memory mapped IO (which is also not my case)

My question is that my code does not stuck in the whiletest();function below when my UART receives data and then triggers the void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) interrupt function

int test;

int main(void)
{
  test = 0;
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&ch, 1);

  while (1)
   {
        Delay(500);     
        printf("the main is runing\r\n");
        whiletest();
   }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)
    {
        test = 1;
        HAL_UART_Receive_IT(&huart1, (uint8_t *)&ch, 1);
    }
}

void whiletest(void)
{
int count =0;
while(!test){
 count++;
 printf("%d\r\n",count);
 Delay(2000);
 }
}

I use keil IDE and stm32cubeIDE. I learned that the compiler would optimize some instructions away if you choose the o2 or o3 optimization level. Therefore, I chose the o2 level for build option, but it seems no effect on my code. The compiler does not optimize the load instruction away in the while loop and cache the test value 0 in the main function as the videos teach on youtube. It is confusing. In what situation I am supposed to use volatile qualifier while keep my code optimized (o2 or o3 level).

note: I am using stm32h743zi (M7)

7
  • Test is not the same thing as test. So, is there more code here you're not showing? Commented Jun 13, 2021 at 15:03
  • Not all resources are of equal quality. I note in particular that in C and C++, volatile has no valid use specific to multithreaded programs, contrary to what some of the resources you examined seem to have told you. That's a somewhat common misconception. Commented Jun 13, 2021 at 15:35
  • 1
    As for "The compiler does not optimize the load instruction away", you would be well advised to write C code for which the C language defines the needed semantics, instead of relying on the compiler performing or avoiding specific optimizations, except to the extent that the language speaks to what kinds of optimizations can be performed (which is where volatile comes in). Where you need opcode-level control over the binary, you should be writing in assembly. Commented Jun 13, 2021 at 15:40
  • Please don't tag irrelevant languages. Commented Jun 13, 2021 at 15:46
  • You won the compiler lottery -- your compiler is more interested in being useful than scoring well on an artificial benchmark. Sadly, you shouldn't rely on this, as it may change with updates or build options. Commented Jun 13, 2021 at 17:08

2 Answers 2

4

volatile informs the compiler that object is side effects prone. It means that it can be changed by something which is not in the program execution path.

As you never call the interrupt routine directly compiler assumes that the test variable will never be 1. You need to tell him (volatile does it) that it may change anyway.

example:

volatile int test;

void interruptHandler(void)
{
    test = 1;
}

void foo(void)
{
    while(!test);
    LED_On();
}

Compiler knows that the test can be changed somehow and always read it in the while loop

foo:
        push    {r4, lr}
        ldr     r2, .L10
.L6:
        ldr     r3, [r2]     //compiler reads the value of the test from the memory as it knows that it can change.
        cmp     r3, #0
        beq     .L6
        bl      LED_On
        pop     {r4, lr}
        bx      lr
.L10:
        .word   .LANCHOR0
test:

Without the volatile compiler will assume that the test always will be zero.

foo:
        ldr     r3, .L10
        ldr     r3, [r3]
        cmp     r3, #0
        bne     .L6
.L7:
        b       .L7     //dead loop here
.L6: 
        push    {r4, lr}
        bl      LED_On
        pop     {r4, lr}
        bx      lr
.L10:
        .word   .LANCHOR0
test:

In your code you have to use volatile if the object is changed by something which is not in the program path.

Sign up to request clarification or add additional context in comments.

2 Comments

May i add to use a bool for these flag type variables.
I think you have misunderstood the question, which says that the while loop does exit
1

The compiler may only optimize (change) code if the optimized code behaves as if it the optimizer did nothing.

In your case you are calling two functions (Delay and printf) in your while loop. The compiler has no visibility of what these functions do since they appear in a separate compiler unit. The compiler therefore must assume they may change the value of the global variable test and therefore cannot optimize out the check for the value in test. Remove the function calls and the compiler may well optimize out the check for value of test.

2 Comments

Declaring static int test; will also localise the shared variable and allow the optimisation.
My way of trying to figure out the problem could be the culprit here ! Thank you for providing the insightful answer. I will look up the assembly code with delay() and printf() deleted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.