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.