Está en la página 1de 87

Proyecto final de carrera

Curso 2005–2006
Ingenierı́a Informática

Reducción de consumo de
energı́a estática en memorias
cache mediante
apagado de ceros

Autor:
Rafael Ubal Tena

Directores:
Julio Sahuquillo Borrás
Salvador Petit Martı́
Índice

Índice i

Resumen iv

1 Introducción 1
1.1 Descripción de la problemática actual . . . . . . . . . . . . . . 1
1.2 Disipación de energı́a . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Soluciones propuestas sobre energı́a estática . . . . . . 3
Modelos identificando las lı́neas menos utilizadas . . . 4
Modelos basados en la localidad . . . . . . . . . . . . . 5
1.2.2 Soluciones propuestas sobre energı́a dinámica . . . . . 5
Compresión dinámica de ceros . . . . . . . . . . . . . . 6
Dynamic Zero-Sensitivity . . . . . . . . . . . . . . . . . 6
Frequent Value Cache . . . . . . . . . . . . . . . . . . 7
1.3 Objetivos y organización del proyecto . . . . . . . . . . . . . . 8

2 Técnicas de ahorro de energı́a estática 9


2.1 Cache decay . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.1.1 Descripción . . . . . . . . . . . . . . . . . . . . . . . . 9
2.1.2 Implementación hardware . . . . . . . . . . . . . . . . 11
2.2 Drowsy caches . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

i
2.2.1 Descripción . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.2 Implementación hardware . . . . . . . . . . . . . . . . 14
2.3 Técnica propuesta: apagado de ceros . . . . . . . . . . . . . . 15
2.3.1 Descripción . . . . . . . . . . . . . . . . . . . . . . . . 15
2.3.2 Implementación hardware . . . . . . . . . . . . . . . . 18
Primera aproximación . . . . . . . . . . . . . . . . . . 20
Segunda aproximación . . . . . . . . . . . . . . . . . . 21
2.3.3 Resolución de apagado . . . . . . . . . . . . . . . . . . 24
2.3.4 Área del chip . . . . . . . . . . . . . . . . . . . . . . . 30

3 Simulador desarrollado 32
3.1 Ficheros del simulador . . . . . . . . . . . . . . . . . . . . . . 35
3.2 Descripción de la arquitectura MIPS . . . . . . . . . . . . . . 39
3.2.1 Juego de instrucciones . . . . . . . . . . . . . . . . . . 40
3.3 Partes principales del simulador . . . . . . . . . . . . . . . . . 41
3.3.1 Etapas del procesador superescalar genérico . . . . . . 41
Etapa fetch . . . . . . . . . . . . . . . . . . . . . . . . 42
Etapa dispatch . . . . . . . . . . . . . . . . . . . . . . 42
Etapa issue . . . . . . . . . . . . . . . . . . . . . . . . 44
Etapa writeback . . . . . . . . . . . . . . . . . . . . . . 45
Etapa commit . . . . . . . . . . . . . . . . . . . . . . . 46
3.3.2 RUU (Register Update Unit) . . . . . . . . . . . . . . . 47
3.3.3 Control de dependencias de datos . . . . . . . . . . . . 51
Referencias a elementos de la RUU . . . . . . . . . . . 51
Listas de dependencias . . . . . . . . . . . . . . . . . . 54
3.3.4 Definición del juego de instrucciones . . . . . . . . . . 56
Definición del comportamiento de una instrucción . . . 57
Definición del formato de una instrucción . . . . . . . . 59

ii
3.4 Herramientas adicionales para el desarrollo y depuración . . . 63
3.4.1 Cálculo de máscaras de instrucción . . . . . . . . . . . 63
3.4.2 Visor de archivos binarios . . . . . . . . . . . . . . . . 64
3.4.3 Aplicación pedagógica de la herramienta . . . . . . . . 65

4 Resultados 68
4.1 Entorno de simulación . . . . . . . . . . . . . . . . . . . . . . 68
4.2 Resultados de las simulaciones . . . . . . . . . . . . . . . . . . 70

5 Conclusiones 74
5.1 Contribuciones . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.2 Trabajo futuro . . . . . . . . . . . . . . . . . . . . . . . . . . 77

Referencias 79

iii
Resumen

El auge de los dispositivos móviles y las necesidades de la computación de


altas prestaciones, entre otros factores, han motivado una amplia investi-
gación en arquitecturas de bajo consumo. Concretamente, el consumo de
energı́a estática en los microprocesadores actuales, ası́ como en futuras tec-
nologı́as, tiene un impacto cada vez más importante sobre la energı́a total
disipada en el chip. Las memorias cache, que ocupan un gran porcentaje de
su superficie, son buenas candidatas sobre las que proponer nuevos diseños
que ataquen este problema.
En este proyecto, se discuten las polı́ticas actuales de reducción de con-
sumo en memorias cache, y se propone una nueva técnica que, combinada
con las anteriores, ofrece resultados satisfactorios.
Esta técnica, denominada apagado de ceros, se basa en explotar el hecho
de que un gran porcentaje de las palabras albergadas en memoria cache tienen
todos sus bits a 0, ó cuanto menos, parte sus bits de mayor peso. Detectando
estas situaciones, se pretende apagar grupos de bits a 0, almacenando en
celdas de memoria adicionales cuáles son los bits afectados.
La parte innovadora del desarrollo del proyecto consiste en evaluar esta
técnica y discutir la mejora obtenida respecto a técnicas anteriores. También
se proponen implementaciones hardware para los procedimientos en los que
repercute su aplicación, como pueden ser la lectura de una palabra, la escri-

iv
tura o el mecanismo de apagado/encendido de las celdas SRAM.
Las ventajas de la aplicación de la técnica de apagado de ceros quedan
avaladas por los resultados estadı́sticos obtenidos a partir de la simulación
de la ejecución de la suite de benchmarks SPEC2000. Para tal efecto, se
considera parte de este proyecto el desarrollo completo de una herramienta
de simulación, basada en otra ya existente, de nombre SimpleScalar.
La nueva herramienta pretende disminuir la complejidad de SimpleSca-
lar, de forma que no se pierda potencia de simulación para los experimentos
que afectan a este proyecto. Por el contrario, se amplı́a dicha potencia,
permitiendo obtener estadı́sticas sobre el consumo de energı́a estática de la
memoria cache.
La construcción del modelo está orientada a tareas de investigación futu-
ras, incorporando cierto soporte para simulación de procesadores multi-hilo
o multiprocesadores.

v
Capı́tulo 1

Introducción

Hasta hace pocos años las memorias cache ocupaban un porcentaje elevado
del área del procesador, del orden de 1/3 a 3/4 [1]. Este hecho motivó que
durante el periodo de tiempo comprendido desde 1996 hasta aproximada-
mente 2002 ó 2003, muchos trabajos de investigación sobre memorias cache
se centraran en la propuesta de organizaciones alternativas a la cache con-
vencional. El problema crı́tico residı́a en el hecho de que los transistores eran
un bien preciado dentro del procesador. Por tanto, si se reducı́a el tamaño
de la cache, manteniendo las prestaciones, se disponı́a de un mayor número
de transistores que se podı́a dedicar otros aspectos del procesador.

1.1 Descripción de la problemática actual


Recientemente, a consecuencia de los avances tecnológicos, la situación ha
cambiado, y el problema crı́tico no aparece en el número de transistores dis-
ponibles; por ejemplo, algunos modelos del procesador Itanium 2 incorporan
una memoria cache de nivel 3 de 6 MB. Actualmente la investigación sobre
memorias cache se dirige a resolver dos problemas principales: i) garantizar

1
el tiempo de ciclo y ii) reducción del consumo.

i) El primer problema se centra en la propuesta de esquemas alternativos


de cache cuyo tiempo de acceso no sobrepase (o por lo menos, no lo
haga en exceso) el tiempo de ciclo del procesador. Para resolverlo,
algunas propuestas se centran en arquitecturas basadas en clusters o
CMPs (Core Multi-Processors), donde distintas unidades funcionales
del procesador se agrupan en varios conjuntos [2].

En lo que a la cache se refiere, cada cluster podrı́a disponer de su propia


cache. Ası́, por ejemplo, en vez de tener una única cache de datos de
nivel 1 de 64 KB se podrı́an tener 4 de 16 KB con un tiempo de acceso
menor. La organización de los esquemas de cache y su interconexión se
puede resolver de varias maneras; por ejemplo, utilizando un protocolo
de coherencia o mediante una organización especı́fica incluyendo buffers
accesibles desde todos los clusters [3].

ii) El problema de la disipación de potencia o reducción del consumo de


energı́a empezó relacionado con los procesadores que incorporaban los
ordenadores portátiles y dispositivos móviles. El problema ha ido cre-
ciendo debido a que un incremento en la frecuencia de reloj precisa de
una mayor densidad de transistores en los procesadores, lo que significa
un incremento sustancial de consumo y de la necesidad de disipación
de energı́a.

Hoy en dı́a, el problema afecta en gran medida a los procesadores de al-


tas prestaciones que representan un importante segmento del mercado
actual. Por tanto, es un problema creciente que necesita de solucio-
nes rápidas y efectivas. Este problema tiene una mayor repercusión,
si cabe, en aquellas partes del procesador que ocupan un importante

2
porcentaje del área del chip, o donde la frecuencia de conmutación de
los transistores es más elevada: por ejemplo la lógica de emisión de
instrucciones (issue) o las memorias cache.

1.2 Disipación de energı́a


En los circuitos CMOS el consumo de potencia dominante es el dinámico,
que tiene lugar cuando los transistores cambian el valor de la salida. La
potencia dinámica es proporcional al cuadrado del voltaje suministrado; por
este motivo, la técnica comúnmente aplicada ha sido reducir el voltaje de
entrada para reducir el consumo. Aunque esta técnica es efectiva, queda
por resolver la energı́a estática, que es aquella que se disipa constantemente,
incluso aunque los transistores no cambien su valor.
La energı́a estática representaba en el pasado un problema despreciable.
Sin embargo, con el tiempo ha adquirido un creciente protagonismo, ya que
está directamente relacionada con la densidad de transistores en el chip.
Hoy en dı́a, se estima que la energı́a estática representa entre el 15 y de
20% del total de potencia disipada en un procesador [4] y las previsiones
tecnológicas estiman que este consumo aumentará exponencialmente cuando
la tecnologı́a de fabricación se sitúe por debajo de 0.1 micra.

1.2.1 Soluciones propuestas sobre energı́a estática

Recientemente se han propuesto distintos mecanismos orientados a la re-


ducción del consumo de la energı́a estática. Atendiendo a la solución pro-
puesta, los modelos publicados se pueden clasificar en dos grandes grupos:
los que persiguen identificar las lı́neas de cache (bloques) no utilizadas du-
rante un periodo de tiempo y aquéllos que orientan el modelo con miras a

3
que se beneficie de la localidad de los datos.

Modelos identificando las lı́neas menos utilizadas

Los modelos que identifican las lı́neas menos utilizadas argumentan que du-
rante un periodo de tiempo dado, el procesador sólo referencia a un porcen-
taje relativamente pequeño de lı́neas de la cache. Por tanto, si se pudieran
identificar estas lı́neas, podrı́an desactivarse para reducir el consumo. Los
modelos deben resolver las siguientes cuestiones:

1. ¿Qué lı́neas desactivar?

2. ¿Cuándo desactivarlas?

3. ¿Cuándo volver a activarlas?

4. ¿Se desactivan completamente o se dejan en un estado de “reposo”?

Por otro lado, algunos de los modelos de este primer grupo desactivan
las lı́neas completamente perdiendo la información almacenada. Por ello,
previo a la desactivación, en caso de que la lı́nea se encuentre modificada
debe actualizarse en la cache de nivel 2. Este es el caso del modelo Cache
Decay propuesto por Kaxiras et al [8]. Por supuesto, si se accede a una lı́nea
que se acaba de desactivar, se producirá un fallo en la cache de nivel 1 y se
accederá a la de nivel 2. Por este motivo, se produce una penalización en las
prestaciones respecto a una cache convencional.
Otros modelos sugieren no desactivar las lı́neas completamente, sino de-
jarlas en un estado dormido o drowsy. En este caso, el funcionamiento es el
siguiente: supongamos que un conjunto tiene 4 vı́as y 2 se encuentran activas
mientras que las otras 2 en estado drowsy. Si se produce un fallo en las lı́neas
activas, se despierta a las que están en estado drowsy para ver si alguna de

4
ellas contiene la lı́nea referenciada. Sólo en el caso de que continúe habiendo
fallo después de despertarlas, se accederá a la cache de nivel 2. Es el caso
del modelo de las Drowsy Caches propuesto por Flautner et al [9]. Se puede
observar que, al igual que el esquema anterior, surge una penalización en las
prestaciones.

Modelos basados en la localidad

Otros autores, como Abella y González [5], segregan el nivel 1 de cache en


dos organizaciones de distinto tamaño y tiempo de acceso independientes,
cada una de ellas implementada con una tecnologı́a diferente. A cada cache
se le suministra un voltaje distinto por lo que su consumo también difiere,
siendo la más rápida la que más consume. Cuando se produce un fallo en
la rápida y un acierto en la lenta, el bloque se transfiere desde la lenta a la
rápida, de manera análoga a la ya clásica Victim Cache.
Otros autores, como Chen et al, proponen un modelo basado en la loca-
lidad espacial para desactivar las lı́neas perdiendo apenas prestaciones [6].

1.2.2 Soluciones propuestas sobre energı́a dinámica

A pesar de que este trabajo se centra en paradigmas de reducción de energı́a


estática, existen modelos publicados destinados a la energı́a dinámica que
gozan de especial interés, puesto que se basan en el mismo fenómeno que la
técnica propuesta en este proyecto. Se trata del predominio de los bits a 0
de los datos de la memoria cache frente a los bits a 1.
Este fenómeno se puede explotar de diferentes maneras, siendo una de
ellas la elusión de la lectura de los bits a 0, con el ahorro de energı́a dinámica
consecuente, a costa de introducir, como siempre, hardware de control adi-
cional y lógica extra por grupo de celdas.

5
A continuación se da una breve descripción de las técnicas existentes:

Compresión dinámica de ceros

Este modelo fue publicado por L. Villa et al [11], y su nombre original es


Dynamic Zero Compression o DZC. Pretende extraer los beneficios de la
asimetrı́a de la distribución de 1’s y 0’s en la memoria cache de datos. Se
basa en agrupar varios bits y adjuntarles un bit extra indicador de cero (ZIB).
Este bit estará activo cuando cada uno de los bits del grupo al que afecta
valga 0, ingorándose el contenido de las celdas asociadas a ellos. Cuando el bit
ZIB esté desactivado, las celdas correspondientes al grupo de bits afectados
serán las que determinen su valor.
Este esquema conlleva un ahorro de energı́a tanto en escrituras como en
lecturas de grupos de bits a 0. En el caso de una escritura de este tipo, el
único bit que se debe modificar es ZIB, dejando el resto de celdas intactas.
En el caso de una lectura, se comprueba en primer lugar el bit ZIB. Si este
bit vale 1, no se leen el resto de celdas, sino que se proporciona un grupo de
0’s al procesador. Además consigue un ahorro de energı́a dinámica adicional
al disminuir el tráfico de bits entre el procesador y la memoria cache.

Dynamic Zero-Sensitivity

