8.11.5 Coherent Variable Attribute

The coherent variable attribute causes the compiler/linker to place the variable into a unique section that is allocated to the kseg1 region, rather than the kseg0 region (which is the default on L1 cached devices). This means that the variable is accessed through an uncached address.

For devices featuring an L1 data cache, data variables are allocated to the kseg0 data-memory region (kseg0_data_mem), making it accessible through the L1 cache. Likewise, the linker-allocated heap and stack are allocated to the kseg0 region.

The coherent variable attribute that allows you to create a DMA buffer allocated to the kseg1_data_mem region:
unsigned int __attribute__((coherent)) buffer [1024];
When combining the coherent attribute with the address attribute, be sure to use the default data-memory region address for the device. On devices featuring an L1 data cache, the default data-memory region is kseg0_data_mem:
unsigned int __attribute__((coherent,address(0x80001000))) buffer[1024]
The __pic32_alloc_coherent(size_t) and __pic32_free_coherent(void *) functions allocate and free memory from the uncached kseg1_data_mem region. The default stack is allocated to the cached kseg0_data_mem region, but you may want to create an uncached DMA buffer, so you can use these functions to allocate an uncached buffer. These functions call the standard malloc()/free() functions, but the pointers that they use are translated from kseg0 to kseg1. For example:
#include<xc.h>
void jak(void) {
   char * buffer = __pic32_alloc_coherent(1024);
   if (buffer) {
     /* do somehing */
   } else {
     /* handle error */
   }
   if (buffer) {
     __pic32_free_coherent(buffer);
   }
}
Important: When allocating uncached storage in this way, it is the user's responsibility to ensure that the storage obtained will not share a cache line with other data items that may be cached. Failing to do so can cause data corruption.
To avoid having cached and uncached data on the same cache line, you can, for example, allocate 16 bytes (one cache line) more than required, then create a new pointer and assign it this address rounded up to the next 16 byte boundary. Use the new pointer to read/write data, and use the original pointer when you want to free the space. For example:
mytype *p1, *p2;

 p1 = __pic32_calloc_coherent (sizeof(mytype) + 16);
 uintptr_t addr = (uintptr_t)p1;
 uintptr_t aligned_addr = (addr + 15) & ~((uintptr_t)15);
 p2 = (mytype *)aligned_addr;