Cómo un microcontrolador puede leer datos a 1.6 Gbps

Buen dia a todos! nunca sucedió y aquí de nuevo . Ha pasado suficiente tiempo desde mi último artículo , y plantea nuevos desafíos. Y si solía transferir datos a una velocidad de 100 Mbps, ahora tenía que girar a 1600 Mbps ...

En KDPV, el héroe de nuestra novela, ¡podía leer datos a esa velocidad!



Por lo tanto, mi próximo proyecto exigió leer un flujo de datos de 32 bits a una velocidad de 50 MHz (esto, por cierto, será el mismo 1.6 Gbps) en una cantidad conocida de antemano, deje que sea 10000. Sería bueno leerlo inmediatamente usando DMA desde un puerto - pero, desafortunadamente, no había procesadores adecuados (espero que alguien corrija este asunto en los comentarios), por alguna razón, todos los puertos que son adecuados para la velocidad son, por alguna razón, de 16 bits.

Pero tal bagatela no nos detendrá: ¡leeremos desde dos puertos a la vez! Es cierto que esto en el caso general no siempre será posible con el grado necesario de control y sincronismo, pero en nuestro caso, no todo es tan triste: hay una señal después de la cual se mantendrán datos de 20 ns en el puerto.

Y dado que el procesador que tenemos es stm32h750 a 400 MHz, y el bus y los temporizadores a 200 MHz, entonces todo debería funcionar.

Parecería un caso simple: activar un solo reenvío de DMA en una señal. Pero solo DMA no tiene esa oportunidad: el puerto puede emitir una interrupción, pero no puede controlar DMA. Pero nuestro procesador tiene algo bueno: DMAMUX, en el que hay un generador de eventos para el canal DMA, pero este generador tiene dos opciones adecuadas: usar la interrupción EXTIT0 o la señal del temporizador TIM12 (los desarrolladores de chips tuvieron una fantasía tan extraña).

No tenemos tiempo para la interrupción: incluso necesitamos alrededor de 47 ciclos de reloj para que funcionen vacíos, y nuestro ciclo de reloj es 2.5 ns ...

Pero a tiempo para un temporizador. Solo resta tomar el temporizador desde una señal externa de 100 MHz, y establecer la longitud del temporizador en 1 y la salida TRGO activará el generador DMAMUX, y luego solicitará el comando de reenvío DMA y leerá el puerto y enviará los datos a la memoria.

Pero para! El puerto es de 16 bits, pero tenemos 32 ... Bueno, puedes intentar leer otro segundo puerto ... Solo para esto necesitamos un segundo canal DMA, y tomará el mismo bus, es decir, tendremos tiempo para leer, pero podemos No tengo tiempo para escribir datos en la memoria. Bueno, teóricamente, este procesador tiene diferentes tipos de memoria, y en una imagen grande de la estructura del procesador se puede ver que tanto la memoria DMA como RAM_D1 están ubicadas en el mismo bus con una frecuencia de 200 MHz. Queda por verificar en la práctica.

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; 

Y, por supuesto, debe colocar las matrices de datos y data2 en el segmento de memoria deseado, esto se hace así:

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

y en el archivo para el enlazador indicar:

 .dma_buffer : { *(.dma_buffer) } >RAM_D1 

Para verificar, bueno, y como primera opción, solo se implementó una copia estúpida usando
CPU (todavía 400 MHz):

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

Para la verificación, los datos de la cpudata se ubicaron en diferentes memorias, la más rápida (bueno, es cierto, solo 64K) fue la memoria más rápida (también 400 MHz) DTCMRAM.

Resultados


Durante las pruebas, resultó que con la ayuda de la CPU es posible leer a una velocidad de 12.5 MHz desde dos puertos. Y 25 MHz de uno. Entonces la opción no funciona ...

Con la ayuda de DMA y una madre así, TIM12 pudo leer con éxito a una velocidad de 50 MHz, y en unas pocas horas no hubo una sola prueba de error. Se leyeron ambos puertos, pero aún no era posible medir hasta qué punto la lectura en el segundo DMA se retrasa ...

Entonces, en mi caso (ligeramente degenerado), logré alcanzar la velocidad de transferencia de información al procesador stm32h750 a una velocidad de 32x50 = 1600 Mbps.

Source: https://habr.com/ru/post/467459/


All Articles