⏱️ Lectura: 12 min

Guardar el contexto de una interrupción en un microcontrolador con RISC-V cuesta, en el mejor de los casos, 44 ciclos de reloj. Un núcleo ARM Cortex-M0, diseñado más de una década atrás, resuelve la misma tarea en 27 ciclos. Esa brecha, medida instrucción por instrucción, es el eje de una crítica técnica extensa que el ingeniero de sistemas embebidos Dmitry.GR publicó bajo el título “RISC-V: They Should Have Known Better”.

📑 En este artículo
  1. TL;DR
  2. Qué pasó
  3. Contexto e historia
  4. RISC-V interrupciones: detalles técnicos y rendimiento
  5. Cómo probarlo
  6. Impacto y análisis
  7. Qué sigue
  8. Preguntas frecuentes
    1. ¿Qué es RISC-V?
    2. ¿Qué diferencia hay entre RV32I y RV32E?
    3. ¿Por qué RISC-V no apila registros automáticamente como Cortex-M0?
    4. ¿Qué es la extensión Zicsr?
    5. ¿RISC-V va a reemplazar a ARM en microcontroladores baratos?
    6. ¿Dónde puedo leer la crítica completa?
  9. Referencias

El texto no ataca la idea de un conjunto de instrucciones (ISA) abierto y gratuito; apunta a decisiones concretas de diseño que, según el autor, dejan a los microcontroladores basados en RISC-V en desventaja frente a la competencia establecida en el segmento de bajo costo, justo el terreno donde el propio ecosistema RISC-V más promete crecer.

TL;DR

  • El ingeniero Dmitry.GR publicó una crítica técnica extensa a la arquitectura RISC-V titulada They Should Have Known Better.
  • En RV32I con Zicsr, guardar el contexto de una interrupción cuesta al menos 21 ciclos vía CSRRW y 19 stores individuales.
  • Restaurar los registros al salir de la interrupción suma al menos 20 ciclos más de loads individuales.
  • El costo mínimo total por interrupción en RV32I es de 44 ciclos, antes de ejecutar una sola línea de C.
  • ARM Cortex-M0 resuelve la entrada en 15 ciclos y la salida en 12: 27 ciclos en total, con el handler escrito directo en C.
  • Con RV32E (16 registros en vez de 32), el costo baja a 38 ciclos: aun así, un tercio más lento que Cortex-M0.
  • El autor señala que extensiones como CLIC nacieron para tapar esta debilidad del ISA base de RISC-V.
  • Pese a la crítica, el autor cree que RISC-V dominará los microcontroladores baratos, reemplazando al 8051.

Qué pasó

Dmitry.GR, un ingeniero conocido en la comunidad de sistemas embebidos por trabajos de ingeniería inversa de bajo nivel, publicó un ensayo extenso organizado en secciones como “Everything for Everyone”, “Optionality”, “Missing Obvious Pieces”, “Ridiculous encoding” y “Alleged Fixes”. El hilo conductor es que RISC-V, al intentar servir simultáneamente a supercomputadoras, servidores y microcontroladores de un dólar, termina sin ser óptimo para ninguno de esos casos de uso.

Su argumento central en la sección “Everything for Everyone” es simple: ninguna arquitectura puede ser la mejor opción para todo. Lo que necesita un procesador de gama alta es, en buena medida, opuesto a lo que necesita un núcleo barato orientado a ahorrar costos. El autor sostiene que esas decisiones no son solo de microarquitectura: terminan impactando el propio conjunto de instrucciones.

Para sostener el punto, describe el caso de uso típico de un microcontrolador barato: interconectar y reconfigurar bloques de hardware dedicados dentro de un chip mayor, como en un reproductor MP3, una tarjeta SD o una memoria USB. El trabajo pesado lo hace lógica dedicada; el núcleo solo prende registros y reacciona a eventos. Ahí importan dos cosas: latencia de interrupción baja y tamaño de código reducido, porque estos dispositivos suelen ejecutar desde ROM o RAM (no desde flash NOR, demasiado cara para producción masiva), donde la densidad de código pesa directamente en el costo del chip.

Microcontrolador RISC-V sobre una placa de circuito
El caso de uso típico: reconfigurar bloques de hardware, no correr matemática pesada. Foto de Robin Glauser en Unsplash

Contexto e historia

RISC-V nació en la Universidad de California, Berkeley, como un conjunto de instrucciones abierto y modular, y hoy lo gestiona la organización sin fines de lucro RISC-V International. A diferencia del modelo de licenciamiento propietario de ARM, cualquiera puede implementar un núcleo RISC-V sin pagar regalías por el ISA en sí.

Esa apertura viene acompañada de una filosofía de diseño particular: un ISA base mínimo (RV32I o RV64I) más un catálogo de extensiones opcionales que cada fabricante puede combinar según su caso de uso: M para multiplicación, A para atómicas, F y D para punto flotante, C para instrucciones comprimidas de 16 bits, y así sucesivamente. Existe también RV32E, una variante “embedded” que recorta el banco de registros de 32 a 16 para chips todavía más pequeños.