El modelo DZS fue propuesto por Y.J. Chang et al [12]. Al igual que el
anterior, pretende explotar la asimetrı́a de distribución de 1’s y 0’s, pero esta
vez reduciendo el consumo dinámico de las lecturas de bits a 0 aislados. Esto
se consigue impidiendo que las lı́neas de bits se descarguen cuando durante
la lectura de un 0, haciendo que ésta sea mucho menos costosa que la lectura
de un 1.
En su publicación, los autores proponen dos alternativas de implemen-

6
tación para su esquema: DZS D y DZS S. Cada una de ellas supone una
desventaja frente a la implementación de una cache normal. La primera im-
plica un aumento considerable del área ocupada dentro del chip, mientras
que la degradación de la estabilidad de la señal es un inconveniente de la
segunda. En cualquier caso, la celda de memoria queda modificada ligera-
mente, incorporando varios transistores extra.
En ambas implementaciones, el diseño se centra en el amplificador que
suministra los datos leı́dos de las celdas al procesador. Para el caso de la
lectura de un 1, el amplificador debe funcionar normalmente, detectando y
amplificando la diferencia de potencial entre las lı́neas de bit. En cambio, es
al leer un 0 cuando debe transformar la ausencia de diferencia de potencial
en un 0 lógico, puesto que el contenido de la celda implicada no ha sido
descargado.

Frequent Value Cache

La propuesta Frequent Value Cache (FVC), presentada por J. Yang et al [7],


consiste en una técnica que intenta detectar los valores más frecuentes alma-
cenados en memoria para depositarlos en repositorios especiales, como por
ejemplo una cache aislada de menor tamaño. El principal beneficio obtenido
con FVC proviene de la reducción del área del chip sin afectar a las presta-
ciones, a costa de diseños relativamente complejos de las antememorias.
En FVC, el repositorio de valores frecuentes se combina con una memo-
ria cache de correspondencia directa, demostrándose que el porcentaje de
aciertos es prácticamente igual que en un diseño estándar de la jerarquı́a de
memoria. Esta aproximación está destinada de nuevo a la reducción de la
energı́a dinámica, explotando ciertas caracterı́sticas de la distribución de los
datos situados en memorias cache.

7
1.3 Objetivos y organización del proyecto
Observando las técnicas ya disponibles, el objetivo del proyecto se centra en
idear una técnica de reducción de consumo innovadora, basada en la energı́a
estática. Una vez establecido su funcionamiento, se trata de evaluar sus
beneficios y de analizar el efecto de su implantación sobre la arquitectura y
las prestaciones globales de un procesador. Tras dicho análisis, el diseñador
se encuentra en condiciones de tomar la decisión de adaptar la nueva técnica
a su implementación, y de elegir coherentemente entre una de sus posibles
variantes.
Esta lı́nea de argumentación conduce a la siguiente estructuración del pro-
yecto: en el capı́tulo 2 se describen diferentes técnicas de ahorro de energı́a
estática, entre las que se encuentra la técnica propuesta; se hacen constar
sus variantes y sus posibles implementaciones hardware. El capı́tulo 3 pre-
senta una herramienta de simulación construida para llevar a cabo el proceso
de evaluación de la técnica, mientras que el capı́tulo 4 describe y muestra
gráficamente los resultados que el simulador proporciona. Por último, el
capı́tulo 5 contiene las conclusiones que se pueden extraer del trabajo reali-
zado (ámbito de aplicación de la técnica, grado de innovación, investigaciones
futuras, etc).

8
Capı́tulo 2

Técnicas de ahorro de energı́a


estática

En este capı́tulo se explican las técnicas de reducción de consumo en la memo-


ria cache de nivel 1 modeladas en la herramienta construida. Estas técnicas
son ortogonales, es decir, pueden aplicarse simultáneamente. A partir de los
resultados obtenidos por el simulador, se toma la decisión de qué técnicas
incorporar, y de cómo influye cada una de ellas en las prestaciones globales
de un procesador.

2.1 Cache decay

2.1.1 Descripción

Este trabajo lo llevaron a cabo S. Kaxiras, Z. Hu y M. Martonosi [8]. El


estudio de estos autores se centra en las técnicas de apagado completo, los
criterios de su aplicación y la implementación hardware de cada una de ellas.
Su propuesta se basa en una propiedad de la distribución de los accesos

9
a una determinada lı́nea de cache: normalmente, cuando se incorpora un
nuevo bloque a una lı́nea, éste recibe una secuencia continua de accesos;
cuando termina esta secuencia, comienza un periodo de tiempo muerto (dead
time) antes de que la lı́nea sea sustituida por otra nueva. Raramente existe
un acceso a la misma lı́nea de cache después de haber entrado en la zona de
tiempo muerto, con lo que es posible apagarla por completo, perdiendo ası́
lo datos, sin afectar prácticamente a las prestaciones.
Concretamente, estiman mediante experimentos que el intervalo entre
accesos a una lı́nea de cache durante su tiempo de vida es del orden de 100
ciclos. Por otra parte, la duración media de los tiempos muertos es del orden
de 20.000 ciclos. La gran diferencia entre estos dos tiempos hace intuir que
se podrá identificar de una manera fiable el comienzo del tiempo muerto
durante la ejecución atendiendo a estos valores.
En general, se busca una aproximación al caso ideal de apagado, en el
que una lı́nea deja de recibir tensión tras el momento en que es accedida por
última vez y hasta que deba ser sustituida por un nuevo bloque, es decir, justo
en el comienzo del tiempo muerto. Esta aproximación requerirı́a información
de lo que va a suceder durante la ejecución de instrucciones futuras, por lo
que su implementación no es viable.
Por otro lado, un apagado equivocado antes de haber alcanzado el tiempo
muerto (tras el cual se accede la misma lı́nea), produce una penalización de
un fallo extra de cache y un posible writeback en el caso en que el bloque
apagado hubiera sido modificado (dirty). Teniendo esto en cuenta, se pro-
pone la polı́tica de apagado basado en el tiempo (time-based leakage control):
la lı́nea se apaga en el momento en que se ha consumido desde el último
acceso la misma energı́a que se habrı́a consumido si se hubiera apagado de
forma errónea justo después de ese acceso. Esto se traduce, dependiendo del

10
fabricante, en un rango de 1K a 512K ciclos del procesador.
Para evaluar las prestaciones en cada caso, los autores realizan simula-
ciones utilizando el conjunto de benchmarks SPEC2000.

2.1.2 Implementación hardware

La implementación hardware correspondiente al apagado de una lı́nea de


cache consiste en la incorporación de un transistor NMOS que usa la técnica
gated Vdd [16]. Este transistor une la masa con las celdas SRAM de la lı́nea
de cache. Cuando el transistor no conduzca, hace que las corrientes de fuga
de los transistores que forman la lı́nea sean despreciables. La inclusión del
hardware de control de apagado supone un área extra del 5%.
Para apagar una lı́nea de cache tras un número determinado de ciclos,
hay que incluir contadores para cada lı́nea. Si hubiera que hacer una cuenta
de 512K ciclos tras un acceso, se precisarı́a un contador de 19 bits por lı́nea,
lo cual supondrı́a un aumento desmesurado del área de la memoria cache,
además de un consecuente consumo dinámico extra en las transiciones de los
contadores.
Esto se soluciona añadiendo un contador global, que genere una cuenta
de Ng ciclos, y un contador local de dos bits para cada lı́nea, que cuenta
desbordamientos del contador global. Esto introduce una inexactitud en la
cuenta, ya que alguna lı́nea puede apagarse transcurridos 3Ng ciclos, mientras
que otra puede llegar a apagarse transcurridos exactamente 4Ng ciclos. En
promedio, las lı́neas de la cache perderán la alimentación después de 3.5 × Ng
ciclos del procesador. Además, los contadores locales siguen una codificación
Gray, en la que sólo cambia de estado un bit en cada transición, con el
objetivo de reducir el consumo dinámico introducido.
El uso de un contador global puede tener como consecuencia la gene-

11
ración de una gran cantidad de writebacks simultáneos, en el caso en que se
deban apagar múltiples bloques que hayan sido modificados. Esto se solu-
ciona incorporando la señal del reloj global en cascada a todos los contadores
locales. Cuando un contador local recibe un tic del reloj global, lo transmi-
tirá al contador local siguiente con un ciclo de latencia. Esto no cambia el
funcionamiento de la polı́tica de apagado, mientras que evita un surgimiento
masivo de writebacks.
La cuenta de ciclos del reloj del procesador mediante el sistema de con-
tadores locales y uno global induce también un comsumo extra en las tran-
siciones de los bits de los contadores locales, al igual que en las del contador
global. Los experimentos realizados demuestran que este consumo es de cua-
tro órdenes de magnitud menor que la energı́a estática de toda la memoria
cache en un ciclo y, por tanto, despreciable.

2.2 Drowsy caches

2.2.1 Descripción

Esta técnica fue propuesta por K. Flautner, N.S. Kim, S. Martin, D. Blaauw
y T. Mudge [9]. Consiste en alimentar las lı́neas de cache con una tensión
menor en el momento en que se prevea que no van a ser accedidas. La
reducción del consumo es menor que en el caso del apagado completo, pero
se obtiene la ventaja de que los datos persisten mientras están en modo de
bajo consumo. Por esta razón, se puede aplicar esta polı́tica de una forma
mucho más agresiva que en el caso de cache decay. Las lı́neas en modo
drowsy requieren una pequeña latencia de activación de uno o dos ciclos del
procesador antes de poder ser accedidos.
La diferencia principal en prestaciones entre el mecanismo de apagado

12
completo y el del modo de bajo consumo es la penalización por una aplicación
errónea. Cuando se predice que una lı́nea no va a accederse, se le aplica una
tensión menor; si la predicción fue incorrecta y la lı́nea se accede, obtenemos
únicamente una latencia y el consumo extra correspondiente a la activación
de esa lı́nea, mientras que en el caso de cache decay, un error implica la
recuperación del bloque desde un nivel inferior de memoria.
Los autores presentan en su publicación una serie de factores que, com-
binados, dan lugar a un conjunto de polı́ticas de puesta en modo de bajo
consumo. El algoritmo genérico consiste en evaluar periódicamente los con-
tenidos de la cache y establecer las lı́neas cuya alimentación es susceptible
de ser reducida. Las variables que perfilan el algoritmo son los siguientes:

• Ventana de actualización: especifica el número de ciclos que deben


transcurrir entre los instantes en que se toman decisiones sobre el con-
sumo de las lı́neas.

• Polı́tica “simple” vs “noaccess”: la polı́tica “simple” implica poner to-


das las lı́neas de la cache en modo de bajo consumo con un intervalo
igual al tamaño de la ventana de actualización. La polı́tica “noaccess”
consiste en poner en estado drowsy sólo aquellas lı́neas que no hayan
sido accedidas en el transcurso de la ventana de actualización.

• Etiquetas “despiertas” vs “drowsy”: con la configuración “drowsy”, la


alimentación de las etiquetas se reduce a VddLow junto con los datos de
la lı́nea de cache.

• Tiempo de transición: establece el número de ciclos necesarios para


restaurar la alimentación original y estabilizar los datos, para realizar
el acceso sin pérdida de información.

13
Combinando diferentes valores para estos cuatro parámetros, los auto-
res realizan simulaciones cuyos resultados exponen en su publicación. Los
programas de prueba que utilizan son, de nuevo, el conjunto de benchmarks
SPEC2000, con la herramienta de simulación SimpleScalar.

2.2.2 Implementación hardware

Para implementar una cache que soporte el modo de bajo consumo, son
necesarios los siguientes elementos extra en cada lı́nea: un bit de drowsy,
activo cuando la lı́nea se encuentre en modo de bajo consumo, un mecanismo
que controle la tensión de alimentación de las celdas de memoria y un circuito
de acceso a la lı́nea, denominado word line gating.
El voltaje de operación de un vector de celdas de memoria en la cache
viene determinado por el controlador de voltaje, que cambia la tensión su-
ministrada según el estado del bit de drowsy. Cuando este bit esté activo,
la alimentación de las celdas SRAM será Vdd (1V), mientras que cuando esté
inactivo, ésta será de VddLow (0.3V).
El mecanismo de control de la tensión de alimentación consiste en dos
únicos transistores PMOS. El primero de ellos une la alimentación de la
lı́nea con Vdd , y está gobernado por la señal drowsy (salida negada del bit
de drowsy, conectada a la puerta del transistor). El segundo transistor
une la alimentación de su lı́nea de cache correspondiente con VddLow , y está
gobernado por la señal drowsy.
Por último, el citado circuito de acceso a la lı́nea impide el acceso a la
lı́nea de cache cuando ésta se encuentra en modo de bajo consumo. Un acceso
indiscriminado al contenido de las lı́neas puede ocasionar la pérdida de los
datos.

14
2.3 Técnica propuesta: apagado de ceros

2.3.1 Descripción

La técnica propuesta a continuación es novedosa y tiene como objetivo, al


igual que las anteriores, reducir la energı́a estática global disipada en la me-
moria cache de nivel 1 sin afectar de forma excesiva a las prestaciones.
La polı́tica de apagado de ceros se basa en el hecho de que las palabras
contienen, en su gran mayorı́a, un gran número de bits a 0 en su parte alta.
La explicación es que la mayor parte de los datos que se albergan en memoria
son números enteros positivos y pequeños, mientras que se está reservando
una palabra entera (en nuestro caso 32 bits) para cada uno de ellos.
Hemos ratificado experimentalmente esta asunción mediante la simu-
lación de la suite de benchmarks SPEC2000, obteniendo los resultados que
se muestran en las figuras 2.1 y 2.2. Estas figuras indican el porcentaje de
palabras en la cache que tienen un determinado número de bits de mayor
peso a 0, haciendo un promedio a lo largo de toda la ejecución. Un ejemplo
de interpretación de la gráfica es el siguiente:
Tengamos en cuenta la curva correspondiente al benchmark 175.vpr de
la figura 2.1. Llamemos m(x, B) al porcentaje medio de palabras que tienen
x bits de mayor peso a B. Los resultados de esta gráfica muestran que
m(32, 0) = 68.34. Teniendo en cuenta que la gráfica muestra un porcentaje
acumulativo, el hecho de que m(31, 0) sea 71.96 se interpreta como que el
71.96% de las palabras (promediando en todos los ciclos) tienen sus 31 bits
de mayor peso a 0, incluyéndose en este grupo también las que tienen 32 bits
a 0.
Si queremos saber qué porcentaje de palabras tiene 31 y sólo 31 bits de
mayor peso a 0, lo calcularemos como M(31, 0) = m(31, 0) − m(32, 0) =

15
100
164.gzip
95 175.vpr
176.gcc
90 181.mcf
186.crafty
Cumulative percentage

85 197.parser
253.perlbmk
80 254.gap
256.bzip2
300.twolf
75

70

65

60

55

50
32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2 0
Number of zeros

Figura 2.1: Distribución del número medio de ceros para benchmarks


SPEC2000 de enteros

100
168.wupwise
95 171.swim
172.mgrid
90 173.applu
177.mesa
Cumulative percentage

85 179.art
183.equake
80 187.facerec
188.ammp
301.apsi
75

70

65

60

55

50
32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2 0
Number of zeros

Figura 2.2: Distribución del número medio de ceros para benchmarks


SPEC2000 de coma flotante

