Story Transcript
PROCESADORES CRUSOE DE TRANSMETA INTRODUCCIÓN Desde hace ya algunos años, la tendencia general en la fabricación de CPUs se ha basado en conseguir sobre todo dos objetivos: • Compatibilidad con la arquitectura llamada x86. • El mayor rendimiento posible en aplicaciones. Para lograr ambos objetivos, los principales fabricantes (como Intel o AMD) fabrican procesadores cada vez más rápidos y complejos, con una arquitectura interna de tipo RISC. Por tanto, para lograr ejecutar las instrucciones de la familia x86, estas CPUs realizan una traducción del código mediante un hardware específico. Las CPUs modernas poseen otro hardware especial para optimizar y reordenar código en tiempo de ejecución. Todos estos elementos hardware requieren espacio en la CPU y consumen energía eléctrica. El procesador Crusoe, en cambio, traslada estas funciones a una capa de software, muy estrechamente ligada al hardware de la CPU, resultando un procesador con menos superficie de silicio y menor consumo. Esto significa que los recursos habituales de una CPU como los encargados de la ejecución de instrucciones, sumas, restas, multiplicaciones, etc. son compartidos por los procesos de usuario y los específicos para las funciones de traducción de código, renombrado de registros, reordenamiento de instrucciones, etc. Este software de conversión de código (Code Morphing) reside en una ROM especial de la CPU, de tal manera que sea el primer software en ser cargado por la CPU, situándose desde ese momento como una capa intermedia entre las aplicaciones software (incluido el sistema operativo) y el hardware. Se trata de una CPU de tipo VLIW (Very Long Instruction Word), capaz de ejecutar hasta 4 operaciones por ciclo de reloj. A continuación pasaremos a describir los detalles técnicos de esta nueva familia de procesadores. ESTRUCTURA INTERNA Debido a la exclusión de elementos de hardware complejos (y caros), debido a la realización de ese trabajo por la capa de software, los procesadores Crusoe poseen una estructura interna simple y muy eficiente, compuesta por: • Dos unidades enteras (Integer ALU). • Una unidad de coma flotante (Floating Point Unit). • Una unidad de memoria para operaciones de lectura y escritura (Load / Store Unit). • Una unidad de salto (Branch Unit). Las instrucciones pueden ser de 64 o 128 bits, y el fabricante les llama moléculas. Cada molécula puede contener hasta cuatro instrucciones de tipo RISC, llamadas átomos. Todo los átomos de una molécula son ejecutados en paralelo, y la estructura de la molécula informa de cómo dichos átomos deben ser conducidos hacia su correspondiente unidad de proceso, logrando así simplificar el hardware de decodificación y planificación de las operaciones. Las moléculas se ejecutan en orden, evitando así complejo hardware para operaciones fuera de secuencia. 1
Para conseguir que el procesador ejecute instrucciones a la máxima velocidad, las moléculas se intenta que estén lo más llenas posible de átomos. Figura 1. Una molécula puede contener hasta cuatro átomos, que son ejecutados en paralelo. El banco de registros está formado por 64 registros enteros, %r0 al %r63. Parte de estos registros pueden estar usados por el software de Code Morphing. El ensamblador definido para este procesador, que observaremos más tarde, representa una molécula por línea, con átomos separados por el símbolo punto y coma. Los procesadores superescalares actuales, basados en la arquitectura x86 de Intel, disponen de un hardware dedicado a funciones tales como reordenar las instrucciones fuera de secuencia. Dicho hardware consume mucha energía, además de ocupar más espacio de silicio. EL SOFTWARE DE TRANSFORMACIÓN DE CÓDIGO (CODE MORPHING) El Code Morphing es fundamentalmente un sistema software de traducción dinámica, un programa que compila instrucciones del juego de instrucciones de una determinada arquitectura, transformándolas en el correspondiente código interno del Crusoe. El Code Morphing reside en una ROM y es el primer programa en ejecutarse al arrancar el procesador. El Code Morphing soporta ISA, y es lo único que el código x86 puede ver; el único programa escrito directamente para el entorno VLIW es el propio Code Morphing. En la figura 2 se muestra gráficamente la relación entre el código x86, el software Code Morphing y el procesador Crusoe. Figura 2. El software Code Morphing media entre el software x86 y el procesador Crusoe. Una ventaja de tener como único programa en contacto con el hardware de la máquina el Code Morphing es que para modificar el comportamiento, o realizar optimizaciones basta con modificar el Code Morphing, sin afectar los cambios a las aplicaciones escritas para x86. Este ocultamiento de la arquitectura bajo una capa de software soluciona en gran medida un problema que poseen los procesadores VLIW tradicionales, e incluso los x86 tradicionales, ya que exponen detalles sobre el pipeline del procesador a los compiladores. Esto requiere que los programas binarios antiguos deban ser recompilados, si no para permitir que sigan funcionando, al menos para poder aprovechar las mejoras que aporta el nuevo hardware. Este problema se aborda por los procesadores Crusoe, ya que el Code Morphing siempre recompila y optimiza el código x86 que está ejecutando. Inevitablemente, esta optimización dinámica de código requiere ciclos de reloj, que podrían ser usados por otros programas de usuario. DIVISIÓN ENTRE CAPAS: HARDWARE Y SOFTWARE. Emular una CPU x86 es tarea dificil, dada la complejidad de la arquitectura x86. La elección de que funciones implementar en software y cuales en hardware es un gran reto de la ingeniería, en el que intervienen aspectos como coste y complejidad, rendimiento global y consumo de energía. En sus primeros productos, la empresa Transmeta ha trazado la línea divisoria entre hardware y software de tal forma que el software se encarga de la compleja tarea de decodificar las instrucciones x86 y generar moléculas paralelas, que el hardware ejecuta a través de un motor VLIW muy simple, pero de gran velocidad. Unas pocas funciones hardware, que explicaremos más adelante, han sido añadidas para proporcionar un mejor soporte a la traducción dinámica. Esta línea divisoria entre hardware y software puede ser modificada, de manera que favorezca las exigencias de otro sector de productos, como por ejemplo servidores de gama alta.
2
La figura 3 muestra de manera gráfica la división que utiliza el procesador Crusoe. DECODIFICACIÓN Y PLANIFICACIÓN Los procesadores x86 superescalares extraen instrucciones x86 binarias de la memoria y la decodifican en micro−operaciones, que son reordenadas por un hardware específico de planificación e introducidas en las unidades funcionales para una ejecución en paralelo. En cambio, el Code Morphing puede traducir un grupo de instrucciones x86 a la vez. Mientras que los x86 tradicionales traducen una instrucción x86 cada vez que se ejecuta, el software de Transmeta traduce las instrucciones una sola vez, guardando el resultado de la traducción en una cache de traducción, de tal forma que la próxima vez que se ejecute esa instrucción se evitará el paso de la traducción, ejecutando el código optimizado que se encuentra en la caché de traducción. Implementar el paso de traducción in software abre nuevas oportunidades y retos. Debido a que un procesador fuera de secuencia (out−of−order) debe traducir y planificar instrucciones cada vez que se ejecutan, deben hacerlo de manera muy rápida. Esto limita drásticamente los tipos de transformaciones que puede producir dicho hardware. La aproximación que realiza el Code Morphing, en cambio, puede amortizar el coste de la traducción en varias ejecuciones, permitiéndole usar algoritmos de traducción y planificación mucho más sofisticados. Además, el coste energético también se reduce, al no tener que realizar esas funciones hardware cada ejecución de una instrucción. Finalmente, el software de traducción puede optimizar el código generado y potencialmente reducir el número de instrucciones ejecutadas en una traducción. Dicho de otra forma, Code Morphing puede acelerar la ejecución al tiempo que reduce el consumo de energía y el calor disipado. CACHE La cache de traducción, junto con el código del Code Morphing, reside en un espacio de memoria separado, inaccesible para el código x86. Para un mejor rendimiento, el software Code Morphing se copia a sí mismo de ROM a DRAM en la fase de inicio. El tamaño de este espacio de memoria puede ser especificado en el arranque, o el sistema operativo puede hacer dicho espacio ajustable. Como todo sistema de cache, la técnica de reutilización de traducciones del Code Morphing toma ventaja de la proximidad de referencias. Más concretamente, el sistema de traducción explota el alto grado de ratios de repeticion (el número de veces que un bloque traducido es ejecutado en media) observado en las aplicaciones reales. Tras haberse traducido un bloque, las repetidas ejecuciones tienen éxito en la caché de traducción y el hardware puede ejecutar la traducción optimizada a la máxima velocidad. Algunos programas de benchmark tratan de realizar una gran cantidad de operaciones en poco tiempo, con muy poca repetición, algo que difiere del uso normal de las aplicaciones. El bajo rendimiento del Code Morphing en dichas pruebas se hace evidente. En cambio, el Code Morphing aprende sobre los programas cada vez que se ejecutan, y optimiza su ejecución para que cada vez ésta sea más rápida. FILTRADO Generalmente, en las aplicaciones típicas una pequeña parte del código (a menudo menos del 10%) ocupa más del 95% del tiempo de ejecución. Es por ello que el sistema de traducción debe escoger cuidadosamente cuanto esfuerzo dedicar a la traducción de todo código x86 que le llegue. El Code Morphing incluye gran variedad de modos de ejecución para el código x86, que van desde la interpretación (que no tiene penalización por traducción, pero ejecuta código x86 más despacio), pasando por la traducción usando generación muy simple, hasta código altamente optimizado (que requiere más tiempo de 3
traducción, pero ejecuta mucho más rápido una vez generado). Un sofisticado conjunto de heurísticas ayudan a elegir entre esos modos de ejecución basándose en la información que dinámicamente aporta el código durante su ejecución. PREDICCIÓN Y SELECCIÓN DE CAMINO Una de las muchas formas que el Code Morphing tiene de recibir información de un programa x86 es la de instrumentar traducciones: el traductor incluye código cuyo único propósito es recoger información tal como frecuencias de ejecución del bloque, o histórico de saltos. Esta información puede ser usada posteriormente para decidir cuándo y qué optimizar y traducir. Por ejemplo, si un salto x86 tiene un comportamiento frecuente (por ejemplo, normalmente salta), el sistema puede optimizar en mayor medida la parte de código a la cual se suele saltar. En otro caso, para saltos más equilibrados (que pueden o no ser tomados, por ejemplo), el traductor puede ejecutar especulatívamente ambas partes de código y seleccionar el resultado correcto después. Análogamente, sabiendo con qué frecuencia se ejecuta una parte del código x86 ayuda a decidir cuánto se debe intentar optimizar ese código. Este tipo de decisiones sería extremadamente difícil de abordar por las implementaciones x86 tradicionales, basadas únicamente en hardware. EJEMPLO DE TRADUCCIÓN DE INSTRUCCIONES Suponiendo que los algoritmos de filtrado y selección de camino han elegido el siguiente conjunto de instrucciones x86 para su traducción: A. addl %eax, (%esp) // load data from stack, add to %eax B. addl %ebx, (%esp) // ditto, for %ebx C. movl %esi,(%ebp) // load %esi from memory D. subl %ecx,5 // subtract 5 from %ecx register En una primera pasada, el sistema de traducción decodifica las instrucciones x86 y las traduce a una secuencia simple de átomos. En este punto, todavía es fácil de ver la correspondencia entre el código original y el generado. (Los registros %r30 y %r31 se usan como temporales para operaciones de lectura de memoria.) ld %r30, [%esp] //load from stack, into temporary add.c %eax,%eax,%r30 //add to %eax, set condition codes ld %r31, [%esp] add.c %ebx,%ebx,%r31 ld %esi, [%ebp] sub.c %ecx,%ecx,5 En una segunda pasada, el optimizador aplica optimizaciones bien conocidas por los compiladores. Esta fase pone de manifiesto optimizaciones que una implementación sólo de hardware no puede hacer: una traducción basada en software puede eliminar átomos del flujo de instrucciones, en lugar de simplemente reordenarlas. En este ejemplo, todas menos la última opción de comando de establecimiento de condición (.c) son 4
innecesarias, y uno de los átomos de carga (ld) es redundante, quedando menos átomos para ejecutar. ld %r30, [%esp] //load from stack only once add %eax,%eax,%r30 add %ebx,%ebx,%r30 //reuse data loaded earlier ld %esi, [%ebp] sub.c %ecx,%ecx,5 //only this last condition code needed En un último paso, el scheduler reordena los átomos agrupándolos en moléculas individuales. Este proceso es similar al realizado por el hardware llamado dispatcher de los procesadores de fuera de secuencia. En cambio, usar software para planificar el código, resulta más flexible a la hora de usar algoritmos de planificación más efectivos. Tras la planificación hemos reducido las cuatro instrucciones x86 originales a dos moléculas. 1. ld %r30, [%esp]; sub.c %ecx,ecx,5 2. ld %esi, [%ebp]; add %eax,eax,%r30; add %ebx,%ebx,%r30 SOPORTE HARDWARE DEL CODE MORPHING La traducción dinámica en los procesadores convencionales supondría un rendimiento bastante pobre. El procesador Crusoe, en cambio, ha sido diseñado desde un principio con la idea de la traducción dinámica, logrando por tanto un buen rendimiento en dicha tarea. Seguidamente, discutiremos tres funcionalidades hardware para dar soporte a excepciones, ejecución especulativa, optimización de operaciones de memoria, y código auto−modificable. EXCEPCIONES Y ESPECULACIÓN Sin un hardware específico es generalmente difícil, para un sistema de traducción dinámico, modelizar correctamente la semántica de las excepciones logrando a su vez un rendimiento adecuado. La razón es que la semántica de las excepciones impone severas restricciones sobre la planificación de instrucciones. Consideremos el ejemplo anterior, en el que el siguiente código x86: A. addl %eax, (%esp) // load data from stack, add to %eax B. addl %ebx, (%esp) // ditto, for %ebx C. movl %esi,(%ebp) // load %esi from memory D. subl %ecx,5 // subtract 5 from %ecx register se traducía en las dos moléculas siguientes: 1. ld %r30, [%esp]; sub.c %ecx,ecx,5 2. ld %esi, [%ebp]; add %eax,eax,%r30; add %ebx,%ebx,%r30 En la arquitectura ISA de los x86, las excepciones son precisas, cuando una instrucción genera una excepción, todas las instrucciones precedentes deben completarse antes de ser comunicada dicha excepción, y ninguna de 5
las instrucciones posteriores deben completarse. Se puede observar que en la traducción mostrada, los átomos se suceden fuera de secuencia respecto al código x86 original. Supongamos ahora que durante la ejecución la instrucción de carga (ld) de la molécula 2, correspondiente a la instrucción x86 C, genera un fallo de página. Sin embargo, en ese instante, el procesador ha ejecutado ya código en la molécula 1 correspondiente a la instrucción D, lo cual viola las reglas de las excepciones precisas. Resolver este problema sin un hardware específico restringe la planificación de instrucciones de manera indebida, o requiere instrucciones adicionales, cada una reduciendo el rendimiento del sistema en el caso común en que no ocurran excepciones. Los procesadores con ejecución fuera de secuencia tradicionales también padecen este problema. Normalmente utilizan mecanismos complejos de hardware para retrasar o deshacer los efectos de microoperaciones ejecutadas demasiado pronto. El procesador Crusoe proporciona una solución hardware mucho más simple que trabaja codo con codo con el software Code Morphing. A todos los registros que contienen información sobre el estado x86 se les realiza una operación de shadow, es decir, existen dos copias de cada registro, la actualmente en ejecución y la shadow. Los átomos normales sólo actualizan la copia actual de los registros. Cuando la ejecución alcanza el final de una traducción sin encontrar una excepción, una operación especial de actualizado (commit) copia todos los registros actuales a sus correspondientes registros shadow. Por otra parte, si cualquier excepción a nivel x86 ocurre durante la traducción, el sistema deshace los efectos de todas las moléculas ejecutadas desde el principio de la traducción. Esto se realiza mediante una operación llamada de rollback, que restaura los valores de los registros actuales con las copias (shadow) existentes previamente a la traducción. En este punto, el Code Morphing re−ejecuta las instrucciones x86 cuidadosamente, es decir, en su orden original, con el fin de determinar la posición concreta de la excepción. Deshacer cambios en la memoria es ligeramente más complejo. El procesador Crusoe mantiene las operaciones de escritura (store) llenando los datos almacenados en un buffer (gated store buffer), de donde sólo son llevados a memoria en la operación antes comentada de commit. En una operación de restauración, las operaciones de store que no han recibido la operación de commit, pueden ser simplemente borradas del buffer. Para aumentar la velocidad en el caso más común (sin excepciones), el hardware del procesador Crusoe está diseñado para que las operaciones de actualizado (commit) sean prácticamente gratuitas. ALIAS DE HARDWARE Cuanta más libertad posea el planificador para mover átomos en el llenado de moléculas, mejor código generará normalmente. Uno de los mayores límites a esta libertad viene dada por las potenciales dependencias entre operaciones de memoria. En particular, a menudo sería deseable reordenar operaciones de lectura delante de operaciones de escritura. En cambio, hacer esto es incorrecto si la operación de lectura usa datos de la operación de escritura, y esta no es tarea fácilmente realizable en tiempo de traducción. El procesador Crusoe proporciona el innovador sistema de hardware alias que ataja en cierta medida el problema. Cuando el traductor mueve una operación de lectura delante de una de escritura, convierte la de lectura en una de lectura−y−protección (que además de leer los datos también registra las direcciones y tamaño de los datos leídos) y la de escritura en una de escritura−bajo−mascara−de−alias (que verifica regiones protegidas). Si la operación de escritura sobreescribe los datos antes leídos, el procesador lanza una excepción y el sistema puede realizar la corrección. Usando este mecanismo, siempre es seguro el reordenamiento de instrucciones de lectura y escritura en memoria. De nuevo, el procesador Crusoe proporciona un mecanismo hardware muy simple, que combinado con software es capaz de resolver el problema. CÓDIGO AUTO−MODIFICABLE
6
A veces, las instrucciones x86 en memoria se sobreescriben, bien porque el sistema operativo está cargando un nuevo programa, o porque una aplicación está utilizando código auto−modificable. Cuando ocurre esto al código ya traducido, el Code Morphing debe ser avisado para evitar que ejecute código antiguo erróneamente. Con este fin, cuando el sistema traduce un bloque de código x86, protege contra escritura la página de memoria x86 que contiene ese código. Esto lo hace utilizando un bit dedicado en la entrada de esa página, situada en la unidad de manejo de la memoria (MMU). Como sucede con otros detalles del hardware VLIW, ese bit es invisible para el software x86. Cuando una página protegida es escrita, el remedio más simple es invalidar el o las traduccion(es) implicada(s). Como el sistema aprende de manera dinámica acerca del comportamiento del programa, utiliza estrategias muy sofisticadas. ARQUITECTURA DEL MODELO TM5400 El procesador Crusoe incorpora unidades enteras y de coma flotante, caches separadas de instrucciones y datos, una cache de nivel 2 con política write−back, unidad de manejo de memoria (MMU) e instrucciones multimedia. Además de estas características, el dispositivo dispone de un controlador de memoria DDR SDRAM, un controlador SDR RDRAM, un controlador de bus PCI y un controlador ROM serie. A continuación describiremos brevemente los detalles técnicos del modelo más alto de la actual gama: el TM5400. • Procesador capaz de operar en el rango de 500−700 MHz. • Cache de nivel 1 de de 64KB integrada, cache de datos de nivel 1 de 64KB, y cache de nivel 2 de 256KB con política write−back. • Controlador de memoria DDR SDRAM con interfaz de 100−133MHz y 2.5V • Controlador de memoria SDR SDRAM con interfaz de 66−133MHz y 3.3V • Controlador de bus PCI con interfaz de 33MHz y 3.3V • LongRun: sistema avanzado de administración de enegía • 1−2 W a 500−700MHz, 1.2−1.6 V con aplicaciones multimedia típicas. • 30 mW en sistema suspendido. • Completo soporte de System Management Mode (SSM) • Encapsulado cerámico compacto BGA de 474 pines. En la figura 4 se puede observar a grandes rasgos el diagrama de bloques del procesador Crusoe TM5400 de Transmeta. Figura 4. Diagrama de bloques del Crusoe TM5400.
7