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

Не забывайте только, что одна транзакция DMA Burst не должна пересекать границу 1кБ (вроде), это связано с физическими адресуемыми подчиненными шин.

Какие интересные грабли.

Это каких именно процессоров касается? И где описано?

 

Этак burst можно применять только в очень ограниченном числе случаев...

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


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

Какие интересные грабли.

Это каких именно процессоров касается? И где описано?

Это не грабли. Это документированная особенность построения ядра ARMv7M. В других версиях архитектуры аналогично, только адресная граница физических slave-подсистем (например, памяти) может отличаться от 1кБ. Поэтому, если, например, Вы запустите burst-транзакцию, пересекающую адресную границу одного подчиненного, DMA завершит транзакцию успешно (во флагах статуса), но в действительности часть данных потеряется. Причем DMA не взведет флаги Transfer Error. Ну в STM32 по крайней мере точно так. Вот Вам выдержка из Reference Manual на STM32F4xx, например:

The burst configuration has to be selected in order to respect the AHB protocol, where

bursts must not cross the 1 KB address boundary because the minimum address space that

can be allocated to a single slave is 1 KB. This means that the 1 KB address boundary

should not be crossed by a burst block transfer, otherwise an AHB error would be

generated, that is not reported by the DMA registers.

 

Этак burst можно применять только в очень ограниченном числе случаев...

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

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


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

Да, неправ, это место просмотрел... Благо объемы у меня небольшие, и разгонять DMA не требуется - до включения burst'а руки так и не дошли...

 

Но это означает, что работу с буфером больше 1кБ надо обкладывать дополнительными проверками.

И маленький буфер тоже по-хорошему выравнивать надо (блин, как же в gcc это неудобно...).

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


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

И маленький буфер тоже по-хорошему выравнивать надо

На рахмер строки кэша (32 байта) да. Я так понимаю речь про килобайт? Так это можно и в динамике сделать (делить на chunks передаваемые блоки). Но это все на этапе проектирования понятно - можно менеджера памяти нагрузить требованиями по выравниванию, например. Если он есть, конечно.

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


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

Но это означает, что работу с буфером больше 1кБ надо обкладывать дополнительными проверками.

И маленький буфер тоже по-хорошему выравнивать надо (блин, как же в gcc это неудобно...).

Если речь про ограничение в 1кБ - то не забывайте, это только для burst-передач, которые, в общем-то, обычно ограничены десятками-сотней байт. Если это обычная транзакция DMA (не burst) - можете передавать сколько угодно, ограничение только DMA делает на количество данных, передаваемых в одном сеансе транзакции (для STM32 это 64кБ).

 

Но это все на этапе проектирования понятно - можно менеджера памяти нагрузить требованиями по выравниванию, например. Если он есть, конечно.

ИМХО, лучше сразу все продумать на этапе составления программы и разлиновки памяти. Динамические диспетчеры так или иначе такты процессора тратят. И тем более было бы ради чего - а тут все буфера статически разместить можно еще на этапе обдумывания структуры проекта.

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


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

Делал работу с видеопамятью через MDMA - заполнение и копирование прямоугольных областей.
Выяснилось, что в случае двухбайтовых пикселей в поле MDMA_CBNDTR_BNDT приходится писать количество пикселей умноженное на четыре. При этом поля MDMA_CBRUR_SUV и MDMA_CBRUR_DUV исчисляются как и положено, в байтах...

Вопрос... У кого есть опыт ходьбы по этим граблям? Так ли обстоят дела, как мне показалось?

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


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

В 20.07.2018 в 09:50, esaulenka сказал:

Какие интересные грабли.

Это каких именно процессоров касается? И где описано?

Интересно. В СТМ ДМА не использовал таким образом, а вот в IMX Allwinner-ах по полной, т.е. перекидывал целиком экранные области по 32 байта за бурст, размеры больше 1 Мбайта и на такое никогда не натыкался, походу это или косяки СТМшиков или именно данной архитектуры.

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

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


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

10 hours ago, GenaSPB said:

Вопрос... У кого есть опыт ходьбы по этим граблям? Так ли обстоят дела, как мне показалось?

Приходилось использовать MDMA по причине того, что он может менять последовательность байт при передаче, что очень важно при пересылке буфера в LCD (иначе пришлосб бы менять байты вручную через _rev16). Всё исправно работает, ниже код.  Правда, пришлось повозиться с настройками.

 

