En muchas aplicaciones, una CPU cuya ejecución de instrucción tiene una relación de tiempo conocida con estímulos de entrada esperados puede manejar tareas que requerirían una CPU mucho más rápida si la relación fuera desconocida. Por ejemplo, en un proyecto que hice usando un PSOC para generar video, usé código para generar un byte de datos de video cada 16 relojes de CPU. Como probar si el dispositivo SPI está listo y bifurcarse si no, IIRC tomaría 13 relojes, y una carga y almacenamiento de datos de salida tomaría 11, no había forma de probar la disponibilidad del dispositivo entre bytes; en cambio, simplemente arreglé para que el procesador ejecutara exactamente 16 ciclos de código para cada byte después del primero (creo que usé una carga indexada real, una carga indexada ficticia y una tienda). La primera escritura de SPI de cada línea ocurrió antes del inicio del video, y para cada escritura posterior había una ventana de 16 ciclos donde la escritura podía ocurrir sin desbordamiento o desbordamiento del búfer. El bucle de ramificación generó una ventana de incertidumbre de 13 ciclos, pero la ejecución predecible de 16 ciclos significó que la incertidumbre para todos los bytes subsiguientes encajaría en la misma ventana de 13 ciclos (que a su vez cabe dentro de la ventana de 16 ciclos de cuándo la escritura podría aceptablemente ocurrir).
Para las CPU más antiguas, la información de sincronización de instrucciones era clara, disponible y sin ambigüedades. Para los ARM más nuevos, la información de temporización parece mucho más vaga. Entiendo que cuando el código se ejecuta desde flash, el comportamiento de almacenamiento en caché puede hacer que las cosas sean mucho más difíciles de predecir, por lo que esperaría que cualquier código contado por ciclo se ejecute desde la RAM. Sin embargo, incluso cuando se ejecuta código desde RAM, las especificaciones parecen un poco vagas. ¿Sigue siendo una buena idea el uso de código contado por ciclo? Si es así, ¿cuáles son las mejores técnicas para que funcione de manera confiable? ¿Hasta qué punto se puede suponer con seguridad que un proveedor de chips no va a introducir silenciosamente un "nuevo chip mejorado" que afeita un ciclo a la ejecución de ciertas instrucciones en ciertos casos?
Suponiendo que el siguiente ciclo comienza en un límite de palabra, ¿cómo se determinaría en función de las especificaciones exactamente cuánto tiempo tomaría (suponga que Cortex-M3 con memoria de estado de espera cero; nada más sobre el sistema debería importar para este ejemplo).
myloop: mov r0, r0; Instrucciones simples y cortas para permitir que se obtengan más instrucciones mov r0, r0; Instrucciones simples y cortas para permitir que se obtengan más instrucciones mov r0, r0; Instrucciones simples y cortas para permitir que se obtengan más instrucciones mov r0, r0; Instrucciones simples y cortas para permitir que se obtengan más instrucciones mov r0, r0; Instrucciones simples y cortas para permitir que se obtengan más instrucciones mov r0, r0; Instrucciones simples y cortas para permitir que se obtengan más instrucciones agrega r2, r1, # 0x12000000; Instrucción de 2 palabras ; Repita lo siguiente, posiblemente con diferentes operandos ; Seguirá agregando valores hasta que ocurra un acarreo itcc agregacc r2, r2, # 0x12000000; Instrucción de 2 palabras, más "palabra" adicional para itcc itcc agregacc r2, r2, # 0x12000000; Instrucción de 2 palabras, más "palabra" adicional para itcc itcc agregacc r2, r2, # 0x12000000; Instrucción de 2 palabras, más "palabra" adicional para itcc itcc agregacc r2, r2, # 0x12000000; Instrucción de 2 palabras, más "palabra" adicional para itcc ; ... etc, con instrucciones de dos palabras más condicionales sub r8, r8, # 1 bpl myloop
Durante la ejecución de las primeras seis instrucciones, el núcleo tendría tiempo para buscar seis palabras, de las cuales tres se ejecutarían, por lo que podría haber hasta tres pretraídas. Las siguientes instrucciones son las tres palabras cada una, por lo que no sería posible que el núcleo obtenga instrucciones tan rápido como se ejecutan. Esperaría que algunas de las instrucciones "it" tomaran un ciclo, pero no sé cómo predecir cuáles.
Sería bueno si ARM pudiera especificar ciertas condiciones bajo las cuales el tiempo de instrucción "it" sería determinista (por ejemplo, si no hay estados de espera o contención de bus de código, y las dos instrucciones anteriores son instrucciones de registro de 16 bits, etc.) pero no he visto ninguna de esas especificaciones.
Aplicación de muestra
Supongamos que uno está tratando de diseñar una placa secundaria para un Atari 2600 para generar salida de video componente a 480P. El 2600 tiene un reloj de píxeles de 3.579MHz y un reloj de CPU de 1.19MHz (dot clock / 3). Para el video componente 480P, cada línea debe emitirse dos veces, lo que implica una salida de reloj de puntos de 7.158MHz. Debido a que el chip de video (TIA) de Atari emite uno de los 128 colores usando una señal luma de 3 bits más una señal de fase con una resolución de aproximadamente 18 ns, sería difícil determinar con precisión el color con solo mirar las salidas. Un mejor enfoque sería interceptar las escrituras en los registros de color, observar los valores escritos y alimentar cada registro en los valores de luminancia TIA correspondientes al número de registro.
Todo esto se podría hacer con un FPGA, pero algunos dispositivos ARM bastante rápidos se pueden obtener mucho más baratos que un FPGA con suficiente RAM para manejar el almacenamiento en búfer necesario (sí, sé que para los volúmenes que tal cosa podría producirse, el costo no es ' t un factor real). Sin embargo, requerir que el ARM mire la señal del reloj entrante aumentaría significativamente la velocidad de CPU requerida. El recuento de ciclos predecible podría hacer las cosas más limpias.
Un enfoque de diseño relativamente simple sería hacer que un CPLD mire la CPU y el TIA y genere una señal de sincronización RGB + de 13 bits, y luego haga que ARM DMA tome valores de 16 bits de un puerto y los escriba en otro con el tiempo adecuado. Sin embargo, sería un desafío de diseño interesante ver si una ARM barata podría hacer todo. DMA podría ser un aspecto útil de un enfoque todo en uno si se pudieran predecir sus efectos en los recuentos de ciclos de la CPU (especialmente si los ciclos de DMA podrían ocurrir en ciclos cuando el bus de memoria estaba inactivo), pero en algún momento del proceso el ARM tendría que realizar sus funciones de búsqueda de tablas y observación de buses. Tenga en cuenta que, a diferencia de muchas arquitecturas de video en las que los registros de color se escriben durante los intervalos de supresión, el Atari 2600 escribe frecuentemente en los registros de color durante la parte mostrada de un cuadro,
Quizás el mejor enfoque sería usar un par de chips de lógica discreta para identificar escrituras en color y forzar los bits más bajos de los registros de color a los valores adecuados, y luego usar dos canales DMA para muestrear el bus de CPU entrante y los datos de salida TIA, y un tercer canal DMA para generar los datos de salida. La CPU sería libre de procesar todos los datos de ambas fuentes para cada línea de exploración, realizar la traducción necesaria y almacenarla en el búfer para la salida. El único aspecto de las tareas del adaptador que tendría que suceder en "tiempo real" sería la anulación de los datos escritos en COLUxx, y eso podría solucionarse utilizando dos chips lógicos comunes.
fuente
La información de tiempo está disponible, pero, como usted señaló, en ocasiones puede ser vaga. Hay mucha información sobre el tiempo en la Sección 18.2 y la Tabla 18.1 del Manual de referencia técnica para el Cortex-M3, por ejemplo ( pdf aquí ) y un extracto aquí:
que dan una lista de condiciones para el tiempo máximo. El tiempo para muchas instrucciones depende de factores externos, algunos de los cuales dejan ambigüedades. He resaltado cada una de las ambigüedades que encontré en el siguiente extracto de esa sección:
Para todos los casos de uso, será más complejo que el conteo "Esta instrucción es un ciclo, esta instrucción es dos ciclos, este es un ciclo ..." posible en procesadores más simples, más lentos y más antiguos. Para algunos casos de uso, no encontrará ambigüedades. Si encuentra ambigüedades, le sugiero:
Estos requisitos probablemente respondan a su pregunta: "No, no es una buena idea, a menos que las dificultades encontradas valgan la pena", pero eso ya lo sabía.
fuente
Una forma de solucionar este problema es utilizar dispositivos con tiempos deterministas o predecibles, como los chips Parallax Propeller y XMOS:
http://www.parallaxsemiconductor.com/multicoreconcept
http://www.xmos.com/
El conteo de ciclos funciona muy bien con el Propeller (se debe usar lenguaje ensamblador), mientras que los dispositivos XMOS tienen una utilidad de software muy poderosa, el Analizador de sincronización XMOS, que funciona con aplicaciones escritas en el lenguaje de programación XC:
https://www.xmos.com/download/public/XMOS-Timing-Analyzer-Whitepaper%281%29.pdf
fuente
El conteo de ciclos se vuelve más problemático a medida que se aleja de los microcontroladores de bajo nivel y se pasa a procesadores informáticos de uso más general. El primero generalmente tiene un tiempo de instrucción bien especificado, en parte por las razones por las que se encuentra. También se debe a que su arquitectura es bastante simple, por lo que los tiempos de instrucción son fijos y reconocibles.
Un buen ejemplo de esto son la mayoría de los PIC de Microchip. Las series 10, 12, 16 y 18 tienen un tiempo de instrucción muy bien documentado y predecible. Esta puede ser una característica útil en el tipo de pequeñas aplicaciones de control para las que están destinados estos chips.
A medida que se aleja del costo ultra bajo y, por lo tanto, el diseñador puede gastar más área de chips para obtener una mayor velocidad de una arquitectura más exótica, también se aleja de la previsibilidad. Eche un vistazo a las variantes modernas de x86 como ejemplos extremos de esto. Hay varios niveles de cachés, vitualización de la memoria, búsqueda anticipada, canalización y más, lo que hace que sea casi imposible contar los ciclos de instrucción. Sin embargo, en esta aplicación no importa, ya que el cliente está interesado en la alta velocidad, no en la previsibilidad del tiempo de instrucción.
Incluso puede ver este efecto en funcionamiento en modelos Microchip superiores. El núcleo de 24 bits (series 24, 30 y 33) tiene un tiempo de instrucción en gran medida predecible, excepto por algunas excepciones cuando hay contenciones de bus de registro. Por ejemplo, en algunos casos, la máquina inserta un bloqueo cuando la siguiente instrucción usa un registro con algunos modos de direccionamiento indirecto cuyo valor se modificó en la instrucción anterior. Este tipo de bloqueo es inusual en un dsPIC, y la mayoría de las veces puede ignorarlo, pero muestra cómo estas cosas se arrastran debido a que los diseñadores intentan darle un procesador más rápido y más capaz.
Entonces, la respuesta básica es que eso es parte de la compensación cuando elige un procesador. Para aplicaciones de control pequeñas, puede elegir algo pequeño, barato, de baja potencia y con tiempos de instrucción predecibles. A medida que exige más potencia de procesamiento, la arquitectura cambia, por lo que debe abandonar el tiempo de instrucción predecible. Afortunadamente, eso no es un problema a medida que llega a aplicaciones de uso general y de uso intensivo de cómputo, por lo que creo que las compensaciones funcionan razonablemente bien.
fuente
Sí, aún puedes hacerlo, incluso en un ARM. El mayor problema con eso en un ARM es que ARM vende núcleos, no chips, y se conoce el momento central, pero lo que el vendedor de chips envuelve varía de un proveedor a otro y, a veces, de una familia de chips a otra dentro del proveedor. Por lo tanto, un chip en particular de un proveedor en particular puede ser bastante determinista (si no usa cachés, por ejemplo), pero se vuelve más difícil de portar. Cuando se trata de 5 relojes aquí y 11 relojes allá usando temporizadores es problemático, ya que la cantidad de instrucciones que se necesitan para probar el temporizador y determinar si su tiempo de espera ha expirado. Por los sonidos de su experiencia de programación pasada, estoy dispuesto a apostar que probablemente depurará con un osciloscopio como lo hago yo, para que pueda probar un circuito cerrado en el chip a la velocidad del reloj, mirar el spi o i2c o cualquier forma de onda, agregar o eliminar los nops, cambiar el número de veces a través del ciclo y básicamente sintonizar. Al igual que con cualquier plataforma, no usar interrupciones ayuda en gran medida la naturaleza determinista de la ejecución de la instrucción.
No, no es tan simple como un PIC, pero aún es bastante factible, especialmente si el retraso / sincronización se acerca a la velocidad del reloj del procesador. Varios proveedores basados en ARM le permiten multiplicar la velocidad del reloj y obtener, por ejemplo, 60 MHz de una referencia de 8 mhz, por lo que si necesita una interfaz de 2 mhz en lugar de hacer algo cada 4 instrucciones, puede aumentar el reloj (si tiene el presupuesto de energía) y luego usa un temporizador y date muchos relojes para hacer otras cosas también.
fuente