RISC-V: nos metemos en la piel de un arquitecto

Siguiente artículo de la serie RISC-V, una vez ya sabes qué es RISC-V, cómo catalogarla dentro de los diferentes tipos de ISAs que existen, y los 7 puntos que han tenido en cuenta los diseñadores de esta arquitectura, ahora es el turno de seguir avanzando para conocer qué tiene debe tener en cuenta el arquitecto que diseña la ISA una vez ha trazado los puntos clave con esas 7 métricas del artículo anterior.

Me refiero a conocer algunos conceptos sobre el funcionamiento básico de una unidad de control (aunque sin entrar en temas de diseño de la microarquitectura, que dejo para más adelante), ciclos, codificación, estructura de una instrucción, tipos de instrucciones, tipos de datos, ortogonalidad, modos de trabajo y direccionamiento, etc.

Diseño y consideraciones de una ISA

La unidad de control

La unidad de control es la unidad funcional de la CPU que se encarga de gestionar al resto de unidades funcionales. Hay dos tipos: cableadas (hardwired) y microprogramables. Las primeras suelen ser de los RISC o microprocesadores con repertorios de instrucciones más sencillos. Las segundas para los más complejos, como los CISC.

Una cableada básicamente es un circuito combinacional cuyas salidas siempre dependen del estado de las entradas. Por eso, ante un estímulo (en este caso una instrucción llegada desde la memoria principal), generarán un código binario que viajará por el bus para ordenar al resto de unidades funcionales y registros lo que deben hacer. Por ejemplo, cuando llega el código binario correspondiente a una ADD, se envía un código a la ALU para que sume los operandos…

La microprogramable tendrá partes de memoria, y guardará un microcódigo que será el que ordene al resto de unidades funcionales lo que deben hacer. Este tipo de unidad de control reduce la complejidad del diseño de las cableadas, pero suelen ser mucho más ineficientes en cuanto a consumo energético y rendimiento que las cableadas.

Lo que pretendo decir con esto es que la ISA va a determinar en gran parte el rendimiento y características del hardware. Por eso es tan importante un buen diseño…

Ciclos de instrucción

CPU ciclos

Para entender el resto también es necesario que entiendas un ciclo de instrucción, es decir, todos los pasos que la CPU sigue para atraer a la instrucción y ejecutarla. Estas fases se componen de varias etapas que podrían variar de una arquitectura a otra, pero básicamente son:

  • Fetch: es la fase en que la unidad de control vuelca el contenido del registro PC (Program Counter) en el bus de direcciones para buscar la siguiente instrucción de la secuencia del programa que está ejecutando en este momento. Si se produce un fallo de cache, la instrucción será buscada en la memoria principal. Una vez la instrucción se ha traído a la CPU, el PC se incrementa (PC+1) para apuntar a la siguiente instrucción a ejecutar y la instrucción se almacenará en un registro de instrucciones o IR. Si se trata de un arranque, el registro PC tomará un valor predefinido para apuntar a direcciones de la rutina de arranque…
  • Decode: es el periodo donde se decodifica la instrucción que se ha traído a la CPU y se quiere procesar. Eso determinará el modo de direccionamiento, el tipo de operación que deben hacer las unidades funcionales de ejecución y los operandos.
  • Execute: el ciclo de ejecución sucede cuando las unidades funcionales (FPU, ALU,…) reciben las órdenes de control de la unidad de control según la instrucción que se ha decodificado y realizan la oepración aritmeticológica necesaria. Por ejemplo, una suma.
  • Store: finalmente, se almacena el resultado de nuevo en la memoria o el envío a través de E/S.

Una CPU con pipeline de 3 etapas quiere decir que podrá hacer 3 ciclos fetch, 3 decode, 3 execute y 3 store de forma paralela. Eso mejora el IPC. Por ejemplo, en la siguiente tabla se puede ver cómo se llenan las etapas de la pipeline en cada ciclo, llegando a k donde todos las etapas estarían operando de forma paralela:

Ciclo Fetch Decode Execute Store
k-3 *
k-2 * *
k-1 * * *
k * * * *

Por cierto, a esos ciclos internos de la CPU, se agregan otros ciclos funcionales externos cuando hay que ir a buscar los datos e instrucciones a la memoria principal o a E/S. Pero tampoco quiero profundizar en eso, simplemente que tengas una idea genérica para lo que quiero explicar de la ISA RISC-V…

Juego de instrucciones

Diagrama de una Instruccion

Es el repertorio de instrucciones que contiene una ISA. Como ya sabes, no todos son iguales. Hay muchos repertorios diferentes como ARM, SPARC, PPC, RISC-V, AMD64, IA-32, IA-64, etc. Las instrucciones que se agregan a estos repertorios no se hacen al azar ni mucho menos, de hecho, no se suelen agregar a no ser que consigan una ganancia de rendimiento considerable. Por ejemplo, en MIPS solo se consideraba agregar una nueva instrucción si suponía al menos una mejora del 1% en al menos el 90% de los programas cotidianos.

Cada instrucción es un chorro de bits que se pueden descomponer en diferentes campos como se puede apreciar en la imagen superior. La longitud y campos dependerá también de la familia de la ISA. Por ejemplo, en el caso de RISC-V, según el tipo de módulo de instrucciones podría variar como se aprecia en la siguiente imagen sobre las instrucciones de tipo inmediato:

Formato de instrucciones RV32I. Fuente: Waterman and Asanović 2017, fig. 2.3.

