Хабрахабр

Как микроконтроллер может читать данные на скорости 1.6 Gbps

Всем доброго времени суток! никогда такого не было и вот опять. С моей прошлой статьи прошло уже достаточно времени, а оно ставит новые задачи. И если раньше я передавал данные на скорости 100 Mbps, то теперь пришлось замахнутся на 1600 Mbps…

На КПДВ — герой нашего романа — он смог читать данные на такой скорости!

6 Gbps) в количестве заранее известном — пусть будет 10000.
Итак, мой очередной проект потребовал читать поток данных вида 32-бита на скорости 50 МГц (это и будет, кстати, тот самый 1. Было бы просто отлично читать сразу с помощью DMA из одного порта — но вот, к сожалению, подходящих процессоров не нашлось (надеюсь, в комментариях это дело кто-нибудь поправит), у всех подходящих по скорости порты почему-то 16-битные.

Правда, это в общем случае не всегда удастся делать с нужной степенью контроля и синхронности, но в нашем случае все не так печально — есть сигнал, после которого 20 ns данные будут на порту держаться. Но такая мелочь нас не остановит — будем читать из двух портов сразу!

А так как процессор у нас — stm32h750 на 400 Мгц, а шина и таймеры на 200 Мгц, то все должно получиться.

Но вот только у DMA нет такой возможности — порт может выдать прерывание, но не может управлять DMA. Казалось бы простой случай — по сигналу запускать одну пересылку DMA. Но зато у нашего процессора есть хорошая штука — DMAMUX, в котором есть генератор событий для канала DMA, а вот в этом генераторе есть две подходящие возможности — или использовать прерывание EXTIT0 или сигнал от таймера TIM12 (такая вот странная фантазия была у разработчиков чипа).

5 ns… По прерыванию мы не успеем по времени — на даже пустую отработку нужно порядка 47 тактов, а такт у нас — 2.

Осталось только затактировать таймер от внешнего сигнала в 100 МГц, причем длину таймера ставим в 1 и по его выходу TRGO будет срабатывать генератор DMAMUX, а уж он-то задаст команду на пересылку DMA и тот прочитает порт и перешлет данные в память. Зато успеем по таймеру.

Порт-то 16-бит, а у нас 32… Что же, можно попробовать читать еще второй порт… Только вот для этого нужен второй канал DMA, и еще это займет ту же шину — то есть прочитать-то мы успеем, но вот можем не успеть записать данные в память. Но стоп! Осталось проверить практически. Что-же, теоретически, у этого процессора есть разные типы памяти, и на большом рисунке структуры процессора можно видеть, что и DMA и память RAM_D1 сидят на одной и той-же шине с частотой 200 МГц.

DMA1->LIFCR |= ~0;
DMA1_Stream0->CR = (0b11 << DMA_SxCR_PL_Pos) | (0b01 << DMA_SxCR_MSIZE_Pos) | (0b01 << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC;
DMA1_Stream0->M0AR = (uint32_t) data;
DMA1_Stream0->PAR = (uint32_t) &(GPIOE->IDR);
DMA1_Stream0->NDTR = 10000;
DMA1_Stream1->CR = (0b11 << DMA_SxCR_PL_Pos) | (0b01 << DMA_SxCR_MSIZE_Pos) | (0b01 << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC;
DMA1_Stream1->M0AR = (uint32_t) data2;
DMA1_Stream1->PAR = (uint32_t) &(GPIOD->IDR);
DMA1_Stream1->NDTR = 10000;
DMAMUX1_Channel0->CCR = DMAMUX_CxCR_EGE | (1);
DMAMUX1_Channel1->CCR = DMAMUX_CxCR_EGE | (2);
DMAMUX1_RequestGenerator0->RGCR = DMAMUX_RGxCR_GE | (0b01 << DMAMUX_RGxCR_GPOL_Pos) | (7);
DMAMUX1_RequestGenerator1->RGCR = DMAMUX_RGxCR_GE | (0b01 << DMAMUX_RGxCR_GPOL_Pos) | (7);
DMA1_Stream0->CR |= DMA_SxCR_EN;
DMA1_Stream1->CR |= DMA_SxCR_EN;
TIM12->CNT = 0;
TIM12->CCMR1 |= TIM_CCMR1_CC2S_0;
TIM12->CR2 = (0b010 << TIM_CR2_MMS_Pos);
TIM12->CR1 |= TIM_CR1_CEN;
while (DMA1_Stream0->NDTR) i++;
TIM12->CR1 &= ~TIM_CR1_CEN;

Ну и конечно, надо разместить массивы data и data2 в нужном сегменте памяти, это делается так:

__attribute__((section(".dma_buffer"))) uint16_t data[10240],data2[10240];

а в файле для линковщика указываем:

.dma_buffer : >RAM_D1

Для проверки, ну и как первый вариант, был реализован просто тупое копирование с помощью
центрального процессора (все же 400 МГц):

uint16_t * ptr = cpudata; volatile uint16_t * src = &(GPIOE->IDR); volatile uint16_t * src2 = &(GPIOD->IDR); for (register int i = 0; i < 10000; i++) { *ptr++ = *src; *ptr++ = *src2; }

Для проверки данные cpudata размещались в разной памяти, быстрее всего подошла (ну она, правда, всего 64К) сверхбыстрая память (тоже 400 МГц) DTCMRAM.

Результаты

В процессе испытаний выяснилось, что с помощью CPU можно читать на скорости 12.5 МГц из двух портов. И 25 МГц из одного. Так что вариант не работает…

Читались оба порта, но замерить, насколько отстает чтение по второму DMA, пока не удалось… С помощью DMA и такой-то матери TIM12 удалось успешно читать на скорости 50 МГц, причем за несколько часов теста ошибок не было ни одной.

Так что в моем (немного вырожденном) случае, удалось достигнуть скорости передачи информации в процессор stm32h750 на скорости 32x50 = 1600 Mbps.

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»