16
71.96% − 68.34% = 3.62%. Definimos, pues, M(x, B) como el porcentaje
medio de palabras que tienen x y sólo x bits de mayor peso a B.
Observando las gráficas, se puede intuir el ahorro de energı́a estática
que puede suponer esta técnica, teniendo en cuenta que todas las curvas
comienzan con un porcentaje muy alto, que oscila entre el 50% y el 85%.
Para estimar el ahorro de energı́a máximo teórico que podemos alcanzar,
vamos a suponer que tenemos un algoritmo ideal sin sobrecarga hardware que
apaga en cada ciclo todos los bits de mayor peso a 0 de todas las palabras
de la cache. De esta forma, podemos establecer el número medio de bits
apagados por ciclo mediante la expresión

(32 · M(32, 0) + 31 · M(31, 0) + . . . + 1 · M(1, 0)) · np

donde np es el número de palabras de la cache de nivel 1. En nuestro caso,


para una cache de nivel 1 de 64KB, np =2K palabras de 32 bits cada una.
Si dividimos el número medio de bits apagados entre el número total de
bits en la cache, obtenemos el porcentaje de bits apagados (rof f ) en el caso
ideal y, por consiguiente, el ahorro máximo de energı́a posible utilizando esta
técnica. Continuemos con el ejemplo de la ejecución de 175.vpr. Teniendo
en cuenta que nb es el número de bits de la cache de nivel 1 (en nuestro caso
64KB × 8 = 512Kbits), obtenemos:

(32 · M(32, 0) + 31 · M(31, 0) + . . . + 1 · M(1, 0)) · np


rof f = =
nb
4.36 · 105
= 83.16%
5.24 · 105
La figura 2.3 muestra el ahorro máximo teórico para algunas de las cargas
SPEC2000. Obteniendo un ahorro máximo teórico que ronda el 70% en la
mayorı́a de las cargas, parece fácil conseguir unos resultados reales satisfac-
torios, ahora ya teniendo en cuenta la sobrecarga de consumo y tiempo de

17
Integer benchmarks Floating-point benchmarks Averages
100

80
Leakage energy savings (%)

60

40

20

0
16
17 gzip
17 vpr
18 gcc
18 mcf
19 craf
25 par
25 per
25 gap k
30 bzip

16
17 wup
17 swi se
17 gr
17 app
17 me
18 art
18 equ
18 face e
30 amm c

in
fp
al
t

l
4.
5.
6.
1.
6.
7. ty
3. se
4. lbm
6.
0. 2

8.
1. w
2. m
3. id
7. lu
9. sa
3.
7. ak
8. re
1.
tw

ap p
ol

si
f
r

Figura 2.3: Ahorro ideal, o porcentaje medio de bits de mayor peso a 0


para benchmarks SPEC2000

los mecanismos de control introducidos. En el capı́tulo 4 se muestran los


resultados de las simulaciones completas.
La técnica de apagado de ceros está destinada a actuar sobre cada palabra
individualmente, y no sobre cada lı́nea, lo que la diferencia de cache decay y
drowsy cache. Como se ha recalcado anteriormente, la técnica propuesta es
ortogonal respecto a las otras dos. Es decir, podemos escoger un diseño que
combine cache decay con apagado de ceros o bien drowsy cache con apagado
de ceros.

2.3.2 Implementación hardware

La figura 2.4 representa la estructura de una celda SRAM básica, utilizada


para almacenar un bit en la memoria cache. Los cuatro transistores centrales
(dos NMOS y dos PMOS) son los encargados de guardar el estado de la celda

18
(0 ó 1), formando un simple latch. Los dos transistores laterales desempeñan
la función de transistores de paso, que al conducir, permiten escribir o leer
del latch.
Word select
1V

C /C

Gnd

Figura 2.4: Celda SRAM básica

El pin Gnd va conectado normalmente a masa. Sin embargo, en nuestro


caso vamos a requerir una conexión a masa configurable dinámicamente,
dependiendo de si se desea tener el circuito en funcionamiento o no.
Sirviéndonos de un transistor de paso, que se puede modelar como un in-
terruptor abierto o cerrado según el voltaje aplicado en su fuente, podemos
controlar la alimentación de grupos de celdas SRAM mediante bits adicio-
nales de control. Cuando un transistor de paso esté cortado, la corriente de
fuga (y la energı́a estática consumida) de los circuitos a los que afecta se
reduce en un orden de magnitud, tal y como se especifica en la descripción
de la técnica Gated-Vdd [16].
El objetivo es introducir la mı́nima circuiterı́a necesaria para controlar
el apagado de las celdas de mayor peso cuyo valor almacenado sea cero.
Además, es responsabilidad nuestra establecer una codificación que indique
en cada momento el número de celdas apagadas, de forma que una lectura
sobre estas celdas tenga como resultado un grupo de ceros. La codificación
escogida debe incorporar poco hardware por celda y poco retardo en las

19
escrituras y lecturas (para estas últimas el retardo es un factor crı́tico).
Con este objetivo, se plantean dos aproximaciones, que se discuten a
continuación.

Primera aproximación

La primera aproximación para la implementación de la polı́tica de apagado


de ceros plantea una codificación C de dos bits por palabra, indicando cua-
tro posibles estados. Cada palabra W está formada por 4 bytes (32 bits),
nombrados desde B3 (byte de mayor peso) hasta B0 (byte de menor peso).

C (C1 C0 ) Significado en la palabra W

00 Todas las celdas que forman la palabra W están


alimentadas
01 Las celdas correspondientes a los dos bytes de ma-
yor peso (B3 , B2 ) están apagadas
10 Las celdas correspondientes a los tres bytes de ma-
yor peso (B3 , B2 , B1 ) están apagadas
11 Todas las celdas que forman la palabra W están
apagadas

Siguiendo esta codificación, es necesario controlar de forma dinámica el


encendido o apagado de cada byte Bi mediante transistores de paso. Estos
transistores proporcionarán la alimentación a las celdas, es decir, habilitarán
o impedirán la conexión de cada celda con masa según conduzcan o no res-
pectivamente. Las celdas correspondientes a B3 y B2 compartirán un único
transistor de paso T32 , que debe conducir para la codificación 00. Las celdas
de B1 estarán unidas a otro transistor de paso T1 , activo para las codifica-
ciones 00 y 01. Por último, la alimentación de las celdas del byte B0 vendrá

20
controlada por otro transistor de paso T0 , que conducirá en los casos 00, 01
y 10.
El último elemento necesario es la lógica que controle las entradas a los
transistores de paso según los bits C1 C0 . Las funciones lógicas que determi-
nan la activación de cada uno de ellos son las siguientes:

Transistor de paso Activo cuando se cumpla que

T32 C1 · C0
T1 C1
T0 C1 + C0

El aspecto final del circuito correspondiente a cada palabra de la memoria


cache es el siguiente:

B3 B2 B1 B0

C1 C0

Figura 2.5: Implementación de una palabra; primera aproximación

Adicionalmente es necesaria circuiterı́a que controle la lectura y escritura


de una palabra, modificando C o leyendo bits a 0 según sea conveniente.
Para esta implementación, no se va a alcanzar ese nivel de detalle, puesto
que no va a ser la que se adopte finalmente.

Segunda aproximación

La codificación en dos bits del estado de una palabra tiene una serie de
desventajas, entre las cuales se puede destacar la inclusión de lógica para la

21
B3 B2 B1 B0

S3 S2 S1 S0

Figura 2.6: Implementación de una palabra; segunda aproximación

decodificación y la imposibilidad de tratar más de cuatro combinaciones de


apagado de la palabra sin añadir bits a C. Ası́ surge la idea de la segunda
aproximación: se trata de no almacenar un estado codificado C, sino un
vector de bits S, en principio de 4 elementos (S3...0 ), indicando el estado de
cada transistor de paso.
La circuiterı́a necesaria para almacenar el vector S es prácticamente igual
a la de almacenar el código C de la primera aproximación más la lógica de
decodificación. Sin embargo, conseguimos una mayor resolución de apagado,
pudiendo actuar sobre cada byte de una palabra individualmente. La figura
2.6 muestra el esquema de implementación.
Hay que tener en cuenta que tenemos la posibilidad de variar la resolución
de apagado, modificando el número de transistores de paso que afectan a una
palabra y, por tanto, cambiando el número de celdas a las que afecta un único
transistor. El siguiente apartado discute la resolución escogida según las
simulaciones ejecutadas. En esta segunda aproximación, vamos a suponer
una resolución de un transistor de paso por byte, con lo cual tenemos la
posibilidad de no apagar ningún byte, o bien apagar el byte B3 , o bien los
bytes B3...2 , o los bytes B3...1 o bien apagar toda la palabra.
Tomando esta alternativa como definitiva, es el momento de discutir cómo
va a influir la nueva estructuración de las palabras en las operaciones de
lectura y escritura.

22
B3 B2 B1 B0
S3 8 S2 8 S1 8 S0 8

8 8 8 8

8 8 8 8

32

Figura 2.7: Lectura; selección de ceros

La figura 2.7 muestra el circuito de lectura. El objetivo en este caso es


reconstruir una palabra a partir de las celdas encendidas, sustituyendo los
bits apagados por ceros. El uso de buffers triestado permite inyectar en un
byte de salida la información de las celdas correspondientes a ese byte o a un
conjunto de ceros cableados a masa. Se seleccionará una de estas dos fuentes
según el valor del elemento Si asociado. Este circuito es común para toda
la cache, y el retardo extra de la lectura es únicamente el inducido por los
buffers.
La figura 2.8 muestra el circuito de escritura. La misión ahora es construir
y almacenar un nuevo vector S que controle el estado de los transistores de
paso según la nueva palabra. A nivel lógico, esto se puede traducir en tres
puertas OR de 9 entradas y otra de 8 entradas, todas ellas conectadas en
cadena, conforme se muestra en el esquema. Nótese que los valores B3...0
hacen ahora referencia a los bytes recibidos desde el exterior de la cache, y
no al contenido de la palabra en memoria, como en el caso anterior.
Este circuito puede imponer un retardo mayor. Sin embargo, una latencia
extra en la operación de escritura no es crı́tica, ya que no bloquea a ninguna
instrucción en el procesador. Por otra parte, los compiladores de lenguajes
de descripción de hardware (como VHDL) pueden optimizar la estructura

23
S3 S2 S1 S0

8 8 8 8
D31…24 D23…16 D15…8 D7…0

Figura 2.8: Escritura; cálculo del vector S

lógica en cadena del circuito de cálculo del vector S.

2.3.3 Resolución de apagado

En la segunda y definitiva aproximación de implementación, hemos observado


que la resolución de apagado puede variarse, según el número de transistores
de paso elegidos para cada palabra. Se convierte este factor, por tanto, en
una decisión de diseño a tomar, basándose de nuevo en las simulaciones.
Para ello, hemos realizado un análisis exhaustivo del ahorro de energı́a real
que supone cada elección. Para ilustrar un ejemplo, escogemos los resultados
del benchmark 256.bzip2, los cuales se muestran en la tabla 2.1.

24
Tabla 2.1: Ahorro de energı́a dependiendo de la resolución de apagado para 256.bzip2

x m(x, 0) M (x, 0) Individual Ideal 1 alter. 32 32+24 32+24+16 32+24+16+8 32+16 32+16+8 32+8
32 0.5584 0.5584 0.5584 0.5584 0.5584 0.5584 0.5584 0.5584 0.5584 0.5584 0.5584 0.5584
31 0.5598 0.0014 0.0014 0.5598 0.5423
30 0.5617 0.0019 0.0018 0.5615 0.5266
29 0.5646 0.0029 0.0026 0.5642 0.5117
28 0.5676 0.003 0.0026 0.5668 0.4967
27 0.5713 0.0037 0.0031 0.5699 0.4820
26 0.5763 0.005 0.0041 0.5740 0.4682
25 0.5809 0.0046 0.0036 0.5776 0.4538
24 0.5846 0.0037 0.0028 0.5803 0.4385 0.0197 0.0197 0.0197
23 0.5908 0.0062 0.0045 0.5848 0.4246
22 0.5948 0.004 0.0028 0.5876 0.4089
21 0.5988 0.004 0.0026 0.5902 0.3930
20 0.6024 0.0036 0.0023 0.5924 0.3765
19 0.6042 0.0018 0.0011 0.5935 0.3587
18 0.6056 0.0014 0.0008 0.5943 0.3407
17 0.6073 0.0017 0.0009 0.5952 0.3226
25

16 0.6099 0.0026 0.0013 0.5965 0.3050 0.0127 0.0127 0.0258 0.0258


15 0.6150 0.0051 0.0024 0.5989 0.2883
14 0.6284 0.0134 0.0059 0.6047 0.2749
13 0.6534 0.025 0.0102 0.6149 0.2654
12 0.6744 0.021 0.0079 0.6228 0.2529
11 0.6830 0.0086 0.0030 0.6257 0.2348
10 0.6913 0.0083 0.0026 0.6283 0.2160
9 0.6967 0.0054 0.0015 0.6298 0.1959
8 0.6985 0.0018 0.0005 0.6303 0.1746 0.0222 0.0222 0.0350
7 0.7045 0.006 0.0013 0.6316 0.1541
6 0.7090 0.0045 0.0008 0.6324 0.1329
5 0.7161 0.0071 0.0011 0.6336 0.1119
4 0.7262 0.0101 0.0013 0.6348 0.0908
3 0.7434 0.0172 0.0016 0.6364 0.0697
2 0.7763 0.0329 0.0021 0.6385 0.0485
1 0.8500 0.0737 0.0023 0.6408 0.0266
Ahorro en energı́a bruto: 0.5584 0.5781 0.5907 0.6129 0.5842 0.6063 0.5934
Celdas extra para vector S: 1 2 3 4 2 3 2
Energı́a extra disipada por S: 0.0313 0.0625 0.0938 0.1250 0.0625 0.0938 0.0625
Ahorro neto: 0.5272 0.5156 0.4970 0.4879 0.5217 0.5126 0.5309
Las columnas de la tabla 2.1 tienen el siguiente significado:

• x: número de bits de mayor peso a 0.

• m(x, 0): como se ha descrito en el apartado anterior, m(x, B) representa


el porcentaje de palabras (en la tabla expresado sobre 1) que tienen sus
x primeros bits de mayor peso a B (en este caso a 0).

• M(x, 0): también explicado en el apartado anterior. Hace referencia al


porcentaje de palabras que tienen exactamente sus x primeros bits de
mayor peso a 0.

• Individual: indica el ahorro individual de energı́a que supondrı́a aplicar


el apagado de ceros únicamente a las palabras con exactamente x bits
x
de mayor peso a 0. Se calcula como M(x, 0) × 32
.

• Ideal1 : el ahorro ideal es igual a la suma acumulativa de los valores


de la columna Individual. El valor de la celda correspondiente a la
última fila de esta columna representa el ahorro ideal global, es decir,
el conseguido en el caso en que apagáramos siempre todos los bits de
mayor peso a 0 de todas las palabras. La figura 2.3 mostrada en el
apartado anterior está construida a partir de este valor en todas las
cargas. La cifra correspondiente a la fila etiquetada como x = i de la
columna Ideal se calcula mediante la siguiente expresión:
32 
j
X 
M(j, 0) ×
j=i 32

• Una alternativa: representa el ahorro obtenido si sólo aplicáramos el