Uno de esos módulos opcionales es Zicsr, el que agrega las instrucciones para leer y escribir Control and Status Registers (CSR). Sin Zicsr no hay forma conforme a la especificación de manejar una interrupción, porque no existe ningún lugar temporal donde guardar un registro antes de empezar a salvar el resto. MIPS, para el mismo problema, reservó directamente dos registros de propósito exclusivo, $k0 y $k1. RISC-V, en cambio, resuelve esto con los CSR mscratch o sscratch, y solo si el núcleo implementa Zicsr.

Del lado de la competencia, ARM lleva más de diez años vendiendo Cortex-M0 como el núcleo de referencia para microcontroladores ultra baratos, con silicio maduro, herramientas consolidadas y un manejo de interrupciones resuelto en hardware desde el diseño original.

RISC-V interrupciones: detalles técnicos y rendimiento

El corazón del ensayo es un conteo de ciclos, instrucción por instrucción, de lo que le toma a un núcleo RV32I con Zicsr atender una interrupción y volver al código interrumpido, comparado con Cortex-M0. Ninguno de los dos es un núcleo fuera de orden: si sacan una instrucción por ciclo, ya es un buen resultado.

En RV32I, la entrada a la interrupción arranca con una CSRRW para guardar un registro temporal (por ejemplo t0) y obtener la dirección base donde se van a guardar los demás. Después hay que guardar, uno por uno, ra, sp, gp, tp, t1 a t6, y a0 a a7. Con otra CSRRW se recupera el valor original de t0 y también se guarda. Eso son, según el autor, al menos 21 ciclos.

// Prologo de interrupcion en RV32I + Zicsr (pseudo-ensamblador)
csrrw t0, mscratch, t0      // intercambia t0 con la base de guardado
sw    ra,  0(t0)
sw    sp,  4(t0)
sw    gp,  8(t0)
sw    tp,  12(t0)
sw    t1,  16(t0)
// ... t2-t6, a0-a7 siguen el mismo patron
csrrw t1, mscratch, t1      // recupera el t0 original y lo guarda
sw    t1,  76(t0)
jal   handler_interrupcion_en_c

A la salida, el proceso se invierte: una CSRRW para recuperar la dirección de guardado y 19 instrucciones load para restaurar cada registro, al menos 20 ciclos más. Sumando el JAL de entrada y el RET de salida, el autor calcula un costo mínimo de 44 ciclos antes de que se ejecute una sola línea del handler en C.

Cortex-M0 resuelve lo mismo con hardware dedicado: apila automáticamente los registros que la convención de llamada (ABI) considera volátiles en 15 ciclos al entrar, y los restaura en 12 ciclos al salir. El desarrollador escribe el handler directamente en C, sin ensamblador manual de por medio.

// Handler de interrupcion en Cortex-M0: sin prologo manual
void __attribute__((interrupt("IRQ"))) uart_rx_handler(void) {
    uint8_t byte = UART0->DR;
    ring_buffer_push(℞_buffer, byte);
}

Con RV32E, que reduce el banco de 32 a 16 registros, el ahorro es de 6 ciclos en cada fase respecto a RV32I, según el propio cálculo del autor, lo que deja el costo total en 38 ciclos: todavía más de un tercio por encima de Cortex-M0.

NúcleoEntrada de interrupciónSalida de interrupciónTotal¿Ensamblador manual?
RV32I + Zicsr21 ciclos (CSRRW + 19 stores)20 ciclos (CSRRW + 19 loads)44 ciclosSí, obligatorio
RV32E + Zicsr6 ciclos menos que RV32I6 ciclos menos que RV32I38 ciclosSí, obligatorio
ARM Cortex-M015 ciclos (hardware)12 ciclos (hardware)27 ciclosNo, handler directo en C

Comparacion de ciclos de interrupcion entre RISC-V y ARM Cortex-M0
44 ciclos en RV32I contra 27 en Cortex-M0: la diferencia es 63%. Foto de Yogesh Phuyal en Unsplash

El texto señala además que la existencia de CLIC (Core-Local Interrupt Controller) y de extensiones propietarias de “fast IRQ” o auto-apilado en distintos fabricantes es, en sí misma, evidencia del problema: el ISA base obliga a cada vendedor a inventar silicio no estándar para alcanzar la paridad con un Cortex-M0 de una década de antigüedad, lo que a su vez fragmenta el propio estándar.

flowchart TD
    A["Interrupcion de hardware"] --> B{"Nucleo"}
    subgraph RV32I ["RISC-V RV32I + Zicsr"]
    C["CSRRW guarda contexto: 21 ciclos"] --> D["JAL a handler en C"]
    D --> E["Handler ejecuta en C"]
    E --> F["Restaura con loads: 20 ciclos"]
    F --> G["RET al codigo interrumpido"]
    end
    subgraph CM0 ["ARM Cortex-M0"]
    H["Hardware apila registros: 15 ciclos"] --> I["Handler ejecuta en C"]
    I --> J["Hardware restaura: 12 ciclos"]
    end
    B -->|"RISC-V"| C
    B -->|"Cortex-M0"| H
