Posted by : Isa Neifors miércoles, 15 de mayo de 2013


PRÁCTICA 3
UNIDAD DE CONTROL

En esta práctica veremos el funcionamiento, paso a paso, de la Unidad de Control que se encuentra en la CPU que hemos visto en clase.

Primero que nada vamos a pasar a hexadecimal las instrucciones, aclarando antes algunas cuestiones.
  • A la hora de pasar una instrucción a hexadecimal, debemos mirar previamente qué número le corresponde a cada registro.



    En el cuadro vemos que aparece el nombre del registro con su número correspondiente.
  • Una vez que conocemos dichos números y sabemos como es la estructura de cada instrucción (Tema 2: Programación de microprocesadores MIPS), procedemos a pasarlas a binario.
  • Cuando ya tenemos en binario toda la instrucción vamos separando en grupos de 4 bits y ¡LISTO para pasarlo a hexadecimal!.
a) ADD $t0, $t1, $t2
0
rs
rt
rd
0
0x20
0 0 0 0 0 0
0 1 0 0 1
0 1 0 0 1
0 1 0 0 0
0 0 0 0 0
1 0 0 0 0 0
0x012A4020
b) ADDI $s0,$s1, 0x0011

8
rs
rt
Inmediato
0 0 1 0 0 0
1 0 0 0 1
1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1
0x22300011

c) ORI $t0, $t2, 0x00A1

0xD
rs
rt
Inmediato
0 0 1 1 0 1
0 1 0 1 0
0 1 0 0 0
0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1
0x354800A1

d) SLL $t0, $t0, 0x0002

0
rs
rt
rd
desp
0
0 0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 1 0 0 1
0 0 0 1 0
0 0 0 0 0 0
0x00084880

e) SLR $t1, $t0, 0x0002

0
rs
rt
rd
desp
2
0 0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 1 0 0 1
0 0 0 1 0
0 0 0 0 1 0
0x01004882
f) LUI $s0,0x0011

0xf
0
rt
Inmediato
0 0 1 1 1 1
0 0 0 0 0
1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1
0x3C100011

g) SW $t4, 0x0111

0x2b
rs
rt
offset
1 0 1 0 1 1
0 0 0 0 0
0 1 1 0 0
0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1
0xAC0C0111



h) SLT $t1, $t2, $t0

0
rs
rt
rd
0
0x2a
0 0 0 0 0 0
0 1 0 1 0
0 1 0 0 0
0 1 0 0 1
0 0 0 0 0
1 0 1 0 1 0
0x0148482A

i) J 0x000001A

2
Dirección
0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0
0x0800001A

j) JR $S0

0
rs
0
8
0 0 0 0 0 0
1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0
0x02000008

Una vez hecho esto, ya tenemos las instrucciones listas para introducirlas en memoria a la hora de la simulación.

A continuación, utilizando la CPU descrita en clase teórica y el lenguaje de transferencia de registro, vamos a realizar la secuencia de transferencias y acciones.




COSAS A TENER EN CUENTA ANTES DE EMPEZAR:

  1. La ALU que se encuentra en la CPU que vamos a utilizar, tiene las siguientes operaciones con sus respectivos códigos, que deberemos tener en cuenta mas tarde a la hora de seleccionar una operación.


    1 (000) Suma
    2 (001) Resta
    3 (010) OR
    4 (011) AND
    5 (100) Desplazamiento a la derecha de 1,2,3,4 bits
    6 (101) Desplazamiento a la izquierda de 1,2,3,4 bits
    7 (110) NOT

    8 (111) XOR

  2. En el circuito que se nos proporciona con la práctica le hemos hecho una pequeña modificación al Sumador-Restador:
    Hemos conectado el acarreo de salida con la entrada de arriba del sumador. De esta forma comprobamos que el resultado es correcto.Sólo tenemos que tener tener en cuenta que cuando realicemos una resta y el primer operando sea mayor que el segundo, el resultado que nos sale para que fuese correcto habría que negarlo. En la imagen anterior vemos como al realizar la resta 8-7 el resultado es 1. Pero, ¿qué pasa si los operandos estuvieran al revés?:
    Vemos como el resultado no es correcto. Significaría que es un número negativo y habría que realizar con él la operación NOT. 
  3. Las dos primeras fases son comunes a todas las instrucciones. Son la fase FETCH y la de DECODIFICACIÓN. Siempre es igual, asique solo lo escribiré una vez.

  • FASE FETCH

  1. MAR ← PC
    T4
    C1
  2. MBR ← MP
    Td, L
    C2
      2. PC ← PC + 4
      C4
  3. RI ← MBR
T3
C6 (suponiendo que el 6 es la carga de RI, porque viene C7 al igual que la carga de RE)

  • DECODIFICACIÓN - - - - - -

De esta forma la instrucción queda cargada en el registro de instrucciones.

NO OS OLVIDEIS QUE SIN ESTOS PASOS NO SE EJECUTARÍA LA INSTRUCCIÓN, ASI QUE RECORDAD QUE, SIEMPRE, SE REALIZA ESTO LO PRIMERO.