apagado de ceros a las palabras con x o más bits de mayor peso a 0. Se
x
calcula como m(x, 0) × 32
.
1
Esta columna se introduce para ilustrar el cálculo de los valores de la figura 2.3, pero
no influye en la decisión de la resolución de apagado a escoger.

26
• Resto de columnas: indican todas las posibilidades de resolución de
apagado. Por ejemplo, la columna con etiqueta 32+24+16 indica que
tenemos la posibilidad de apagar bien 32 bits (palabra completa), o bien
24 bits, o bien 16 (además de la posibilidad de no apagar ninguno). Los
contenidos de la columna indican el ahorro obtenido según las palabras
afectadas por cada posibilidad de apagado.

Estos valores se calculan como el porcentaje de palabras afectadas,


multiplicado por el número de bits apagados en cada una de ellas, y
dividido por el tamaño de la palabra (32). Ası́ pues, el valor de la celda
en la columna con etiqueta 32+16 para x = 16 se calcula mediante la
siguiente expresión:
31
x X
· M(i, 0)
32 i=16

• Ahorro en energı́a bruto: esta fila calcula el ahorro bruto que se obtiene
para una determinada combinación de números de bits apagados. Es
la suma de todos los valores situados por encima en la misma columna.

• Celdas extra para vector S: en esta fila se especifica el número de celdas


por palabra necesarias para soportar una cierta resolución de apagado.
Es igual al número de valores que aparecen por arriba en la misma
columna.

• Energı́a disipada por S: indica el porcentaje de bits que supone el al-


macenamiento del vector S respecto al tamaño de la palabra. También
se puede definir como el porcentaje del tamaño original de la cache
que supone el hardware extra introducido por la polı́tica de apagado de
ceros. Se calcula como el número de bits de S dividido por el tamaño
de palabra (32 bits).

27
• Ahorro neto: en los resultados de esta fila, por fin, se basa la decisión de
la codificación escogida. El ahorro neto se calcula restando el porcen-
taje de energı́a disipada por S (hardware extra de control de apagado)
del ahorro bruto en cada una de las combinaciones de apagado.

En el caso del benchmark 256.bzip2, se obtiene que la combinación


32+8 proporciona un ahorro neto del 53.09%, superando a cualquier
otra combinación. Por tanto, en el caso de esta carga en concreto, el
ahorro de energı́a más cercano al ideal se obtiene de una polı́tica que
o bien apaga la palabra completa cuando el valor almacenado es 0, o
bien sólo el byte de mayor peso cuando su valor es 0.

Para analizar el comportamiento de cada resolución de apagado aplicado a


las diferentes cargas computacionales, se muestran las figuras 2.9 y 2.10, sepa-
rando los benchmarks de enteros y de reales. Las figuras incluyen únicamente
un subconjunto de las cargas SPEC2000 para facilitar la comprensión de la
información representada. Además, sólo se plasma ya el ahorro neto para
cada caso, es decir, la información relevante de la tabla 2.1.
Cada grupo de barras representa los resultados obtenidos en la simulación
de una carga particular, mientras que cada barra dentro del grupo representa
el ahorro neto para una combinación de apagado determinada. En ambas
gráficas, se ha incluido un bloque de barras que representa el ahorro neto
medio obtenido para todas las cargas con una determinada combinación.
Atendiendo a las tendencias de las barras entre los diferentes grupos,
se puede destacar a simple vista un ahorro más bajo en las resoluciones de
apagado altas (por ejemplo 32+24+16 ó 32+24+16+8). En cambio, las reso-
luciones bajas hacen que el ahorro aumente de forma similar entre ellas. Para
estos dos tipos de combinaciones, se observa un comportamiento diferenciado
en el caso de la aritmética entera y de coma flotante.

28
80

75
Leakage energy savings (%)

70

65

60

55

50

45

40
16

17

17

18

18

19

25

25

25

30

Av
4.

5.

6.

1.

6.

7.

3.

4.

6.

0.

er
gz

vp

gc

cr

pa

pe

ga

bz

tw

ag
cf

ol
ip

ip
rs

rlb

e
fty

f
2
er

m
k
32 32+16 32+24+16 32+16+8
32+24 32+8 32+24+8 32+24+16+8

Figura 2.9: Ahorro neto para diferentes resoluciones de apagado y cargas


con aritmética entera

80

75
Leakage energy savings (%)

70

65

60

55

50

45

40
16

17

17

17

17

17

18

18

18

30

Av
8

er
.w

.s

.m

.a

.m

.a

.e

.fa

.a

.a

ag
w

pp

rt

qu

ps
up

gr

es

ce
im

e
m

i
lu

ak
id
w

re

p
is

c
e

32 32+16 32+24+16 32+16+8


32+24 32+8 32+24+8 32+24+16+8

Figura 2.10: Ahorro neto para diferentes resoluciones de apagado y


cargas con aritmética en coma flotante

29
Es a partir de esta información de donde se extrae la conclusión de que
una resolución de apagado de 32+24 ó 32+16 es óptima para los benchmarks
de enteros, contrastando con la optimalidad de una resolución de apagado de
32 (únicamente la palabra completa) para el caso de la aritmética en coma
flotante.
En cualquier caso, la diferencia entre los resultados para las diferentes
resoluciones de apagado plausibles (que explotan una o dos combinaciones)
es pequeña. Sea cual sea la combinación escogida, se va a obtener un ahorro
neto que ronda el 60%.

2.3.4 Área del chip

Un factor importante a tratar en cualquier propuesta hardware es, además


del consumo adicional, el tamaño que ocupa dentro del chip en el que se sitúa.
En el apartado anterior se ha estudiado el consumo adicional ocasionado por
el hardware extra para apagado de ceros, dependiente de la resolución de apa-
gado. En este apartado, se complementa este estudio calculando el porcentaje
de área adicional causado por la lógica de control, también dependiente de
la resolución de apagado.
Para ello, definiremos las siguientes variables:

• nword : tamaño de una palabra en bits.

• nwblock : tamaño de un bloque en palabras.

• nlabel : tamaño en bits de las etiquetas de cache.

• nS : número de bits que forman el vector S, dependiente de la reso-


lución.

30
Resoluciones Bits en S Área extra
32 1 2.94%
32+24, 32+16, 32+8 2 5.88%
32+24+16, 32+24+8, 32+16+8 3 8.82%
32+24+16+8 4 11.76%

Tabla 2.2: Área extra debida a hardware extra para diferentes


resoluciones

La relación entre el área de una memoria cache C1 que implemente apa-


gado de ceros y el área de una cache C2 estándar se puede aproximar como el
cociente del número total de bits (de control, datos y etiquetas) en un bloque
de C1 entre el número de bits en un bloque de C2 . Ası́ pues, el porcentaje de
área extra se puede expresar, utilizando las variables anteriores, mediante la
siguiente ecuación:

(nword + nS ) · nwblock + nlabel


−1
nword · nwblock + nlabel
La tabla 2.2 muestra la aplicación de esta fórmula a implementaciones
de apagado de ceros con diferentes resoluciones de apagado, de forma que
se observa cómo aumenta el área de la cache en cada caso. Se supone una
memoria cache L1 de 64KB, bloques de 64 bytes, palabras de 32 bits y
etiquetas de 32 bits.
Los experimentos sobre la elección de la resolución de apagado han des-
cartado, tanto para benchmarks de enteros como de coma flotante, las com-
binaciones de más de 2 bits en S. Por tanto, se puede afirmar que la técnica
de apagado de ceros va a proporcionar un aumento máximo del 5.88% del
área total de la cache.

31
Capı́tulo 3

Simulador desarrollado

El desarrollo de este proyecto es posible gracias a la construcción de una


herramienta de simulación para Linux, basada en SimpleScalar [13]. Sin em-
bargo, la herramienta se ha programado completamente desde cero, variando
su estructura general de acuerdo a lı́neas de investigación actuales y futuras,
y añadiendo las caracterı́sticas necesarias para modelar la técnica de apagado
de ceros. Además, ciertas limitaciones de SimpleScalar se han sustituido o
mejorado.
Algunas de las propiedades de la herramienta desarrollada en las que se
plasman las necesidades actuales de simulación son las siguientes:

• Implementación de la arquitectura MIPS32, utilizando la documen-


tación que se encuentra en [15]. Esta documentación describe la ar-
quitectura MIPS de 64 bits, a la cual da soporte el nuevo simulador.
Sin embargo, las cargas SPEC2000 utilizadas se han compilado para
MIPS32, con lo que sólo se implementa para este proyecto el subcon-
junto de instrucciones correspondientes a la arquitectura MIPS de 32
bits.

32
• Adopción de partes principales de SimpleScalar. Por ejemplo, se ha
replicado prácticamente (con las modificaciones pertinentes) la simu-
lación del núcleo del procesador, el predictor de saltos, la memoria
principal, el cargador de ejecutables en formato ELF, gestor de llama-
das al sistema o gestor de lı́nea de órdenes y de estadı́sticas.

• Sintaxis de la lı́nea de órdenes compatible con SimpleScalar. Los


parámetros del simulador pueden pasarse como parte de la lı́nea de
órdenes o como fichero de configuración.

• Muestra de las estadı́sticas de la simulación utilizando espacios de nom-


bres. Por ejemplo, los nombres de las estadı́sticas que hacen referencia
al consumo de la cache tienen como prefijo decay:, las variables globales
del simulador, sim:, etc. De esta forma, resulta sencilla la interpretación
de los resultados a través de shell-scripts.

• Creación de un módulo independiente y desacoplado para la medición


del consumo y las polı́ticas de apagado propuestas (drowsy, cache de-
cay y apagado de ceros). Está basado en las propiedades de una he-
rramienta de simulación del consumo disponible en la web, llamada
HotLeakage [14].

Al simulador propiamente dicho, acompaña la creación de tres herramien-


tas auxiliares para el diseño y la depuración del mismo. La primera es un
creador de máscaras de instrucciones, cuya utilidad se describe en secciones
posteriores. La segunda es un visualizador de archivos objeto compilados
para MIPS, que funciona de la misma forma que la utilidad obj-dump de
GNU, pero utilizando funciones del núcleo del simulador para decodificar las
instrucciones. Por último, la tercera herramienta se describe en la sección

33
3.4.3, y ofrece un enfoque pedagógico al simulador, puesto que analiza los fi-
cheros de traza de una determinada ejecución y los muestra en forma tabular
ciclo a ciclo, junto con el estado del procesador en cada instante.
Por otra parte, el simulador desarrollado incorpora ciertas caracterı́sticas
que van más allá del diseño de SimpleScalar. Todas ellas se basan en la
temática asociada a las perspectivas de investigación de cara al futuro: los
multiprocesadores, los procesadores multi-hilo (multithread) y los procesado-
res CMP (core multi-processor).
Estos aspectos no han sido explotados en las simulaciones llevadas a cabo
en este proyecto. Sin embargo, constituyen una serie de decisiones de imple-
mentación que afectan a las partes principales del simulador. Las extensiones
al diseño original de SimpleScalar se resumen en los siguientes componentes:

• Creación de una estructura de datos correspondiente al modelo de un


procesador. SimpleScalar trata todos sus componentes como varia-
bles globales. Sin embargo, un diseño orientado a multiprocesadores
requiere el empaquetamiento de esta información para su posterior re-
plicación.

• Simulación de la MMU (memory management unit). SimpleScalar uti-


liza las propias direcciones virtuales de memoria generadas por las ins-
trucciones de acceso a memoria para acceder a la cache. Sin embargo,
un simulador que permite varios hilos de ejecución, o incluso múltiples
procesadores, implica la presencia de varios procesos, con sus correspon-
dientes mapas de memoria. Por tanto, el modelo debe proporcionar un
mecanismo de traducción de direcciones virtuales a direcciones fı́sicas
(MMU), simulado únicamente a nivel funcional.

Este mecanismo de traducción es el que deshace las colisiones entre los

34
espacios de direccionamiento de memoria de los procesos presentes en
el modelo. Por tanto, van a ser las direcciones fı́sicas generadas por la
MMU las que se utilizarán para acceder a la memoria cache.

• Polı́tica de búsqueda de instrucciones de varios hilos. En un procesador


multi-hilo de n hilos, existen n contadores de programa. Una polı́tica
de búsqueda de instrucción establece qué contadores de programa se es-
cogen en un determinado ciclo, y cuántas instrucciones de buscan para
cada hilo. En este proyecto, este aspecto no tiene ninguna repercusión,
puesto que disponemos de un único hilo en todo momento.

Las propiedades que ofrecen soporte a múltiples procesos y sus detalles


de implementación no se van a describir en detalle, ya que ninguna de sus
ventajas ha servido para obtener los resultados de este proyecto. Los futu-
ros trabajos de investigación van a ser los que se centren en mediciones de
prestaciones, consumo u otros aspectos en modelos con múltiples procesos.
El resto de esta sección describe la estructura general de la herramienta
desarrollada, y se centra en los aspectos innovadores para la medición del
consumo en la memoria cache, ası́ como las polı́ticas de apagado de bytes.

3.1 Ficheros del simulador


La organización de los ficheros es similar a la de SimpleScalar, pero pretende
ser más estructurada, abstrayendo ciertos aspectos de ejecución de instruc-
ciones, etapas del procesador o manejo de estructuras de datos especı́ficas en
diferentes módulos. Los ficheros proporcionados son los siguientes:

• bpred.c: Implementación del predictor de saltos. No añade aportacio-


nes a la implementación original de SimpleScalar. Proporciona modelos

35
para un predictor de saltos adaptativo de dos niveles (con sus variacio-
nes GAg, GAp, PAg y PAp), predictor de dos bits, predictor estático de
saltos siempre tomados y predictor estático de saltos nunca tomados.

• cache.c: Contiene las rutinas de acceso a la memoria cache. La mo-


dificación principal es la inclusión de varias funciones “abstractas”, es
decir, punteros a funciones inicialmente nulos que se llamarán cuando
ocurran determinados eventos en la cache (por ejemplo, invalidación de
un bloque, o modificación de los datos que contiene). Estos punteros
son asignados por las rutinas de inicialización del módulo cache leak.c,
que lleva a cabo las acciones pertinentes para cada evento.

• cache leak.c: Módulo de medición del consumo de la cache e imple-


mentación de las polı́ticas de apagado. Las funciones aquı́ dispuestas
interactúan con el módulo cache.c, interceptando las modificaciones de
los datos de la cache en el caso de la polı́tica de ceros, y cuantificando
el consumo consecuentemente. También modifican el estado de validez
de los bloques en el caso de las polı́ticas de apagado que no preservan
el estado (non state-preserving).

• eval.c: Este módulo ofrece funciones para la creación de autómatas fi-


nitos deterministas que, combinados, dan lugar a un analizador léxico,
utilizable para cualquier fin. Uso de ello hace el evaluador de expresio-
nes aritméticas, también incluido en el módulo, y utilizado desde stat.c
para el registro de fórmulas como resultados estadı́sticos del simulador.

• loader.c: Carga de un fichero binario en formato ELF en la memoria


simulada. El módulo cargador actúa llamando a las funciones de las bi-
bliotecas libbfd y libiberty de glibc. Estos componentes dan soporte
al análisis del formato ELF, ası́ como a la creación de tablas de sı́mbolos

36
para programas compilados con la opción -g, lo cual resulta útil para
la utilidad de visualización de archivos binarios implementada.

