4

I'm writing a driver in petalinux for a device in my FPGA and I have implemented the mmap function in order to control the device in the user space. My problem is that, also if I'm using

vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

in the mmap function and MAP_SHARED flag in the user application, it seems that the cache is enabled.

The test I did is to write a value (say 5) to a specific register of my mmaped device that actually stores only the least significant bit of the data coming from the AXI bus. If I read immediately after the write operation, I expect to read 1 (this happened when using a bare metal application on Microblaze), instead I read 5. However, the value is correctly wrote in the register, because what has to happen....happens.

Thanks in advance.

arandomuser
  • 521
  • 1
  • 7
  • 22
  • 1
    How exactly are you accessing (write/read) the mmap'd region in your code? Specifically, can you provide a code snippet of how you're writing/reading the specific register you're talking about? – pah Aug 06 '16 at 18:15
  • In my application I'm using `address = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);` while when writing/reading from the mmaped region I'm using `*(unsigned int*)(address+REG_OFFSET) = 5` and `temp_var = *(unsigned int*)(address+REG_OFFSET)`. – arandomuser Aug 06 '16 at 18:38
  • 2
    is `address` declared as `volatile` ? – pah Aug 06 '16 at 18:39
  • uhm...actually it isn't....I will try in one hour... – arandomuser Aug 06 '16 at 18:41
  • 1
    also I recommend you to use `memcpy(&temp_var, address + REG_OFFSET, sizeof(unsigned int))`. You should also use `inttypes.h`, such as `uint32_t` (or the type that reflects the size of the register) instead of `unsigned int`. – pah Aug 06 '16 at 18:50
  • 1
    You should also rule out any latency problems (the time between you write and read the value, was it updated by the device in between?) – pah Aug 06 '16 at 18:58
  • Thank you for all your valuable suggestions...ok, `volatile` fixes the thing (and it was a big mistake from me...). But now I wonder why also using `memcpy` (without `volatile`) the code behaves correctly. Which is the difference between `temp=*(addr)` and `memcpy(&temp, addr,...)` ? – arandomuser Aug 06 '16 at 19:29
  • 1
    You should look at the assembly your compiler generates. You may find that the value was simply stored in a (CPU) register and no additional memory access happened. – Jonathon Reinhart Aug 06 '16 at 19:42
  • @Alessandro that (using memcpy() without `address` declared as `volatile`) is also prone to compiler optimizations and it's not granted to deliver the expected results. In your case, the compiler isn't performing optimizations over the `memcpy()`, that's why it's behaving as you expect. Using `volatile` basically will prevent the compiler from optimizing (or makes assumptions) over the contents of `address`, and that's what you really expect it to do. – pah Aug 06 '16 at 21:22

1 Answers1

2

Based on what was discussed in the question comments, the address pointer being assigned here:

address = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

wasn't declared with the type qualifier volatile, allowing the compiler to preform assumptions over it, leading to potential compile time optimizations over the read/write operations.

pah
  • 4,700
  • 6
  • 28
  • 37