¿Porqué hay dos microinstrucciones con el mismo número?
Porque no hay nada que les impida ejecutarse a la vez. Y recordemos que lo mejor es hacer todos los pasos posibles en un mismo ciclo con el fin de acelerar el proceso.

¿Porqué algunas señales las separo en dos líneas si pertenecen a la misma microinstrucción?
Porque cada línea representa un semiciclo debido a que la señal tiene un pequeño tiempo de retardo y si activásemos la carga del registro a la vez que le damos salida a una señal activando un triestado, quizás la señal que sale aún no haya llegado a este registro y lo que capture sea basurilla. Por lo tanto si una señal se activa en flanco de subida, la otra se hará en flanco de bajada. (Esto se deberá tener en cuenta en todas las instrucciones que vamos a ver a continuación).

Una vez dicho todo esto comenzamos con la secuencia de transferencia y acciones:

a) ADD $t0, $t1, $t2
$t0 ç $t1 + $t2
RA=01001($t1), RB=01010($t2), MA=0, MB=0, OP=000, RC=01000($t0), T5
SC

b) ADDI $s0,$s1, 0x0011

RT2 ç RI(inmediato)
T8
C10
$s0 ç $s1 + RT2
RA=10001($s1), MA=0, MB=1, OP=000, RC=10000($s0), T5
SC

c) ORI $t0, $t2, 0x00A1

RT2 ç RI(inmediato)
T8
C10
$t0 ç $t2 OR RT2
RA=01010($t2), MA=0, MB=1, OP=010, RC=01000($t0), T5
SC



d) SLL $t0, $t0, 0x0002

RT2 ç RI(desp)
T8
C10
RT3 ç $t0 SLL RT2
RA=01000($t0), MA=0, MB=1, OP=101
C11
$t0 ç RT3
RC=01000($t0), T6
SC

e) SRL $t1, $t0, 0x0002

RT2 ç RI(desp)
T8
C10
RT3 ç $t0 SRL RT2
RA=01000($t0), MA=0, MB=1, OP=100
C11
$t1 ç RT3
RC=01001($t1), T6
SC

f) LUI $s0,0x0011

$s0 ç RI(inmediato)
T8, RC=10000($s0)
SC

g) SW $t4, 0x0111

MAR ç RI(dir)
T8
C1
MBR ç $t4
RA=01100($t4), T1
C3
MP ç MBR
Td, Ta
E





h) SLT $t1, $t2, $t0


En esta instrucción tropezamos con una pequeña dificultad, y es que nuestra ALU no tiene la operación que queremos realizar. ¿Qué hacemos?, vamos a simularla, usando las operaciones que sí tenemos.


  1. Antes que nada debemos saber que la resta que realiza nuestra ALU es en "Complemento a2", es decir, el segundo operando lo pasa a "Ca2" y realiza una suma.
  2. Nos interesa saber también que el bit de acarreo en el RE es el tercero empezando por la derecha, y en este caso, es el único que nos interesa estudiar, asi que para usarlo a él sólo, tendremos que desplazarlo dos bits a la derecha.
  3. La operación SLT consiste en guardar (en este caso) en $t1:
  • 0x00000001 si el dato de $t2 es menor que el de $t0
  • 0x00000000 si no lo es
  1. El bit de acarreo en este caso va a funcionar al contrario, es decir, nos dará un 0 en el caso de que el primer dato sea menor que el segundo, y un 1 en caso contrario. ¿Qué haremos entonces para conseguir el resultado real?, pues tendremos que realizar la operación NOT con dicho dato.
  2. Después de este último paso, nos volvemos a encontrar con un pequeño problema. Imaginemos que el dato al que vamos a realizarle el NOT es ...00000001(32 bits) (queremos que ese 1, sea un 0). Después de la operación nos quedaríamos con ...11111110. Ya tenemos el 0 que queríamos, pero ahora todos los ceros originales se nos han transformado en 1. Para devolverlos a su estado natural le tendremos que restar todos esos 1, que en hexadecimal le estaríamos restando 0xfffffffe (31 bits en 1 y el último en 0). De esta forma nuestro resultado final, ya es el que buscabamos.
  3. Sabiendo todo esto, vamos a realizar la secuencia de transferencia y acciones de la operación SLT, dividida en cuatro pasos:



h.1) SUB RT3, $t2, $t0

RT3 ç $t2-$t0
ñRE
RA=01010($t2), RB=01000($t0), MA=0, MB=0,Op=001
C8(para actualizar RE), C11



h.2) SRL RT3, RE, 0x0002

RT1 ç RE
T7
C9
RT2 ç RI(desp)
T8
C10
RT3 ç RT1 SRL RT2
MA=1, MB=1, OP=100
C11


h.3) NOT RT3, RT3

RT1 ç RT3
T6
C9
RT3 ç NOT RT1
MA=1, MB=0, OP=110
C11

h.4) RT3 - 0xfffffffe

RT1 ç RT3
T6
C9
RT2 ç RI(0xfffffffe)
T8
C10
$t1 ç RT1 - RT2
MA=1, MB=1,Op=001, RC=01001($t1), T5
SC


