Está en la página 1de 7

GUÍA DE ESTUDIO – INTRODUCCIÓN AL RISC-V

1 – Introducción a RISC-V
Revise las diapositivas 2 a 7 y el video explicativo (https://youtu.be/Gvs7CfOmayQ). Con base en ello explique:
• Un openISA como RISC-V significa que cualquier estudiante o entusiasta pueda diseñar su propio
microprocesador, montarlo en una FPGA, usar el conjunto de herramientas software creadas por terceros
(como compiladores) y construir una aplicación completa que pueda vender sin pagar licencias a nadie.
¿Es esto cierto? Explique. ¿Qué ventajas tiene RISC-V?

2 – Funcionamiento básico de un microprocesador


Revise las diapositivas 8 a 12 y el video explicativo (https://youtu.be/jOTJZiJgkGs). Con base en ello explique:
• El diagrama mostrado en la explicación usa la arquitectura Neumann, estudiada al principio del curso.
¿Qué cambios habría que hacer en el diagrama para que empleara una arquitectura Hardvard?
• Completa la siguiente tabla con una breve descripción del significado
Unidad de Control

Contador de Programa (PC)

ALU

Register File
• ¿Qué es un opcode?
• Con base en tabla de instrucciones de La Guía Práctica de RISC-V (pg. 44, http://www.riscvbook.com/), cómo
ensamblaría manualmente a mano las instrucciones “andi x5, x7, 0x2FC” y “add x3, x2, x1”.

3 – Especificación del RV32I


• La RV32I es la arquitectura más básica del RISC-V que soporta operaciones sólo con números enteros, de allí su
nombre (I: Integer).
• Es una arquitectura de 32 bits, por lo que todos sus registros, incluido el contador de programa (PC), son de 32
bits.
• El register file está compuesto por 32 registros de 32 bits y reciben los nombres de x0 a x31.
• Todos los registros son de propósito general, es decir, pueden ser usados como los desee el programador. Sin
embargo, algunos reciben nombres y funciones especiales con el fin de garantizar la estandarización con las
herramientas software de terceros como los compiladores. En la página 47 de La Guía Práctica de RISC-V (pg. 43,
http://www.riscvbook.com/), se encuentra el detalle de los nombres alternativos de los registros del register file.
• El registro x0 siempre se lee cero y si se escribe en este no pasa nada.
• Aunque la memoria también se accede por palabras (word) de 32 bits, es importante tener en cuenta que cada
posición de memoria es de 8bits (byte), por lo cual un número de 32 bits (4 bytes) se almacena con el formato
little-endian, es decir, en la dirección más baja se almacena el byte menos significativo.
• Al igual que en el microcontrolador ATMega328P del Arduino, los dispositivos externos, como timers, se mapean
en memoria (memory mapped I/O), sin embargo, este mapeo no es estándar y lo puede personalizar el diseñador
del microcontrolador.

Preguntas:
• Cargue el simulador en línea https://www.cs.cornell.edu/courses/cs3410/2019sp/riscv/interpreter/
y revise la sección de registros y memoria. ¿Por qué en el simulador las direcciones de memoria
incrementan de 4 en 4?

1
• Según la Tarjeta de Referencia del RISC-V (Página 5 de La Guía Práctica de RISC-V) o la página 44 de la La Guía Práctica
de RISC-V, ¿Cuáles operaciones aritméticas y lógicas puede realizar la ALU?
• ¿El RV32I soporta multiplicaciones y divisiones? Si no, ¿Cómo se pueden soportar estas operaciones en RISC-V?
• Suponga que se quiere almacenar la cadena “HOLA” en la memoria de datos, en la posición 0x00000000, ¿cómo
tendría que escribir dicho valor?

4 – Modos de direccionamiento e instrucciones de la ALU


Si revisa las instrucciones aritméticas de la Tarjeta de Referencia del RISC-V podrá darse cuenta de que hay dos formatos
para la instrucción sumar (ADD), el formato ADD y ADD inmediato:
• ADD rd, rs1, rs2
o Este formato se denomina direccionamiento por registros (Formato R) en el que el tanto las fuentes como
el destino son registros del Register File.
o Esta línea se debe leer como rd = rs1 + rs2.
o Ejemplo: ADD x3, x4, x5 → Realiza la operación x3 = x4 + x5
o El código de máquina de esta instrucción usa el empaquetamiento R mostrado en la parte inferior de la
Tarjeta de Referencia del RISC-V (ver figura de abajo).
• ADDI rd, rs1, imm
o Este formato se denomina direccionamiento inmediato (Formato I) en el que una de las fuentes es un
registro del Register File, mientras que el otro operando es una constante númerica que se encuentra
directamente en código de máquina.
o Esta línea se debe leer como rd = rs1 + imm
o Ejemplo: ADD x3, x4, 100 → Realiza la operación x3 = x4 + 100
o El código de máquina de esta instrucción usa el empaquetamiento I mostrado en la parte inferior de la
Tarjeta de Referencia del RISC-V. Nótese que, en el empaquetamiento, imm debe tomar un valor de sólo
12 bits. Al ser los registros del procesador de 32bits, todos los datos que se usen en este formato se les
realiza una extensión de signo a 32 bits.

Para entender la extensión con signo, considere el número negativo (-1). En 12 bits, este número se representa en
complemento a dos como 0xFFF = 0B1111 1111 1111 (todos unos), donde el bit más significativo denota el signo. Si el
número (-1) se tuviera que reescribir en 32 bits, basta con replicar el signo (bit más significativo) hasta completar 32 bits.
En el ejemplo, basta con extender este bit de la forma: 0B 1111 1111 1111 1111 1111 1111 1111 1111 = 0xFFFFFFFF

Nótese que, en la tabla de instrucciones, todas las instrucciones aritméticas, lógicas y desplazamientos soportan los modos
de direccionamiento por registros e inmediato.

En las instrucciones lógicas hay dos tipos de desplazamiento hacia la derecha, el lógico y el aritmético. La diferencia radica
en la forma como se extiende el signo en el desplazamiento. Por ejemplo, el desplazamiento lógico siempre ingresa un “0”
por la izquierda, mientras que, en el desplazamiento aritmético, se desplaza el bit más significativo garantizando la
preservación del signo:

2
Por ejemplo, suponga que el registro x5 tiene el número decimal -100 (). Un corrimiento a la derecha x5 >> 1, equivale a
dividir por 2, por lo que se esperaría que el resultado fuera -50 (). Esto solamente es posible si se hace un corrimiento
aritmético y no lógico, porque el aritmético preserva el signo.

Preguntas:
• Prueba el desplazamiento lógico y aritmético del ejemplo en el simulador en línea usando el siguiente código.
¿cuánto da x5 y x6 después de las siguientes instrucciones?
addi x5, x0, -100 ; x5 = -100
srli x5, x5, 1 ; x5 = x5 >> 1

addi x6, x0, -100 ; x6 = -100


srai x6, x6, 1 ; x6 = x6 >> 1

• En el código anterior se cargó el valor inicial de los registros x5 y x6 con el número -100 usando una instrucción de
suma, ¿por qué funciona esta instrucción?
• ¿Cómo implementaría la operación x7 = x5*4 - x3/2? Pruebe su resultado en el simulador.

5 – Reflexión
• Comparta con sus compañeros lo que ha aprendido en forma individual y elabore un mapa mental, diagrama, o
imagen que resuma lo que aprendido.
• Formule un problema sencillo con instrucciones aritmeticológicas el cual usted debe solucionar, probar en el
simulador y pasar al equipo de al lado.
• Solucione el reto que el equipo del lado le pasó a su grupo e identifique si hay algún error en su formulación.

6 – Comprendiendo las arquitecturas RISC


Revise el video explicativo (https://youtu.be/38WIjtX0c4A) o lea la siguiente explicación.

Las arquitecturas RISC habitualmente se denominan arquitecturas LOAD/STORE (Carga/Almacenamiento) dado a que
todas las instrucciones, como las realizadas por la ALU, solamente pueden tomar como argumentos valores almacenados
en el Register File (Formato de instrucción R) o números inmediatos (Formato de Instrucción I). En las arquicturas RISC no
se permite operaciones en la ALU con valores almacenados en la memoria.

Por ejemplo, supongamos que en memoria se tienen 3 variables X, Y y Z, ubicadas en las posiciones 0x10, 0x14, 0x18,
respectivamente, y se quiere hacer la operación Z = X+Y. Entonces, para poder implementar este algoritmo, es necesario
cargar (LOAD) las variables X y Y en registros temporales al interior del Register file, por ejemplo, x5 y x6; luego sumar los
registros x5 y x6 y almacenarlos en otro registro, por ejemplo, x7, y el resultado final, se debe almacenar (STORE) de nuevo
en la memoria.
lw x5,0x10(x0) ;Carga la posición de memoria 0x10 en el registro x5
lw x6,0x14(x0) ;Carga la posición de memoria 0x14 en el registro x6
add x7,x5,x6 ;x7 = x5 + x6
sw x7,0x18(x0) ;Almacena en la posición de memoria 0x18 el registro x7

Para verificar el resultado de este programa en el simulador es necesario inicializar la memoria incluyendo estas
instrucciones al inicio:
addi x5,x0,12 ;Carga el registro x5 con el número 12
sw x5,0x10(x0) ;Almacena el número 12 en la posición de memoria 0x10 (variable X)
addi x5,x0,23 ;Carga el registro x5 con el número 23
sw x5,0x14(x0) ;Almacena el número 12 en la posición de memoria 0x14 (variable Y)
3
Preguntas:
• ¿Con qué valor queda la variable Z (posición de memoria 0x18) después de ejecutar el código anterior?
• En el código anterior note que las direcciones se escribieron con un formato del tipo 0x10(x0), es decir, que la
dirección donde se carga o almacena es x0 + 0x10. ¿Qué pasaría si en lugar de usar x0 se hubiera puesto x1, y x1
se hubiera cargado con el número 4?.
• Revise las instrucciones de carga en La Guía Práctica de RISC-V (pg. 4, http://www.riscvbook.com/). ¿Qué diferencia
habrá entre las instrucciones LB, LH, LW?

7 – Modos de direccionamiento indirecto


Si revisa las instrucciones de Carga (LOAD) y Almacenamiento (STORE) de La Guía Práctica de RISC-V (pg. 4,
http://www.riscvbook.com/) encontrará que existen varios formatos: Byte, Halfword y Word, y para la instrucción de carga
el formato Signed.
Estas instrucciones permiten cargar/almacenar valores de 8 bits (Byte), 16 bits (Halfword) o 32bits (Word). Como los
registros son de 32 bits, cuando se realizan cargas de 8 o 16bits, es necesario completar los bits restantes del registro
haciendo una extensión de signo para garantizar que un número negativo de 8 o 16 bits siga siendo negativo en 32 bits. Si
se usa la instrucción de carga Unsigned, no se realiza el procedimiento de extensión de signo.
Pruebe el siguiente código en el simulador, revise los resultados en decimal y binario, y complete los comentarios:
addi x1,x0,0x78 ;Carga en la posición de memoria 0x10 el número 120
sw x1,0x10(x0) ;La posición de memoria 0x10 =

addi x1,x0,0x9c ;Carga en la posición de memoria 0x14 el número 156 o equivalentemente el -100 en 8 bits
sw x1,0x14(x0) ;La posición de memoria 0x14 =

lb x5,0x10(x0) ;Registro x5 =
lb x6,0x14(x0) ;Registro x6 =

Note que en los anteriores programas siempre se han usado las referencias a la memoria del tipo DirecciónInmediata(x0),
pues esto asume que los datos se cargarán de la posición DirecciónInmediata + x0 = DirecciónInmediata, ya que el registro
x0 siempre vale 0.
Los programadores y/o compiladores explotan esta propiedad del conjunto de Posición de memoria Dato
instrucciones para implementar lo que se denomina direccionamiento indirecto, 0x10 5
donde se usa un registro como puntero a una zona de memoria. Por ejemplo, 0x14 -1
suponga que la memoria está cargada con los siguientes números y se ejecuta el 0x18 2
siguiente programa: 0x1C 3
add x5, x0, x0
addi a4, x0, 0x10
lw x6, 0(a4)
add x5, x5, x6
addi a4, a4, 4
lw x6, 0(a4)
add x5, x5, x6
addi a4, a4, 4
lw x6, 0(a4)
add x5, x5, x6
addi a4, a4, 4
lw x6, 0(a4)
add x5, x5, x6
• ¿Qué ecuación está implementando este programa? ¿Es decir con que valor queda al final el registro x5?
• ¿Qué función cumple el registro a4?
• ¿Qué función cumple las instrucciones addi a4,a4,4?
4
8 – Instrucciones de salto (Branches)
El microprocesador no estaría completo si no tuviera instrucciones para implementar instrucciones de alto nivel como
“if”, ciclos “for”, ciclos “while” y “do…while”. En estos casos se usan instrucciones de salto condicional.

En los siguientes ejemplos se muestra como usar las instrucciones de branches para implementar un “if … else” y un
ciclo “while”, note que en estos casos se usan etiquetas como argumentos inmediatos para los saltos.

if (x5 == x6 ) { bne x5, x6, else1


x7 = 1; iftrue1:
} else { addi x7, x0, 1
x7 = 2; jal x0, cont
} else1:
x8 = 3; addi x7, x0, 2
cont:
addi x8, x0, 3
x5 = 0; add x5, x0, x0 Este programa asume que el arreglo ar
while (x5 < 5) { addi x6, x0, 5 tiene la posición de memoria 0x10
ar[x5] = x5; while1:
x5++; bge x5, x6, finwhile Como cada celda de memoria ocupa 32bits
} slli a0, x5, 2 (direcciones múltiplo de 4), al codificar la
sw x5, 0x10(a0) línea ar[x5] es necesario hacer el truco de
addi x5, x5, 1 multiplicar por 4 el registro x5 para generar
jal x0, while1 la dirección de memoria correcta.
finwhile:

Preguntas:
• En el ejemplo del if … else:
o ¿Por qué en el ejemplo del if, si se hace una comparación de igualdad el salto se con la condición contraria,
es decir, con diferente?
o ¿Por qué se pone una instrucción de salto incondicional jal antes de la etiqueta else1?
o ¿Cómo cambiaría el if para que la condición sea (x5 > x6)?
• En el ejemplo del while,
o ¿por qué se usa el registro x6 y se inicia con el número 5?
o ¿En la instrucción bge se puede hacer una comparación con un dato inmediato 5?
o ¿Por qué si la condición del while es (x5 < 5) se usa la instrucción de salto bge?

9 – Reflexión final
• Comparta con sus compañeros lo que ha aprendido en forma individual y elabore un mapa mental, diagrama, o
imagen que resuma lo que aprendido.
• Modifique el programa de la sección 7 para que use un ciclo while.
• En RISC-V, para agregar hardware externo se debe mapear en la memoria. Suponga que la matriz de LEDs de 8x8
que se usó para mostrar la carita en un laboratorio pasado está conectada a dos puertos de 8 bits, mapeados en
la dirección 0x06 (las filas) y 0x08 (las columnas). Escriba el programa que muestra la carita en ensamblador de
RISC-V (aquí no hay que hacer trucos de máscaras ):
for (int r=0;r<8;r++) {
row=1<<r;
col = carita[r];
}

5
Anexos – Códigos para probar en el Ensamblador
Ejemplo Sección 4:

addi x5, x0, -100


srli x5, x5, 1

addi x6, x0, -100


srai x6, x6, 1

Ejemplo Sección 6:

addi x5,x0,12
sw x5,0x10(x0)
addi x5,x0,23
sw x5,0x14(x0)

lw x5,0x10(x0)
lw x6,0x14(x0)
add x7,x5,x6
sw x7,0x18(x0)

Ejemplo Sección 7:

addi x1,x0,5
sw x1,0x10(x0)
addi x1,x0,-1
sw x1,0x14(x0)
addi x1,x0,2
sw x1,0x18(x0)
addi x1,x0,3
sw x1,0x1C(x0)

add x5, x0, x0


addi a4, x0, 0x10
lw x6, 0(a4)
add x5, x5, x6
addi a4, a4, 4
lw x6, 0(a4)
add x5, x5, x6
addi a4, a4, 4
lw x6, 0(a4)
add x5, x5, x6
addi a4, a4, 4
lw x6, 0(a4)
add x5, x5, x6

6
Ejemplos Sección 8:

If..else:

addi x5, x0, 3


addi x6, x0, 5
bne x5, x6, else1
iftrue1:
addi x7, x0, 1
jal x0, cont
else1:
addi x7, x0, 2
cont:
addi x8, x0, 3

while:

add x5, x0, x0


addi x6, x0, 5
while1:
bge x5, x6, finwhile
slli a0, x5, 2
sw x5, 0x10(a0)
addi x5, x5, 1
jal x0, while1
finwhile:

También podría gustarte