ESTRUCTURA Y TECNOLOGÍA DE COMPUTADORES I.T.I. Gestión, I.T.I. Sistemas e I. Superior, Grupos I y II Segundo Parcial – 15 de marzo de 2005 SOLUCIONES PROBLEMAS: 1. (3.5 puntos) Dado el siguiente procedimiento escrito en código MIPS: Proc: loop: fin: move xor bge sll add lw add addi j jr $t0,$0 $v0, $v0, $v0 $t0, $a1, fin $t1, $t0, 2 $t2, $a0, $t1 $t2, 0($t2) $v0, $v0, $t2 $t0, $t0, 1 loop $ra a) (0.25 puntos) Describir en castellano, de forma muy breve y concisa (una única frase bastaría) qué utilidad tiene esta rutina, es decir, qué valor devuelve en función de los parámetros $a0 (que contiene la dirección de memoria de un array de enteros de 32 bits) y $a1 (que indica el número de elementos del array). b) (0.75 puntos) Escribir el código máquina MIPS mínimo a que daría lugar el programa dado que éste se sitúa a partir de la dirección de memoria 0x0400 0010 (ver Notas 1 y 2). c) (0.5 puntos) El equipo de diseño de MIPS ha incluido una nueva instrucción en el ISA, aw $x, Cte($y) —accumulate word—. La nueva instrucción actúa de igual forma que la instrucción lw salvo que en lugar de sobrescribir el contenido del registro destino ($x) con el valor leído de memoria, aw suma éste al contenido de $x (es decir, acumula el valor leído de memoria en $x). Reescribir la rutina anterior haciendo uso de la nueva instrucción aw. d) (1 punto) Suponer que las unidades funcionales del camino de datos monociclo introducen los siguientes retardos: Memorias (instrucciones y datos)→2 ns, ALU y Sumadores→2ns y Banco de Registros (lectura y escritura)→1 ns. Dado que se llama a Proc con los siguientes parámetros: $a0→dirección de memoria del array {0,1,2,3,-3,-2,-1,0} y $a1→8 y que la instrucción aw se implementa en el procesador monociclo igual que lw pero añadiendo un sumador entre la salida de la memoria de datos y la entrada de escribir dato del banco de registros, ¿recomendarías al equipo de diseño incluir la instrucción en el procesador monociclo?. e) (1 punto) ¿Y en el procesador multiciclo?. Supón los mismos valores utilizados en el apartado anterior y que la instrucción aw se implementa utilizando dos veces la ALU (además de las que se usa en los dos primeros ciclos), una para calcular la dirección de memoria efectiva y la otra para acumular el valor leído de memoria en el registro destino (ver Nota 3) Notas.1. Invente los códigos de operación y función de las instrucciones. Construir una tabla como la que se muestra a continuación: 0x0400 0010 011... 011... 100... ... addi $11, ... Formato X Dir. Siguiente ... Direcciones (hexadecimal) 2. 3. Campos de las instrucciones (binarios o hexadecimal). Cada una con su formato divido en campos. 1 instrucción por fila (ensamblador), no pseudoinstrucciones. Los registros $a0 y $a1 se corresponden con el $4 y el $5 respectivamente, $v0 se corresponde con el $2, $ra con el $31, mientras que $t0, $t1 y $t2 son los registros $8, $9 y $10 respectivamente. Para el apartado e) suponer que las instrucciones aritmético-lógicas inmediatas (addi, andi, lui, ...) y de desplazamiento duran 4 ciclos. Para el resto de instrucciones asumir los retardos vistos en clase para el camino de datos multiciclo. Solución a) El procedimiento devuelve el resultado de la suma de los n primeros enteros de un array de enteros cuya dirección de comienzo se pasa como parámetro ($a0). El número n es el segundo parámetro que se pasa al procedimiento ($a1). b) b.1) Identificación de pseudoinstrucciones y obtención de las instrucciones MIPS equivalentes: move $t0, $0 bge $t0, $a1, fin → → add $8, $0, $0 slt $1, $8, $5 beq $1, $0, fin b.2) El código máquina resultante es el siguiente: Dirección 0x04000010 0x04000014 0x04000018 0x0400001C 0x04000020 0x04000024 0x04000028 0x0400002C 0x04000030 0x04000034 0x04000038 c) loop: fin: Inst MIPS add $8, $0, $0 xor $2, $2, $2 slt $1, $8, $5 beq $1,$0,fin sll $9, $8, 2 add $10,$4,$9 lw $10,0($10) add $2,$2,$10 addi $8,$8,1 j loop jr $31 Instrucción Máquina 0 $0 $0 $8 0 add 0 $2 $2 $2 0 xor 0 $8 $5 $1 0 slt beq $1 $0 0x0006 0 0 $8 $9 2 sll 0 $4 $9 $10 0 add lw $10 $10 0x0000 0 $2 $10 $2 0 add addi $8 $8 0x0001 j 0x1000006 (26 bits) jr $31 $0 $0 0 0 Formato Tipo R Tipo R Tipo R Tipo I Tipo R Tipo R Tipo I Tipo R Tipo I Tipo J Tipo R Después de utilizar la instrucción aw el código quedaría como sigue: Proc: loop: fin: add xor slt beq sll add aw addi j jr $t0,$0,$0 $v0, $v0, $v0 $at, $t0, $a1 $at, $0, fin $t1, $t0, 2 $t2, $a0, $t1 $v0, 0($t2) $t0, $t0, 1 loop $ra # Se sustituye el lw y add por la instrucción aw. d) Para implementar la instrucción en el procesador monociclo nos dicen que se ha añadido un sumador entre la salida de la memoria de datos y la entrada escribir dato del banco de registros. Este sumador será el encargado de sumar el valor leído de memoria al contenido del registro destino (para el código anterior $v0, que es el registro en el que se van acumulando todos los valores leídos de memoria). El resto de pasos de la instrucción aw son los mismos que para lw. De esta forma, los pasos necesarios para ejecutar aw son: 1. Buscar la instrucción e incrementar el PC en 4, 2. Leer los registros $t2 y $v0 y decodificar la instrucción, 3. Calcular con la ALU la dirección de memoria a leer, 4. Leer la posición de memoria, 5. Sumar el valor leído de memoria al contenido de $v0 y 6. Almacenar el resultado de la suma en $v0. A partir de los retardos de las unidades funcionales que se nos dan, una instrucción aw tardaría: 2 ns. (Memoria de instrucciones) + 1 ns. (BR) + 2 ns. (ALU) + 2 ns. (Memoria de datos) + 2 ns. (Sumador extra) + 1 ns. (BR) = 10 ns. Puesto que la duración de la instrucción de carga es de 8 ns., esta instrucción deja de ser la “más lenta” y por tanto la que determina la duración del ciclo de reloj en el procesador monociclo. Cuando incluimos la instrucción aw en el procesador monociclo la duración del ciclo de reloj se incrementa 2 ns. con respecto al procesador monociclo sin esta instrucción. El determinar si merece la pena incluir la nueva instrucción o no se reduce a calcular los tiempos de ejecución de la rutina antes y después de incluir en el procesador monociclo la instrucción aw. Supongamos que procesador monociclo estudiado en clase y que no incluye la instrucción aw, en este caso el tiempo de ejecución vendrá dado por la expresión: tantes = NºInstruccionesantes×CPI×TCiclo_antes Dados los valores de los parámetros del enunciado, el cuerpo del bucle se ejecuta 8 veces, con lo que NºInstruccionesantes = 1 (add) + 1 (xor) + 8×( 1 (slt) + 1 (beq) + 1 (sll) + 1 (add) + 1 (lw) + 1 (add) + 1 (addi) + 1 (j) ) + 1 (slt) + 1 (beq) + 1 (jr) = 2 + 8×8 + 3 = 69. Con lo que como tciclo_antes sabemos que es 8 ns. y el CPI es 1 (procesador monociclo) nos queda que tantes = 69×1×8 = 552 ns. Cuando incluimos la nueva instrucción en el procesador monociclo se modifican tanto el NºInstrucciones como el tiempo de ciclo del procesador. Ahora NºInstruccionesdespués = 1 (add) + 1 (xor) + 8×( 1 (slt) + 1 (beq) + 1 (sll) + 1 (add) + 1 (aw) + 1 (addi) + 1 (j) ) + 1 (slt) + 1 (beq) + 1 (jr) = 2 + 8×7 + 3 = 61. Tciclo_despues = 10 ns. y el CPI sigue siendo 1, con lo que tdespués = 61×1×10 = 610 ns. Vemos como después de incluir la nueva instrucción el tiempo de ejecución del programa ha aumentado, por lo que podemos concluir que no es conveniente el aumentar el procesador monociclo con esta nueva instrucción. e) Para la ejecución de la instrucción en el caso multiciclo son exactamente los mismos que los mostrados en el apartado anterior. De esta manera la instrucción requerirá un total de 6 ciclos de reloj para ejecutarse. En el caso multiciclo el tiempo de ciclo viene determinado por la etapa más lenta, que a partir de los retardos que nos dan, es la de ALU o la de acceso a memoria (2 ns.). En este caso ambas versiones del procesador (con o sin incluir la instrucción aw) van a tener el mismo ciclo de reloj, es decir 2ns. Lo que cambia ahora es el número de ciclos empleados para ejecutar el programa, dado que para el caso multiciclo la expresión del tiempo de ejecución la podemos escribir como tejecución = NºCiclos×TCiclo= NºCiclos×2. De esta forma, el procesador más rápido será aquel que requiera menos ciclos de reloj para ejecutar el programa: NºCiclosantes = 4 (add) + 4 (xor) + 8×( 4 (slt) + 3 (beq) + 4 (sll) + 4 (add) + 5 (lw) + 4 (add) + 4 (addi) + 3 (j) ) + 4 (slt) + 3 (beq) + 4 (jr) = 8 + 8×31 + 11 = 267. NºCiclosdespués = 4 (add) + 4 (xor) + 8×( 4 (slt) + 3 (beq) + 4 (sll) + 4 (add) + 6 (aw) + 4 (addi) + 3 (j) ) + 4 (slt) + 3 (beq) + 4 (jr) = 8 + 8×28 + 11 = 243. Por lo tanto para el caso multiciclo el incluir la instrucción aw es beneficioso y el tiempo de ejecución del código es menor que cuando no la tenemos. Conviene, pues, en este caso ampliar el procesador con la nueva instrucción. 2.- Utilizando la metodología vista en clase para la inclusión de nuevas instrucciones en el esquema de implementación multiciclo, realizar la Fases de Análisis y Diseño de una nueva instrucción, incrcte, que permite incrementar en un valor constante el contenido del registro $x, y guardar el resultado de la suma de la dirección de memoria apuntada por $y y una constante en el registro $y: incrcte $x, $y, cte # $x = $x + cte # $y = Memoria[$y] + cte Solución: En primer lugar debemos de proceder a identificar el uso que se va a realizar de los tres elementos básicos del camino de datos: Banco de Registros, ALU y Memoria. • • • Banco de Registros: Leer $x, Leer $y, Escribir $x, Escribir $y ALU: $x+cte, Memoria[$y]+cte Memoria: Leer Memoria[$y] A continuación debemos de establecer el orden en que se deben de realizar estas operaciones a partir de las dependencias existentes para llegar a una planificación del trabajo a realizar en los diferentes ciclos de ejecución: Banco Reg: Leer $x Leer $y Escribir $x $x+cte Leer Memoria[$y] ALU: Memoria: Escribir $y Memoria[$y]+cte (1) El incremento del registro $x sólo depende de que se haya hecho la lectura del registro correspondiente previamente. (2) La escritura del registro $x se realiza, una vez que se haya realizado la operación aritmética $x+cte. (3) La lectura de la dirección de memoria apuntada por $y depende de la lectura previa de dicho registro. (4) La operación aritmética Memoria[$y]+cte depende de la lectura previa de memoria. (5) La escritura del registro $y se realiza, una vez que se haya realiza la operación aritmética Memoria[$y]+cte. Ciclos: Banco Reg: 2 Leer $x Leer $y 3 4 5 Escribir $x $x+cte Leer Memoria[$y] ALU: Memoria: Escribir $y Memoria[$y]+cte Formato a dar a la instrucción: Formato (I): Ciclos: Banco de Reg.: ALU: Memoria: incrcte 2 A=Leer $x B=Leer $y $x $y cte 3 ALUOut=$x+cte MDR=Leer Memoria[$y] 4 5 $x=ALUOut $y=ALUOut ALUOut=Memoria[$y]+cte En gris aparecen aquellas operaciones que no podemos realizar con la ruta de datos actual. Será necesario modificar la ruta de datos para hacerlo posible. Leer Memoria[$y]: Anadir una nueva entrada al multiplexor controlado por señal de control IorD procedente de la salida del registro B. ALUOut=Memoria[$y]+cte: Añadir una nueva entrada al multiplexor controlado por la señal de control ALUSrcA procedente de la salida del registro MDR. $x=ALUOut: Añadir una nueva entrada al multiplexor controlado por señal de control RegDst procedente de la salida del registro de instrucción 25-21.