Otra opción...




Añadiendo esto último al circuito nos ahorramos un par de pasos de la opción de antes, puesto que de esta forma sólo tendríamos que hacer la resta, actualizar el registro de estado y sacaríamos directamente el bit de acarreo en la posición que queremos. Lo de rellenar todo lo demás con unos es para ahorrarnos el último paso puesto que al bit que nos interesa le tenemos que realizar un NOT (ya que queremos almacenar lo contrario), y al hacerlo todos los unos se convertirán en ceros y el bit lo tendremos negado y listo para almacenar en el registro que se nos pide.




i) J 0x000001A

PC ç RI(dir)
T8
C5

j) JR $S0


PC ç $s0
RA=10000($s0), T1
C5

Lo útimo que tenemos que hacer en esta práctica es comprobar que todo lo anterior se ha realizado de forma correcta mediante el simulador Logisim. Se introducirá el código de instrucción en la memoria de manera que sean posible realizar todas las fases de la instrucción.

  • Para comenzar, abriremos con Logisim el circuito que se nos adjunta en la práctica. Y como primer paso, añadiremos las instrucciones (ya en hexadecimal) a la memoria. Para ello, pulsamos el botón derecho del ratón sobre la memoria, y le damos a "Editar contenidos...".


Se nos abrirá una ventana, ahí escribimos las instrucciones y cerramos ventana.



Antes de pasar a simular las instrucciones vamos a simular, una sola vez, la fase FETCH que como ya comenté anteriormente, se repite de igual modo en todas y cada una de ellas.

  • Hay que pasar de PC a MAR la dirección de la memoria principal a la que queremos acceder. Para ello activamos el triestado de PC (T4) para dar salida a la señal, y así, ésta, accede al bus interno.
  • Justo después activamos la carga de MAR (C1).
  • Una vez hemos guardado la dirección que queremos en MAR, debemos llevarla al bus de direcciones. Para ello activamos (Td) y asi la señal recorre dicho bus. A la vez activamos el modo lectura (L) en la memoria principal y la selección de memoria (sel). La memoria accede a dicha dirección, LEE el dato (instrucción) y sale al bus de datos.
  • Justo después activamos la carga del MBR (C2) desde el bus de datos (el C3 es para cargar desde el bus interno).
  • Mientras esto ocurre por un lado, por otro se está incrementado la dirección contenida en PC. La señal pasa sola al sumador al igual que el 4 que está fijo. Para hacer efectivo el incremento tan solo tenemos que cargar el resultado de la suma en PC (C4)







a) ADD $t0, $t1, $t2
Teniendo preparada en RI esta instrucción, empezariamos indicando cuáles son los registros de los que van a salir nuestros datos A y B. Para ello RA=01001 y RB=01010. El banco de registros los selecciona y saca los datos por su sitio respectivamente. Los datos llegan a la ALU y, dejando por defecto C.OP=0000, se realiza la suma de ambos. Y para almacenar el resultado de la suma debemos accionar T5, para dar salida al resultado al bus interno, indicar el registro dónde queremos guardar (RC=01000) y por último activamos la carga del banco de registros. A la vez que se almacena la suma en el banco debemos almacenar los indicadores de la operación en el registro de indicadores mediante la senal de carga C8.



b) Con esta instrucción (ADDI) nos encontramos con un problema a la hora de simular. A la hora de sacar el inmediato de RI solo tenemos un triestado que al activarlo da salida a la instrucción entera. Para solucionarlo hemos puesto unos separadores que solo sacarán los 16 últimos bits rellenando los otros 16 con ceros. Este mismo problema nos aparece con otras instucciones, algunas que necesitan sacar los mismos bits y otras como <J dir> que necesitariamos sacar de la instrucción los 26 últimos bits que se corresponen con la dirección a la que se quiere saltar. También les ocurre esto a las instrucciones de desplazamiento, que la cantidad de bits a desplazar está contenido en la instrucción, en los 5 bits contados a partir del sexto bit (empezando a contar por la derecha).






b) ADDI $s0,$s1, 0x0011

Para no tener que explicar tan detalladamente todas las instrucciones, simplemente voy a ir activando y seleccionando, tal y como he indicado en las señales del segunto apartado del trabajo y voy a ir adjuntando las capturas de dichas acciones.





c) ORI $t0, $t2, 0x00A1




d) SLL $t0, $t0, 0x0002 y e) SRL $t1, $t0, 0x0002 son los mismos pasos pero cambiando los registros que se seleccionan y el C.OP según viene en el segundo apartado del trabajo.







f) LUI $s0,0x0011



g) SW $t4, 0x0111





i) J 0x000001A



j) JR $S0





AQUÍ DEJAMOS EL FICHERO DE LA CPU MODIFICADO COMO SE MUESTRA EN LAS IMÁGENES, PARA QUE, QUIÉN QUIERA, PUEDA PROBARLO:  
CPU_GA3.circ



Manuel Alberto Según López
Isabel Repetto García-Plata

Leave a Reply

Subscribe to Posts | Subscribe to Comments

- Copyright © 2025 Fec -