• machine.c: Aquı́ se especifican las constantes especı́ficas de la arqui-


tectura MIPS implementada. Además, se ofrecen funciones con tres
propósitos diferentes: impresión de una instrucción por pantalla, deco-
dificación de una instrucción y ejecución de una instrucción.

• machine.def: Éste es el corazón de la arquitectura MIPS, en el que se


define el juego de instrucciones. Para cada una de ellas se especifica el
formato, la categorı́a, los recursos utilizados y la forma en que modifica
el estado del procesador.

• main.c: Programa principal, que únicamente se encarga de llamar a las


rutinas de inicialización, ejecución y finalización del simulador.

• memory.c: Simulación funcional de la memoria principal. Las funcio-


nes aquı́ presentes implementan, entre otras cosas, la lectura, escritura,
comienzo de ejecución especulativa y restauración del estado de la me-
moria para reanudar ejecución no especulativa.

• misc.c: Funciones auxiliares de manejo de bits, transformación de en-


dian, extensión de signo, mensajes de error, definición de tipos de datos,
etc.

• mm.c: Memory management unit o MMU. Proporciona un mecanismo


de traducción de direcciones virtuales a direcciones fı́sicas y viceversa.
Además, simula la memoria fı́sica, permitiendo especificar su tamaño y,
por tanto, el número máximo de páginas en memoria fı́sica. Se permite
aplicar diferentes polı́ticas de reemplazamiento de página y se notifican
las situaciones en las que se produce un fallo de página.

37
• options.c: Control de la lı́nea de órdenes y el fichero de configuración.

• proc.c: Incluye la estructura de datos que representa el estado de un


procesador multi-hilo. Además, se exportan funciones para la gestión
de unidades funcionales, manejo de la cola de eventos, de la cola de pre-
parados y de la cola fetch-dispatch, todas ellas comentadas en apartados
posteriores.

• ptrace.c: Generador de información de traza. Si el flag de generación


de traza está activo, cada ciclo se vuelca en un fichero información
asociada al estado del procesador. Una vez finalizada la simulación,
la utilidad de interpretación de traza (cuyo fichero principal tiene el
mismo nombre, pero está situado en el subdirectorio ptrace) lo procesa
y representa de forma tabular los resultados.

• ruu.c: Contiene la definición de las estructuras relacionadas con la


RUU (Register Update Unit), además de las funciones necesarias para
su manejo. En el apartado 3.3.2 se desarrolla con detalle el modelado
de la RUU.

• sim.c: Aquı́ se encuentran las rutinas principales de inicialización y


liberación del simulador, llamadas desde main.c. Este módulo se en-
carga de poner a punto todas las estructuras de datos para comenzar
la ejecución. Además, contiene el bucle principal de simulación, desde
el cual se ejecutan las rutinas asociadas a las diferentes etapas de un
procesador superescalar.

• sim-fast.c: Este módulo forma un segundo programa principal, y re-


copila las caracterı́sticas elementales del resto de módulos, dando lugar
a un simulador funcional rápido, que únicamente ejecuta el código del

38
programa pasado como parámetro. Resulta útil para comprobar la
correcta implementación del juego de instrucciones, o que el compor-
tamiento de un programa compilado para MIPS es el esperado.

• stages.c: Implementación del núcleo del procesador. Cada una de las


funciones contiene el código asociado a una etapa del procesador.

• stat.c: Manejo de las estadı́sticas. Permite establecer variables globa-


les como resultados estadı́sticos del simulador o registrar una fórmula
que calcule un nuevo dato a partir de los anteriores. Al finalizar la
simulación, todos los datos se imprimen por la salida estándar de error.

• syscall.c: Implementación de un subconjunto de las llamadas al sis-


tema UNIX. El simulador intercepta la instrucción syscall y actualiza
la memoria y los registros simulados de la misma forma que lo harı́a
el sistema operativo o, en ocasiones, de una forma simplificada que
permita continuar a la aplicación.

3.2 Descripción de la arquitectura MIPS


Podemos destacar las siguientes caracterı́sticas principales de la arquitectura
simulada:

• Se dispone de 32 registros de propósito general (r0...r31), cuyo tamaño


es de 64 bits y trabajan con aritmética entera. Este tamaño de regis-
tros da soporte a la futura ampliación del simulador a una arquitec-
tura de 64 bits, como MIPS64. El registro 0 toma un valor constante
de 0. Además, hay 32 registros de coma flotante de simple precisión
(f 0...f 31), que también pueden usarse como 16 registros de doble pre-
cisión, accediendo sólo a los registros pares. Los registros f ir y f csr

39
sirven como resultados de ciertas operaciones de coma flotante. Los
registros hi y lo reciben los resultados de las multiplicaciones enteras.
Por último, el registro pc almacena el contador de programa, es decir,
la dirección de la siguiente instrucción a ejecutar.

• La memoria tiene un acceso big-endian. Para un valor numérico de


4 ó 2 bytes, el byte de menor peso se almacena en la dirección más
grande de memoria, mientras que el el byte de menor peso se almacena
en la más pequeña. Además, el acceso a memoria es alineado. Esto
quiere decir que la dirección de memoria debe ser un múltiplo de 2
cuando se almacene/cargue un valor de 2 bytes, múltiplo de 4 cuando
se almacene/cargue un valor de 4 bytes, etc.

• Existe un delay slot de una instrucción. Cuando nos encontramos con


una instrucción de salto tomado, la instrucción que se encuentra en la
dirección P C + 4 se ejecuta antes de que el flujo de programa alcance
a la instrucción destino del salto.

3.2.1 Juego de instrucciones

El juego de instrucciones está basado en el modelo load/store, que segrega


las operaciones de acceso a memoria del resto de operaciones. El tamaño de
las instrucciones es constante y de 32 bits. Se diferencian principalmente tres
formatos de instrucción: I, R y J.
En general, las instrucciones de tipo I son las aritmético-lógicas con un
argumento inmediato, las instrucciones condicionales y las de acceso a me-
moria. Las instrucciones de tipo R son las que involucran a tres registros
(dos operandos fuente y uno destino), mientras que las de tipo J son las
instrucciones de salto incondicional.

40
Formato R
31 26 25 21 20 16 15 11 10 6 5 0

op rs rt rd shift func

Formato I
31 26 25 21 20 16 15 0

op rs rt imm

31 26 25
Formato J 0

op target

Tabla 3.1: Formatos de instrucción de la arquitectura MIPS32

Para los tres formatos, existe alguna instrucción aislada que viola esta
clasificación general. La tabla 3.1 muestra los campos que impone cada
formato, ası́ como el número de bits que abarca.

3.3 Partes principales del simulador

3.3.1 Etapas del procesador superescalar genérico

El procesador superescalar genérico modelado consta de cinco etapas, cada


una de las cuales se centra en una tarea muy concreta y accede a estructuras
hardware determinadas. Mediante la segmentación del procesador, se puede
simular una ejecución de instrucciones fuera de orden, o un mecanismo de
predicción de saltos, con la consiguiente ejecución especulativa ocasional.
Las diferentes etapas son fetch, dispatch, issue, writeback y commit. A
continuación se explica brevemente la tarea que se lleva a cabo en cada una
de ellas.

41
Etapa fetch

En la primera etapa o front end del procesador, se implementa la búsqueda


de instrucciones desde la memoria cache de instrucciones, dando soporte a la
existencia de múltiples contadores de programa, uno por cada hilo activo en
el sistema.
Una vez extraı́da una instrucción de la memoria cache de instrucciones, se
debe llenar una entrada en la cola fetch-dispatch (llamada ruu ifq y accedida
mediante las macros IFQ XXX), la cual comunica la etapa fetch con la siguiente.
Esta entrada contiene toda la información decodificada de la instrucción en
el caso del modelo (en el procesador real, la instrucción no se decodifica
hasta la etapa siguiente), de tal forma que ya quedan accesibles todos sus
campos para su consulta. La etapa fetch debe bloquearse cuando la cola
fetch-dispatch esté llena.
La última acción en esta etapa es consultar el predictor de saltos, que
establece la dirección en que buscar la siguiente instrucción para el hilo actual.
El número de instrucciones buscadas depende del parámetro fetch speed,
fijado mediante la lı́nea de órdenes o fichero de configuración por el usua-
rio. Cuando se haya alcanzado este lı́mite, la función que implementa la
búsqueda de instrucciones finaliza: ya ha dejado en la cola fetch-dispatch las
instrucciones a analizar en la siguiente etapa.

Etapa dispatch

En esta etapa, se toman instrucciones de la cola fetch-dispatch, se decodifican


y se van insertando en la RUU (Register Update Unit) o en la LSQ (Load
Store Queue). Además, las instrucciones que tengan sus operandos listos,
se colocan directamente en la cola ready queue, desde la cual se lanzan a
ejecución en etapas posteriores.

42
En primer lugar, se toman los datos de la nueva instrucción a decodificar
desde la cola fetch-dispatch, cuyas entradas fueron rellenadas en la etapa
anterior. Las instrucciones de esta cola se han buscado siguiendo el orden que
estableció el predictor de saltos, con lo que puede haber alguna instrucción
que suponga el comienzo de una ejecución especulativa.
Seguidamente, el modelo utiliza la información del fichero machine.def
(explicado en 3.3.4) para actualizar los valores del banco de registros y la
memoria según la instrucción que se esté ejecutando, y para extraer las de-
pendencias de datos y la unidad funcional consumida por dicha instrucción.
Esta información servirá en etapas posteriores para controlar la ejecución
fuera de orden y el lanzamiento a ejecución.
Dicho de otra forma, en esta etapa se ejecuta la funcionalidad de la ins-
trucción, especificada en machine.def, modificando el banco de registros y la
memoria. En un procesador real, esto no ocurre hasta la etapa commit. Sin
embargo, en el modelo se sigue esta estrategia con el objetivo de facilitar
el diseño centralizado del juego de instrucciones y aumentar la velocidad de
simulación. Las siguientes etapas simplemente se encargan de simular los
aspectos temporales.
Para ello, se dispone de mecanismos de reversibilidad de ejecución espe-
culativa en cuanto al estado de los registros y la memoria. En un procesador
real, sólo se conoce la presencia de especulación en la etapa commit. Sin
embargo, en el modelo podemos ser conscientes de ella en esta misma etapa,
puesto que ya se conoce el resultado de la operación de salto (en su caso), que
a su vez se puede comparar con el resultado del predictor de saltos, proferido
en la etapa anterior. En el caso de que ambos difieran, se establece un punto
de recuperación del banco de registros y la memoria, restaurándolo cuando
la instrucción especulativa alcance la etapa commit del procesador.

43
Nótese que es necesario continuar la simulación funcional tras un fallo de
especulación, puesto que la secuencia de instrucciones decodificadas y ejecu-
tadas (sin llegar a la etapa commit) en dicho intervalo puede repercutir sobre
los contenidos de la memoria cache, la utilización de las unidades funcionales,
etc.; en definitiva, sobre las estadı́sticas de la simulación.
Seguidamente, se rellena una entrada de la RUU. También se establecen
las listas de dependencias de los datos, que determinan el orden en que las
instrucciones se lanzan a ejecución. Además, las instrucciones de acceso a
memoria rellenan una entrada de la LSQ.
El número de instrucciones que pueden decodificarse en la etapa dispatch
viene determinado por el parámetro ruu decode width.

Etapa issue

Como se puede observar hasta el momento, en cada etapa se van recogiendo


instrucciones de una cola determinada, para procesarse y volcarse en otras
estructuras de datos accedidas en etapas venideras. En este caso, es la cola
de preparados (ready queue), rellenada en la etapas dispatch y writeback, de
la que se leen instrucciones con las dependencias de entrada ya resueltas.
Se leen tantas instrucciones como establezca el parámetro ruu issue width.
A cada una de ellas se le asigna la unidad funcional correspondiente, en el
caso de que haya una libre adecuada a las necesidades de la instrucción. Si
no es ası́, ésta se vuelve a insertar en la cola de preparados para ser procesada
en ciclos posteriores.
Cuando se trata de una instrucción de lectura en memoria con la di-
rección de acceso disponible, se procede al propio acceso mediante la llamada
a mem latency, que atraviesa todo el sistema de memoria con el protocolo
pertinente (acceso a TLB, acceso a cache, y en su caso, acceso a memoria

44
principal).
La comunicación de la etapa issue con las siguientes no ocurre a través de
una estructura de datos hardware. Las instrucciones se encuentran disemina-
das en las diferentes unidades funcionales, o esperando el acceso a memoria.
En ambos casos, la latencia de la operación se conoce en el simulador desde
el momento en que se lanzan a ejecución.
La forma de permitir a las instrucciones continuar en la siguiente etapa
(writeback) es mediante una simulación orientada a eventos, insertándolos en
una lista de eventos y adjuntando el ciclo en que se deben disparar, depen-
diendo de la latencia de los operadores.

Etapa writeback

Aquı́ se activan los eventos programados en la etapa issue y almacenados


en la cola de eventos o event queue. Cuando un evento se programó con
una marca temporal igual al ciclo actual, se deduce que la instrucción a
la que hace referencia ha completado su ejecución en la unidad funcional
correspondiente.
El siguiente paso es analizar las dependencias de salida de dicha ins-
trucción. Para cada una de estas dependencias, se recorre la RUU en busca
de otras instrucciones que estuvieran esperando el resultado recientemente
generado.
Al recibir una instrucción un resultado que le impedı́a continuar, puede
alcanzar el estado en que tenga ya todas las dependencias de entrada resuel-
tas, momento en el cual se encola en ready queue.
Para las instrucciones cuya ejecución finalizó, se activa el campo completed
de la entrada asociada en la RUU, quedando ası́ marcadas para su proceso
en la última etapa del procesador.

45
Etapa commit

La función de esta última etapa es recolectar instrucciones completadas de


la cabeza de la RUU, tantas como permita el parámetro ruu commit width.
Cuando hay una entrada en la RUU asociada a un cálculo de dirección de
memoria, hay por sincronismo una entrada en la cola LSQ que corresponde
al propio acceso a memoria. Si se trata de una operación de escritura, la
etapa commit es la encargada de consumarlo, en el caso en que haya algún
puerto de escritura libre.
En el caso en que exista alguna instrucción en la cabeza de la RUU que no
ha completado todavı́a su ejecución, la etapa commit suspende su funciona-
miento, para garantizar una finalización en orden de todas las instrucciones.
La última etapa del procesador es la encargada también de vaciar por
completo el pipeline (estructuras RUU y LSQ) en el caso de que se haya
producido un fallo de especulación en la búsqueda de instrucciones. Las
acciones pertinentes son las siguientes:

• Recuperación del estado de la memoria al punto en que comenzó la eje-


cución especulativa, mediante la función mem recover. En un procesador
real, no es necesario recuperar el estado de la memoria fı́sica, puesto
que éste sólo se modifica en la etapa commit, cuando las escrituras en
memoria ya son seguras y definitivas.

Sin embargo, el simulador ejecuta la funcionalidad de las instrucciones


internamente en la etapa dispatch, con lo cual es necesario recuperar
un estado anterior, tanto en la memoria como en los registros.

• Recuperación del estado de los registros. Mediante la puesta a falso


del campo spec mode, conseguimos que el banco de registros accedido
durante la ejecución funcional de las instrucciones sea el original.