static void MX_MDMA_Init(void) 
{
  /* MDMA controller clock enable */
  __HAL_RCC_MDMA_CLK_ENABLE();
  /* Local variables */

  /* Configure MDMA channel MDMA_Channel7 */
  /* Configure MDMA request hmdma_mdma_channel7_sw_0 on MDMA_Channel7 */
  hmdma_mdma_channel7_sw_0.Instance = MDMA_Channel7;
  hmdma_mdma_channel7_sw_0.Init.Request = MDMA_REQUEST_SW;
  hmdma_mdma_channel7_sw_0.Init.TransferTriggerMode = MDMA_FULL_TRANSFER;
  hmdma_mdma_channel7_sw_0.Init.Priority = MDMA_PRIORITY_LOW;

  hmdma_mdma_channel7_sw_0.Init.Endianness = MDMA_LITTLE_BYTE_ENDIANNESS_EXCHANGE; //Меняем местами байты (для LCD)

  hmdma_mdma_channel7_sw_0.Init.SourceInc      = MDMA_SRC_INC_HALFWORD;
  hmdma_mdma_channel7_sw_0.Init.DestinationInc = MDMA_DEST_INC_DISABLE;

  hmdma_mdma_channel7_sw_0.Init.SourceDataSize = MDMA_SRC_DATASIZE_HALFWORD;
  hmdma_mdma_channel7_sw_0.Init.DestDataSize = MDMA_DEST_DATASIZE_HALFWORD;

  hmdma_mdma_channel7_sw_0.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;

  hmdma_mdma_channel7_sw_0.Init.BufferTransferLength = 2;                     //1 для BYTE, 2 для HALFWORD, 4 для WORD

  hmdma_mdma_channel7_sw_0.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE;
  hmdma_mdma_channel7_sw_0.Init.DestBurst = MDMA_DEST_BURST_SINGLE;
  hmdma_mdma_channel7_sw_0.Init.SourceBlockAddressOffset = 0;
  hmdma_mdma_channel7_sw_0.Init.DestBlockAddressOffset = 0;
  if (HAL_MDMA_Init(&hmdma_mdma_channel7_sw_0) != HAL_OK)
  {
    Error_Handler();
  }

}

void SaI2xTransfer(void)
{
 HAL_MDMA_PollForTransfer(&hmdma_mdma_channel7_sw_0,HAL_MDMA_FULL_TRANSFER,100);

 SaI2x((u8*)&BufferAndBorder[(1+160+2)+1],1+160+2,FilterBuffer,LCD_W<<1,160,120);

 SCB_CleanDCache();

 HAL_MDMA_Start(&hmdma_mdma_channel7_sw_0,(u32)FilterBuffer,(u32)&LCD_DAT32,((LCD_H*LCD_W)<<1)/3,3);
}

 

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


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

Полуторный размер? Предпоследний параметр функции HAL_MDMA_Start.... судя по всему у вас такой проблемы не проявилось.

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

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


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

2 hours ago, GenaSPB said:

Полуторный размер? Предпоследний параметр функции HAL_MDMA_Start.... судя по всему у вас такой проблемы не проявилось.

 

Одна DMA-транзакция: не более 65535 байт за 1 раз.  Пришлось бить на 3 равные части: 

LCD_H*LCD_W)<<1)/3

((400*240 пикспелей) *2  байт) /3 = 64000 байт.  По три раза(последний аргумент).

 

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


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

Понял. Хоть у вас по 16 бит написано... размер задаётся как надо. Странно. Что за ненормальность у меня...

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

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


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

Добрый день. Делал замер скорости ДМА и оказалось, что МДМА очень медленно работает. Пересылка память ту память всего на 43 МГц работает AXI_SRAM->МДМА -> AXI_SRAM, обычный ДМА еще медленее 16 Мгц. Серия F4 быстрее, это как так вышло то у STM? либо есть какой-то нюанс. Кэши все включены, скорость мк 480 Мгц. Пересылка 16 бит. Причем с переферией типа портов вводы-вывода обычный ДМА шустрее работает, если в память SRAM1, но тоже медленно. Тот же FMC 34Мгц через МДМА.  через ДМА 20 Мгц, а должен 120Мгц держать, так вот как заставить его с частотой 120 Мгц работать, если ДМА еле передают данные?

даже еще хуже, 17 Мгц FMC -> МДМА -> AXI_SRAM или SRAM1 для МДМА все равно в какую память один фиг медленно. Обычный ДМА чуть шустрее, если в SRAM1  но все равно медленно, что это за скорость 20 Мгц.

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


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

11 часов назад, GFX сказал:

Обычный ДМА чуть шустрее, если в SRAM1  но все равно медленно, что это за скорость 20 Мгц.

А клоки или делители поднастроить нельзя там разве?

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


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

10 часов назад, mantech сказал:

А клоки или делители поднастроить нельзя там разве?

Чего именно? Итак все на максимум 

