Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема с DMA. Неправильные физические адреса буфера запроса.  (Прочитано 13891 раз)
0 Пользователей и 1 Гость смотрят эту тему.
NeStor46
Интересующийся

ru
Offline Offline

« : 10-08-2014 13:28 » new

Здравствуйте.

При написании WDF драйвера под 64-разрядную Windows 7 для платы PCI Express немного запутался, когда дело дошло до организации чтения/записи через dma. Обо всем по порядку.
Моё устройство (ПЛИС с PCI Express Hard IP) сконфигурировано следующим образом: BAR0 (64 bit Prefetchable, т.е. объединен с BAR1 в единый 64-битный prefetchable BAR), BAR0 имеет размер в 128 МБайт (0x8000000), в него необходимо писать данные; и 32-битный BAR2, размером 32 КБайта, в котором находятся управляющие регистры. Имеется компонента mSGDMA (описание его есть в прикрепленном мануале), с помощью которой я пытаюсь осуществлять передачу данных между пользовательским приложением и устройством? её управляющие регистры находятся в BAR2.

Теперь немного о проблеме. При создании DMA Enabler задаю профиль для устройства WdfDmaProfileScatterGather, т.е. 32-разрядное устройство с поддержкой ScatterGather.  При получении запросов на чтение или запись для инициализации транзакции пользуюсь процедурой WdfDmaTransactionInitializeUsingRequest, а она в свою очередь вызывает EvtProgramReadDma, где настраивается устройство и запускается передача. Здесь и начинаются проблемы.

 
Код:
...
//инициализация транзакции
status = WdfDmaTransactionInitializeUsingRequest(
                                           devExt->WriteDmaTransaction,
                                           Request,           //запрос от пользовательского приложения
                                           PLxEvtProgramWriteDma,
                                           WdfDmaDirectionReadFromDevice );
...

//код EvtProgramReadDma
BOOLEAN
EvtProgramReadDma(
    IN  WDFDMATRANSACTION       Transaction,
    IN  WDFDEVICE               Device,
    IN  WDFCONTEXT              Context,
    IN  WDF_DMA_DIRECTION       Direction,
    IN  PSCATTER_GATHER_LIST    SgList
    )

{
    PDEVICE_EXTENSION        devExt;
    size_t                   offset;
    ULONG                    i;

    UNREFERENCED_PARAMETER( Context );
    UNREFERENCED_PARAMETER( Direction );


    devExt = PLxGetDeviceContext(Device);

    offset = WdfDmaTransactionGetBytesTransferred(Transaction);

    WdfInterruptAcquireLock( devExt->Interrupt );
...

//заполняем таблицу дескпипторов
for (i=0; i < SgList->NumberOfElements; i++) {

        WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->ReadAddr, offset);

WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->WriteAddr, SgList->Elements[i].Address.LowPart);
 
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->Length, SgList->Elements[i].Length);
...
   }

    WdfInterruptReleaseLock( devExt->Interrupt );

    return TRUE;
}


Насколько я понимаю драйвер при вызове WdfDmaTransactionInitializeUsingRequest формирует из Request список Scatter/Gather, который передается в EvtProgramReadDma. Этот список должен содержать пары адрес/длина, указывающие на страницы физической памяти, соответствующие буферу запроса от пользовательского приложения. Т.е. при записи в эти участки памяти должен заполнятся и пользовательский буфер (вроде бы так).

Но чтение или запись по этим адресам никак не меняют этот самый буфер. Будто эти соответствуют не ему, а вообще неизвестно чему. При запросах записи в устройство по данным адресам абсолютно пусто, при операциях чтения из устройства по этим адресам данные записываются, но буфер чтения в приложении остается пуст. Есть какие-нибудь предположения, что с этим делать? Никто с таким не сталкивался?

P.S. При операциях чтения или записи внутри адресного пространства BAR0 все проходит нормально. Задаю адреса чтения и записи просто смещением внутри BAR0 и всё работает. Пробовал писать по адресу общего буфера, выделенного под таблиц дескрипторов - тоже пишет, читает. А вот с буфером от приложения проблема.

Вопросов еще куча, но думаю выливать всё сразу будет некрасиво с моей стороны. Очень многое хочется обсудить, чтобы понять верно ли я понимаю некоторые аспекты, или заблуждаюсь. Буду благодарен за любую помощь.