46
Si se observa la definición de la macro SET GPR, utilizada en la imple-
mentación del juego de instrucciones, se ve que el acceso al banco de
registros varı́a según el estado del campo spec mode. Cuando se da eje-
cución especulativa, se crea una copia auxiliar del banco de registros,
que se accede temporalmente, hasta que se restaure el modo de eje-
cución normal.

• Recuperación del estado de la pila de retorno en el predictor de saltos.


Mediante la llamada a la función bpred recover, establecemos el tope de
la pila de retorno en el punto establecido antes de comenzar la ejecución
especulativa. De esta forma, después de descartar todas las instruccio-
nes de la RUU, el predictor puede continuar su funcionamiento normal,
sin generar fallos de especulación a causa de una corrupción de la pila
de retorno.

• Vaciado de la RUU y LSQ. Todas las entradas de estas dos estructu-


ras contienen información inútil, que se elimina en el momento de la
recuperación.

La última operación en esta etapa es actualizar la información del predic-


tor de saltos, en el caso en que se esté graduando una instrucción de control,
puesto que ya se conoce si es un salto tomado o no.
Cuando se ha procesado con éxito la instrucción completada, se elimina
su entrada en la RUU y LSQ (en su caso).

3.3.2 RUU (Register Update Unit)

La RUU es una estructura de datos hardware que se encarga de almacenar


el estado de las instrucciones en curso, es decir, las que ya han sido deco-
dificadas, pero todavı́a no han alcanzado la última etapa. En cada nueva

47
etapa del procesador se van a utilizar las entradas de la RUU para modificar
convenientemente el estado del pipeline.
La RUU es una cola FIFO, en la que se incorpora una nueva entrada en
la etapa dispatch y en cuya cabeza se extrae otra entrada en la etapa commit.
También se admite la operación de vaciado completo de la RUU.
La estructura que modela la RUU es la siguiente:

struct RUU_struct {
int size;
int num;
int head, tail;
struct RUU_station *elem;
[...]
};

Como en cualquier otra estructura de datos que represente una cola en


este simulador (event queue o ifq), los campos que la definen son: size
(tamaño de la cola), num (ocupación), head y tail (ı́ndices de la cabeza y
cola) y elem (elementos, en este caso de tipo RUU station).
Los campos de cada uno de los elementos del a cola RUU definen el estado
de una instrucción dentro del procesador. La estructura que los implementa
en el simulador es la siguiente:

struct RUU_station {
word inst;
struct md_inst_fld_t inst_fld;
dword regs_PC, regs_NPC, regs_NNPC;
dword pred_NPC, pred_NNPC;
int thread_id;
int in_LSQ;
int ea_comp;
int spec_mode;

48
int kill;
dword addr;
dword tag;
dword seq;
byte data[8];
int recover_inst;
int stack_recover_idx;
struct bpred_update_t dir_update;

int queued;
int issued;
int completed;
int squashed;

int onames[2];
struct RS_link *odep_list[2];
int idep_ready[3];
};

Esta estructura es la principal dentro de la simulación de un procesador


segmentado. A continuación se explican uno a uno todos sus campos:

• inst, inst fld: bits de la instrucción e instrucción decodificada. En


el procesador real, una instrucción no se decodifica hasta la etapa
dispatch. Sin embargo, durante la simulación se llama a la función
md decode inst en la primera etapa, puesto que resulta útil conocer de
antemano todos los atributos asociados a la instrucción buscada.

• regs PC, regs NPC, regs NNPC: contador de programa de la instrucción,


contador de programa de la siguiente instrucción, y contador de pro-
grama para la segunda instrucción siguiente. Este último valor es el
modificado por una instrucción de salto tomada en la etapa dispatch

49
(etapa en que se simula la funcionalidad de las instrucciones), puesto
que nos encontramos ante una arquitectura con un delay slot de una
instrucción.

• pred NPC, pred NNPC: contadores de programa predichos. El predictor


de saltos genera predicciones para pred NNPC, a causa del delay slot. El
campo pred NPC es igual al campo pred NNPC de la instrucción anterior
buscada.

• thread id: hilo al que pertenece la instrucción

• in LSQ: TRUE cuando la instrucción se encuentra también en la cola LSQ.

• ea comp: TRUE si se trata de una instrucción de cálculo de la dirección


efectiva de un acceso a memoria.

• spec mode: TRUE si la instrucción se está ejecutando en modo especula-


tivo. En un procesador real, esto no se conoce hasta la etapa commit.
Sin embargo, durante la simulación advertimos un fallo de especulación
desde el momento de una predicción errónea. Como consecuencia, de-
bemos establecer puntos de recuperación para el banco de registros y
el estado de la memoria, y restaurarlo en el momento en que la primera
instrucción incorrectamente predicha llegue a la última etapa.

• kill: TRUE si es una instrucción de finalización del hilo actual. Por


ejemplo, la instrucción break, o una llamada al sistema de finalización.

• addr: dirección virtual efectiva para una instrucción de acceso a memo-


ria.

• tag: identificador único durante toda la simulación para la instrucción.


Más adelante, veremos la utilidad de este campo.

50
• seq: identificador de secuencia de la instrucción, útil para la generación
de una traza de ejecución.

• data: dato a almacenar en memoria en la última etapa. Este dato se


necesita para el caso en que tratemos con memorias cache que necesiten
almacenar los datos durante la simulación, por ejemplo para generar
estadı́sticas de consumo dependiente de los datos.

• recover inst: TRUE si es la última instrucción ejecutada en modo no


especulativo.

• stack recover idx, dir update: control del predictor de saltos.

• queued, issued, completed, squashed: indicadores del estado de una ins-


trucción, según la etapa en que se encuentra y las modificaciones que
haya sufrido en cada una de ellas.

• onames, odep list, idep ready: dependencias de datos. En el apartado


3.3.3 se explica detalladamente la funcionalidad de estos campos.

3.3.3 Control de dependencias de datos

Referencias a elementos de la RUU

Desde múltiples posiciones del código del simulador es necesario hacer refe-
rencia a elementos de la RUU. Estas referencias tienen normalmente asociada
cierta información; por ejemplo, si tenemos referencias a instrucciones que
fueron lanzadas a ejecución y están esperando la finalización de la actividad
de su unidad funcional correspondiente, es necesario almacenar el tiempo en
que se dará este evento. Para este y otros propósitos, nos encontramos con
la estructura RS link:

51
struct RS_link {
struct RS_link *next;
struct RUU_station *rs;
dword tag;

union {
sdword when;
dword seq;
int opnum;
} x;
};

A continuación se describe el significado de cada uno de los campos:

• next: Cada vez que se requiere hacer referencia a un elemento de la


RUU, es necesario crear una variable de tipo RS link. Si esta creación
se realizara mediante funciones de alojamiento dinámico de memoria
(malloc y free), su gestión serı́a demasiado lenta.

La estrategia para evitar este problema consiste en disponer de un gran


conjunto de elementos RS link ya creados y no utilizados, del cual se van
escogiendo siempre que se necesiten, y en el que se van depositando,
una vez se liberen.

El campo next de la estructura sirve para formar una lista enlazada que
forma el conjunto de elementos RS link libres.

• tag: este campo forma parte de un mecanismo de versatilidad en la


referencia a instrucciones de la RUU. Su incorporación está motivada
por la posibilidad de la eliminación de entradas en la RUU que todavı́a
poseen referencias. Un ejemplo de esta situación es el vaciado del pipe-
line ante un fallo de especulación; en tal caso, la cola de eventos, por
ejemplo, contiene referencias a instrucciones ya inexistentes.

52
Por otra parte, hay que recordar que la estructura RUU station posee
a su vez un campo tag, al que se asigna un valor en el momento de
la creación de la entrada en la RUU. Dicho valor es creciente en una
unidad respecto a la última asignación, por lo que nunca puede haber
dos instrucciones cuyo campo tag haya recibido el mismo valor a lo
largo de toda la simulación.

De esta forma, se define el criterio de validez de un elemento RS link


(referencia a una instrucción de la RUU) en base al resultado de la
comparación del campo tag de la propia estructura que representa el
enlace y el campo del mismo nombre de la estructura RUU station a
la que hace referencia. Si ambos difieren, el enlace se considera no
efectivo.

Ası́ pues, cuando se elimine una entrada de la RUU, habrá que tomar
como precaución el establecimiento de su campo tag a 0. Como ninguna
instrucción que se instale en la misma entrada tomará el mismo valor,
el enlace quedará sin efecto permanentemente.

• Unión x: sus campos sirven para aportar cierta información al enlace,


según el contexto en que éste se aplique. Las tres posibilidades son:

– when: cuando la referencia forma parte de un elemento de la cola


de eventos, compuesta por las instrucciones que están en ejecución
en las unidades funcionales, este campo indica el ciclo futuro en
que la instrucción finaliza su ejecución.

– seq: utilizado para establecer una relación de orden en la cola


de instrucciones preparadas para ser lanzadas a ejecución (ready -
queue).

53
– opnum: cuando se usa una referencia a una instrucción dentro de
una lista de dependencias de salida de una operación, este campo
indica cuál de los posibles operandos de salida (0, 1 ó 2) es el que
provoca la dependencia.

Listas de dependencias

Las listas de dependencias están gestionadas por estructuras de datos, fun-


ciones y macros definidas e implementadas en ruu.h y ruu.c. El elemento
principal es el vector de creadores, uno por cada hilo, que contiene tantos
elementos de tipo CV link como registros destino posibles. El elemento i del
vector de creadores indica qué entrada de la RUU realizó la última escri-
tura sobre el registro i, observando el pipeline desde la etapa dispatch. La
definición del vector de creadores es ésta:

struct CV_link {
struct RUU_station *rs;
int odep_num;
};
struct proc_t {
struct {
struct CV_link create_vector[MD_TOTAL_REGS];
struct CV_link spec_create_vector[MD_TOTAL_REGS];
int use_spec_cv[MD_TOTAL_REGS];
...
} thread[MAX_THREADS];
...
}

El campo create vector de cada hilo corresponde al vector de creado-


res. Con el mismo nombre y anteponiendo el prefijo spec , se llama al
campo que representa el vector de creadores en modo especulativo. El vector

54
use spec cv contiene TRUE en aquellas posiciones correspondientes a los regis-
tros cuyos valores se escribieron estando en modo especulativo, y por tanto,
aquéllos para los que se accederá al vector de creadores a través del campo
spec create vector.

Cada elemento i del vector de creadores es de tipo CV link, y contiene


una referencia a una entrada de la RUU y un campo odep num, que indica
cuál es el ı́ndice de la dependencia de salida que produjo una escritura sobre
el registro i.
Puede decirse que un elemento i del vector de creadores representa la ca-
beza de una lista de dependencias. Por otro lado, es necesario un mecanismo
para enlazar las instrucciones (o elementos de la RUU) que consumen el valor
escrito en el registro i. Estos enlaces vienen representados por los siguientes
campos asociados a cada entrada de la RUU:

struct RUU_station {
...
int onames[2];
struct RS_link *odep_list[2];
int idep_ready[3];
};

El campo odep list es una referencia a otra entrada de la RUU, formando


una lista enlazada de entradas que consumen el valor de un determinado re-
gistro. El campo onames indica los registros que escribe la instrucción repre-
sentada por una entrada de la RUU (como máximo 2), y el campo idep ready
indica si las dependencias de entrada de la instrucción están satisfechas.
Por último, las funciones que gestionan los valores asociados a todos estos
campos son éstas:

void ruu_link_idep(struct proc_t *proc, struct RUU_station *rs,

55
int idep_num, int idep_name);

void ruu_install_odep(struct proc_t *proc, struct RUU_station *rs,


int odep_num, int odep_name);

3.3.4 Definición del juego de instrucciones

El conjunto de instrucciones MIPS implementadas viene especificado


únicamente, y sin ningún acoplamiento con otros módulos, en el fichero
machine.def. La estrategia para encapsular toda la información relativa
al juego de instrucciones en un solo fichero se ha adoptado del simulador
SimpleScalar.
Para comprender el funcionamiento de este sistema, se muestra primero
la implementación de la instrucción ADDI, que realiza la suma del contenido
de un registro con un valor inmediato, depositando el resultado en un registro
destino. Como se especifica en [15], el formato de esta instrucción máquina
es el siguiente:
31 26 25 21 20 16 15 0

ADDI
rs rt immediate
001000
6 5 5 16

Figura 3.1: Formato de la instrucción ADDI

Por otra parte, y a modo de guı́a, el siguiente código en el archivo


machine.def define el formato y comportamiento de esta instrucción:

#define ADDI_IMPL { \
int_t temp = (int_t) GPR(RS) + (int_t) IMM; \
SET_GPR(RT, SEXT64(temp, 32)); \
}

56
DEFINST(ADDI, 0x20000000,
0xfc000000, "t,s,i",
IntALU, F_ICOMP|F_IMM,
DGPR(RT),DNA, DGPR(RS),DNA,DNA)

Como se puede observar, la especificación de una nueva instrucción se


lleva a cabo mediante la definición de una macro con el nombre XXX IMPL
seguida del uso de la macro DEFINST, cuyo primer parámetro es XXX, siendo
XXX el nombre de la nueva instrucción. En los dos siguientes subapartados se
explica el funcionamiento de ambas.

Definición del comportamiento de una instrucción

El código encerrado entre las llaves que siguen a ADDI IMPL en el ejemplo
especifica el comportamiento de la instrucción ADDI. Puede definir variables al
comienzo y puede usar cada una de las siguientes macros, que se presuponen
definidas y dedicadas al uso en la definición del comportamiento de una
instrucción:

• OPCODE, RS, RT, RD, SHIFT, FUNC, TARGET: campos de la instrucción de-
codificada. Por ejemplo, la macro RS devuelve un valor entre 0 y 31,
correspondiente a los 5 bits de la instrucción correspondientes al campo
rs.

• IMM, UIMM: el primero devuelve el valor del campo imm de la instrucción


como un entero con signo de 64 bits (tipo lint t definido en misc.h), ex-
tendiendo su signo a partir del bit 15. El segundo devuelve este mismo
valor pero como un entero sin signo de 64 bits (tipo dword t). Según la
instrucción de que se trate, el valor inmediato puede interpretarse de
una forma u otra.

57
• GPR(X), FPRS(X), FPRD(X): lectura del banco de registros. Con la macro
GPR se accede al banco de registros de enteros. Con FPRS, al banco de
registros de coma flotante, como valores IEEE de simple precisión. Por
último, con FPRD se accede a este mismo banco de registros, aunque
considerando grupos de dos registros que, combinados, forman valores
IEEE de doble precisión. En este último caso, X debe ser un número
par.

• SET GPR, SET FPRS, SET FPRD: escritura en el banco de registros. Las dife-
rentes posibilidades son las citadas en el punto anterior.

• PC, NPC: lectura del registro contador de programa y de NPC, que indica
el siguiente valor que tomará PC.

• BRANCH(X), RELBRANCH(X): el primero realiza un salto a la instrucción


situada en la dirección X, siendo X un entero sin signo de 64 bits. El
segundo lleva a cabo un salto relativo al PC actual, y X es un entero con
signo de 64 bits.

• FPCR, SET FPCR: lectura y escritura en el registro floating point condi-


tion register, que alberga los resultados de las comparaciones de coma
flotante.

• READ XXX, WRITE XXX: lectura y escritura en memoria. XXX puede ser BYTE,
HALF, WORD o DWORD.

