Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: PCI-E и mSGDMA от Altera
Форум разработчиков электроники ELECTRONIX.ru > Интерфейсы > Форумы по интерфейсам > ISA/PCI/PCI-X/PCI Express
NeStor46
Доброго времени суток всем!

Наверное, кто-то разбирался с альтровским mSGDMA и сможет помочь немного разобраться.
При написании драйвера под Windows для платы PCI-E дошел до организации транзакций чтения и записи с использованием mSGDMA и сел. Немного не понятно, как работает этот самый mSGDMA с конфигурацией Memory-Mapped to Memory-Mapped.

Допустим в драйвер поступил запрос на чтение. Выполнив необходимые программные действия дело доходит до программирования устройства и собственно нашего mSGDMA. Заносить в дескрипторы необходимо адреса и длину элементов SGL(scatter-gather list), сформированного ОС, т.е. большое число буферов малой длины (4кБ), или же просто записать в дескриптор mSGDMA адрес откуда читать и длину читаемых данных, а дальше он сам разберется что и как ему делать?
Запуск dma передачи начинается автоматически, как только данные из дескриптора передаются в дескрипторное FIFO при установке контрольного бита Go? а как только передача выполняется выставляется прерывание, которое сообщает о выполнении операции? и нужно ли что-то настраивать, чтобы эти прерывания генерировались?

Пока нашел только пример драйвера для Linux, описывающий работу с этим mSGDMA, но еще не совсем в нем разобрался. Здесь есть и описание управляющего регистра дескриптора для разных видов передачи http://lxr.free-electrons.com/source/drive...tera_msgdmahw.h , но не знаю применимы для они для dma в pci-e.

Откликнитесь, кто-нибудь, пожалуйста. Вопросов, пока много, а понимания мало. Буду благодарен за любую консультацию.
doom13
Разбирался с работой Sg-DMA для отправки данных по Ethernet с использованием TSE. В моём случае использовался Sg-DMA сконфигурированный как Memory To Stream, но принцип тот же (без использования ОС).
Цитата
Заносить в дескрипторы необходимо адреса и длину элементов SGL(scatter-gather list), сформированного ОС, т.е. большое число буферов малой длины (4кБ), или же просто записать в дескриптор mSGDMA адрес откуда читать и длину читаемых данных, а дальше он сам разберется что и как ему делать?

Формируете дескриптор (src addr, dst addr, length, флаги нужные выставляете) и подсовываете его Sg-DMA, далее делаете другую работу, пока не придёт прерывание о завершениеи обработки дескриптора. Можно выстроить цепочку дескрипторов.
Цитата
Запуск dma передачи начинается автоматически, как только данные из дескриптора передаются в дескрипторное FIFO при установке контрольного бита Go? а как только передача выполняется выставляется прерывание, которое сообщает о выполнении операции?

Использовал функции из либы для Sg-DMA (alt_avalon_sgdma_do_sync_transfer, alt_avalon_sgdma_do_async_transfer), они по сути и устанавливают этот бит. После передачи/приёма можно настроить прерывание.
Цитата
и нужно ли что-то настраивать, чтобы эти прерывания генерировались?

Настраивать нужно, используется функция alt_avalon_sgdma_register_callback (если ОС будете использовать, возможно, там что-то по-другому), через которую задаётся обработчик прерывания и флаги, по какому событию оно будет срабатывать.
dsmv
Цитата(NeStor46 @ Aug 4 2014, 23:47) *
... т.е. большое число буферов малой длины (4кБ), или же просто записать в дескриптор mSGDMA адрес откуда читать и длину читаемых данных, а дальше он сам разберется что и как ему делать?

DMA канал работает с физической памятью, т.е. надо ему указать цепочку из блоков по 4 кБайта.


NeStor46
Цитата(dsmv @ Aug 5 2014, 12:54) *
DMA канал работает с физической памятью, т.е. надо ему указать цепочку из блоков по 4 кБайта.

Спасибо, т.е. записывать в дескрипторы элементы SGL, сформированные виндой, пока не заполнится дескрипторные фифо, потом заново заполнять дескрипторы и т.д. до тех пор, пока не будут переданы все данные.

Цитата(doom13 @ Aug 5 2014, 01:39) *
Формируете дескриптор (src addr, dst addr, length, флаги нужные выставляете) и подсовываете его Sg-DMA, далее делаете другую работу, пока не придёт прерывание о завершениеи обработки дескриптора. Можно выстроить цепочку дескрипторов.


Спасибо за разъяснение. в dst addr я так понимаю необходимо записывать адрес выделенного пользовательским приложением буфера (приложение, от которого шел запрос на чтение).
doom13
Цитата(NeStor46 @ Aug 5 2014, 21:22) *
Спасибо за разъяснение. в dst addr я так понимаю необходимо записывать адрес выделенного пользовательским приложением буфера (приложение, от которого шел запрос на чтение).

Да, первый адрес - откуда читать, второй - куда писать. В этой теме был прмер по настройке и работе с Sg-DMA, правда, для Stream To Memory и Memory To Stream, можете ознакомиться.
NeStor46
Цитата(doom13 @ Aug 6 2014, 00:19) *

спасибо а помощь)
NeStor46
Никак не получается запустить mSGDMA (modular SGDMA (мануал прикреплен ниже)). Написал следующий код, который по идее должен стартовать DMA транзакцию, но ничего не выходит.

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);

offset = WdfDmaTransactionGetBytesTransferred(Transaction);

WdfInterruptAcquireLock( devExt->Interrupt );