Спойлер

 

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Supply configuration update enable 
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
  /** Configure the main internal regulator output voltage 
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
  /** Configure LSE Drive Capability 
  */
  HAL_PWR_EnableBkUpAccess();
  __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
  /** Macro to configure the PLL clock source 
  */
  __HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSE);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 5;
  RCC_OscInitStruct.PLL.PLLN = 192;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 20;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_HRTIM1
                              |RCC_PERIPHCLK_SPI2|RCC_PERIPHCLK_SDMMC
                              |RCC_PERIPHCLK_ADC|RCC_PERIPHCLK_USB
                              |RCC_PERIPHCLK_QSPI|RCC_PERIPHCLK_FMC;
  PeriphClkInitStruct.PLL2.PLL2M = 2;
  PeriphClkInitStruct.PLL2.PLL2N = 12;
  PeriphClkInitStruct.PLL2.PLL2P = 1;
  PeriphClkInitStruct.PLL2.PLL2Q = 2;
  PeriphClkInitStruct.PLL2.PLL2R = 2;
  PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_3;
  PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOMEDIUM;
  PeriphClkInitStruct.PLL2.PLL2FRACN = 0;
  PeriphClkInitStruct.FmcClockSelection = RCC_FMCCLKSOURCE_D1HCLK;
  PeriphClkInitStruct.QspiClockSelection = RCC_QSPICLKSOURCE_D1HCLK;
  PeriphClkInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL;
  PeriphClkInitStruct.Spi123ClockSelection = RCC_SPI123CLKSOURCE_PLL2;
  PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
  PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  PeriphClkInitStruct.Hrtim1ClockSelection = RCC_HRTIM1CLK_CPUCLK;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Enable USB Voltage detector 
  */
  HAL_PWREx_EnableUSBVoltageDetector();
}

 

Спойлер

 

/* FMC initialization function */
static void MX_FMC_Init(void)
{

  /* USER CODE BEGIN FMC_Init 0 */

  /* USER CODE END FMC_Init 0 */

  FMC_NORSRAM_TimingTypeDef Timing = {0};

  /* USER CODE BEGIN FMC_Init 1 */

  /* USER CODE END FMC_Init 1 */

  /** Perform the SRAM1 memory initialization sequence
  */
  hsram1.Instance = FMC_NORSRAM_DEVICE;
  hsram1.Extended = FMC_NORSRAM_EXTENDED_DEVICE;
  /* hsram1.Init */
  hsram1.Init.NSBank = FMC_NORSRAM_BANK1;
  hsram1.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;
  hsram1.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;
  hsram1.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;
  hsram1.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;
  hsram1.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_LOW;
  hsram1.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;
  hsram1.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;
  hsram1.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;
  hsram1.Init.ExtendedMode = FMC_EXTENDED_MODE_DISABLE;
  hsram1.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;
  hsram1.Init.WriteBurst = FMC_WRITE_BURST_ENABLE;//FMC_WRITE_BURST_DISABLE;
  hsram1.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;
  hsram1.Init.WriteFifo = FMC_WRITE_FIFO_ENABLE;
  hsram1.Init.PageSize = FMC_PAGE_SIZE_NONE;//
  /* Timing */
  Timing.AddressSetupTime = 0;
  Timing.AddressHoldTime = 0;
  Timing.DataSetupTime = 1;
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 0;
  Timing.DataLatency = 0;
  Timing.AccessMode = FMC_ACCESS_MODE_A;
  /* ExtTiming */

  if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FMC_Init 2 */

  /* USER CODE END FMC_Init 2 */
}

 

Даже проц закидывает в память со скоростью 43Мгц из FMC, т.е. сам проц быстрый, а толку нет, периферия еще медленнее чем была. AXI_SRAM->ПРОЦ ДКЕШ->SRAM1 = 188МГц, но МДМА всего 17Мгц в том же случае да и вообще при любой памяти. ДМА в этом случае всего 16 Мгц, но обычный ДМА не на AXI шине так что это понятно, с периферией в SRAM1 обычный ДМА пошустрее МДМА скидывает, но все равно медленно все это. Как FMC заставить на 120 Мгц работать то, если его даже проц не раскачигаривает на такую скорость.

Считывание с FMC-> проц->AXI_SRAM 43 МГц, FMC->МДМА->AXI_SRAM - 17 МГц, FMC->ДМА->SRAM1 (1 или 2 не важно) - 20 МГц. 

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


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

1 час назад, GFX сказал:

Даже проц закидывает в память со скоростью 43Мгц из FMC

Странно. Нафига тогда 100 или 133МГц клок для памяти если шина в 2 раза медленнее. Только ради видеопорта что-ли... В А серии это решается за счет увеличения разрядности шины, тут не в курсе, может тоже что есть. 

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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