Tema 2: Soluciones a los ejercicios - myTeachingURJC/Arq-computadores-01 GitHub Wiki

Soluciones a los ejercicios del Tema 2: Diseño de un repertorio de instrucciones

Contenido

Sesión 5: ISA. Repertorios CISC y RISC

Soluciones a los ejercicios propuestos en la sesión 5: ISA. Repertorios CISC y RISC

Ejercicio 1

  • Apartado a) ¿Cuantas instrucciones como máximo se pueden tener en esta arquitectura?

En el formato de la instrucción observamos que el campo opcodes es de 5 bits. Esto significa que hay un total de 32 posibles valores. Por tanto, este procesador sólo puede tener 32 instrucciones como máximo

  • Apartado b) ¿Cual es el tamaño máximo de la memoria del EDSAC (en palabras)? Supón que todos los datos son palabras de 17 bits

El bit del campo T nos define el tamaño del operando: pequeño o grande. Es decir, que las instrucciones pueden trabajar con palabras de 17 bits ó de 35 bits. Para hacer este apartado nos indican que supongamos que las palabras son de 17 bits solamente

El campo Dirección del dato contiene la dirección del operando en memoria. Este campo tiene 10 bits, por lo que sólo puede haber 2 elevado a 10 = 1024 direcciones posibles. Sólo tenemos acceso a 1024 palabras desde las instrucciones. Por tanto, el tamaño máximo de la memoria será de 1024 palabras

  • Apartado c) En la posición de memoria 921 se encuentra la variable var1, que tiene el valor 30 (de 17 bits). El código de operación de la instrucción de suma es el 11100 en binario, que suma al regisgtro acumulador el contenido de la variable de memoria indicada: A = A + variable, donde A es el acumulador del EDSAC. Escribe escribe la instrucción en código máquina que hace la operación: A = A + var1

Utilizando el formato de las instrucciones del EDSAC, asignamos los valores correspondientes a cada campo:

  • Opcode = 11100 (binario)
  • R = 0 (Bit reservador, lo ponemos a 0)
  • Dirección del dato: 921 en decimal = 1110011001 binario (10 bits)
  • Tamaño: 17 bits. Por tanto el bit T lo ponemos a cero
Opcode R Direccion T
11100 0 1110011001 0

Por tanto, la instrucción en código máquina es:

11100011100110010

Ejercicio 2

  • Apartado a) ¿Cuantas instrucciones como máximo se pueden tener en esta arquitectura?

En el formato de la instrucción observamos que el campo opcodes es de 3 bits. Esto significa que hay un total de 2 elevado a 3 posibles valores. Por tanto, este procesador sólo puede tener 8 instrucciones como máximo. ¡Con un computador de sólo 8 tipos de instrucciones llegamos a la luna! Bueno, en realidad, 8 son las instrucciones implementadas por hardware, sin embargo se crearon más implementadas por software

  • Apartado b) ¿Cual es el tamaño máximo de la memoria del AGC (en unidades de 16 bits)?

El campo Dirección del dato contiene la dirección del operando en memoria. Este campo tiene 12 bits, por lo que sólo puede haber 2 elevado a 12 = 4096 direcciones posibles. Sólo tenemos acceso a 4096 posiciones de 16 bits desde las instrucciones. Por tanto, el tamaño máximo de la memoria será de 4096 palabras

Como curiosidad, en la actualidad, la unidad mínima de almacenamiento es de 1 byte, por eso cada dirección de memoria se refiere un byte. Un dato de 16 bits ocupa 2 posiciones de memoria. No es así en el computador del Apollo, ya que la unidad mínima de intercambio son de 16 bits

  • Apartado c) [...] Escribe las instrucciones en ensamblador del AGCC para calcular A = var1 + var2

La instrución AD hace la operación A = A + var. Sabemos que inicialmente A=0, por lo tanto la instrucción AD var1 lee el valor de la primera variable y lo guarda en el acumulador. Con la instruccion AD var2 se le suma var2 al resultado anterior, obteniéndose la suma pedida. Por tanto, el programa es el siguiente:

AD var1
AD var2
  • Apartado d) [...] escribe en código máquina (en hexadecimal) el programa del apartado c (var1 = 0x080, var2=0x1F0)

Siguiendo el formato descrito, colocamos los dos campos de las instrucciones, en binario