* Modular SGDMA Dispatcher Core.pdf (361.76 Кб - загружено 5958 раз.)
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 10-08-2014 20:34 » 

не совсем понял фразу " с буфером из приложения - проблема".
то есть с таким же буфером, выделенным в драйвере - все работает?
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
NeStor46
Интересующийся

ru
Offline Offline

« Ответ #2 : 10-08-2014 22:04 » 

Заранее простите за много букв.
Пока успел попробовать с писать/читать с общим буфером, в который обычно заносят дескрипторы - вот с ним все работает (его же можно считать выделенным в драйвере?). Насчет буфера из приложения. В пользовательской программе создается 2 буфера - один для запросов за чтения из устройства, другой для записи в устройство.  Так как метод доступа в память выбран прямой, то при запросах Request, приходящий в драйвер должен содержать адрес списка MDL, который описывает буфер запроса. Это я имел в виду под буфером из приложения.
Дальше драйвер, по идее, используя имеющийся в запросе MDL должен вернуть списки физических страниц, соответствующих этому буферу. По идее же при записи по этим страницам должен же записываться и буфер в приложении? Мне кажется, что я ничего и не объяснил...

Думаю стоит попробовать выделить непрерывный буфер в драйвере и связать это как-то с MDL из запроса, т.е. подсунуть приложению? Но пока не знаю как это сделать, если это вообще возможно.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 11-08-2014 16:18 » 

тут народ сталкивался с особенностью у некоторых контроллеров - не все физические адреса доступны были.
по коду:
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->ReadAddr, offset);
- offset у вас вроде как размер а не адрес. видимо просто название ReadAddr неудачное?

Добавлено через 1 минуту и 2 секунды:
и собственно главный вопрос... или я чего то не совсем понимаю.. ну вот вы заполнили в железке список адресов... а откуда она знает что уже пора стартовать и писать/читать по ним? или там пропущено много кода?
« Последнее редактирование: 11-08-2014 16:20 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
NeStor46
Интересующийся

ru
Offline Offline

« Ответ #4 : 11-08-2014 17:48 » 

Часть кода тут пропущена, вот полностью функция
Код:
[code]

BOOLEAN
EvtProgramReadDma(
    IN  WDFDMATRANSACTION       Transaction,
    IN  WDFDEVICE               Device,
    IN  WDFCONTEXT              Context,
    IN  WDF_DMA_DIRECTION       Direction,
    IN  PSCATTER_GATHER_LIST    SgList
    )

{
    PDEVICE_EXTENSION        devExt;
    size_t                   offset;
    ULONG                    i;

    UNREFERENCED_PARAMETER( Context );
    UNREFERENCED_PARAMETER( Direction );


    devExt = PLxGetDeviceContext(Device);

    WdfInterruptAcquireLock( devExt->Interrupt );


//временно останавляваю диспетчер
{
        union {
            DISP_CONTROL  bits;
            ULONG     ulong;
        } DispCtrl;

        DispCtrl.ulong =
            READ_REGISTER_ULONG( (PULONG) &devExt->Regs->ControlDisp );

             DispCtrl.bits.DispStop = TRUE;

        WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->ControlDisp,
                              DispCtrl.ulong );
    }

//заполняем таблицу дескпипторов
for (i=0; i < SgList->NumberOfElements; i++) {

        WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->ReadAddr, 0х7000000);

WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->WriteAddr, SgList->Elements[i].Address.LowPart);
 
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->Length, SgList->Elements[i].Length);


{
        union {
            DESC_CONTROL  bits;
            ULONG     ulong;
        } DescCtrl;

        DescCtrl.ulong =
            READ_REGISTER_ULONG( (PULONG) &devExt->Regs->ControlDesc );
//последний пакет
if (i==SgList->NumberOfElements-1)
{
DescCtrl.bits.TransferCOMP_IRQ = TRUE;
}
else
{
DescCtrl.bits.DoneEarly = TRUE;
}
          //при выставлении бита Go данные скидываются в дескрипторное FIFO
DescCtrl.bits.Go = TRUE;

        WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->ControlDesc,
                              DescCtrl.ulong );
    }

       
}

   //запускаю диспетчер
{
        union {
            DISP_CONTROL  bits;
            ULONG     ulong;
        } DispCtrl;

        DispCtrl.ulong =
            READ_REGISTER_ULONG( (PULONG) &devExt->Regs->ControlDisp );

             DispCtrl.bits.DispStop = FALSE;

        WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->ControlDisp,
                              DispCtrl.ulong );
    }


    WdfInterruptReleaseLock( devExt->Interrupt );


    return TRUE;
}

