S06 - myTeachingURJC/Arq-computadores-01 GitHub Wiki
Sesión de Teoría 6: Almacenamiento de operandos. ISA RISCV
- Tiempo: 2h
- Objetivos de la sesión:
- Conocer los tipos de almacenamiento interno de los operandos
- Introducción al RISC-V
- Formato de instrucciones del RISC-V
Contenido
- Introducción
- Tipos de almacenamiento de los operandos
- Ejercicio: Comparación de las arquitecturas registro-registro frente a memoria-memoria
- El procesador RISC-V
- Lecturas del libro de referencia
- Ejercicios
- Autores
- Licencia
- Enlaces
Introducción
Los procesadores tiene diferentes formas de acceder a los operandos. El cómo se usan estos operandos tiene un fuerte impacto en la arquitectura, y queda reflejado en su ISA. En esta sesión veremos de forma general las diferentes variantes que hay para acceder a los operandos y en la segunda parte nos centraremos en el RISCV y en su formato de instrucción (ISA)
Tipos de almacenamiento de los operandos
Según cómo se realice el almacenamiento interno de los operandos, los repertorios de instrucciones se clasifican en: Pila, acumulador y Registros de propósito general (GPR, General Purpose Registers). El procesador lee esos datos para realizar las operaciones con ellos y depositar el resultado de vuelta
Operandos en Pila
En algunos procesadores los operandos se sitúan en una pila interna, y las instrucciones usan de forma implícita estos operandos. Así por ejemplo, al ejecutarse la instrucción ADD (sin indicar argumentos), el procesador suma los dos datos de la cima de la pila y deja el resultado en la cima
Un ejemplo de un programa en ensamblador de una máquina ficticia que tiene pila interna sería el siguiente. Primero se sitúan tres valores en la pila: 10, 20 y 30. Luego se ejecuta dos veces la instrucción de suma ADD
Push 10
Push 20
Push 30
ADD
ADD
En esta figura se muestra lo que ocurre al ejecutarse la instrucción ADD
A esta instrucción ADD NO se le pasan los operandos de forma explícita, sino que asume que están en la pila. Accede a ella para leerlos, realiza la suma y deposita el resultado de vuelta en la pila. Así, la primera instrucción hace la suma 30 + 20, mientras que la segunda hace 50 + 10
NOTA Friki: Lenguaje Forth
Operandos en el acumulador
En otros procesadores existe un registro especial, denominado acumulador, que se usa para realizar ciertas operaciones. En las instrucciones en ensamblador el acumulador se usa de forma implícita, y no se especifica. Este es un ejemplo de implementación de la operación de suma de dos variables situadas en memoria, usando un procesador ficticio que usa el acumulador: "var3 = var1 + var2". El resultado se almacena en la variable var3:
load var1 #-- El acumulador se carga con el valor de var1 (A = var1)
add var2 #-- Sumar al acumulador la variable var2 (A = A + var2)
store var3 #-- Almacenar el acumulador en la variable var3
En estas instrucciones no se hecho mención explícita al acumulador (se usa implícitamente), pero los otros operandos sí se mencionan explícitamente
Operandos en registros de propósito general
En la mayoría de los repertorios de instrucciones se utilizan registros de propósito general (GPRs). Son registros que se pueden usar para cualquier almacenamiento. Desde las instrucciones en ensamblador se mencionan explícitamente
Según el uso de los registros de propósito general tenemos las siguientes arquitecturas: registro-registro, registro-memoria y memoria-memoria
Arquitectura registro-registro
Todos los operandos deben estar en los registros. Es el que se usa en el RISC-V. Un ejemplo es la instrucción add, en la que se suman dos registros y se deposita el resultado en un tercero. En este tipo de arquitecturas se utilizan instrucciones load para situar datos en los registros e instrucciones store para almacenarlos en memoria
La suma de dos variables de memoria, en una arquitectura registro-registro ser haría de la siguiente forma:
load x1, var1 #-- Leer la variable var1 y situarla en el registro x1
load x2, var2 #-- Leer la variable var2 y situarla en el registro x2
add x3,x2,x1 #-- Suma: Todos los operandos están en registros
store x3, var3 #-- Almacenar el registro x3 en la variable var3
Arquitectura registro-memoria
Uno de los operandos está en un registro, y los otros en memoria. El ejemplo de la suma de dos variables se realizaría de la siguiente forma, en un procesador con arquitectura registro-memoria:
load x1, var1 #-- Leer la variable var1 y situarla en el registro x1
add x1, var2 #-- x1 = x1 + var2. Un operando está en el registro x1 y el otro en memoria
store x1,var3 #-- Depositar el resultado en la variable var3
Arquitectura memoria-memoria
Todos los operandos están en memoria y por tanto no se usan registros. Para sumar dos variables de memoria y depositar el resultado en otra variable se usaría una única instrucción:
add var3, var2, var1 #-- var3 = var2 + var1
Las arquitecturas memoria-memoria son las que tenían los procesadores antiguos. Aunque el código ensamblador es más compacto y requiere de menos instrucciones, la memoria se convierte en el cuello de botella. Además hay muchas diferencias entre la longitud de sus instrucciones, y puede variar mucho el CPI
Ejercicio: Comparación de las arquitecturas registro-registro frente a memoria-memoria
Vamos a evaluar dos computadores ficticios, uno tiene arquitectura registro-registro mientras que el otro memoria-memoria Utilizaremos un programa de prueba, y estamos insteresados en calcular el tráfico de datos con la memoria. Dado que los accesos a memoria son lentos, la alternativa con menor tráfico con la memoria será la más rápida
El programa de prueba realizará estas operaciones con variables situadas en memoria:
var3 = var1 + var2
var4 = var1 - var2
var5 = var3 + var4
Las variables var1 y var2 ya están inicializadas. Todas las variables son de 32 bits (4 bytes) y las direcciones también son de 32 bits (4 bytes)
Computador A
El computador A tiene una arquitectura registro-registro, con un total de 32 registros de propósito general de 32 bits, denominados x0-x31. El fabricante nos proporciona los formatos de las instrucciones de carga, almacenamiento, suma y resta:
Las instrucciones de load y store ocupan 6 bytes y las de suma y resta 3 bytes
Utilizando este repertorio de instrucciones, el programa en ensamblador para realizar las operaciones del programa de prueba es:
load x1, var1 #-- x1 = var1
load x2, var2 #-- x2 = var2
add x3,x1,x2 #-- x3 = x1 + x2 = var1 + var2
sub x4,x1,x2 #-- x4 = x1 - x2 = var1 - var2
add x5,x3,x4 #-- x5 = x3 + x4
store x3, var3 #-- var3 = x3
store x4, var4 #-- var4 = x4
store x5, var5 #-- var5 = x5
El trafico de datos con la memoria lo separamos en dos partes: por un lado la lectura de las instrucciones, y por otro lado los accesos para leer/escribir las variables en memoria. Dado que las variables son de 32 bits (4 bytes), cada lectura o escritura conlleva un tráfico de 4 bytes
Analizaremos la primera instrucción: load x1, var1. Para que el procesador la pueda ejecutar tiene que leerla de memoria. Como ocupa 6 bytes, el tráfico será de 6 bytes (se leen 6 bytes de memoria). Una vez dentro del procesador, se ejecuta. En la ejecución hay un acceso a memoria para la lectura de la variable var1, que ocupa 4 Bytes. Por tanto hay un tráfico de datos de 4 Bytes
En la siguiente tabla se resume el tráfico de datos e instrucciones para todas las instrucciones:
Instrucción | Trafico de instrucciones | Tráfico de datos |
---|---|---|
load x1, var1 | 6 Bytes | 4 Bytes |
load x2, var2 | 6 Bytes | 4 Bytes |
add x3, x1, x2 | 3 Bytes | 0 |
sub x4, x1, x2 | 3 Bytes | 0 |
add x5, x3, x4 | 3 Bytes | 0 |
store x3, var3 | 6 Bytes | 4 Bytes |
store x4, var4 | 6 Bytes | 4 Bytes |
store x5, var5 | 6 Bytes | 4 Bytes |
TOTAL: | 39 Bytes | 20 Bytes |
El tráfico total de accesos a memoria para la lectura de instrucciones es de 39 Bytes, y para la tansferencia de datos de las variables es de 20 Bytes
Computador B
El computador B tiene una arquitectura memoria-memoria, por lo que puede realizar operaciones directamente con los elementos de memoria, y almacenar los resultados en memoria. El fabricante nos proporciona el formato de las instrucciones de suma y resta
Las instrucciones de suma y resta tienen 3 campos para indicar las direcciones de memoria donde se encuentran los operandos. El primero es la variable destino, donde escribir el resultado y los otros dos contienen las direcciones de las variables fuente
Las instrucciones de add y sub ocupan 13 bytes
Utilizando este repertorio de instrucciones, el programa en ensamblador para realizar las operaciones del programa de prueba es:
add var3, var1, var2 #-- var3 = var1 + var2
sub var4, var1, var2 #-- var4 = var1 - var2
add var5, var3, var4 #-- var5 = var3 + var4
Analizaremos la primera instrucción: add var3, var1, var2. Para que el procesador la pueda ejecutar tiene que leerla de memoria. Como ocupa 13 bytes, el tráfico será de 13 bytes (se leen 13 bytes de memoria). Una vez dentro del procesador, se ejecuta. En la ejecución hay tres accesos a memoria. Uno para la lectura de la variable var1, otro para la de var2 y otro para la escritura del resultado en var3. Como las variables son de 32 bits (4 bytes), hay un tráfico total de 12 Bytes
En la siguiente tabla se resume el tráfico de datos e instrucciones para todas las instrucciones:
Instrucción | Trafico de instrucciones | Tráfico de datos |
---|---|---|
add var3, var1, var2 | 13 Bytes | 12 Bytes |
sub var4, var1, var2 | 13 Bytes | 12 Bytes |
add var5, var3, var4 | 13 Bytes | 12 Bytes |
TOTAL: | 39 Bytes | 36 Bytes |
El tráfico total de accesos a memoria para la lectura de instrucciones es de 39 Bytes, y para la tansferencia de datos de las variables es de 36 Bytes
Comparación
El tráfico con la memoria generado por la lectura de instrucciones es igual en ambos computadores: 39 Bytes. El programa del computador A tiene 8 instrucciones y el del computador B sólo 3, pero el tráfico es el mismo
Sin embargo, el tráfico generado por los datos es de 20 Bytes en el computador A frente a 36 Bytes del computador B. Aunque el ensamblador del computador B es más compacto y más parecido a las operaciones descritas en alto nivel, el acceso a memoria no es óptimo. Esto es debido a que hay accesos a memoria duplicados, que en el caso del computador A sólo se hacen una vez. Por ejemplo, la variable var1 se está leyendo 2 veces en el Computador B, mientras que sólo 1 vez en el computador A
El computador A será el que elegiremos: realiza las mismas operaciones pero con menos accesos a memoria y por tanto su rendimiento será mayor
Esta es una de las razones por las que las arquitecturas memoria-memoria ya no se usan en los comptuadores modernos
El procesador RISC-V
En esta asignatura nos centraremos en el procesador RISC-V. No se trata de un procesador concreto, sino de una ISA. Al decir procesador RISC-V nos referimos a un procesador genérico que sigue la especificación RISC-V. Un procesador que implementa el ISA del RISC-V. Cada fabricante puede hacer su propia implementación del RISC-V, pero todos son compatibles porque tienen la misma ISA
Estas son algunas de las características del RISC-V:
-
ISA abierto: La arquitectura del RISC-V es abierta. Esto significa que cualquier fabricante puede implementar un procesador siguiendo esa ISA, sin necesidad de tener que pedir permiso ni tener que pagar royalties
-
ISA moderno: La arquitectura del RISC-V es la más moderna hasta el momento. Se ha recopilado todo el conocimeinto disponible en los 60 años de historia de los procesadores y se ha creado desde cero esta nueva ISA
-
Tamaño de palabras: Se especifican procesadores RISC-V de 32, 64 y 128 bits
-
Tipo de arquitectura: RISC. Load-store. Registro-Registro
-
Registros: 32 registros de propósito general (x0-x31), aunque el x0 siempre vale 0
-
ISA Modular: La arquitectura es modular, permitiendo que los fabricantes incluyan extensiones. Hay unas arquitecturas bases a la que se añaden las extensiones
-
ISA base: El fabricante selecciona una de las arquitecturas bases:
- RV32I: Instrucciones para trabajar con números enteros de 32 bits (es la usada en el Laboratorio)
- RV64I: Instrucciones para trabajar con números enteros de 64 bits (La usaremos en teoría)
- RV128I: Instrucciones para trabajar con números enteros de 128 bits
- RV32E: Versión reducida para sistemas empotrados. 32 bits y 16 registros de propósito general
-
Extensiones: El fabricante selecciona ninguna, una o varias extensiones:
- M: Multiplicación y división de enteros
- F: Trabajar con números en coma flotante
- C: Instrucciones comprimidas: Permite meter dos instrucciones cortas en una palabra de 32 bits
- Hay más extensiones que se están terminando de especificar: Manipulación de bits, Transacciones, operaciones con vectores...
Así, por ejemplo, si un fabricante tiene un chip RV32IM significa que es un RISC-V que trabaja con números enteros de 32 bits (RV32I) y que incluye las operaciones de multiplicación y división por hardware (M)
Tipos de instrucciones y formato
En el RISCV todas las instrucciones son de tamaño fijo de 32 bits (Aunque en la extensión C se implementa la compresión de instrucciones, permitiendo meter 2 instrucciones en 32 bits). En total se utilizan 6 tipos de formatos para las instrucciones, cada uno con unos campos diferentes
Lo ideal sería tener siempre el mismo formato: esto simplifica el hardware, pero habría instrucciones más largas que otras. Se ha llegado al compromiso de mantener fijo el tamaño, pero permitir que haya estos 6 tipos de formatos diferentes
Todas las instrucciones tienen un campo opcode de 7 bits, que identifica el formato de la instrucción y el tipo de instrucción a ejecutar. ¿Cuantas instrucciones diferentes se podrían tener en total? Como el campo opcode es de 7 bits, en total podría haber hasta 2 elevado a 7 = 128 instrucciones diferentes. Sin embargo, dado que el RISC-V está pensado para que se pueda extender, en varios de los formatos existen campos adicionales (func) que nos permite ampliar el número de instrucciones
Instrucciones de tipo R
Son las instrucciones que utilizan 3 registros. No acceden a memoria. Realizan operaciones matemáticas: sumas, restas, comparaciones, etc... Por ejemplo, las instrucciones add
y sub
son de tipo R. Su formato es el siguiente:
- El campo opcode identifica el formato y el tipo de operación
- Adicionalmente, existen los campos func7 y func3 que permiten ampliar el código de operación. Así, en total, se dispone de 7 + 7 + 3 = 17 bits para codificar las instrucciones de tipo R: Un total de 2 elevado a 17 = 131072 instrucciones!
- El campo rd indica el número del registro destino. Tiene 5 bits, lo que nos permite acceder a los 32 registros del RISC-V
- Los campos rs1 y rs2 se corresponden con los números de los registros fuente: el primero y el segundo. También son de 5 bits, por lo que podemos referenciar cualquier de los 32 registros
Instrucciones de tipo I
Tienen un campo con el valor inmediato y utilizan dos registros: uno destino y uno fuente
Es el formato de las instrucciones que usan el direccionamiento inmediato (como addi) y las instruccines de load. En las primeras el campo inmediato se usa como un valor que utilizar como uno más de los operandos. En las instrucciones de load es el campo que se utiliza como offset para el acceso a memoria
Instrucciones de tipo S
Es el formato de las instrucciones de store. El formato es igual que el de las instrucciones de tipo R, pero los campos func7 y rd se reutilizan para almacenar el valor inmediato del offset. El offset, por tanto, está partido en dos trozos dentro de la instrucción
Instrucciones de tipo B
Es el formato de las instrucciones de salto condicional. Es el mismo formato que las instrucciones S, pero ahora el valor inmediato usado para el salto (el número de medias palabras hasta llegar a la instrucción destino) está en 4 partes. Esto es así porque hay que interpretar el salto con signo, por lo que se añade el bit de signo (salto[12]) y el bit perdido se reutiliza en el bit bajo del campo offset
Instrucciones de tipo U
Es el formato de las instrucciones auipc
y lui
para asignar un valor en los 20 bits de mayor peso del registro destino
Instrucciones de tipo J
Es el formato de la instrucción de salto jal. Es el mismo formato U, pero los bits del inmediato están en diferentes posiciones
Resumen del formato de las instrucciones
Esta es una tabla resumen con todos los formatos de las instrucciones
Lecturas del libro de referencia
Libro de referencia: "Computer Organization and Design. Hardware/Software interface. Risc-V"
- Apartado 2.1: Introduction (Pag 62-63)
- Apartado 2.2: Operations of the Computer Hardware (Pag 63-66)
Otras lecturas recomendadas
Ejercicios
Para concretar las ideas aprendidas en esta sesión es fundamental que hagas los ejercicios. Sólo practicando conseguirás entender todas las ideas importantes que se quieren transmitir
Ejercicio 1
Un computador A tiene una arquitectura registro-registro, con 16 registros de propósito general de 32 bits. Las direcciones de memoria son de 32 bits y las variables almacenadas en memoria son palabras de 32 bits también. El fabricante nos proporciona el formato de las instrucciones load/store así como las de suma (add) y resta (sub). La instrucción de suma realiza esta operación: Rd = Rs1 + Rs2 y la de resta: Rd = Rs1 - Rs2
En memoria están almacenadas las variables a, b, c y d. Se quiere hacer la operación f = (a + b) - c + d
, dejando el resultado en la variable de memoria f
a) Escribe el programa en ensamblador del computador A que realiza esta operación
b) Calcula el tráfico total (en bytes) entre el procesador y la memoria cuando se ejecuta este programa
Ejercicio 2
Un computador B tiene una arquitectura registro-memoria, con 16 registros de propósito general de 32 bits. Las direcciones de memoria son de 32 bits y las variables almacenadas en memoria son palabras de 32 bits también. El fabricante nos proporciona el formato de las instrucciones store así como las de suma (add) y resta (sub). La operación de suma realiza esta operación: Reg = Reg + dato, y la de resta: Reg = Reg - dato
En memoria están almacenadas las variables a, b, c y d. Se quiere hacer la operación f = (a + b) - c + d
, dejando el resultado en la variable de memoria f (Misma operación que en el ejercicio anterior). Suponed que los registros del computador B están inicialmente a 0
a) Escribe el programa en ensamblador del computador B que realiza esta operación
b) Calcula el tráfico total (en bytes) entre el procesador y la memoria cuando se ejecuta este programa
Nota para el profesor
- Título informal de la clase: "Yo soy tu operando..." (Dígase con voz de Darth Vader...)
- En la primera parte nos centramos en describir las arquitecturas desde el punto de vista de los operandos usados para las operaciones aritméticas
Autores
- Katia Leal Algara
- Juan González-Gómez (Obijuan)