tags:

views:

73

answers:

3

I find myself wanting to write a routine that will operate on both volatile and non-volatile memory blocks. Something along the lines of:

void byte_swap (unsigned * block_start, size_t amount) {
    for (unsigned * lp = block_start; lp < block_start + amount; lp++) {
        *lp = htonl(*lp);
    }
    memcpy (block_start, some_other_address, amount);
}

(Not my real code, but an example).

The problem I have is that if I try to use a pointer to a volatile memory area, the compiler complains about losing the volatile qualifier. I could cast away volatile, but it seems like that might make the routine itself unsafe if it tries to cache the changes (or previous reads), would it not?

The other option would be to make the routine itself take unsigned volatile *, but that would require me to convert all the non-volatile callers to volatile pointers.

I suppose a third option would be to make two exact duplicates of the routine, differing only in whether the volatile keyword appears. That sucks.

Is there a Right Way to handle this, and if so what is it?

As a mostly related question, are predefined routines (specifically memcpy) safe to use with volatile pointers? What bad things could happen to me if I did? It seems kinda silly to have to reroll any memory-related library routine myself simply because it doesn't use volatile void pointers.

A: 

You might consider using a template:

template <typename PointerT>
void byte_swap (PointerT block_start, size_t amount) {
    for (PointerT lp = block_start; lp < block_start + amount; lp++) {
        *lp = htonl(*lp);
    }
}

For safety, you might add a static assert of some sort to verify that PointerT is a pointer to a (potentially volatile-qualified) unsigned.

James McNellis
+1  A: 

Although the code might be pessimistic for non-volatile objects (although given your function, probably not in this case) you should be able to code for volatile unsigned* and pass in unsigned*. Just like const you can add a volatile qualifier without an explicit (and potentially dangerous) cast.

You shouldn't have to change all your pointers to volatile, your non-volatile callers can stay as they are.

Although memcpy isn't defined to use volatile memory, because of its nature (it reads memory from one place once and writes it once somewhere else) the class of problematic optimizations that volatile inhibits don't apply. You're likely to be able to cast away volatile and use memcpy without any issues (other than those you already have with volatile areas of memory).

Charles Bailey
That does seem to be the case. The only issue I have is that with the code I'm working with, there are quite a few layers that will have pointers that need to be changed (including some memcpy calls, which I obviously **can't** change). Still, it beats having to make templates out of them all...
T.E.D.
+1  A: 

You only need to use volatile when you are dealing with memory mapped IO, that is where successive reads or writes to the same memory address can and should return different values. If you are writing to what amounts to simply shared memory, a simple formal handover mechanism which allocates access the either the CPU or the hardware device (such as a DMA controller) should suffice.

Also be aware that performing operations on non-cached memory or, even worse, a memory mapped device will be really slow. You may well be far better off copying to normal uncached memory before performing any byte swapping operations. When copying large amounts of data to memory mapped IO, DMA can safe you a large number of CPU cycles.

volatile tends to act as a barrier to compiler optimization and should be avoided where necessary.

doron
Very good info. In my case the `volatile` area does indeed happen to be used for memory-mapped IO. However, its not the device's register area, but its data buffer. So it should work more or less like normal memory.
T.E.D.
Hmmm. Just read your second paragraph more thouroughly. Actually, the memory I have is local RAM (Contiguous and uncached, I believe) that is only DMAed to/from the device upon my request. It is probably really only `volatile` in the sense that when I tell it to, the hardware can read it or write it.
T.E.D.
Sounds like you do not need to use volatile. The main use is when you are using things like hardware FIFOs in say a UART.
doron
Accepting this one, as much for the advice in the comment as anything else. I asked around offline too, and got mostly the same advice. So I removed volatile from everything save the memory-mapped hardware registers.
T.E.D.