[/code]

Насколько я понимаю, что как только что-то появляется в фифо (бит Descriptor Buffer Empty обнуляется), то и передача запускается. Но на всякий случай останавливаю диспетчер, заполняю дескрипторы и обратно запускаю.

В offset заносится количество уже переданных битов. Извините, я немного изменил уже тот кусочек кода, теперь там смещение внутри BAR0. (так же и должно быть, будто система понимает, что начальные адреса относятся именно к устройству? т.е. задавая адрес 0х7000000, читаются или записываются данные расположенные по смещению 0х7000000 внутри платы.).

У меня тоже есть подозрения, что контроллеру не доступны данные физические адреса, но есть одна особенность. Сегодня с помощью MmAllocateContiguousMemory выделял буфер, устраивал обмен данными между BAR0 устройства и этим буфером - работает. И самое интересное, что физические адреса из построенного системой scatter\gather списка и физические адреса выделенного с помощью MmAllocateContiguousMemory буфера примерно равны, т.е. не скажешь, что между ними такая большая разница, что по одним (буфер выделенный с помощью MmAllocateContiguousMemory) запись и чтение идет нормально, а по другим (сформированный инфраструктурой из запроса scatter\gather список ) пустота и не пишет. Будто страницы не те). И вот то ли реально физические адреса не те, либо контроллер не может с ними работать (но это в этом сомневаюсь).

Стоит попробовать поставить ограничение на адрес, но как это сделать? MmAllocateContiguousMemory выделяет буфер с заданными ограничениями,  но как связать его с запросом?


Если делать так, то все работает, но к DMA это не имеет никакого отношения.
Код:
[code]

//получает буфер вывода запроса ввода-вывода.
status = WdfRequestRetrieveOutputBuffer( Request,
                                                    (size_t)Length,
                                                    &configurationDescriptor,
                                                    NULL );

//копируем данные из BAR0 в буфер вывода
status = WdfMemoryCopyToBuffer( outputBuff,
                                       0,
                                       AddrBAR0,
                                       Length,  );

[/code]

Попробовать выделить так буфер, а потом с помощью IoAllocateMdl и MmBuildMdlForNonPagedPool получить его MDL, а потом с полученным MDL, соответствующим этому буферу вызывать BuildScatterGatherList или GetScatterGatherList? Но это вроде бред какой-то)) (вопрос не в тему: почему написано, что вы ответили в 16:18, а я увидел это только в 20:18? у нас 4 часа разницы? )
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 18-08-2014 08:45 » 

В offset заносится количество уже переданных битов.
-наверное байтов, все таки)

насчет адресов - сравните побитно адреса. если различаются старшим битом - стоит задуматься и потыкать в этом направлении. тут были случаи что контроллеры поддерживали не 32 адресацию а меньше.(это было для PCI но мало ли)

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


PS у меня ответ « Ответ #3 : Вчера в 20:18:43 »
Профиль->внешний вид форума->Часовой пояс
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
NeStor46
Интересующийся

ru
Offline Offline

« Ответ #6 : 18-08-2014 13:30 » 

Байтов, конечно)

При инициализации указано, что плата работает с 32 битной адресацией; страшная часть адреса, возвращаемого системой равна 0 постоянно ( SgList->Elements.Address.HighPart=0 ).

Адреса сравню еще раз (но позже, пока в отпуске), но насколько помню, они начинались все с 0xBD******. Что у буфера, выделенного с помощью MmAllocateContiguousMemory, что у страниц из SGL. Кстати, SgList->Elements.Address.LowPart больше, чем в половине случаев содержит один и тот же адрес (т.е. сделал я запрос на запись в устройство - Address.LowPart=0xBDВCA290 (допустим), делаю запрос в другой раз -  опять Address.LowPart=0xBDВCA290, но иногда и другое значение бывает). Да и странный порядок следования страниц этих, адреса по убывающей. Получается, что система заранее резервирует регистры отображения, а потом уже по мере необходимости использует их.

