Problema de velocidad RPi GPIO en bare metal

Después de leer el excelente artículo sobre bare metal de David Welch ( https://github.com/dwelch67/raspberrypi/tree/master/baremetal ), un amigo y yo estamos tratando de implementar un conmutador GPIO simple. Está construido sobre el blinker01 de David Welch ( https://github.com/dwelch67/raspberrypi/tree/master/blinker01 ). Simplemente actualicé los registros periféricos para que correspondan a GPIO 3 en la frambuesa pi. Encontré estas direcciones mirando la hoja de datos BCM2835 ( https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf ). El Capítulo 6 enumera todas las ubicaciones de la configuración de pines GPIO.

Cargamos el código en una tarjeta SD y luego lo encendemos. El programa funciona: obtenemos una onda cuadrada de 2,5 MHz en el osciloscopio.

** ¿Por qué es tan lento? **

El procesador tiene una velocidad de reloj de 1 Ghz. La operación que implementamos recorre 10 líneas de ensamblaje (es decir, hace 10 líneas de ensamblaje y luego se bifurca de nuevo a la primera línea de esas 10). Soy nuevo en los procesadores/bare metal y entiendo que una determinada línea de ensamblaje puede tomar varios ciclos de reloj. Suponiendo que cada línea toma 10 ciclos de reloj, aún esperaría un período de 100 ns para la salida, que sería de 10 MHz. Siento que es un límite inferior conservador en la frecuencia de salida, ya que algunas líneas de ensamblaje solo toman un ciclo.

Además, encontré este artículo: http://codeandlife.com/2012/07/03/benchmarking-raspberry-pi-gpio-speed/

Esta persona logró obtener una salida de 22 MHz en una raspberry pi 1 usando un enfoque similar pero usando mmap de linux. El código que usaron como base se encuentra como primer ejemplo en esta página: http://elinux.org/RPi_GPIO_Code_Samples

** Editar: originalmente pensé que los 22MHz se lograron en una raspberry pi 2, pero eso es incorrecto. Afirman que lograron esta tasa de salida en un pi 1 que tiene el chip BCM 2835 **

Nota: aunque esto discutiblemente pertenece a StackOverflow, sentí que es un problema "más difícil" porque tiene que ver con los circuitos del procesador y los periféricos.

Editar: el código de ensamblaje está aquí: Desensamblaje de la sección .text:

00008000 `<_start`>:

    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000005    bl  8020 <notmain>

00008008 `<hang`>:

    8008:   eafffffe    b   8008 <hang>

0000800c `<PUT32`>:

    800c:   e5801000    str r1, [r0]
    8010:   e12fff1e    bx  lr

00008014 `<GET32`>:

    8014:   e5900000    ldr r0, [r0]
    8018:   e12fff1e    bx  lr

0000801c `<dummy`>:

    801c:   e12fff1e    bx  lr

00008020 `<notmain`>:

    8020:   e92d4010    push    {r4, lr}
    8024:   e59f002c    ldr r0, [pc, #44]   ; 8058 <notmain+0x38>
    8028:   ebfffff9    bl  8014 <GET32>
    802c:   e3c01c0e    bic r1, r0, #3584   ; 0xe00
    8030:   e3811c02    orr r1, r1, #512    ; 0x200
    8034:   e59f001c    ldr r0, [pc, #28]   ; 8058 <notmain+0x38>
    8038:   ebfffff3    bl  800c <PUT32>
    803c:   e3a01008    mov r1, #8
    8040:   e59f0014    ldr r0, [pc, #20]   ; 805c <notmain+0x3c>
    8044:   ebfffff0    bl  800c <PUT32>
    8048:   e3a01008    mov r1, #8
    804c:   e59f000c    ldr r0, [pc, #12]   ; 8060 <notmain+0x40>
    8050:   ebffffed    bl  800c <PUT32>
    8054:   eafffff8    b   803c <notmain+0x1c>
    8058:   20200000    eorcs   r0, r0, r0
    805c:   2020001c    eorcs   r0, r0, ip, lsl r0
    8060:   20200028    eorcs   r0, r0, r8, lsr #32

`

¿Podría simplemente colocar un fragmento de código para su bucle real? Las referencias están bien, excepto que no puedo encontrar ninguna señal de código ensamblador allí.
¿Cuál es el ancho de banda de su alcance?
El ancho de banda es de 100MHz
Lo más probable es que el reloj del sistema y/o el PLL interno no estén configurados correctamente. El principal problema de hacer bare metal en estas MCU de gama alta es que tiene que configurar todo usted mismo, incluida la asignación de memoria y la MMU, lo cual está lejos de ser trivial a menos que pueda obtener el código de configuración de algún lugar.
@Lundin El BCM2835 es una especie de parte extraña. Hasta donde yo sé, el control del reloj está en el lado de la GPU, que aparece antes que el núcleo ARM.
Apuesto a que la sugerencia de Lundin es correcta. Creo que es muy poco probable que la GPU controle el reloj de la CPU. La única otra cosa que inmediatamente me viene a la mente es que la tarjeta SD es muy lenta para leer. Si lee constantemente estas instrucciones de la tarjeta SD en lugar de quedarse sin RAM o su caché, podría terminar siendo muy lento. Sin embargo, eso es una verdadera puñalada en la oscuridad. ¿Puedes ver la actividad constante de la tarjeta SD mientras se ejecuta el programa?
@DiBosco El núcleo de la GPU carga la aplicación desde la tarjeta SD a la DRAM antes de iniciar el procesador ARM. ¿Mencioné que el BCM2835 es una parte extraña? :)
Creo que la recuperación de la memoria podría ser el problema. Encontré esta publicación: raspberrypi.stackexchange.com/questions/61186/… ¿Cómo crees que mmap acelera esto? ¿No es trivial almacenar los periféricos en un caché para tiempos de escritura más rápidos?

Respuestas (2)

El culpable principal es una combinación de limitaciones de hardware/periféricos y configuración del reloj. Si bien no he tenido que trabajar específicamente con BCM Baremetal, estos son problemas comunes de los proyectos baremetal en cualquier arquitectura compleja.

Como sugerencia sobre las limitaciones de los controladores de salida GPIO, puede ver que cuando está conectado como salida de reloj de hardware, la frecuencia de salida máxima es de 125 MHz.

De la página 106 de la hoja de datos del periférico BCM que proporcionó

Frecuencia de funcionamiento : la frecuencia de funcionamiento máxima de los relojes de uso general es de ~125 MHz a 1,2 V, pero se reducirá si los pines GPIO están muy cargados o tienen una carga capacitiva.

Esto es en el contexto de configurar el periférico GPIO para generar el reloj periférico directamente sin alternar el software.

Diría que es razonable esperar que incluso si los relojes están configurados correctamente y la CPU funciona a la velocidad máxima, no puede esperar que un GPIO cambie más rápido que esto debido a limitaciones de hardware.

También conocido como el hecho de que el periférico pueda bloquear su comando de software a tiempo, no significa que los transistores del controlador de salida física, que son grandes y robustos, con mucha carga inherente, puedan cambiar tan rápido como su código puede ejecutarse. Si está realizando pruebas, es imperativo que utilice un osciloscopio con suficiente ancho de banda analógico y sondas de alta calidad, porque también está cambiando su resultado con el sistema de medición. Un analizador lógico puede no ser suficiente, una velocidad de respuesta lenta no es identificable con entradas con umbral.


Cómo proceder

Parece que si su objetivo es conducir GPIO tan rápido como lo necesita, para fines de reloj, debe usar estos pines de salida de reloj incorporados del periférico. Estos se configuran a través de registros.

CM_GP0CTL CM_GP0DIV(repita para las salidas de reloj gp 1 y 2)

Luego, según los resultados de esto, identificará la frecuencia de conmutación máxima para su sistema de hardware teniendo en cuenta la carga máxima de GPIO y el VDD de su circuito PIO.

Si la salida del reloj es más lenta de lo esperado para la configuración de su divisor nominal, esto indicaría que no ha configurado el reloj del sistema, el enrutamiento del reloj y los PLL de manera adecuada.

Una vez que haya identificado ese desacuerdo, puede modificar su código de arranque básico para configurar los PLL y ver si se puede hacer que un cambio controlado por software se ejecute tan rápido como la salida del reloj controlada por hardware y continuar desde allí.

Los factores contribuyentes adicionales pueden estar en las memorias caché de instrucciones y datos, que requieren configuración de software, si no puede alinear el software alternando con el límite de hardware solo a través de PLL, ese sería el próximo lugar en el que buscaría.

Acabo de encontrar esta pregunta en una publicación cruzada.
El problema principal que no se discute en todos los comentarios anteriores es la velocidad del autobús.. El procesador puede funcionar a 3 GHz, pero los buses del chip no. El bus del lado ARM es mucho más lento que 3GHz. El bus hacia el GPIO es aún más lento, funcionando a la velocidad de la GPU. Agregue a eso el tiempo perdido por el cruce del dominio del reloj y el hecho de que los buses son un recurso compartido entre los núcleos ARM y la GPU. Además, el chip utiliza el sistema AXI que tiene un reconocimiento de escritura, por lo que el procesador puede esperar a que llegue el reconocimiento de escritura, que es el doble de la velocidad de respuesta del bus. En la infraestructura del bus, se necesitan varios ciclos de reloj para llevar la señal de un lado del bus al otro (pero permite que haya múltiples transacciones en el bus al mismo tiempo).

No es probable que esto explique la magnitud total del comportamiento observado.