volatile keyword usage in ISR function in micro-co

2019-09-22 14:26发布

As a general concept Global variable ("value") used in ISR function should be declared as volatile to avoid compiler optimization. but my doubt is a global variable is used in sub-function "ISR-SUB" which is invoked in ISR, whether the global variable used sub-function which is invoked in ISR also needs to be declared as volatile ?

unsigned int STATUS;  // -----> needs to be declared as volatile ?

void ISR-SUB()
{
  STATUS = 1; -->accessed in sub function invoked in ISR which will be optimized or not
}

void ISR ()
{
  ISR-SUB();
}

void main()
{

  /* interrupt occurred and ISR called */

  if (1 == STATUS)
  {
    code part
  }
}

3条回答
叛逆
2楼-- · 2019-09-22 15:17

Yes it does.

The function of volatile is to tell the compiler that the value of the variable may be read or written at any time without its knowledge. With a normal variable the compiler assumes that it has full information about how and when it is changed.

Consider this function to go with your ISR:

void normal_function()
{
    STATUS = 3;
    // ... some more code ...
    if (STATUS != 3)
        // do something
}

If STATUS is not marked volatile then the compiler is allowed to keep the value of STATUS in a register between the two statements that reference it or even to assume that the if statement will never trigger.

A more common pattern would be

while (STATUS != 1)
    // do something

You are expecting that the ISR will set STATUS to 1 and stop the while loop. However the compiler is allowed to read it once, keep that value in a register and never read it again. It may even only test it once.

The complementary problem is

void normal_function()
{
    STATUS = 3;
    while (!hell_frozen)
        // ... lots of code not involving STATUS...
    STATUS = STATUS + 1;
}

Here the compiler is allowed to not actually write the value of STATUS until the end of the function. So long as it keeps track of what its value should be it can put off writing to the memory location. If your ISR was waiting for STATUS to be 3 that would be a problem.

查看更多
来,给爷笑一个
3楼-- · 2019-09-22 15:18

Volatile :

this key word is used generally when we are working in embedded hardware system, because the declared variable may be not modified only by our C code but could be modified by our embedded hardware system

For example , if we choose to map the address of our variable to be a part of the address of our external embedded hardware system then we choose as option for our compiler(GCC for example) to make an optimization on the code. What is going to happen if we poll to the value of this variable not declared as volatile to check if it become 1 or not, in fact our compiler will see that we are checking the value of a variable that have always the same address, so it will copy the initial value of our non volatile variable into a temporary internal register to make easy the check and not to take each time the time to go reading the contain of our hardware address.

But what if our embedded hardware system modify the contain of this hardware variable address in this case we will never figure out because the compiler is reading from the temporary register and not the hardware address of our variable and here we figure out how much is important the key word volatile.

查看更多
Rolldiameter
4楼-- · 2019-09-22 15:29

Of course you do.
volatile isn't a prerogative of an ISR, it has a specific definition in the C11 standard:

An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects.
Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3.

Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously.
What constitutes an access to an object that has volatile-qualified type is implementation-defined.

So whenever you control flows in a manner that cannot be deduced from the sources (like when an interrupt occurs), the compiler cannot know that a variable may have changed meanwhile.
You have to use volatile to tell it that such variable is subject to change at any moment.


If that's too abstract, consider this toy code for the AVR microcontroller:

unsigned char STATUS;

void ISR_SUB()
{
  STATUS = 0x80;
}

void ISR ()
{
  ISR_SUB();
}

int main()
{
  unsigned char i=1;

  while (STATUS & 0x80)
  {
    STATUS |= i;
  }

  return 0;
}

This gets compiled into this assembly code

main:
        lds r24,STATUS             ;r24 = STATUS 
        sbrs r24,7                 ;Skip next inst if bit7 of r27 is set
        rjmp .L4                   ;Jump to the end
.L6:
        ori r24,lo8(1)             ;OR r24 with 1
        sbrc r24,7                 ;Do the test again, break loop if bit7 set
        rjmp .L6                   ;Jump back to the loop

        sts STATUS,r24             ;STATUS = r24
.L4:
        ldi r24,lo8(0)
        ldi r25,hi8(0)
        ret

As you can see, the variable STATUS is read once and updated in the register r24, so the loop will never end!
Now look at what happens when we use volatile

main:
        rjmp .L8
.L6:
        lds r24,STATUS           ;Now status is load in r24 at each iteration ...
        ori r24,lo8(1)           ;... updated and ...
        sts STATUS,r24           ;... stored back
.L8:
        lds r24,STATUS
        sbrc r24,7
        rjmp .L6
        ldi r24,lo8(0)
        ldi r25,hi8(0)
        ret

This time STATUS is read and updated at every iteration, as requested.

Note on synchronisation

Many thanks to @Olaf for pointing out the necessity of this section.

I made no claims above that volatile is a sufficient condition for whatever the OP is trying to implement (there is simply not enough context to made any claim).
The way this answer is to be interpreted is that volatile is a necessary condition (as the simple counter-example above shows).

The code shown, as said, is a toy example meant to show a simple issue that can arise without volatile.
It is not meant to be working code because actually I will not work.

When dealing with concurrent flows of execution synchronisation is mandatory and for C this can be achieved using the stdatomic.h header and functions.

Being this a uC question, stdatomic.h may not be present or no synchronisation may be required (this is rare).
Just to avoid any misunderstanding: if you have stdatomic.h then use it (it will eventually compile to nothing but it made the code portable).

The example above contains a RMW operation (the |=) that is not atomic can thus cancel the update made by the ISR.


So yes, you do need (at least) volatile.

查看更多
登录 后发表回答