Так получилось, что коллеги топикстартера вышли на меня и попросили глянуть этот кейс на тех платах, на которых я работаю.
Я посмотрел - действительно, воспроизводится - результат аналогичный (видны пропуски).
Для исключения ошибки в FPGA коде я написал свой, который делает аналогичные действия, но пишет шириной данных по 32 бита (вместо 128, как было у автора). Результат тоже аналогичный - видны пропуски, хотя я писал и через интерфейсы fpga2hps и через fpga2sdram.
Если открыть Cyclone V Hard Processor System Technical Reference Manual Figure 1-2: HPS Block Diagram, то видно, что при использовании этих интерфейсов поток данных идет в обход процессора ARM (MPU Subsystem), в том числе его кэша L2 (64 КБ). Следовательно, когда программа в юзерспейсе просит процессор прочитать данные из DDR-памяти, то если данные остались в кэше, то он вычитает из кэша.
Эта идея подтвердилась тем, что в той же самой утилите перед запуском DMA-транзакции сделали фейковую обработку данных вида:
Код
#define BIG_ARR_SIZE 1000000
uint32_t* big_arr;
big_arr = malloc( BIG_ARR_SIZE * sizeof( uint32_t ) );
if( big_arr == NULL ) {
printf("big_arr: can't alloc\n");
exit( -1 );
}
int i;
uint32_t sum = 0;
for( i = 0; i < BIG_ARR_SIZE; i++ ) {
big_arr[i] = rand();
}
for( i = 0; i < BIG_ARR_SIZE; i++ ) {
sum += big_arr[i];
}
А после DMA-транзакции делали печать sum (для того, чтобы ничего не соптимизировалось).
Идея этой фейковой обработки в том, что данные из кэша которые принадлежали той области памяти, куда будет писать FPGA, сбросятся в DDR.
А когда процессор попросит даные из этой памяти, то он будет обязан взять их из DDR-памяти - т.е. самые новые.
На железе было попробовано - действительно пропуски пропали.
Хак с фейковыми расчетами никуда не годится в реальных задачах: при выделении памяти под DMA вы должны гарантировать, что когда процессор
будет их забирать, он возьмет самые актуальные.
Самый правильный и классический путь - это писать драйвер, который будет заниматься выделением такой памяти (а так же обрабатывать прерывания, если необходимо).
Например, в драйвере aclsoc (Altera OpenCL) для этого делают так:
Код
kalloc_memory = dma_alloc_coherent(NULL, allocated_size, &dma_handle, GFP_KERNEL);
if (kalloc_memory == NULL) {
return -ENOMEM;
}
// kmalloc returns "kernel logical addresses".
// __pa() maps "kernel logical addresses" to "physical addresses".
// remap_pfn_range maps "physical addresses" to "user virtual addresses".
// kernel logical addresses are usually just physical addresses with an offset.
// Make the pages uncache-able. Otherwise, will run into consistency issues.
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (remap_pfn_range(vma, vma->vm_start,
dma_handle >> PAGE_SHIFT,
size,
vma->vm_page_prot) < 0) {
return -EAGAIN;
}
В конце статьи про DMA (
http://habrahabr.ru/company/metrotek/blog/248145/) Денис (des333) привел пример драйвера, который делает подобное. Можно воспользоваться им, либо написать какой-то свой.