Запись в аппаратные регистры через /dev/mem требует дополнительной записи

Запись в аппаратные регистры через /dev/mem требует дополнительной записи

Я хочу записать в некоторые аппаратные регистры, используя /dev/mem в Linux. Целевая плата — ZYBO (Zynq, ARM Cortex-A9), а аппаратное обеспечение — AXI4 Lite Slave с 4 регистрами, которое автоматически генерируется Xilinx Vivado.

Вот код C для записи в регистры HW.

#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>

#define MAP_BASE        (0x43c30000)
#define MAP_RANGE       (0x10000)

int main(int argc, char *argv[])
{
        volatile unsigned int *p;
        void *iomap_ptr;
        int fd, i, v;

        fd = open("/dev/mem", O_RDWR | O_SYNC);
        iomap_ptr = mmap(0, MAP_RANGE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MAP_BASE);

        p = (volatile unsigned int *)iomap_ptr;
        for (i = 0; i < 4; i++)
                p[i] = i;
        for (i = 0; i < 4; i++)
                printf("%04X: %08X\n", i, p[i]);

        munmap(iomap_ptr, MAP_RANGE);
        close(fd);
        return 0;
}

Когда я запускаю код C в первый раз, результат такой:

0000: 00000000
0001: 00000001
0002: 00000002
0003: 00000000

Похоже, что финальная запись не применяется.

Затем, когда я снова запускаю код C, результат:

0000: 00000000
0001: 00000001
0002: 00000002
0003: 00000003

После расследования я понял, что мне нужна дополнительная запись в HW, чтобы применить последнюю запись.

Как я могу писать в регистры HW без дополнительной записи?


person TABATA    schedule 19.09.2016    source источник
comment
Вам нужно сбросить часть D-Cache? Xilinx должен иметь некоторые макросы/функции для аннулирования/очистки кеша для определенных диапазонов адресов.   -  person rjp    schedule 19.09.2016
comment
@rjp: Если адресное пространство не является общим / не кешируемым / строго упорядоченным (надеюсь, я ничего не пропустил), аппаратная конструкция нарушена. Проблемы с кэшем не должны возникать на периферийном регистровом пространстве.   -  person too honest for this site    schedule 19.09.2016
comment
@ Олаф, я не согласен. Я не работал на стороне Vivado, поэтому я не знаю, нужно ли это указывать и/или это что-то, что можно настроить неправильно.   -  person rjp    schedule 19.09.2016
comment
Примечание: не используйте стандартные целые типы для периферийных регистров и где еще вам нужны типы с фиксированной шириной. Используйте stdint.h типы. Также рекомендуется инкапсулировать полное определение указателя регистра в макрос. Это позволяет избежать ручного приведения каждый раз, когда вы используете этот указатель, который подвержен ошибкам.   -  person too honest for this site    schedule 19.09.2016
comment
Я удалил видео DMA, конвертер протоколов и интерконнекты из базовой системы Digilent. Затем, наконец, код C работал как-то правильно. Это странно... Я думаю, что базовый дизайн системы или мой дизайн действовал как FIFO. В любом случае спасибо всем за ответы.   -  person TABATA    schedule 20.09.2016


Ответы (1)


Предваряя это, я каким-то образом пропустил важную информацию в вопросе, поэтому содержание этого ответа гораздо менее портативно, чем должно быть, но я считаю, что он все же может решить проблему. Есть два связанных вопроса о SO, специфичных для Zynq и mmap:

Сбросить кеш в DRAM

Как получить записи через mmap отображал указатель памяти для немедленной очистки?

Перечисленные здесь функции кэширования Xilinx в конечном итоге используют тот же метод, что и в верхнем ответе на первый связанный вопрос.


Вероятно, это проблема кэширования. В этом конкретном случае вы можете использовать библиотеку Xilinx для очистки диапазона адресов, в котором находятся ваши регистры. Заголовочный файл xil_cache.h имеет две функции, которые можно использовать для этого:

void Xil_DCacheFlush(void); 
void Xil_DCacheFlushRange(unsigned int adr, unsigned len); 

В зависимости от версии у вас может быть третий вариант (с немного отличающимися прототипами):

void Xil_DCacheFlush(void);
void Xil_DCacheFlushRange(INTPTR adr, u32 len);
void Xil_DCacheFlushLine(INTPTR adr);

Первая функция, Xil_DCacheFlush, очистит весь кеш. Это нежелательное поведение, так как потенциально может быть много кэшированных данных, которые не имеют отношения к проблеме, которая очищается и становится недействительной, поэтому вы можете увидеть влияние на производительность.

Третья функция, Xil_DCacheFlushLine, если она доступна, скорее всего, будет работать в этом случае, так как у вас есть только четыре регистра. Это приведет к принудительной записи (если строка грязная), а затем сделает кеш недействительным, поэтому следующее чтение будет пропущено и должно быть извлечено из вашего регистра. Однако это не лучшее решение, поскольку оно основано на нескольких предположениях о вашем периферийном устройстве.

Вторая функция, Xil_DCacheFlushRange, возможно, вам нужна. С помощью этой функции вы можете указать диапазон адресов вашего периферийного устройства. В данном случае он небольшой, но это решение масштабируемо для сложных периферийных устройств с очень большими наборами регистров.

После того, как вы выполните запись регистра в периферийное устройство, вы сможете вызвать любую из вышеперечисленных функций, чтобы сбросить весь или часть кеша данных.

Также может быть способ отключить кэширование этого диапазона адресов в вашем дизайне, хотя мне трудно найти хорошую ссылку для этого, и у вас может не быть детализации в расположении/размере региона, который вы хотите.

person rjp    schedule 19.09.2016
comment
Если /dev/mem возвращает кешируемое отображение, вероятно, что-то пошло не так. - person Notlikethat; 19.09.2016
comment
Да, я как-то пропустил этот момент. Похоже, что у других людей была такая же проблема с Zynq: pointer-to-flush-immediately" title="как получить записи через указатель памяти с отображением mmap для немедленной очистки"> stackoverflow.com/questions/20750176/ - person rjp; 19.09.2016
comment
Чем больше я об этом думаю, тем меньше в этом смысла. Другие вопросы действительно связаны с проблемами кеша с mmap в Zynq, но в этом случае я не понимаю, почему записанное значение не вернется, даже если оно не попало в периферийные регистры, поскольку правильное значение должно быть в кеше. - person rjp; 19.09.2016
comment
Я не думаю, что на этот вопрос можно ответить. Мы не знаем ни о конкретном дизайне, ни о конкретном ядре и т.д. - person too honest for this site; 19.09.2016
comment
Конечно, если это некэшируемое, а не устройство, сопоставление, то оно все еще может быть буферизовано для записи, но тогда последующее чтение должно попасть в буфер записи. По сути, что касается ЦП, кэшей и межсоединений (т. е. архитектурной модели памяти), запись по адресу, за которой следует чтение того же адреса на одном и том же ЦП, всегда должна возвращать значение записи, так что, скорее всего, это проблема синхронизации с самим периферийным устройством. Лучшее, что вы можете сделать с помощью программного обеспечения, — это выдать DSB, чтобы принудительно завершить незавершенные операции записи. - person Notlikethat; 19.09.2016