💭 Clave: el problema no es que RISC-V carezca de una solución rápida para interrupciones, sino que esa solución no forma parte del ISA base: cada fabricante la resuelve a su manera, y el código deja de ser portable entre núcleos RISC-V distintos.

Cómo probarlo

Podés verificar este análisis vos mismo compilando un handler mínimo y leyendo el ensamblador generado con objdump. Primero hace falta un toolchain cruzado para RISC-V.

# Linux (Debian/Ubuntu)
sudo apt install gcc-riscv64-unknown-elf gdb-multiarch

# macOS (Homebrew)
brew tap riscv-software-src/riscv
brew install riscv-tools

# Windows (via xPack, requiere Node.js)
npm install --global xpm@latest
xpm install --global @xpack-dev-tools/riscv-none-elf-gcc@latest

Con el toolchain instalado, compilá un handler de interrupción simple y desensamblalo:

riscv64-unknown-elf-gcc -march=rv32i_zicsr -mabi=ilp32 \
  -O2 -c interrupt_handler.c -o interrupt_handler.o

riscv64-unknown-elf-objdump -d interrupt_handler.o | less

En la salida vas a ver la secuencia de csrrw seguida de una cadena de instrucciones sw (store word) en el prólogo, y su espejo de instrucciones lw (load word) en el epílogo. Contar esas líneas es, literalmente, reproducir el cálculo de ciclos del artículo original.

Impacto y análisis

La consecuencia práctica más citada del ensayo es la fragmentación. Si cada fabricante de silicio RISC-V necesita su propia extensión de interrupciones rápidas para competir con Cortex-M0, el código de un handler deja de ser portable entre un chip de SiFive, uno de Espressif y uno de otro proveedor, aunque todos digan “RISC-V” en la caja.

Hay una contrapartida real en el diseño de RV32E: reducir el banco de registros de 32 a 16 mejora la densidad de código y el costo del prólogo de interrupción, pero también reduce cuánto puede mantener el compilador en registros durante código normal, lo que puede aumentar el tráfico hacia la pila en funciones con muchas variables locales. No es una mejora gratis: es un cambio de balance.

⚠️ Ojo: comparar cifras de ciclos entre arquitecturas distintas depende del compilador, las banderas de optimización y la implementación exacta del núcleo. Los números de este artículo son los que reporta el autor original con su propio conteo manual, no un benchmark estandarizado.

Qué sigue

RISC-V International sigue ratificando extensiones orientadas a interrupciones rápidas, entre ellas CLIC, con el objetivo declarado de estandarizar lo que hoy resuelven implementaciones propietarias caso por caso. El propio Dmitry.GR es explícito en que esto no condena a RISC-V en el segmento de microcontroladores baratos: predice que terminará desplazando al 8051, no por la calidad de su ISA, sino porque 8051 es un piso todavía más bajo. El debate de fondo (si conviene un ISA con casi todo opcional o uno con menos partes pero mejor resueltas de fábrica) sigue abierto en la comunidad de sistemas embebidos.

📖 Resumen en Telegram: Ver resumen

Probalo vos: instalá el toolchain de RISC-V, compilá un handler de interrupción con -march=rv32i_zicsr y contá los sw/lw del prólogo con objdump -d para ver estos 44 ciclos con tus propios ojos.

Preguntas frecuentes

¿Qué es RISC-V?

Es un conjunto de instrucciones (ISA) abierto y modular, gestionado por RISC-V International, que cualquier fabricante puede implementar sin pagar regalías por el diseño del ISA en sí.

¿Qué diferencia hay entre RV32I y RV32E?

RV32I es el ISA base de 32 bits con 32 registros de propósito general. RV32E es una variante pensada para microcontroladores muy pequeños que recorta el banco a 16 registros para ahorrar área de silicio.

¿Por qué RISC-V no apila registros automáticamente como Cortex-M0?

Porque el ISA base no incluye esa función en hardware: cada fabricante decide si la implementa mediante una extensión propia, lo que genera soluciones distintas entre núcleos.

¿Qué es la extensión Zicsr?

Es el conjunto de instrucciones para leer y escribir Control and Status Registers. Sin ella no hay forma conforme a la especificación de manejar interrupciones en RISC-V.

¿RISC-V va a reemplazar a ARM en microcontroladores baratos?

Según el propio autor de la crítica, sí, pero en el segmento de microcontroladores de un solo uso desplazará principalmente al 8051, no porque su ISA sea superior, sino porque el 8051 parte de una base todavía más antigua.

¿Dónde puedo leer la crítica completa?

El ensayo original, escrito por Dmitry.GR, está publicado en dmitry.gr.

Referencias

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.

Imagen destacada: Foto de Bartosz Kwitkowski en Unsplash


Javier Alarcón

Ingeniero de infraestructura especializado en redes, sistemas Linux, Kubernetes y arquitecturas cloud. Cubre hardware, networking, observabilidad y prácticas de ingeniería para equipos de producción.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.