Opcode (3 bits) Dir del Dato (12bits)
011 000010000000 (0x080)
011 000111110000 (0x1F0)

Así, el programa en código máquina (en binario) sería:

011000010000000
011000111110000

Ahora lo pasamos a hexadecimal (el enunciado nos lo piden en hexadecimal):

0x3080
0x31F0

Como las direcciones son de 12 bits se representan exactamente con 3 dígitos hexadecimales. Así, el código máquina del Apolo es muy fácil de leer: El primer dígito en hexadecimal sólo vale 0 - 7 e indica la instruccion (3 para AD). Los siguientes 3 dígitos son las dirección de acceso a memoria

Ejercicio 3

  • Apartado a): Obtén el código máquina (en hexadecimal) del siguiente programa:
addi x0, x0, 0
addi x31, x15, 0x0F

Ponemos las instrucciones en el formato de la instrucción, descomponiéndola en sus campos y luego agrupamos los bits de 4 en 4 para obtener su número hexadecimal

Instrucción addi x0, x0, 0:

Instrucción addi x31, x15, 0x0F:

El programa en código máquina es:

0x00000013
0x00F78F93
  • Apartado b): Desensambla este programa en código máquina y obtén su equivalente en lenguaje ensamblador. ¿Qué hace el programa?
0x00100193
0x00218193

Hacemos las operaciones inversas. Primero lo ponemos en binario y luego lo agrupamos en sus campos. Por el código de operación vemos que se trata de instrucciones addi, así que el formato es el mismo indicado en el enunciado

La instrucción 0x00100193 la decodificamos así:

La instrucción 0x00218193 se codifica así:

Por tanto, el programa en ensamblador es:

addi x3, x0, 1
addi x3, x3, 2

Este programa calcula la expresión x3 = 1 + 2

Sesión 6: ISA. Almacenamiento de operandos

Soluciones a los ejercicios propuestos en la sesión 6: ISA. Almacenamiento de operandos

Ejercicio 1

  • Apartado a) Escribe el programa en ensamblador del computador A que realiza esta operación

Como el computador A tiene una **arquitectura Registro-Registro+*, lo primero es cargar en los registros las variables. Luego se realizan las operaciones y finalmente se almacena el resultado en la variable f, en la memoria principal. Los registros los denominamos x0 - x15 y como son de propósito general, podemos usar los que queramos para el almacenamiento de los cálculos intermedios

# -- Llevar las variables en memoria a registros
load x1, a
load x2, b
load x3, c
load x4, d

# -- Realizar las operaciones intermedias
add x5, x1, x2  #-- x5 = (a + b)
sub x5, x5, x3  #-- x5 = (a + b) - c
add x5, x5, x4  #-- x5 = (a + b) - c + d

# -- Almacenar el resultado en f
store x5, f

El programa tiene 8 instrucciones en total

Apartado b) Calcula el tráfico total (en bytes) entre el procesador y la memoria cuando se ejecuta este programa

Las instrucciones de load y store ocupan 5 bytes (40 bits) y al ejecutarse realizan la lectura/escritura de un dato de 4 bytes (32 bits)

Las instrucciones add y sub ocupan 2 bytes (16 bits) y al ejecurse no acceden a memoria por lo que no generan tráfico de datos

En esta tabla se resumen el tráfico total de bytes con la memoria, dividido en el tráfico debido a la lectura de las instrucciones más el tráfico debido a los datos

Instruccion Trafico Instruciones (bytes) Trafico datos (Bytes) Total (bytes)
load x1, a 5 4 9
load x2, b 5 4 9
load x3, c 5 4 9
load x4, d 5 4 9
add x5, x1, x2 2 0 2
sub x5, x5, x3 2 0 2
add x5, x5, x4 2 0 2
store x5, f 5 4 9
Total 31 20 51

Ejercicio 2

  • Apartado a) Escribe el programa en ensamblador del computador B que realiza esta operación

Este computador tiene una arquitectura registro - memoria, por lo que las instrucciones de suma y resta tienen un operando en un registro y el otro en la memoria. Teniendo en cuanta que inicialmente los registros está inicializados a 0, este programa realiza la operación pedida:

#-- Realizar las operaciones
add x1, a  #-- x1 = 0 + a
add x1, b  #-- x1 = (a + b)
sub x1, c  #-- x1 = (a + b) - c
add x1, d  #-- x1 = (a + b) - c + d