Especialmente para la implementación del juego de instrucciones, resul-


tan útiles algunas macros implementadas en misc.h, relacionadas con el tra-
tamiento de bits (BITS32, BITS64, etc.) o con la extensión de signo (SEXT32 o
SEXT64).

58
Definición del formato de una instrucción

La macro DEFINST, con todos sus parámetros, es la encargada de describir


el formato y otras caracterı́sticas de una instrucción máquina MIPS. Esta
macro recibe los siguientes argumentos:

DEFINST(INST, BITS,
MASK, FORMAT,
FUCLASS, FLAGS,
O1, O2, I1, I2, I3)

• INST: nombre de la instrucción. La macro que implemente la funcio-


nalidad de la instrucción debe recibir este nombre seguido del sufijo
IMPL.

• BITS y MASK: éstos son los dos campos que identifican los bits de la
función. El valor de MASK es una máscara de bits, cuyos bits a 1 indican
las posiciones de una instrucción a las que se les exige tomar un cierto
valor para poderse considerar que representan a la instrucción INST. El
valor de BITS indica cuáles son los valores de esas posiciones.

Tomando como ejemplo la instrucción ADDI, cuyo formato se ha mos-


trado anteriormente, se puede afirmar que las posiciones 31 a 26 son
las que deben tomar un determinado valor para que la instrucción sea
la que es, es decir, el campo MASK debe contener una máscara de bits
cuyas posiciones 31 a 26 valgan 1, y el resto, 0. El campo BITS debe
indicar el valor de los bits en las posiciones indicadas por MASK, siendo
en este caso 001000000...0.

De esta forma, para identificar qué instrucción es la palabra de 32


bits w extraı́da de un programa compilado, debemos recorrer todas las

59
definiciones, correspondiéndole la que cumpla que w and MASK sea igual
a BITS, siendo la operación and un producto lógico bit a bit.

En el apartado 3.4.1 se describe una pequeña utilidad, creada para


extraer los campos BITS y MASK de forma sencilla y automática a partir
de la especificación del formato de la instrucción.

• FORMAT: este campo indica los argumentos que tiene la instrucción en en-
samblador. Está compuesto por una serie de letras, indicando cada una
de ellas un campo de la instrucción. Cualquier otro carácter es interpre-
tado como un literal. Las siguientes letras, distinguiendo mayúsculas
y minúsculas, y entre otras, son posibles en este campo:

– J: campo target, interpretado como entero sin signo.

– j: campo imm, interpretado como etiqueta entera con signo rela-


tiva al PC.

– s, t, d: campos rs, rt y rd respectivamente.

– R, S, T, D: campos fr, fs, ft y fd respectivamente, interpretándose


como registros de coma flotante (véase [15]).

– i, u: Campo imm, considerado como entero con o sin signo, res-


pectivamente.

El campo FORMAT resulta útil para la impresión de la instrucción deco-


dificada por pantalla, utilizada principalmente por la utilidad de visua-
lización de archivos binarios, expuesta en la sección 3.4.2.

• FUCLASS: unidad funcional que necesita la instrucción para llevar a cabo


su ejecución. La ejecución fuera de orden del simulador va asignando
instrucciones preparadas para la ejecución a las diferentes unidades

60
funcionales, en el caso de que estén libres. Cada instrucción tiene una
(o ninguna) unidad funcional asociada. Los posibles valores de FUCLASS
son los siguientes:

– fuNONE: la instrucción no utiliza ninguna unidad funcional.

– IntALU: unidad de aritmética entera sencilla (suma/resta).

– IntMULT, IntDIV: unidad de multiplicación o división entera.

– FloatADD: unidad de suma/resta de coma flotante.

– FloatCMP: unidad de comparación de coma flotante.

– FloatCVT: unidad de conversión de enteros-coma flotante.

– FloatMULT, FloatDIV: unidad de multiplicación/división de coma


flotante.

– FloatSQRT: unidad de raı́z cuadrada en coma flotante.

– RdPort, WrPort: puertos de lectura/escritura de la cache de nivel 1.

• FLAGS: este campo da información sobre la instrucción. Está compuesto


por la combinación de varias constantes mediante la operación de suma
lógica. Un subconjunto de los posibles valores es éste:

– F ICOMP: computación entera.

– F FCOMP: computación en coma flotante.

– F CTRL: instrucción de control.

– F MEM: instrucción de acceso a memoria.

– F CALL: llamada a procedimiento.

– F RET: retorno de procedimiento.

61
• INx y OUTx: estos campos indican las dependencias de entrada (3 como
máximo) y de salida (2 como máximo) de la instrucción. Las depen-
dencias se expresan mediante las siguientes macros:

– DGPR(N), DFPR(N): dependencia con el registro N del banco de enteros


o de coma flotante, respectivamente. N suele tomar el valor de un
campo de la instrucción, como por ejemplo RS.

– DREGHI, DREGLO: registros HI y LO, donde se suelen almacenar los


resultados de una multiplicación de enteros.

– DFPC: dependencia con el registro FPCR (floating point condition


register).

– DNA: no hay dependencia.

