Como um microcontrolador pode ler dados em 1,6 Gbps

Bom dia a todos! isso nunca aconteceu e aqui novamente . Tempo suficiente se passou desde o meu último artigo e apresenta novos desafios. E se eu costumava transferir dados a uma velocidade de 100 Mbps, agora precisava balançar a 1600 Mbps ...

No KDPV - o herói do nosso romance - ele foi capaz de ler dados com tanta velocidade!



Portanto, meu próximo projeto exigiu a leitura de um fluxo de dados de 32 bits a uma velocidade de 50 MHz (a propósito, será o mesmo 1,6 Gbps) em uma quantidade previamente conhecida - seja 10000. Seria bom ler imediatamente usando o DMA de uma porta - mas, infelizmente, não havia processadores adequados (espero que alguém corrija esse assunto nos comentários); por alguma razão, todas as portas adequadas à velocidade são, por alguma razão, 16 bits.

Mas essa ninharia não vai nos parar - vamos ler de dois portos ao mesmo tempo! É verdade que nem sempre isso é possível com o grau necessário de controle e sincronismo, mas, no nosso caso, tudo não é tão triste - há um sinal após o qual 20 ns de dados serão mantidos na porta.

E como o processador que temos é stm32h750 a 400 MHz e o barramento e os temporizadores a 200 MHz, tudo deve dar certo.

Parece um caso simples - acionar um único encaminhamento de DMA em um sinal. Mas apenas o DMA não tem essa oportunidade - a porta pode emitir uma interrupção, mas não pode controlar o DMA. Mas nosso processador tem uma coisa boa - DMAMUX, na qual existe um gerador de eventos para o canal DMA, mas esse gerador tem duas possibilidades adequadas - use a interrupção EXTIT0 ou o sinal do timer TIM12 (essa foi uma fantasia estranha para os desenvolvedores de chips).

Não temos tempo para interrupção - precisamos até de 47 ciclos de relógio para trabalhar vazio, e nosso ciclo de relógio é de 2,5 ns ...

Mas a tempo para um cronômetro. Resta apenas ligar o timer a partir de um sinal externo de 100 MHz e definir a duração do timer como 1 e a saída TRGO acionará o gerador DMAMUX e, em seguida, emitirá um comando para enviar o DMA e ler a porta e enviar os dados para a memória.

Mas pare! A porta é de 16 bits, mas temos 32 ... Bem, você pode tentar ler outra segunda porta ... Só para isso precisamos de um segundo canal DMA, e ele pegará o mesmo barramento - ou seja, teremos tempo para ler, mas podemos não tem tempo para gravar dados na memória. Bem, teoricamente, esse processador possui diferentes tipos de memória e, em uma imagem geral da estrutura do processador, você pode ver que as memórias DMA e RAM_D1 estão no mesmo barramento com uma frequência de 200 MHz. Resta verificar na prática.

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; 

E, é claro, você precisa colocar os dados e as matrizes data2 no segmento de memória desejado, isso é feito da seguinte maneira:

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

e no arquivo do vinculador, indique:

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

Para verificar, bem, e como a primeira opção, apenas cópias estúpidas foram implementadas usando
CPU (ainda 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 verificação, os dados do cpudata foram localizados em memória diferente, o mais rápido (bem, é verdade, apenas 64 K) foi o DTCMRAM de memória mais rápido (também 400 MHz).

Resultados


Durante os testes, descobriu-se que, com a ajuda da CPU, é possível ler a uma velocidade de 12,5 MHz a partir de duas portas. E 25 MHz a partir de um. Portanto, a opção não funciona ...

Com a ajuda do DMA e de uma mãe assim, o TIM12 conseguiu ler com êxito a uma velocidade de 50 MHz e, em poucas horas, não houve um único teste de erro. Ambas as portas foram lidas, mas ainda não era possível medir até que ponto a leitura no segundo DMA fica atrasada ...

Portanto, no meu caso (um pouco degenerado), consegui atingir a velocidade de transferência de informações para o processador stm32h750 a uma velocidade de 32x50 = 1600 Mbps.

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


All Articles