0

My code is written in C. I have an ISR (Interrupt Service Routine) that communicates with the main code using global variables. The ISR is in a different compilation unit from the main code. Is there any reason I cannot use "volatile" for the main code but leave it off in the ISR?

My reasoning is as follows: The volatile qualifier is preventing the compiler from fully optimizing the ISR. From the ISR's point of view the variable is not volatile - i.e. it cannot be externally changed for the duration of the ISR and the value does not need to be output for the duration of the ISR. Additionally, if the ISR is in its own compilation unit, the compiler MUST have the ISR read the global from memory before its first use and it MUST store changes back before returning. My reasoning for this is: Different compilation units need not be compiled at the same time so the compiler has no idea what is happening beyond the confines of the ISR (or it should pretend to) and so it must ensure that the global is read/written at the boundaries of the ISR.

Perhaps, I am misunderstanding the significance of compilation units? One reference that I found said that GCC has made this volatile mismatch a compile time error; I am not sure how it could, if they are in different compilation units, shouldn't they be independent? Can I not compile a library function separately and link it in later?

Nine ways to break your systems code using volatile

Perhaps an argument could be made from the concept of sequence points. I do not fully understand the concepts of sequence points or side effects; but, the C99 spec states in 5.1.2.3 paragraph 2: "... At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place."

Annex C, lists sequence points that include:

  • The call to a function, after the arguments have been evaluated.
  • Immediately before a library function returns.

Ref:WG14 Document: N1013, Date: 07-May-2003

Note: A previous question, Global Variable Access Relative to Function Calls and Returns asked whether globals are stored/written before/after function calls and and returns. But this is a different question which asks whether a global variable may be differently qualified as "volatile" in different compilation units. I used much of the same reasoning to justify my preliminary conclusions, which prompted some readers to think it is the same question.

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
PaulB
  • 128
  • 8
  • Possible duplicate of [Global Variable Access Relative to Function Calls and Returns](http://stackoverflow.com/questions/39416426/global-variable-access-relative-to-function-calls-and-returns) – tofro Sep 11 '16 at 15:02
  • That is also a question from me. This question is more specifically focused on the "volatile" keyword and whether it must always be "matched" in separate compilation units. While much of my rationale is similar, the question is different. I was hoping to get a definitive answer to the other question that could be used as a stepping stone to answering this question. But I have not yet gotten that answer - so I asked the follow on question. – PaulB Sep 11 '16 at 15:21
  • What is an ISR when it's at home? I'm not sure how much it matters, but it's not a term I've come across before, AFAICR. [AcronymFinder](https://acronymfinder.com/ISR.html) suggests it might be 'interrupt service routine'. – Jonathan Leffler Sep 11 '16 at 22:32

1 Answers1

0

ISO/IEC 9899:2011 (the C11 standard) says:

6.7.3 Type qualifiers

¶6 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.133)

133) This applies to those objects that behave as if they were defined with qualified types, even if they are never actually defined as objects in the program (such as an object at a memory-mapped input/output address).

The second sentence of ¶6 says that you invoke undefined behaviour if you have either of the organizations shown here:

File main.c                             File isr.c:

volatile int thingamyjig = 37;          extern int thingamyjig;         // V1

extern int thingamyjig;                 volatile int thingamyjig = 37;  // V2

In each case of V1 or V2, you run foul of the undefined behaviour specified in that section of the standard — though V1 is what I think you're describing in the question.

The volatile qualifier must be applied consistently:

File main.c                               File isr.c:

volatile int thingamyjig = 37;            extern volatile int thingamyjig;  // V3

extern volatile int thingamyjig;          volatile int thingamyjig = 37;    // V4

Both V3 and V4 preserve the volatile-qualifiers consistently.

Note that one valid manifestation of 'undefined behaviour' is 'it behaves sanely and as you would like it to'. Unfortunately, that is not the only, or necessarily the most plausible, possible manifestation of undefined behaviour. Don't risk it. Be self-consistent.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I think that perhaps the conflicts that you cite can only apply within a single compilation unit. I.e. by using a pointer that is not qualified as pointing to a volatile. If the conflict is in different compilation units the behavior would have to be defined because the compiler has no way to know that a conflict exists. – PaulB Sep 12 '16 at 20:02
  • My take is that the behaviour is undefined because the compiler has no way to know that the conflict exists. The code it generates in each TU will be consistent with the presence or absence of volatile-qualifiers; however, the linker will make both lots of code reference the same variable, and the result will be undefined behaviour (which may appear to work OK most of the time). However, I'm willing to let the matter stand there, until and unless you can find someone else to pontificate on the issue. – Jonathan Leffler Sep 12 '16 at 20:07
  • 1
    The phrase "undefined" is usually used to refer to the fact that the spec does not sufficiently constrain the compiler to know what it will do. That is not the case here (just as you state). In my question, I have put forth my reasoning for why the behavior is NOT undefined. I have asked for others to help me find flaws in my reasoning. You may be "willing to let it stand"; but you have not proven your point. The interaction between the main code and an ISR is known - not undefined. – PaulB Sep 14 '16 at 02:57