#-- Almacenar el resultado en memoria
store x1, f

En total hay 5 instrucciones

  • Apartado b: Calcula el tráfico total (en bytes) entre el procesador y la memoria cuando se ejecuta este programa

Todas las instrucciones ocupan 5 bytes (40 bits), y todas realizan acceso a memoria, por lo que su tráfico de datos es de 4 bytes

El tráfico total debido a la lectura de las instrucciones es de 5 * 5 = 25 bytes. El trafíco debido a los datos es de 5 * 4 = 20 bytes. El tráfico total es de 25 + 25 = 45 bytes

Sesión 7: Modos de direccionamiento. Tipos de instrucciones

Soluciones a los ejercicios propuestos en la sesión 7: Modos de direccionamiento. Tipos de instrucciones

Ejercicio 1

  • a) load_byte x1, 0x10010003

Los bytes se leen tal cual. Al ser las unidades mínimas de almacenamiento son atómicas y NO se aplica el concepto de ordenación. Así que el valor de x1 es de 0x1D

  • b) load_byte x1, 0x10010005

x1 = 0xCA

  • c) load_half x1, 0x10010003

Cuando se cargan datos mayores que un bytes, siempre se especifica la dirección del primer byte del dato. En las direcciones consecutivas se almacenan el resto de bytes. En este caso, queremos leer una media palabra (16-bits, 2 bytes) situada a partir de la dirección 0x10010003. Los bytes de esta media palabrá están almacenados por tanto en las direcciones 0x10010003 y 0x10010004. Pero... ¿En qué orden? En este computador nos dicen que la ordenación es little endian por lo que primero está el byte de menos peso, y luego el de mayor

x1 = 0x001D

  • d) load_half x1, 0x10010005

x1 = 0xBACA

  • e) load_word x1, 0x10010002

Las palabras ocupan 4 bytes. Con la instrucción dada se leen 4 bytes, de las posiciones de memoria 0x10010002, 0x10010003, 0x10010004 y 0x10010005. ¿En qué orden? Igual que antes. Este computador tiene ordenación little endian por lo el primer byte es el de menor peso y el último el de mayor

x1 = 0xCA001D2C

  • f) load_word x1, 0x10010005

x1 = 0xDEFEBACA

  • g) load_dword x1, 0x10010001

Las dobles palabras ocupan 8 bytes. Con esta intrucción se leen los 8 bytes a partir de la dirección 0x10010001. Sabemos que el priimer byte (el de la dirección 0x10010001 será el de menor peso)

x1 = 0xDEFEBACA001D2C3B

Ejercicio 2

  • a) Como los bytes son unidades atómicas, el ordenamiento no les afecta por lo que: x1 = 0x1D
  • b) x1 = 0xCA
  • c) Se construye a partir de los 2 bytes situados en 0x10010003 y 0x10010004. El primero es el de mayor peso y el segundo el de menor: x1 = 0x1D00
  • d) x1 = 0xCABA
  • e) Se construye a partir de los 4 bytes situados en las direcciones 0x10010002, 0x10010003, 0x10010004 y 0x10010005
    x1=0x2C1D00CA
  • f) x1 = 0xCABAFEDE
  • g) Se construye a partir de los 8 bytes situados desde la dirección 0x10010001 en adelante
    x1 = 0x3B2C1D00CABAFEDE

Ejercicio 3

Para poder ejecutarse cada una de las instrucciones será necesario primero un acceso a memoria para su lectura. Sin embargo, lo que nos están pidiendo son los accesos que se producen en la ejecución, una vez que la instrucción ya ha sido leida de memoria y está dentro del procesador listsa para ser ejecutada

  • a) Como los bytes son las unidades mínimas de información (y son atómicas) no hay nunca problemas de alineamiento. Sólo habrá 1 acceso para leer el dato
  • b) 1 acceso
  • c) En el caso de las medias palabras, el número de accesos depende de si se accede a una dirección alineada o no. Las direcciones alineadas para las palabras son las direcciones pares. Así, si se lee una media palabra de una dirección par, sólo habrá un único acceso a memoria. Pero si se lee de una dirección impar, habrá 2. Como el dato está en la dirección 0x2000, que es par, el número de accesos es 1
  • d) De los dos bytes de la media palabra, uno está en la palabra de la dirección 0x2004 y el otro en la de la dirección 0x2008. Por tanto el número de accesos es de 2
  • e) Número de accesos: 1
  • f) La direcciones alineadas para las palabras son las que son múltiplo de 4. En hexadecimal es fácil reconocerlas porque acaban en 0, 4, 8 ó C. Si la lectura es en una dirección alineada, sólo habrá un único acceso. Si es a una dirección no alineada entonces se necesitarán 2 accesos. La dirección 0x2000 está alineada, así que el número de accesos es de 1
  • g) Dirección alineada: Número de accesos: 1
  • h) Dirección NO alineada: Número de accesos: 2
  • i) Dirección NO alineada: Número de accesos: 2