for (i=0; i < SgList->NumberOfElements; i++) {

//физический адрес начала памяти BAR0, полученный при инициализации устройства. Отсюда надо читать
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->ReadAddrLow, &devExt->ReadAddr.LowPart); //Read Address[31..0]
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->ReadAddrHigh, &devExt->ReadAddr.HighPart); //Read Address[63..32]
//физические адреса страниц, сооставляющих выделенный пользовательским приложением буффер. Сюда необходимо передать
//сформированный виндой Scatter/Gather List, соответствующий запросу от приложения
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->WriteAddrLow, SgList->Elements[i].Address.LowPart); //Write Address[31..0]
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->WriteAddrHigh, gList->Elements[i].Address.HighPart); //Write Address[63..32]
//длина каждой передаваемой страницы
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->Length, SgList->Elements[i].Length); //Length[31..0]

WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->Burst_SeqNumber, 0);
WRITE_REGISTER_ULONG ((PULONG) &devExt->Regs->Stride, 0x10001); //Write Stride[15..0], Read Stride[15..0]

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

DescCtrl.ulong =
READ_REGISTER_ULONG( (PULONG) &devExt->Regs->ControlDesc );
//первый пакет
if (i==0)
{
DescCtrl.bits.SOP = TRUE;
}
//последний пакет
else if (i==SgList->NumberOfElements-1)
{
DescCtrl.bits.EOP = TRUE;
DescCtrl.bits.TransferCOMP_IRQ = TRUE;
DescCtrl.bits.EARLY_IRQ = TRUE;
}
//одиночный пакет
else
{
DescCtrl.bits.SOP = TRUE;
DescCtrl.bits.EOP = TRUE;
DescCtrl.bits.TransferCOMP_IRQ = TRUE;
DescCtrl.bits.EARLY_IRQ = TRUE;
}

DescCtrl.bits.TransmitERR_IRQ = 0xFF;
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.IntMask = TRUE;

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

WdfInterruptReleaseLock( devExt->Interrupt );


return TRUE;
}


При этом, если пытаться отправить один пакет, то чтение статусного регистра диспетчера говорит, что дескрипторное фифо пусто, если отправлять 2 и более пакетов, то выставляется Busy статусного регистра диспетчера говорит, что в фифо что-то есть или идет передача, а Fill Level показывает количество занесеный в фифо дескрипторов. Но этот Busy стоит вечно, пока вручную не уберешь.

Выходит, что дескрипторы записываются, отправляются в фифо, откуда их должен вызывать диспетчер, но сам диспетчер передачу не запускает? потому они и остаются там висеть. Или же я не совсем верно заполняю эти дескрипторы?

В альтеровских дровах к модулю есть функции stop_dispatcher() и start_dispatcher(), смысл которых сводится к установке в 1 или 0 бита "Stop Dispatcher". Может быть стоит перед записью дескрипторов остановить диспетчер, а после записи запускать его? (почему-то не подумал об этом сразу).

Или же необходимо настраивать что-то еще?

Кстати, не совсем понял почему так происходит, но порой при установке бита "Global Interrupt Enable Mask" в диспетчере, компьютер намертво повисал. Может это было от того, что диспетчер засыпал его прерываниями, а я их толком не обработал? Прерывания либо вообще не приходят, либо система зависает намертво, но пока не выяснил чем именно это было вызвано. Есть какие-нибудь идеи у кого?

Буду очень признателен за любую помощь! Заранее спасибо, если хотя бы дочитали)
dsmv
Необработанные прерывания могут завесить систему.

У себя я ввел таймаут 1 мс на формирование прерываний.


NeStor46
Цитата(dsmv @ Aug 7 2014, 12:14) *
Необработанные прерывания могут завесить систему.


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

ОС у меня х64, дискриптор в mSGDMA выбран расширенный, т.е. должен работать с 64 разрядными адресами (64-bit addressing requires the use of the extended descriptor format), но передает на read_master он всего одну часть адреса, младшую.


Изначально в проекте был выбран стандартный дискриптор с 32 адресацией. Альтеровский софт для теста скорости передачи по PCI-E похоже и использовал стандартный дискриптор, так как при смене его на расширенный, софт перестал работать. Выходит, что и мне надо работать с 32-разрядными адресами??
NeStor46
отвечу сам себе) с 32-разрядными регистрами адреса работает неплохо, то пока только в пределах адресного пространства, выделенного под BAR0. Т.е. драйвер может, используя mSGDMA, писать и читать данные, но пока только по адресам, задаваемым смещением относительно начала BAR0.

Приведу кусок кода драйвера, чтобы было понятней)

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);

offset = WdfDmaTransactionGetBytesTransferred(Transaction);

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, offset);

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;
}

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;
}



Вот. Если задавать адреса записи и чтения только смещением (в принципе любое число от 0 до 0x8000000 в моем случае, на BAR0 выделено 128 МБ), то все работает и работает хорошо. Винда, похоже, отображает эти адреса в системную память и все довольны, все работают.

Но проблема возникла при записи или чтения пользовательского буфера. В приведенном выше коде я хочу прочитать данные по смещению из BAR0 и занести их в пользовательский буфер, который по идее должен быть задан страницами, физические адреса которых передала мне Винда в виде Scatter/Gather списка, но ничего не выходит. ДМА пишет по адресу записи, но этот адрес, судя по всему, не тот, что мне нужен.

В общем не знаю пока как подсунуть контроллеру нужный адрес пользовательского буфера (выделенного приложением, от которого шел запрос на чтение). Есть какие-нибудь предложения?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.