25

I have a volatile char * start_address; which is pointing to register sections (might change due to hardware behavior). I need to read it and I am using:

memcpy (
    result_p,            // starting address of destination
    start_address,       // starting address of source
    result_len           // for the length of the payload
);

I am getting this warning:

passing argument 2 of 'memcpy' discards 'volatile' qualifier from pointer target type

Is a safer way to read the sections or a better way to use memcpy and prevent this warning?

10
  • What's a typical value for result_len? Are the registers all 8-bits with no gaps in between registers? Personally, using memcpy would not be my first choice when dealing with hardware registers. But without more information about the hardware, it's difficult to answer your question. Commented Apr 19, 2016 at 21:01
  • Checking: You want to read multiple registers at once? Commented Apr 19, 2016 at 21:05
  • How do you synchronise this memory to avoid race conditions? And if you are sure it is synchronised, why not just cast away the volatile modifier? Commented Apr 19, 2016 at 21:12
  • In most cases you really don't want to use volatile. It is severely underspecified by the standard and it doesn't make things thread safe (although many people seem to believe that), it mostly just serves to inhibit the optimizer from doing clever things with your code. Are you sure you don't actually want an atomic variable? Commented Apr 19, 2016 at 21:18
  • 4
    @JesperJuhl: This is an embedded project and there is an acnowledged defect in the standard which luckily is not implemented by any implementation. Instead they implement how it is supposed to be. For hardware-registers, volatile is the correct qualifier. Commented Apr 20, 2016 at 1:08

3 Answers 3

27

memcpy is incompatible with volatile objects, and the mismatched pointer type in the function signature is helping point this out to you. memcpy may copy in any order, in any unit size, read parts of the source multiple times, write parts of the destination multiple times, etc. On the other hand, volatile expresses an intent that the sequence and number of accesses to the object must be exactly what they would be in the abstract machine. If you want to copy volatile arrays, you need to write your own copy loop that looks like a naive memcpy, and use the right volatile type for the pointer in your loop.

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

7 Comments

@jforberg depends of what is corrupt data in each case. You might not care that data changes while reading, but do care that each register is read once and just once. Depends on the hw implementation and on the meaning of the registers.
@jforberg: Race conditions are outside the scope of the question. A volatile object is allowed to (and will, for various hardware) produce a different result/behavior each time you read it; this is implementation-defined. But that's not the reason you can't use memcpy.
@atturri OK, that makes more sense. It seems I did misread the question. I see in the source for glibc that it can actually copy whole VM pages sometimes if the OS supports it. Which would probably not work terribly well if the VM page refers to memory mapped hardware.
@jforberg: The question is about hardware peripheral registers. This is a completely different beast than concurrent code execution by the CPU(s). For instance, such regions are typically strictly ordered/non-cachable.
@Hani_l: Well that's only valid if the MMIO registers or whatever the underlying volatile object is can safely be accessed as bytes (but in that case it should generally work). I've worked with hardware where byte IO either does nothing or horribly misbehaves, and 32-bit word IO is needed to work properly. So you really need to write a function that's appropriate to whatever you're accessing.
|
16

General advice is not to use memcpy for hardware peripheral registers or volatile qualified objects in general, even iff they occupy a gap-less memory region. They typically need a specific access pattern memcpy does not guarantee. This includes using optimised wider transfers, accessing the same location multiple times or changing the order of accesses.

For the reasons above and the following, don't even think about casting away the volatile qualifier! The compiler might very well optimize the call away (e.g. if you have two identical calls without changes to source nor destination area in-between), combine two accesses or move the call before/after other hardware accesses.

Instead write your own copy function/loop keeping the qualifier. That will force the compiler to generate code which exactly does what you want. Remember to use the correct type for the copy pointers. Note also that the standard integer types are not a good choice for hardware registers of a specific size. Use the fixed-width types from stdint.h like uint8_t, uint16_t, ... instead.

2 Comments

"if you copy the same source to the same destination" This isn't allowed by memcpy, the parameters are restrict pointers. You'll have to use memmove for that.
@Lundin: Sorry, that was badly worded. I hope the edit make more clear what I actually meant.
6

In C++, you could use std::copy. It will take any type of (input) iterator, and a pointer to volatile is a perfectly valid input iterator.

1 Comment

it is written all in C (C++ tag was added by stack-overflow suggestions, I am not even sure when!)

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.