Hay que destacar que DEFINST es una macro que no está definida global-
mente, lo cual proporciona una gran flexibilidad para la definición del juego
de instrucciones. El fichero machine.def está diseñado para ser incluido (me-
diante la directiva #include) desde cualquier punto del resto de ficheros de
código, los cuales deben proporcionar una definición de la macro DEFINST.
Según la conveniencia en cada instante, se utilizan en la definición unos ar-
gumentos u otros, de entre todos los incorporados en machine.def.
Como ejemplo de la versatilidad de este mecanismo, podemos citar
la inclusión de este fichero desde machine.h para construir la enumeración
enum md opcode, que contiene una lista con los nombres de todas las instruc-
ciones máquina, incorporándoles el prefijo “OP ”:

enum md_opcode {
OP_ERR = 0,
#define DEFINST(NAME,BITS,MASK,FORMAT,FUCLASS,FLAGS,O1,O2,I1,I2,I3) OP_##NAME,
#include "machine.def"

62
#undef DEFINST
OP_MAX
};

3.4 Herramientas adicionales para el desarro-


llo y depuración
La complejidad inherente al desarrollo de un modelo de un procesador su-
perescalar genérico motiva la construcción de tres programas adicionales e
independientes del simulador principal.
El primero de ellos resulta de utilidad para el cálculo de las máscaras de
bits utilizadas en el archivo de definición del juego de instrucciones. El se-
gundo es un decodificador de archivos binarios ejecutables, útil para analizar
los ficheros generados por el compilador para MIPS, utilizando módulos per-
tenecientes al simulador principal. Por último, el tercer programa muestra
de forma tabular la información de traza generada por el modelo en tiempo
de simulación.
A continuación se describe en detalle cada uno de estos tres programas.

3.4.1 Cálculo de máscaras de instrucción

Como se ha explicado anteriormente, una palabra de 32 bits se asocia a una


instrucción máquina aplicando los campos MASK y BITS de la macro DEFINST.
Además, si una palabra cuadra con dos instrucciones aplicando esta regla,
se considera que se trata de la instrucción que aparece antes en el fichero
machine.def.

Para calcular los valores de estos dos campos, se ha construido una he-
rramienta simple, de nombre inst, que recibe como parámetros los bits que

63
deben tomar un valor determinado. Por tanto, a partir de la especificación del
formato de la instrucción en [15], se pueden extraer fácilmente las máscaras
de instrucción, para su posterior inserción en la macro DEFINST.
Considérese, por ejemplo, la instrucción de comparación de números en
coma flotante C.cond.fmt:
31 26 25 21 20 16 15 11 10 8 7 6 5 4 3 0

COP1 A FC
fmt ft fs cc 0 cond
010001 0 11
6 5 5 5 3 1 1 2 4

Figura 3.2: Formato de la instrucción C.fmt.cond

La ejecución de la herramienta inst para extraer los campos de esta ins-


trucción es la siguiente:

$ inst -opcode 0x11 -bits 7 4 0011


(bits, mask) = (0x44000030, 0xfc0000f0)

Se puede comprobar que toda palabra w de 32 bits que haga verdadera


la expresión “w and 0x44000030 == 0xfc0000f0” puede considerarse que repre-
senta una instrucción máquina C.fmt.cond, con los campos variables de la
instrucción tomando cualquier valor.

3.4.2 Visor de archivos binarios

Otra de la utilidades desarrolladas para la depuración del simulador principal


es un visor de archivos binarios, aquı́ llamado viewer. Este visor funciona de
la misma forma que el programa objdump, parte de GNU binutils.
Su función es mostrar un listado de todas las instrucciones decodificadas
que forman parte de un archivo binario ejecutable. Además, interpreta la
información de sı́mbolos incrustada en el fichero analizado (principalmente

64
etiquetas de instrucciones), y las muestra en el listado final. Las direcciones
a zonas de código emitidas en las instrucciones se muestran relativas a la
etiqueta más cercana anterior.
La utilidad del visor de archivos binarios como herramienta depuradora
del simulador reside en la comparación del código decodificado con el de
objdump de GNU, y en la posibilidad de localizar instrucciones todavı́a no
implementadas (se muestran con el código -err-).
Una posible ejecución del visor provoca una salida como ésta:

...
4003e4 00000000 nop
4003e8 03e00008 jr ra
4003ec 27bd0020 addiu sp, sp, 32

004003f0 <main>:
4003f0 3c1c0fc1 lui gp, 4033
4003f4 279cc0e0 addiu gp, gp, -16160
4003f8 0399e021 addu gp, gp, t9
4003fc 27bdff70 addiu sp, sp, -144
400400 afbc0028 sw gp, 40(sp)
...

3.4.3 Aplicación pedagógica de la herramienta

La última herramienta auxiliar implementada es un programa para el segui-


miento de la traza de simulación. El simulador tiene dos opciones de lı́nea
de órdenes que controlan la traza, desactivada por defecto:

• -ptrace <fichero>: vuelca en <fichero> la información de traza en cada


ciclo de simulación. Está compuesta en principio únicamente por el

65
estado del pipeline del procesador, es decir, se notifica la entrada, avance
o salida de una instrucción a través de sus diferentes etapas.

• -ptrace details <unidades funcionales> <RUU> <memoria>: establece la


información adicional que se adjunta al estado del pipeline. Cada uno
de estos campos es simplemente un flag (verdadero o falso) que indica si
se debe volcar información sobre el estado de las unidades funcionales,
la RUU o la memoria.

La información de traza elemental está implementada en el módulo


ptrace.c, mientras que cualquier información adicional se añade mediante
las funciones con sufijo dump (por ejemplo mem dump), destinadas a volcar in-
formación sobre el contenido de una estructura.
Ası́ pues, la herramienta adicional a la que se alude en este apartado tiene
como objetivo la interpretación de los ficheros de traza para su posterior
representación tabular. El nombre de este programa es ptrace1 .
La tabla 3.2 es un ejemplo de la ejecución del programa ptrace. Cada vez
que el usuario pulsa Intro, la traza avanza un ciclo. Además, por lı́nea de
órdenes se puede especificar el ciclo en que comenzar la traza, la dirección de
la instrucción en la que parar el avance, etc.
El programa obtiene la información de traza a analizar desde la entrada
estándar, pudiendo venir directamente desde la salida redireccionada del si-
mulador, o desde el volcado, también redireccionado, del fichero de traza.
La capacidad de analizar el estado del procesador en un determinado
ciclo (o al alcanzar una determinada instrucción) da un enfoque pedagógico
adicional al simulador. Las tablas resultantes adoptan aspectos diferentes
según se especifiquen los parámetros de configuración (ancho de búsqueda,
ancho de decodificación, latencia de unidades funcionales, etc).
1
No confundir con el módulo del simulador con el mismo nombre (ptrace.c)

66
***** Traza (seq, thread_id, pc, inst) - ciclo = 10075 *****************************
46 47 48 49 50 51 52 53 54 55 56 57 58
---- 0 4377c4 ’ld/st interno’ WB C
---- 0 4377c8 lw s5, 44(sp) IF DI EX WB C
---- 0 4377cc lw s4, 40(sp) IF DI EX WB C
---- 0 4377d0 lw s3, 36(sp) IF DI EX WB C
---- 0 4377d4 lw s2, 32(sp) IF DI EX WB C
---- 0 4377c8 ’ld/st interno’ DI EX WB C
---- 0 4377d8 lw s1, 28(sp) IF DI EX WB C
---- 0 4377dc lw s0, 24(sp) IF DI EX WB C
---- 0 4377e0 addu v0, a0, zero IF DI EX WB C
---- 0 4377e4 jr ra IF DI EX WB C
---- 0 4377cc ’ld/st interno’ DI EX WB C
---- 0 4377e8 addiu sp, sp, 56 IF DI EX WB C
---- 0 434c10 lw gp, 16(sp) IF DI EX WB

Tabla 3.2: Ejemplo de ejecución de ptrace

La observación y extracción de conclusiones sobre el manifiesto de de-


terminados parámetros en la disposición de los elementos de la tabla de
traza requiere una comprensión de los conceptos básicos relacionados con los
procesadores superescalares, y por tanto, constituye una forma de (auto)-
evaluación de los conocimientos adquiridos.

67
Capı́tulo 4

Resultados

En este capı́tulo se describe cómo se han alcanzado resultados que ratifican


la efectividad de la técnica de apagado de ceros y que muestran el efecto
de combinarla con cache decay. Se exponen los parámetros de la máquina
modelada en la que se basan las simulaciones y se analiza la metodologı́a
seguida para obtener resultados gráficos.

4.1 Entorno de simulación


Los parámetros de la máquina simulada mediante la herramienta desarrollada
quedan reflejados en la tabla 4.1. La técnica de apagado de ceros se aplica
sobre la memoria cache de nivel 1.
Para compilar las cargas para MIPS ha sido necesario crear un compilador
cruzado (cross-compiler), que ejecutado sobre una máquina con arquitectura
i386, genere ejecutables MIPS. El compilador original utilizado es gcc, a
partir del cual se han construido versiones para compilación de C y Fortran,
generando archivos ejecutables con formato ELF32, e instrucciones máquina
de MIPS32.

68
Tabla 4.1: Parámetros de la máquina

Núcleo del microprocesador


Polı́tica issue Fuera de orden
Tipo del predictor de saltos Predictor hı́brido gShare-
bimodal:
Gshare tiene un registro histórico
de 16 bits y 64K contadores de 2
bits
Bimodal tiene 2K contadores de
2 bits, y el predictor de elección
tiene 1K contadores de 2 bits cada
uno
Penalización del predictor de saltos 2 ciclos
Ancho de fetch, issue y commit 2 ciclos
Tamaño del ROB 64 entradas
Número de ALUs de enteros / multipli- 4/1
cadores/divisores
Número de ALUs de reales / multipli- 2/1
cadores/divisores
Jerarquı́a de memoria
Cache de datos L1 16KB, 4 vı́as, bloque de 64 bytes
Tiempo de acceso a L1 1 ciclo
Cache de datos L2 512KB, 8 vı́as, bloque de 64 bytes
Tiempo de acceso a L2 18 ciclos
Tiempo de acceso a memoria 200 ciclos

69
4.2 Resultados de las simulaciones
Como ya se ha explicado en secciones anteriores, la técnica de apagado
de ceros es ortogonal a otras polı́ticas de reducción de consumo. Resulta,
por tanto, de gran interés experimentar con la combinación de más de una
técnica, para observar en qué grado aumentan los ahorros de energı́a estática.
En concreto, la técnica cache decay es la escogida para los experimentos reali-
zados, puesto que supera el ahorro de consumo de técnicas alternativas exis-
tentes y utiliza el mismo mecanismo de desconexión de celdas que apagado
de ceros(Gated-Vdd).
Los parámetros especı́ficos de cache decay son los siguientes:

• Siendo cache decay una técnica basada en contadores locales por cada
lı́nea de cache y un contador global para toda la memoria, se deben
establecer sus rangos, que repercutirán en los ahorros de energı́a y en
el grado de disminución del IPC. Se asumen contadores locales de 2 bits,
con rango de 3 a 0. Una lı́nea de cache se apaga cuando su contador
local asociado vale 0 y recibe una notificación del contador global. En
ese momento, se produce una pérdida de los datos de la lı́nea, debido
a la naturaleza de la técnica de apagado utilizada por cache decay.

• Los contadores globales tienen un rango de 8191 a 0. Cada vez que un


contador global se desborda bajo 0, se envı́a una señal a todos los con-
tadores locales para que disminuyan su contenido. Más detalles sobre
el funcionamiento de cache decay se pueden encontrar en la sección 2.1.

Como ya hemos demostrado en el capı́tulo 2, las resoluciones más apro-


piadas para apagado de ceros son las que explotan tres combinaciones del
vector S para cargas de enteros, y la que explota sólo dos combinaciones

70
Integer benchmarks Floating-point benchmarks Averages
100

80
Leakage energy savings (%)

60

40

20

0
16
17 gzip
17 vpr
18 gcc
18 mcf
19 craf
25 par
25 per
25 gap k
30 bzip

16
17 wup
17 swi se
17 gr
17 app
17 me
18 art
18 equ
18 face e
30 amm c

in
fp
al
t

l
4.
5.
6.
1.
6.
7. ty
3. se
4. lbm
6.
0. 2

8.
1. w
2. m
3. id
7. lu
9. sa
3.
7. ak
8. re
1.
tw

ap p
ol

si
f
r

Decay Decay+Zeros switch-off


Zeros switch-off

Figura 4.1: Ahorro de energı́a estática para cache decay, apagado de


ceros y una combinación de ambas

de S para cargas de reales (quedando incluidas las combinaciones que man-


tienen la palabra completamente encendida). Por tanto, para las siguientes
simulaciones, escogemos la resolución 32+24 para enteros y 32 para reales.
La figura 4.1 muestra los resultados para tres mecanismos de ahorro de
consumo posibles: cache decay, apagado de ceros, y una combinación de am-
bos. Se puede observar que en la mayorı́a de las cargas, esta polı́tica produce
ahorros menores que las otras dos. En la mayorı́a de las cargas, la polı́tica
de apagado de ceros aumenta el ahorro de energı́a estática, y adicionalmente,
tiene la ventaja de no repercutir en las prestaciones, al contrario que cache de-
cay. Por último, el ahorro aumenta sensiblemente al aplicar ambas técnicas,
aunque se incurre al mismo tiempo en una pequeña pérdida de prestaciones.
El bloque de barras de la figura 4.1 muestra que, en promedio, la polı́tica
de apagado de ceros mejora en un 8.2% a cache decay (4.6% respecto a la

71
Integer benchmarks Floating-point benchmarks Averages
1.1
1
0.9
0.8
0.7
IPC loss (%)

0.6
0.5
0.4
0.3
0.2
0.1
0
16
17 gzip
17 vpr
18 gcc
18 mcf
19 craf
25 par
25 per
25 gap k
30 bzip

16
17 wup
17 swi se
17 gr
17 app
17 me
18 art
18 equ
18 face e
30 amm c

in
fp
al
t

l
4.
5.
6.
1.
6.
7. ty
3. se
4. lbm
6.
0. 2

8.
1. w
2. m
3. id
7. lu
9. sa
3.
7. ak
8. re
1.
tw

ap p
ol

si
f
r

Figura 4.2: Pérdida de IPC en la combinación de cache decay con


apagado de ceros

energı́a total). Combinando ambas técnicas, se mejora a cache decay en un


12.7% (11.5% respecto al total).
Un aspecto adicional analizado en las simulaciones lanzadas es la cuantifi-
cación del IPC (Instructions Per Cycle) en las combinaciones que incorporan
cache decay. Recordemos que cache decay es una polı́tica de reducción de con-
sumo agresiva, puesto que apaga lı́neas completas de cache cuando estima
que su tiempo de vida ha finalizado. Sin embargo, una estimación incorrecta
incurre en un aumento de la tasa de fallos en L1 y, por tanto, en un aumento
del IPC. La figura 4.2 muestra los resultados obtenidos al respecto.
Como muestra la última barra, el aumento del IPC al aplicar cache de-
cay (combinado o no con apagado de ceros) es de 0.46% para los valores de
los contadores escogidos. Esta pequeña degradación de prestaciones es to-
lerable aunque prescindible en una arquitectura de bajo consumo. Como se
ha demostrado en este proyecto, la polı́tica de apagado de ceros no produce

72
ninguna variación en el IPC, y a su vez mejora los ahorros de cache decay.
En consecuencia, la técnica de apagado de ceros se revela como una opción
más efectiva que cache decay. Por otra parte, si para un determinado diseño
no resulta especialmente crı́tica la pequeña pérdida de prestaciones y sı́ lo es,
en cambio, el consumo, queda en manos del diseñador la decisión de escoger
o no una implementación que combine ambas técnicas, y por tanto ofrezca
un consumo todavı́a más bajo.

73
Capı́tulo 5

Conclusiones

En este proyecto se ha presentado una técnica, denominada apagado de ceros,


destinada a reducir el consumo de energı́a estática en memorias cache. Está
basada en la explotación de la distribución de bits a cero en los datos de
los programas más comunes, y en concreto, de los benchmarks SPEC2000:
estos bits están concentrados comúnmente en la parte más significativa de
las palabras. El mecanismo de apagado de ceros apaga todos o parte de los
bits más significativos cuando su valor es cero, codificando en celdas SRAM
extra cuántos bits han perdido la alimentación.
El estudio realizado se ha centrado en caches de datos de nivel 1, donde
el problema de corrientes de fuga es crı́tico. Sin embargo, el mecanismo es
extrapolable a otras caches de la jerarquı́a de memoria. Los datos albergados
en memorias cache de nivel superior siguen la misma distribución que los de
L1, lo cual las hace susceptibles de implementar apagado de ceros, ortogonal
a polı́ticas de apagado existentes.
Hemos analizado el potencial de la técnica propuesta mediante una im-
plementación hardware, que confiere flexibilidad al diseñador mediante la
variación de la resolución de apagado. Cada una de las resoluciones explo-

74
radas tiene una carga hardware extra distinta, además de que proporciona
diferentes formas de explotación en mayor o menor medida de las posibilida-
des de apagar bits en las palabras. Implementando una resolución más alta,
se tendrán en promedio más bits apagados en los datos de la memoria cache;
sin embargo, la circuiterı́a de control aumenta en complejidad y, por tanto,
en consumo.
En el capı́tulo 2, se muestran los experimentos realizados, algunos de ellos
centrados en hallar la resolución de apagado idónea, que consiga un mayor
ahorro de consumo teniendo en cuenta ambos factores. Se ha llegado a la
conclusión de que la resolución 32+24 es la óptima para las cargas SPEC2000
de aritmética entera, mientras que la resolución 32 lo es para las de aritmética
en coma flotante (la nomenclatura utilizada en las resoluciones de apagado
queda descrita en la sección 2.3.3). Un diseñador puede escoger una de estas
dos según la aplicación más común que vaya a recibir su implementación.
En el capı́tulo 4, se muestran los experimentos destinados a analizar la
combinación de apagado de ceros con cache decay, utilizando ya para la pri-
mera técnica las resoluciones óptimas establecidas en el capı́tulo 2. Para las
cargas SPEC2000, apagado de ceros proporciona un ahorro medio de energı́a
estática del 60.3%, mientras que la combinación de ambas técnicas alcanza el
67.3%. Estos resultados consideran la carga hardware adicional ocasionada
por la lógica de control.
El incremento del área de la cache depende de la resolución escogida. En
el caso de 32+24, la porción del chip ocupada por la memoria cache L1 crece
en un 5.88%. Para una resolución de apagado de 32, el aumento del área
es del 2.94%. De nuevo, apagado de ceros ofrece flexibilidad en este sentido.
Si el tamaño del chip es crı́tico en un determinado diseño, por ejemplo por
existir una limitación en el número de transistores empleados, puede optarse

75
por una resolución de 32, que incrementa casi de forma despreciable el área
de la memoria cache, obteniendo ahorros de consumo muy altos incluso en
los casos en los que no se trata de la resolución óptima.
Por último, y si bien no es reciente la idea del aprovechamiento de la
distribución de los datos para disminuir el consumo, se puede decir que sı́
resulta novedosa su aplicación a la reducción de la energı́a estática disipada
debida a las corrientes de fuga. Teniendo en cuenta todas las consideraciones
previas, procede afirmar que la técnica de apagado de ceros constituye una
propuesta original, factible y efectiva en el ámbito de las arquitecturas de
bajo consumo.

5.1 Contribuciones
El trabajo realizado puede ser reutilizado con diferentes objetivos. Uno de
ellos es la docencia, ámbito en el que se puede hacer uso de la herramienta
diseñada para la evaluación de las prestaciones de un procesador ante dife-
rentes configuraciones de sus parámetros. Adicionalmente, al tratarse de una
aplicación de código abierto, se pueden proponer a los estudiantes ejercicios
de modificación de la arquitectura, ampliándola para dar soporte a nuevas
técnicas de procesamiento, reducción del consumo, etc.
El programa de visualización de traza (ptrace) pretende potenciar la apli-
cación del simulador a la docencia, dando la posibilidad al estudiante de exa-
minar paso a paso la ejecución de un programa sobre una arquitectura ya
implementada o sobre un nuevo diseño propio.
Asimismo, la técnica de apagado de ceros ha supuesto una contribución
a la investigación en el ámbito de arquitectura del procesador. Los resulta-
dos preliminares de este proyecto se han enviado a diferentes congresos, de

76
ámbito nacional e internacional, para su evaluación y, si procede, su posterior
publicación. Los trabajos enviados son los siguientes:

Trabajo: Exploiting zero-bit patterns to reduce static energy in data ca-


ches
Autores: R. Ubal, J. Sahuquillo, S. Petit y P. López
Congreso: Jornadas de Paralelismo 2006, Albacete (congreso nacional)

Trabajo: Applying the zeros switch-off technique to reduce static energy


in data caches
Autores: R. Ubal, J. Sahuquillo, S. Petit y P. López
Congreso: SBAC-PAD 2006, Brasil (congreso internacional)

5.2 Trabajo futuro


Una parte importante del trabajo invertido en el desarrollo de este proyecto
se ha dedicado a la construcción de una herramienta de simulación persona-
lizada, que ejecuta los programas SPEC2000, y que puede ser instrumentada
para tomar medidas sobre el consumo en la memoria cache. La herramienta
construida está orientada desde un primer momento a poder adaptarse de
forma rápida y sencilla a una nueva lı́nea de investigación: los entornos mul-
tiprocesador. El objetivo futuro, basándose en el trabajo realizado hasta el
momento, es poder simular cargas paralelas (como por ejemplo, el conjunto
de benchmarks Splash o Splash-2) con el fin de adaptar en este tipo de ar-
quitecturas nuevas técnicas de mejora de prestaciones, tolerancia a fallos o
reducción del consumo.
Por el momento, la herramienta anexada a este proyecto, y a través de la

77
cual se han llevado a cabo los experimentos expuestos, ya incorpora soporte
para la simulación de procesadores multi-hilo. En este tipo de arquitectu-
ras, varios hilos de ejecución, con mapas de memoria independientes y sin
operaciones de comunicación entre ellos, transcurren simultáneamente, com-
partiendo las unidades funcionales del procesador (puertos de memoria y
operadores aritmético-lógicos).
Tanto los procesadores multi-hilo como los CMP constituyen una
parte importante de la tecnologı́a moderna, tanto de procesadores de uso
doméstico, como de los procesadores de altas prestaciones. En consecuen-
cia, la investigación actual en el ámbito de la arquitectura de computadores
se centra en ellos, motivando ası́ la orientación de futuros trabajos en esta
dirección.

78
Referencias

[1] J. Sánchez y A. González, “A Locality Sensitive Multi-Module Cache


with Explicit Management”, Proceedings of the ACM International
Conference on Supercomputing, Rhodes, Greece, 1999.

[2] J.M. Parcerisa, J. Sahuquillo, A. González, and J. Duato, “Efficient


Interconects for Clustered Microarchitectures”, Proceedings of the 7th
International Conference on Parallel Architectures and Compilation
Techniques, Charlottesville, USA, Septiembre 2002.

[3] P. Racunas, and Y. Patt, “Partitioning First-Level Data Cache Design


for Clustered Microarchitectures”, Proceedings of the ACM International
Conference on Supercomputing, San Fracnisco, USA, Junio 2003.

[4] K. Flautner, N.S. Kim, S. Martin, D. Blaauw, and T. Mudge, “Drowsy


Caches: Simple Techniques for Reducing Leakage Power”, Proceedings
of the 29th International Symposium on Computer Architecture, Mayo
2002.

[5] J. Abella, and A. Gonzalez, “Power Efficient Data Cache Desings,”


Proceedings of the 21th International Conference on Computer Design,
Octubre 2003.

79
[6] C. F. Chen, S. H. Yang, B. Falsafi, and A. Moshovos, “Accurate
and Complexity-Effective Spatial Pattern Prediction”, Proceedings of
the 10th International Symposium on High-Performance Computer
Architecture, Febrero 2004.

[7] J. Yang y R. Gupta, “Energy Efficient Frequent Value Data Cache


Design”, International Symposium on Microarchitecture, Noviembre
2002.

[8] S. Kaxiras, Z. Hu y M. Martonosi, “Cache Decay: Exploiting


Generational Behaviour to Reduce Cache Leakage Power”, 28th
International Symposium on Computer Architecture (ISCA), Junio
2001.

[9] K. Flautner, N.S. Kim, S. Martin, D. Blaauw, T. Mudge, “Drowsy


Caches: Simple Techniques for Reducing Leakage Power”.

[10] J. Sahuquillo, J.M. Such, S. Petit, “Non-state preserving approaches for


reducing leakage power in L2 caches”.

[11] L. Villa, M. Zhang, K. Asanović, “Dynamic Zero Compression


for Cache Energy Reduction”, 33rd International Symposium on
Microarchitecture, Monterey, CA, 2000.

[12] Y.J. Chang, F. Lai, “Dynamic Zero-Sensitivity Scheme for Low-Power


Cache Memories”, IEEE Computer Society, 2005.

[13] SimpleScalar LLC, www.simplescalar.com.

[14] HotLeakage, http://lava.cs.virginia.edu/HotLeakage/

[15] MIPS64 Architecture For Programmers (Volume I and II). Copyright


2001-2003, 2005 MIPS Technologies Inc.

80
[16] Y. Ye, S. Borkar, and V. De, “A New Technique for Standby Leakage
Reduction in High-Performance Circuits”, Symposium on VLSI Circuits,
Junio 1998

81

También podría gustarte