Перейти к содержанию
    

PCI-E и mSGDMA от Altera

Доброго времени суток всем!

 

Наверное, кто-то разбирался с альтровским 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.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Разбирался с работой 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 (если ОС будете использовать, возможно, там что-то по-другому), через которую задаётся обработчик прерывания и флаги, по какому событию оно будет срабатывать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

... т.е. большое число буферов малой длины (4кБ), или же просто записать в дескриптор mSGDMA адрес откуда читать и длину читаемых данных, а дальше он сам разберется что и как ему делать?

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

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

 

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

 

Спасибо за разъяснение. в dst addr я так понимаю необходимо записывать адрес выделенного пользовательским приложением буфера (приложение, от которого шел запрос на чтение).

Изменено пользователем NeStor46

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за разъяснение. в dst addr я так понимаю необходимо записывать адрес выделенного пользовательским приложением буфера (приложение, от которого шел запрос на чтение).

Да, первый адрес - откуда читать, второй - куда писать. В этой теме был прмер по настройке и работе с Sg-DMA, правда, для Stream To Memory и Memory To Stream, можете ознакомиться.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

спасибо а помощь)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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" в диспетчере, компьютер намертво повисал. Может это было от того, что диспетчер засыпал его прерываниями, а я их толком не обработал? Прерывания либо вообще не приходят, либо система зависает намертво, но пока не выяснил чем именно это было вызвано. Есть какие-нибудь идеи у кого?

 

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

Modular_SGDMA_Dispatcher_Core.pdf

Изменено пользователем NeStor46

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Необработанные прерывания могут завесить систему.

 

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

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Необработанные прерывания могут завесить систему.

 

по ходу они и вешали) сегодня запустил этот уже изрядно задолбавший 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.

 

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

 

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 списка, но ничего не выходит. ДМА пишет по адресу записи, но этот адрес, судя по всему, не тот, что мне нужен.

 

В общем не знаю пока как подсунуть контроллеру нужный адрес пользовательского буфера (выделенного приложением, от которого шел запрос на чтение). Есть какие-нибудь предложения?

Изменено пользователем NeStor46

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...