Ejercicio 4

  • Apartado a) Los valores que se han almacenado en cada posición de memoria (Rellena el dibujo)

  • Apartado b) ¿Cuantos accesos de escritura a memoria se han realizado en total?

Nos piden accesos de escritura, por tanto la lectura de las instrucciones está descartada. El número de accesos en escritura depende del dato utilizado y si es una dirección alineada o no (para ese dato). Si es alineada, cada instrucción provoca 1 acceso de escritura, mientras que si no es alineada se realizan 2 accesos de escritura

En esta tabla se resume toda la información:

Instrucción Dir alineada Accesos escritura
store_byte x1, 0xBA05 si 1
store_byte x1, 0xBA1F si 1
store_byte x2, 0xBA10 si 1
store_half x3, 0xBA07 no 2
store_half x4, 0xBA0A si 1
store_half x5, 0xBA1D x 1
store_word x6, 0xBA01 no 2
store_word x7, 0xBA0F no 2
store_word x8, 0xBA18 si 1
Total 12 accesos

Ejercicio 5

En los ejercicios de teoría que haya que programar un Risc-V, no se pide el programa completo, sino sólo la parte del programa con las instrucciones que implementan lo pedido

  • a) a = h + A[8]. Utiliza el registro t0 para la variable a

El enunciado nos proporciona la dirección base del array, que está en s0 y también la variable h, que está en el registro s1. Para implementar la expresión lo primero es obtener el elemento A[8] que está en memoria. Esto lo hacemos una instrucción de load-doble word (ld). El elemento del array a leer es el que tiene índice 8. Como es un array de dobles palabras, este elemento se encuentra en la dirección A + 8 * 8 (ya que una doble palabra son 8 bytes)

El código pedido es el siguiente:

ld t1, 64(s0)    #-- t1 = A[8]
addi t0, s1, t1  #-- t0 = h + A[8]
  • b) A[12] = h + A[8]. El resultado de h + A[8] se almacena en el propio array A, en el índice 12

Para almacenar h + A[8] en la posición 12 del array hay que obtener su dirección: A + 12 * 8 = A + 96. En el apartado anterir ya tenemos en el registro t0 el resultado h + A[8], así que simplemente habrá que guardar t0 en la dirección A + 96 usando la instrucción store-double-word (sd). El programa completo es:

ld t1, 64(s0)    #-- t1 = A[8]
addi t0, s1, t1  #-- t0 = h + A[8]
sd t0, 96(s0)    #-- A[12] = h + A[8]

Ejercicio 6

El código será muy parecido en todos los casos: las acciones a realizar son las mismas, pero cambian los valores a multiplicar los índices de los arrays para acceder a los elementos. Hay que multiplicar por el tamaño en bytes de cada tipo de dato. En el caso de dobles palabras se multiplica por 8, para las palabras por 4, para las medias palabras por 2 y se multiplica por 1 para los bytes

  • Apartado a) Los elementos del array son dobles palabras

Multiplicamos por 8. Usamos las instrucciones ld (load-double) y sd (store-double)

#-- Datos enunciado: 
# -- s1 = i
#-- s2 = h
#-- s3 = A

li t0, 8
mul t1, s1, t0  #-- t1 = i * 8

add s4, s3, t1  #-- s4 = A + i * 8
ld t2, 0(s4)    #-- t2 = A[i*8]
add t3, s2, t2  #-- t3 = h + A[i*8]
sd t3, 96(s3)   #-- A[12] = h + A[i*8]
  • Apartado b) Los elementos del array son palabras

Multiplicamos por 4. Usamos las instrucciones lw (load-word) y sw (store-word)

#-- Datos enunciado: 
# -- s1 = i
#-- s2 = h
#-- s3 = A

