Why are some parameters marked _IO

Just looking at the code for Delay:

void Delay(__IO uint32_t nTime)
    TimingDelay = nTime;

    while (TimingDelay != 0x00);

Can someone explain why the nTime parameter is qualified with__IO? This is defined as “volatile”.

I don’t see the point of marking a method parameter volatile? it’s on the stack so I can’t see how it can change?

I don’t know how smart the compiler is to see that it’s a stack allocated variable, but by marking this volatile the compiler is hindered from optimizing variable access.

Pinging our firmware experts to weigh-in when they’re available ( @zachary, @mohit, or @satishgn ) who wrote this stuff and could answer

Thanks @mdma. Seems very minor, but a good question. I’m confused by that one too—I can’t mentally translate that one to assembly and don’t understand exactly what effect the __IO qualifier will have other than generally preventing some inlining.

@satishgn I know that TimingDelay will be altered in an IRQ, but is there any reason we can’t remove the __IO qualifier on the nTime parameter? Also /cc @AndyW in case he has any thoughts.

I love seeking (and finding) answers. A little google searching later:


The __IO is just a macro defined in the STM32 Firmware libraries. Just right-click on it and select “Go to definition” under Ride7 and you will see that it just defines the variable as “volatile”.

The Delay() function from our examples is not properly calibrated. Its goal is only to wait for a given period of time, hence it just loops doing nothing a given number of times. You will have to manually calibrate it if you need some specific precision. However, the calibration will change if you recompile the application with a different optimization level, so you should not consider our Delay() function as accurate.

If precise delays are required, you should consider taking advantage of the the STM32 timers.

So then what’s volatile good for you ask? 1st and more importantly 2nd answer here:


Another use for volatile is signal handlers. If you have code like this:

quit = 0;
while (!quit)
     /* very small loop which is completely visible to the compiler */

The compiler is allowed to notice the loop body does not touch the quit variable and convert the loop to a while (true) loop. Even if the quit variable is set on the signal handler for SIGINT and SIGTERM; the compiler has no way to know that.

However, if the quit variable is declared volatile, the compiler is forced to load it every time, because it can be modified elsewhere. This is exactly what you want in this situation.

And that’s exactly what we want in our situation :wink:

Thanks for the analysis BDub, but I was referring to the nTime parameter being volatile, not the TimingDelay - that has to be volatile so the compiler is aware of external changes. (Otherwise it might just read from cache, or optimize away subsequent reads.) But it doesn’t make much sense making the function parameter volatile - it’s stored on the stack so not really going to change.

I was just wondering if there is some voodoo magic going on here or if it’s a simple oversight.

Hard to tell what exactly the compiler may do without checking specifically. Not all compilers are made equal. It’s probably just someone’s way at ST (I’m assuming it was a helper function from their code) to be extra sure nothing funny happens in this Delay() function. Might not seem like it matters the way it currently sits, but what if you implemented something a little differently in there for your count down. You’d want to make sure the compiler doesn’t try to optimize the nTime in any way.

I’ve run into this problem personally with timer registers being reordered. I’ll never forget to check stuff like that again.

Even if you removed __IO from nTime, and looked at the assembly code now and determined everything was fine… several changes later it might not be. Certainly doesn’t hurt to have it there :smile: Over-designing is always good if it’s cheap or free.