Como se aprecia en la imagen, las instrucciones reparten sus 32-bits de longitud de esta forma:

  • 7 primeros bits (0-6) al opcode o código de operación, es decir, el que determina el tipo de operación implícita en la instrucción.
  • El registro destino se marca con un código compuesto de 5 bits (7-11). No en todas está presente este campo.
  • Se marca el primer registro fuente donde está el operando. Cuando está presente siempre estará en los siguientes 5 bits (15-19).
  • El segundo registro fuente, si está presente, estará siempre en los 5 bits que van del 20 al 24.
  • También pueden contener valores inmediatos, es decir, un valor constante.
  • Los campos de función determinan la variante de la operación establecida en el campo de opcode.

Las instrucciones están codificadas (véase p.e.: Huffman) para poder comprimir más información en un menor número de bits y así hacerlas más ligeras.

Se parecen bastante a las instrucciones ARM y MIPS como puedes comprobar…

Tipos de instrucciones

Las instrucciones se pueden catalogar en diferentes tipos:

  • Instrucciones de carga o transferencia: aquellas que se necesitan para mover datos desde o hacia un lugar. Por ejemplo, instrucciones como store, load, clear, push, pop, set y similares.
  • Instrucciones lógicas: para realizar operaciones booleanas sobre operandos. Por ejemplo, and, or, xor, not,…
  • Instrucciones aritméticas: realizan operaciones matemáticas como la suma, multiplicación, división, resta,…
  • Instrucciones de desplazamiento: operan sobre bits, desplazando a la derecha, izquierda, rotando, etc.
  • Instrucciones de conversión: para convertir de un formato a otro, como por ejemplo de ASCII a Unicode, etc.
  • Instrucciones de control: saltos (condicionales, incondicionales,…), llamadas a subrutinas, gestión de interrupciones, etc.
  • Instrucciones de E/S: igual al primer grupo, pero operan fuera de la CPU, con datos almacenados en el mapa de entrada y salida.
  • Otros: podría haber algunos otros ejemplos, especialmente ahora que se están añadiendo instrucciones para aplicaciones de IA, instrucciones ocultas o no documentadas, etc.

En la ISA RISC-V no hay instrucciones ocultas o no documentadas, pero sí que existen en muchas otras ISAs comerciales, al igual que registros ocultos no documentados, zonas reservadas, etc., que Intel y otros diseñadores no especifican para qué sirven. Todo este tipo de cosas son bastante interesantes desde el punto de vista de la seguridad…

Toda ISA debe contener instrucciones ortogonales, es decir, que puedan ser capaces de realizar todas las operaciones con todos los tipso de datos.

Tipos de datos

Otra de las cosas que se definen cuando se diseña la ISA es el tipo de datos. Eso determinará el tamaño de bits de los operandos o datos y también el tipo de operandos que puede manejar: enteros, BCD o decimal empaquetado, coma flotante (de precisión simple, precisión doble, estandarizados IEEE 754), etc.

Modos de direccionamiento

Uno de los campos que suelen tener las instrucciones es el modo de direccionamiento con el que trabajarán. En el caso de las instrucciones RISC-V mostradas anteriormente, era de tipo inmediato, pero puede haber otros. Los RISC apenas tienen unos 5 modos de direccionamiento, pero en el caso de los CISC podrían tener muchos más, como en el caso de x86.

Seguramente también te estés preguntando por los CPU modes, es decir, los modos de trabajo o privilegios que cada arquitectura permite. En los RISC se usan dos, el modo privilegiado y el modo usuario. En otras ISA, como ARM, puede haber hasta siete: modo usuario, modo FIQ, modo IRQ, modo supervisor, modo abort, modo undef y modo system. En x86 se emplean los básicos y otros agregados para mantener la retrocompatibilidad: modo real, modo protegido, modo virtual, modo irreal, modo largo, etc.

Los modos de direccionamiento son maneras de especificar un operando dentro de una instrucción. Por ejemplo,

  • Implícito: la dirección del operando va implícita en la propia instrucción.
  • Registros: los datos u operandos están en los registros de la CPU.
  • Inmediato: el valor del dato se especifica dentro de la propia instrucción.
  • Directo: se especifica la dirección de memoria donde se encuentra el operando se incluye en algún campo de la instrucción.
  • Indirecto: la instrucción contiene en un campo una dirección de memoria, pero a diferencia del directo, en esa dirección de memoria no se encuentra el valor del operando almacenado, sino otra dirección que sí que contiene el operando.
  • Otros: existen otros modos como el de pila, relativo, indexado, absoluto, etc.

Todos estos aspectos deben quedar definidos cuando diseñas una ISA.

Interrupciones

La CPU es un dispositivo que ejecuta instrucciones de modo constante. Para interrumpir ese flujo se hace uso de las interrupciones y excepciones. Básicamente son señales de control que indican a la CPU que debe detener el flujo de procesamiento actual y realizar alguna operación específica dada la situación. La CPU pasará entonces a procesar una subrutina de servicio de interrupción que no forma parte del programa que estaba en ejecución en el momento de la interrupción. Una vez termine la subrutina, se volverá al punto donde se encontraba la ejecución para seguir procesando el programa…

Isaac

Apasionado de la computación y la tecnología en general. Siempre intentando desaprender para apreHender.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

A %d blogueros les gusta esto:

Si continuas utilizando este sitio aceptas el uso de cookies. más información

Los ajustes de cookies de esta web están configurados para "permitir cookies" y así ofrecerte la mejor experiencia de navegación posible. Si sigues utilizando esta web sin cambiar tus ajustes de cookies o haces clic en "Aceptar" estarás dando tu consentimiento a esto.

Cerrar