li t0, 4
mul t1, s1, t0  #-- t1 = i * 4
add s4, s3, t1  #-- s4 = A + i * 4
lw t2, 0(s4)    #-- t2 = A[i*4]
add t3, s2, t2  #-- t3 = h + A[i*4]
sw t3, 48(s3)   #-- A[12] = h + A[i*4]
  • Apartado c) Los elementos del array son medias palabras

Multiplicamos por 2. Usamos las instrucciones lh (load-half) y sh (store-half)

#-- Datos enunciado: 
# -- s1 = i
#-- s2 = h
#-- s3 = A

li t0, 2
mul t1, s1, t0  #-- t1 = i * 2
add s4, s3, t1  #-- s4 = A + i * 2
lh t2, 0(s4)    #-- t2 = A[i*2]
add t3, s2, t2  #-- t3 = h + A[i*2]
sh t3, 24(s3)   #-- A[12] = h + A[i*2]
  • Apartado d) Los elementos del array son bytes

No se multiplica (se usa el indice directamente). Usamos las instrucciones lb (load-byte) y sb (store-byte)

#-- Datos enunciado: 
# -- s1 = i
#-- s2 = h
#-- s3 = A

li t0, 2
mv t1, s1,      #-- t1 = i
add s4, s3, t1  #-- s4 = A + i
lb t2, 0(s4)    #-- t2 = A[i]
add t3, s2, t2  #-- t3 = h + A[i]
sb t3, 12(s3)   #-- A[12] = h + A[i]

Ejercicio 7

El código será muy parecido en todos los casos: las acciones a realizar son las mismas, pero cambian los valores a multiplicar los índices de los arrays para acceder a los elementos. Hay que multiplicar por el tamaño en bytes de cada tipo de dato. En el caso de dobles palabras se multiplica por 8, para las palabras por 4, para las medias palabras por 2 y se multiplica por 1 para los bytes

  • Apartado a) Los elementos del array son dobles palabras

Multiplicamos por 8. Usamos las instrucciones ld (load-double) y sd (store-double)

#-- Datos enunciado:
#-- s1 = i
#-- s2 = h
#-- s3 = A
#-- s4 = B
li t0, 8
mul t1, s1, t0  #-- t1 = i * 8
add s5, s3, t1  #-- s5 = A + i * 8
ld t2, 0(s5)    #-- t2 = A[i*8]
sub t3, s2, t2  #-- t3 = h - A[i*8]
sd t3, 96(s4)   #-- B[12] = h - A[i*8]
  • Apartado b) Los elementos del array son palabras

Multiplicamos por 4. Usamos las instrucciones lw (load-word) y sw (store-word)

#-- Datos enunciado:
#-- s1 = i
#-- s2 = h
#-- s3 = A
#-- s4 = B
li t0, 4
mul t1, s1, t0  #-- t1 = i * 4
add s5, s3, t1  #-- s5 = A + i * 4
lw t2, 0(s5)    #-- t2 = A[i*4]
sub t3, s2, t2  #-- t3 = h - A[i*4]
sw t3, 48(s4)   #-- B[12] = h - A[i*4]
  • Apartado c) Los elementos del array son medias palabras

Multiplicamos por 2. Usamos las instrucciones lh (load-half) y sh (store-half)

#-- Datos enunciado:
#-- s1 = i
#-- s2 = h
#-- s3 = A
#-- s4 = B
li t0, 2
mul t1, s1, t0  #-- t1 = i * 2
add s5, s3, t1  #-- s5 = A + i * 2
lh t2, 0(s5)    #-- t2 = A[i*2]
sub t3, s2, t2  #-- t3 = h - A[i*2]
sh t3, 24(s4)   #-- B[12] = h - A[i*2]
  • Apartado d) Los elementos del array son bytes

No se multiplica (se usa el indice directamente). Usamos las instrucciones lb (load-byte) y sb (store-byte)

#-- Datos enunciado:
#-- s1 = i
#-- s2 = h
#-- s3 = A
#-- s4 = B
mv t1, s1  #-- t1 = i 
add s5, s3, t1  #-- s5 = A + i
lb t2, 0(s5)    #-- t2 = A[i]
sub t3, s2, t2  #-- t3 = h - A[i]
sb t3, 12(s4)   #-- B[12] = h - A[i]

Autores

Licencia

Créditos

Enlaces