Нашел тут что-то на альтеровском форуме http://www.alteraforum.com/forum/showthread.php?t=28167&p=113189#post113189 . Парень адрес получателя задает Destination = pcieaddresstranslated, где pciaddresstranslated is a PCIe Host address, where I want to transfer to. It is correctly masked with the Page Size of the Translation table, and combined with the Index number of the table entry I want to translate. I have Address Translation working properly with the Simple DMA.
Похоже речь идет о Avalon-MM-to-PCI Express Address Translation Table ( http://www.altera.com/literature/ug/ug_c5_pcie_avmm.pdf стр.80). Но с ней толком не разобрался, какие адреса туда писать и где их потом забирать (или же ядро само понимает что к чему там).

Спасибо, что откликаетесь) Посижу, подумаю, отпишусь через пару дней.

Кстати, пробовал задавать ограничения на количество регистров отображения через WdfDmaEnablerSetMaximumScatterGatherElements  ( http://msdn.microsoft.com/en-us/library/windows/hardware/ff547014(v=vs.85).aspx ), так порой по адресам из SGL не только пустота была, но и какие-то данные (но не мой).
Записан
NeStor46
Интересующийся

ru
Offline Offline

« Ответ #7 : 21-08-2014 20:21 » 

Ochkarik, хм. Интересную штуку мне сегодня рассказали о моей плате. Оказывается, что dma там подцеплен только на чтение (впрочем проблема с dma всё еще висит).
Запись в устройство, похоже, необходимо будет производить через WdfMemoryCopyToBuffer или подобные ей процедуры. Возник вопрос. А можно ли заставить запись работать быстрее? На данный момент реализовал вариант с использованием WRITE_REGISTER_BUFFER_ULONG64 (команду MmMapIoSpace вызываю с параметром CacheEnable = MmWriteCombined). Запись при этом работает как пакетная - собирает 4 слова по 32 бита и записывает. Работает быстрее.

А можно ли еще ускорить запись? Нашел тут, что люди использовали SSE и достигали скоростей в 2 раза больше, чем при использовании WRITE_REGISTER_BUFFER_ULONG, но как это сделать не нашел. Или же то, что процессор итак уже собирает 4 слова, а потом уже записывает - есть использование этих регистров? Как их использовать-то, не подскажите?

P.S. Не надо же создавать отдельную тему для этого вопроса?
« Последнее редактирование: 21-08-2014 20:27 от NeStor46 » Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 21-08-2014 20:48 » 

memcpy(), выравнивать структуры на размер кэша и т.д. memcpy внутри довольно хитрый.
как-то давно видел ассемблерную реализацию memcpy от AMD. кажется как раз через через mmx/sse. кстати может быть на форуме где то осталось)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
NeStor46
Интересующийся

ru
Offline Offline

« Ответ #9 : 21-08-2014 22:50 » 

Ochkarik, нашел пока только вот эту тему - https://forum.shelek.ru/index.php/topic,16959.0.html . Там через MMX реализовано)
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 22-08-2014 06:50 » 

ага, оно)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
NeStor46
Интересующийся

ru
Offline Offline

« Ответ #11 : 02-09-2014 19:36 » 

Ochkarik, пока не уверен, но похоже проблема с DMA заключает именно в том, что плата не может работать с 32-битными адресами. Что-то совсем в край запутался.

Если с записью просто и, вроде, понятно. BAR0 подрублен на память платы OnChip http://itmages.ru/image/view/1894192/b6a7b059 и записывать данные можно просто через WRITE_REGISTER_BUFFER_ULONG64, то вот чтение так работает очень очень очень медленно. Для сравнения скорость записи ~750 MB/s, скорость чтения ~15 MB/s.

Чтение надо через DMA. В контроллер записываю адрес откуда читать (OnChip платы) и адрес (32-битный) куда записать прочитанные данные (буфер в памяти ПК).
Контроллер DMA считывает данные, а потом отправляет их на Txs (судя по схеме соединения в плате http://itmages.ru/image/view/1894192/b6a7b059 ), откуда они по ходу и должны попасть в память ПК, но txs_address имеет разрядность 25 бит, получается, что он отрезает старшие разряды начального 32-битного адреса и отправляет данные неизвестно куда. Или же я неправильно понимаю весь механизм передачи данных.
Записан
NeStor46
Интересующийся

ru
Offline Offline

« Ответ #12 : 17-09-2014 07:28 » 

С адресами разобрался, тему можно закрывать. На самом деле все до безумия просто. Старшую часть адреса надо заносить в таблицу переадресации, а контроллеру давать только младшие биты.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines