S10 - myTeachingURJC/Arq-computadores-01 GitHub Wiki
Sesión de Teoría 10: El computador NanoRisc-V
- Tiempo: 2h
- Objetivos de la sesión:
- Descripción del nanoRISC-V
- Entender el formato de las instrucciones
- Simulación de programas sencillos
Contenido
Introducción
Para entender el funcionamiento del procesador vamos a estudiar una versión muy simplificada del RISC-V, que llamaremos nanoRISC-V. En esta sesión definiremos nuestro procesador nanoRISC-V, tanto a nivel de instrucciones como a nivel de chip. Con este procesador construimos el computador NanoRISC-V, que ya es capaz de ejecutar las instrucciones almacenadas en la memoria. Estudiaremos qué ocurre a nivel hardware para que este computador ejecute los programas. En las siguientes sesiones estudiaremos los detalles internos del procesador
Procesador nanoRISC-V
El procesador que vamos a implementar es el nanoRISC-V: Un procesador RISC-V simplificado, de 64 bits, que tiene sólo 8 instrucciones. En esta primera implementación todas las instrucciones se ejecutarán en 1 ciclo de reloj (CPI = 1) y la estructura final del circuito se puede modelar como la conexión en cadena de un circuito secuencial más uno combinacional. Serán los retardos de estos componentes los que definan la frecuencia máxima de funcionamiento
- Arquitectura base RV64I (64-bits)
- 8 Instrucciones: add, addi, sub, and, or, ld, sd, beq
- 32 Registros de 64 bits: x0,x1,...,x31
- CPI = 1. Todas las instrucciones se ejecutan en 1 ciclo de reloj
- Memoria de código y datos separadas
- ¿Frecuencia máxima de funcionamiento?
Repertorio de instrucciones
Nuestro nanoRISC-V tiene 8 instrucciones: ld
y sd
para acceder a memoria (load-store), tres para realizar operaciones aritméticas (add
, addi
, sub
), dos para las operaciones lógicas (and
, or
) y una para hacer un salto condicional (beq)
- Instrucciones de acceso a memoria
Inst. | Operandos | Descripción |
---|---|---|
LD | rd,off(rs1) |
Load-word. Ejemplo: ld x5, 0(x3) . Cargar en el registro destino (rd) el dato de 64-bits situado en la dirección de memoria rs1 + off |
SD | rs2,off(rs1) |
Store-word. Ejemplo: sd x1, 16(x2) . Almacenar el registro rs2 en memoria, en la dirección rs1 + off. |
- Instrucciones Aritmético-lógicas
Inst. | Operandos | Descripción |
---|---|---|
ADD | rd,rs1,rs2 |
Suma. Ejemplo: add x3, x5, x15 . Guardar en el registro destino el resultado de sumar los registros rs1 y rs2 |
ADDi | rd,rs1,valor |
Suma. Ejemplo: add x3, x5, 30 . Guardar en el registro destino el resultado de sumar rs1 con el valor inmediato |
SUB | rd,rs1,rs2 |
Resta. Ejemplo: sub x7, x6, x13 . Guardar en el registro destino el resultado de restar rs1 - rs2 |
AND | rd,rs1,rs2 |
Operación Y-Lógico. Ejemplo: and x1, x30, x20 . Guardar en el registro destino el resultado de hacer una operación AND bit a bit entre los registros rs1 y rs2 |
OR | rd,rs1,rs2 |
Operación O-Lógico. Ejemplo: or x31, x15, x16 . Guardar en el registro destino el resultado de hacer la operación OR bitg a bit entre los registros rs1 y rs2 |
- Instrucción de salto condicional
Inst. | Operandos | Descripción |
---|---|---|
BEQ | rs1,rs2,dest |
Salto condicional. Ejemplo: beq x1,x0,fin . Realizar un salto a destino si los registros rs1 y rs2 son iguales. En caso contrario se ejecuta la siguiente instrucción. El salto es relativo al PC, en unidades de medias-palabras. Si la condición se cumple, PC = PC + destino*2 |
Nuestro procesador NanoRISC-V es tan pequeño que no incluye la instrucción ecall
. Esto significa que NO TIENE SISTEMA OPERATIVO. No está pensado para realizar llamadas a un sistema operativo. Cualquier función que necesitemos la tenemos que implementar nosotros
Tampoco tenemos la instrucción jal
por lo que NO PODEMOS IMPLEMENTAR SUBRUTINAS
Actividades para practicar
-
Actividad 1: Escribe un programa para el NanoRisc-v que implemente la operación de alto nivel
f = a + 1
. El valor inicial que tienea
es de 170. Recuerda que NO hay sistema operativo, por lo que no uses la instrucción ecall. Así que, de momento, no uses nada para terminar -
Actividad 2: Escribe un programa para el NanoRISC-V que lea una variable (de 64 bits) de la posición de memoria 0x100 y la guarde en el registro x1. Como estamos a bajo nivel y las direcciones las queremos controlar nosotros, NO uses etiquetas, sino direcciones directamente. También recuerda que en el nanoRV NO tenemos subrutinas, por lo que el registro x1 lo podemos usar como cualqueir otro (x1 es el registro ra, que almacena la dirección de retorno... pero NO en el nanoRV)
-
Actividad 3: Escribe un programa para el NanoRISC-V que almacene el valor 73 (decimal) en la posición de memoria 0x108. No utilices etiquetas, sino direcciones directamente
Formato de las instrucciones
En esta tabla resumen se muestra el formato de las 8 instrucciones. Todas las aritmético-lógicas (menos ADDi) son de tipo R, ya que usan tres registros. Las instrucciones de load y addi son de tipo I, la de store es de tipo S y la de salto condicional de tipo B
El campo opcode nos indica qué instrucción es o de qué tipo. Por ejemplo, las instrucciones add
, sub
, and
y or
tiene todas el mismo opcode: 0110011. Esto indica que son de tipo R. Se diferencian entre ellas por los campos func3 y func7
Retos
- Reto 1: Esta es una instrucción en código máquina del NanoRISC-V:
0x0C000293
. ¿Sabrías decodificarla y obtener la instrucción en ensamblador? - Reto 2: ¿Y qué instrucción en ensamblador es esta otra
0x00128313
? - Reto 3: ¿Cuál es el código máquina de la instrucción
ld x1, 0x100(x0)
?
El chip nanoRISC-V
Dada una arquitectura de un procesador, existen muchas posibles implementaciones diferentes. Unas nos ofrecerán mejor rendimiento, otras serán más baratas, otras consumirán menos, etc. Cada fabricante implementa su propio chip. Pero a nivel de instrucciones, como la arquitectura es la misma, serían compatibles
El objetivo del nanoRISC-V es que sea muy sencillo de implementar. Por eso la memoria principal se divide en dos memorias separadas, que físicamente están en chips distintos:
- Memoria de instrucciones: Contiene nuestro programa (segmento de código)
- Memoria de datos: Contiene las variables (Segmento de datos)
Este es el aspecto del nanoRISC-V:
Es un chip que tiene en total 290 patas, cada una de 1 bit. En el dibujo se han agrupado por funcionalidad. En la izquierda están las señales de entrada al chip y en la derecha las señales que salen del chip
-
Señales de salida:
- PC (64-bits): Contiene la dirección de memoria donde se encuentra almacenada la siguiente instrucción a ejecutar
- Addr(64-bits): Dirección de acceso a la memoria de datos, para leer o escribir
- Dout (64-bits): Dato de salida del procesador para ser escrito en la memoria
- MemWr (1 bit): Señal de control. Indica si el acceso es de escritura (MemWr = 1). En caso contrario será de lectura
-
Señales de entrada:
- Instr (32-bits): La instrucción a ejecutar, proveniente de la memoria de instrucciones
- Din (64-bits): El dato recibido de la memoria de datos
- clk (1-bit): Señal de reloj
El computador nanoRISC-V
El procesador por sí sólo no hace nada. Necesita que exista una memoria donde está almacenado el programa y los datos. Para convertirlo en un computador le añadimos la memoria de datos y la memoria de Instrucciones. Además le conectamos la señal de reloj
Memoria de instrucciones
La memoria de instrucciones almacena el código máquina del programa a ejecutar. Como las instrucciones son de 32 bits, su anchura es de 32-bits. El chip tiene este aspecto:
Tiene una entrada de 64-bits, addr
, por donde se recibe la dirección de la instrucción a leer, y una salida de 32-bits, data
, por donde sale la instrucción leída. Esta memoria es COMBINACIONAL. En cuanto hay una dirección en sus pines addr
la memoria devuelve la instrucción pedida, tras un retardo
Memoria de datos
La memoria de datos contiene todas las variables y constantes que se sitúan en el segmento de datos. Es una memoria de lectura y escritura: el procesador puede leer los datos ahí situados y modificarlos. La memoria tiene 3 señales entrada:
Addr
: Dirección a la que se quiere acceder, tanto para escritura como lecturaDin
: Dato de 64 bits a grabar en la memoria, en la dirección indicada por AddrWr
: Señal de control para indicar si el acceso es de lectura o escritura (Wr = 1)
Este es el aspecto del Chip:
La memoria de datos se comporta como un circuito COMBINACIONAL en las lecturas (decimos que las lecturas son combinacionales), pero para la escritura se comporta como un registro: tiene una señal de reloj y la escritura sólo se realiza cuando Wr es 1 y llega un flanco de subida del reloj. Decimos que la escritura es síncrona
La memoria de datos también tiene un retardo, pero es diferente, según que el tipo de acceso. Así habrá un retardo para el caso de la lectura y otro para la escritura
Esquema general
Este es el esquema general del computador nanoRISC-V. Está formado por 3 chips: el procesador nanoRISC-V, un chip de memoria ROM que contiene el programa a ejecutar y un chip de memoria RAM donde se almacenan los datos
Programa de prueba: TEST-1
Antes de entrar en los detalles, vamos a ver cómo funciona nuestro computador nanoRiscV con un programa de 2 instrucciones, que asigna el valor 0xC0 al registro t0, y deposita en t1 su valor incrementado en una unidad: 0xC1. Este sería el programa escrito en el simulador RARs que usamos en el laboratorio
#-- TEST 1: Programa de prueba para el nanoRiscV
#-- Assigna los siguientes valores a los registros
#-- t0 = 0xC0
#-- t1 = t0 + 1 = 0xC1
.text
li t0, 0xC0 #-- t0 = 0xC0
addi t1, t0, 1 #-- t1 = t0 + 1
En los programas escritos por humanos usamos pseudoinstrucciones, comentarios, directivas para el ensamblador y los nombres de los registros definidos en la ABI del RISCV.
Conversión a instrucciones y registros básicos
En un primero paso del ensamblado el programa se convierte a instrucciones básicas del RISCV, usando los registros con sus números. El procesador no sabe nada del registro t0 ni t1. Para él estos son los registros x5 y x6 de su banco de registros
Así queda el programa, listo para convertirse a código máquina
addi x5, x0, 0xC0
addi x6, x5, 1
Generación del código máquina
Las instrucciones básicas se traducen ahora a código máquina, utilizando el formato de las instrucciones del RISC-V. Ambas instrucciones son de tipo I. La primera se traduce al ćodigo: 0x0C000293
La segunda se traduce a: 0x00128313
Ya tenemos el programa final, con el código ejecutable
Sólo tiene dos números de 32 bits, que se cargarán en la memoria de instrucción
0x0C000293
0x00128313
Simulando el programa TEST-1
Para que nuestro computador nanoRISC-V ejecute el programa de TEST-1, primero se debe cargar en la memoria de instrucciones. Esto se hace externamente. En nuestro nanoRISC-V NO tenemos sistema operativo, así que lo cargamos a partir de la dirección 0, para que se ejecute nada más arrancar. Como las instrucciones son de 32 bits, ocupan 4 bytes, por lo que la primera está en la dirección 0 y la siguiente en la dirección 4
Dato que el programa TEST-1 NO accede a memoria para leer ningún dato, para simular el funcionamiento sólo incluiremos la memoria de instrucciones. Así es como queda nuestro computador nanoRISC-V en el estado inicial, antes de empezar a funcionar:
En esta animación se muestra una simulación de la ejecución de este programa. Partimos del estado inicial. El valor del PC (0) sale del procesador hacia la memoria. La memoria devuelve el contenido de la posición 0, donde está la primera instrucción. Pero el procesador NO la leerá hasta que no llegue un pulso de reloj (El corazon debe latir. Mientras no lata el procesador no hace cambios en su interior)
Cuando llega el pulso, la instrucción entra en el procesador y la empieza a ejecutar. Determina su formato a partir del opcode y la ejecuta. El resultado de la ejecución es que se guarda el valor 0xC0 en el registro x5, y se incrementa el contador de programa en 4 bytes, para apuntar a la siguiente instrucción. El nuevo valor del PC (4) llega a la memoria de instrucciones y devuelve la siguiente instrucción, que se queda esperando hasta que el procesador la lea
Al llegar otro pulso de reloj se captura la nueva instrucción y se ejecuta: Se incrementa el valor de x5 y el resultado se guarda en x6: 0xC1. Se incrementa el contador de programa en 4 bytes, por lo que ahora vale 8. Este valor sale al exterior hacia la memoria de instrucciones
¡Las dos instrucciones de nuestro programa se han ejecutado!
Deteniendo el computador
El procesador NO se detiene nunca. Siempre está leyendo instrucciones y ejecutándolas. En el ejemplo de la simulación del programa TEST-1, hemos dejado de simular al ejecutarse la segunda instrucción pero... ¡El procesador sigue! Tras ejecutar la segunda instrucción, el contador de programa ha pasado a valer 8, por lo que se ejecutará la instrucción de esa dirección. ¿Y qué es lo que hay? Como no hemos cargado nada, habrá cualquier número
En el laboratorio, para terminar un programa llamamos al servicio exit del sistema operativo. Sin embargo, como estamos construyendo desde cero el computador nanoRISC-V, no tenemos el sistema operativo. ¿Cómo podemos prevenir la ejecución de instrucciones desconocidas?
Lo que hacemos es colocar un bucle infinito en la tercera instrucción. En el nanoRISC-V no tenemos instrucciones de salto incondicional, pero las implementamos muy fácilmente a partir de la instrucción BEQ, usando el registro x0 en los términos de la comparación. Como x0 es igual al x0, el salto se realizará siempre:
# -- Bucle infinito para el procesador
inf: beq x0, x0, inf
El código máquina de esta instrucción es: 0x00000063
El programa TEST-1 completo queda así:
#-- TEST 1: Programa de prueba para el nanoRiscV
#-- Assigna los siguientes valores a los registros
#-- t0 = 0xC0
#-- t1 = t0 + 1 = 0xC1
.text
li t0, 0xC0 #-- t0 = 0xC0
addi t1, t0, 1 #-- t1 = t0 + 1
#-- Detener al procesador para que
#-- no siga ejecutando instrucciones de la memoria
stop: beq x0,x0, stop #-- (Bucle infinito)
Y el código máquina que se carga en la memoria es:
0x0C000293
0x00128313
0x00000063
Al ejecutarse y llegar la última instrucción, el procesador permanece en un estado estable, donde nada cambia:
Programa de prueba: TEST-2
Este programa de prueba utiliza las instrucciones ld
y sd
para leer un dato de 64 bits situado en la dirección 0x100 y copiarlo a la dirección 0x108. Supondremos que antes de comenzar el programa el dato ya está situado en la memoria (al cargar el programa se han cargado también los datos iniciales)
# -- TEST-2
# -- Ejemplo de load y store
# -- El dato situado en direccion 0x100 se
# -- copia a la dirección 0x108
.text
#-- Leer dato
ld x1, 0x100(x0) #-- x1 = dato1
#-- Copiarlo en destino
sd x1, 0x108(x0) #-- dato2 = x1
# -- Detener la ejecución
stop: beq x0,x0,stop
El programa en código máquina es el siguiente:
0x10003083
0x10103423
0x00000063
En esta figura se muestra con más detalle cómo están codificadas las instrucciones sd
y ld
:
Al cargar el programa y los datos, nuestro computador se encuentra en este estado:
En la posición 0x100 de la memoria de datos se ha cargado el valor 0xCAFE, y el programa se ha cargado a partir de la dirección 0 de la memoria de instrucciones
La simulación comienza con la ejecución de la instrucción ld
, que saca la dirección 0x100 por la salida addr y la memoria de datos devuelve 0xCAFE. Al llegar el flanco de reloj este dato se guarda en el registro x1 y el PC se incrementa en 4 unidades para apuntar a la siguiente instrucción
La siguiente instrucción se lee de memoria, se saca 0x108 por Addr y el registro x1 por Dout. Al llegar el flanco de reloj se hace la escritura y se incrementa PC en 4. Por último se carga la instrucción beq
que mantiene el procesador en el mismo estado: nuestro programa se detiene
Este es el estado final
Conclusiones
En esta sesión hemos definido el computador nanoRisc-V, que consta de 3 elementos: El procesador (nanoRisc-V), la memoria de instrucciones y la memoria de datos. Ya sabemos también cómo ejecuta instrucciones y programas básicos. Tenemos todo lo necesario para realizar una primera implementación
Lecturas del libro de referencia
Libro de referencia: "Computer Organization and Design. Hardware/Software interface. Risc-V"
(Las mismas que las de la sesión 6)
- Apartado 4.1: Introduction (Pag 236-240)
- Apartado 4.2: Logic Design Conventions (Pag 240-243)
Ejercicios
Ejerccios para practicar y asimilar los conceptos más importantes
Ejercicio 1
Partimos de este circuito:
- a) Calcula su máxima frecuencia de funcionamiento
- b) Si inicialmente el registro tiene el valor 0x00400000, indica sus valores en los siguientes 4 ciclos de reloj
A la salida del registro conectamos una memoria ROM que contiene instrucciones, que se almacenan en otro registro en cada ciclo de reloj
- c) Calcula frecuencia máxima de funcionamiento de este nuevo circuito
Ejercicio 2
Hemos construido un computador nanoRISC-v que sólo tiene memoria de instrucciones:
Los accesos a memoria para la lectura de las instrucciones tardan 10ns. El tiempo que tarda en ejecutarse una instrucción depende del tipo de instrucción. Las instrucciones de tipo R tardan 50ns, las de tipo I 60ns y las de salto condicional 70ns. Las instrucciones se ejecutan en un único ciclo de reloj
-
a) Calcular la frecuencia máxima a la que puede funcionar este computador para que se ejecuten correctamente las instrucciones de tipo R, I y los saltos condicionales (el resto de instrucciones se ignoran en este ejemplo)
-
b) Si estamos usando la frecuencia máxima calculada anteriormente, ¿Cuanto tiempo tarda el procesador en ejecutar estas 6 instrucciones?
beq x0,x0,next
next: li t0, 1
addi t1, t0, 1
add t2, t1, t0
li t3, 1
sub t4, t2, t3
...
Ejercicio 3
Hemos construido un computador nanoRISC-v que tiene memoria de instrucciones y memoria de datos. El CPI es de 1
Dentro del procesador, todas las instrucciones tardan 20ns. Las instrucciones ld y sd, tienen un retardo adicional determinado por el acceso a la memoria de datos: 30ns en la lectura y 40ns en la escritura
- a) Calcula el tiempo que tardan las instrucciones de tipo de R en ejecutarse
- b) Calcula el tiempo que tardan la instrucción beq en ejecutarse
- c) Calcula el tiempo que tarda la instrucción addi en ejecutar
- d) Calcula el tiempo que tarda la instrucción ld en ejecutarse
- e) Calcula el tiempo que tarda la instrucción sd en ejecutarse
- f) Indica cuál es el camino crítico
- g) ¿Cual es la frecuencia máxima a la que puede funcionar este procesador?
- h) Calcula el número de ciclos que tarda el procesador en ejecutar este programa (sin contar la instrucción que está en la etiqueta fin):
.text
li t0,0
li t1,3
bucle: beq t0,t1,fin
sd t0, 0x100(x0)
addi t0,t0,1
beq t0,t0,bucle
fin: #-- Contar los ciclos hasta aquí
i) Si funciona a su frecuencia máxima, ¿Cuánto tiempo tarda en ejecutar este programa?
Notas para el profesor:
- Título informal de la clase "La pastilla roja..."
- Nos sumergimos en el interior de un computador: el computador nano-riscv. En esta primera toma de contacto definimos sus componentes a nivel del chip, y estudiamos cómo funcionan algunos programas sencillos de pocas instrucciones
Autores
- Katia Leal Algara
- Juan González-Gómez (Obijuan)