feat: update RISCV_EMULATION documentation with English translations and improvements

pull/47/head
David Montero Crespo 2026-03-16 14:22:18 -03:00
parent 8e38bd2fa1
commit a40471fa38
1 changed files with 201 additions and 201 deletions

View File

@ -1,74 +1,74 @@
# RISC-V Emulation (ESP32-C3 / XIAO-C3 / C3 SuperMini) # RISC-V Emulation (ESP32-C3 / XIAO-C3 / C3 SuperMini)
> Estado: **Funcional** · Emulación en el navegador · Sin dependencias de backend > Status: **Functional** · In-browser emulation · No backend dependencies
> Motor: **RiscVCore (RV32IMC)** — implementado en TypeScript > Engine: **RiscVCore (RV32IMC)** — implemented in TypeScript
> Plataforma: **ESP32-C3 @ 160 MHz** — arquitectura RISC-V de 32 bits > Platform: **ESP32-C3 @ 160 MHz** — 32-bit RISC-V architecture
--- ---
## Índice ## Table of Contents
1. [Visión general](#1-visión-general) 1. [Overview](#1-overview)
2. [Boards soportadas](#2-boards-soportadas) 2. [Supported Boards](#2-supported-boards)
3. [Arquitectura del emulador](#3-arquitectura-del-emulador) 3. [Emulator Architecture](#3-emulator-architecture)
4. [Memoria y periféricos emulados](#4-memoria-y-periféricos-emulados) 4. [Emulated Memory and Peripherals](#4-emulated-memory-and-peripherals)
5. [Flujo completo: compilar y ejecutar](#5-flujo-completo-compilar-y-ejecutar) 5. [Full Flow: Compile and Run](#5-full-flow-compile-and-run)
6. [Formato de imagen ESP32](#6-formato-de-imagen-esp32) 6. [ESP32 Image Format](#6-esp32-image-format)
7. [ISA soportada — RV32IMC](#7-isa-soportada--rv32imc) 7. [Supported ISA — RV32IMC](#7-supported-isa--rv32imc)
8. [GPIO](#8-gpio) 8. [GPIO](#8-gpio)
9. [UART0 — Serial Monitor](#9-uart0--serial-monitor) 9. [UART0 — Serial Monitor](#9-uart0--serial-monitor)
10. [Limitaciones](#10-limitaciones) 10. [Limitations](#10-limitations)
11. [Tests](#11-tests) 11. [Tests](#11-tests)
12. [Diferencias vs emulación Xtensa (ESP32 / ESP32-S3)](#12-diferencias-vs-emulación-xtensa-esp32--esp32-s3) 12. [Differences vs Xtensa Emulation (ESP32 / ESP32-S3)](#12-differences-vs-xtensa-emulation-esp32--esp32-s3)
13. [Archivos clave](#13-archivos-clave) 13. [Key Files](#13-key-files)
--- ---
## 1. Visión general ## 1. Overview
Los boards basados en **ESP32-C3** usan el procesador **ESP32-C3** de Espressif, que implementa la arquitectura **RISC-V RV32IMC** (32 bits, Multiply, Compressed instructions). A diferencia del ESP32 y ESP32-S3 (Xtensa LX6/LX7), el C3 **no requiere QEMU ni backend** para emularse. Boards based on **ESP32-C3** use Espressif's **ESP32-C3** processor, which implements the **RISC-V RV32IMC** architecture (32-bit, Multiply, Compressed instructions). Unlike the ESP32 and ESP32-S3 (Xtensa LX6/LX7), the C3 **does not require QEMU or a backend** to be emulated.
### Comparación de motores de emulación ### Emulation Engine Comparison
| Board | CPU | Motor | | Board | CPU | Engine |
|-------|-----|-------| | ----- | --- | ------ |
| ESP32, ESP32-S3 | Xtensa LX6/LX7 | QEMU lcgamboa (backend WebSocket) | | ESP32, ESP32-S3 | Xtensa LX6/LX7 | QEMU lcgamboa (backend WebSocket) |
| **ESP32-C3, XIAO-C3, C3 SuperMini** | **RV32IMC @ 160 MHz** | **RiscVCore.ts (navegador, sin backend)** | | **ESP32-C3, XIAO-C3, C3 SuperMini** | **RV32IMC @ 160 MHz** | **RiscVCore.ts (browser, no backend)** |
| Arduino Uno/Nano/Mega | AVR ATmega | avr8js (navegador) | | Arduino Uno/Nano/Mega | AVR ATmega | avr8js (browser) |
| Raspberry Pi Pico | RP2040 | rp2040js (navegador) | | Raspberry Pi Pico | RP2040 | rp2040js (browser) |
### Ventajas del emulador JS ### Advantages of the JS Emulator
- **Sin dependencias de red** — funciona offline, sin conexión WebSocket al backend - **No network dependencies** — works offline, no WebSocket connection to the backend
- **Arranque instantáneo** — no hay proceso QEMU que arrancar (0 ms de latencia) - **Instant startup** — no QEMU process to launch (0 ms latency)
- **Testable con Vitest** — el mismo código TypeScript que se ejecuta en producción se puede probar en CI - **Testable with Vitest** — the same TypeScript code that runs in production can be tested in CI
- **Multiplataforma** — funciona igual en Windows, macOS, Linux y Docker - **Cross-platform** — works the same on Windows, macOS, Linux, and Docker
--- ---
## 2. Boards soportadas ## 2. Supported Boards
| Board | FQBN arduino-cli | LED built-in | | Board | arduino-cli FQBN | Built-in LED |
|-------|-----------------|--------------| | ----- | ---------------- | ------------ |
| ESP32-C3 DevKit | `esp32:esp32:esp32c3` | GPIO 8 | | ESP32-C3 DevKit | `esp32:esp32:esp32c3` | GPIO 8 |
| Seeed XIAO ESP32-C3 | `esp32:esp32:XIAO_ESP32C3` | GPIO 10 (active-low) | | Seeed XIAO ESP32-C3 | `esp32:esp32:XIAO_ESP32C3` | GPIO 10 (active-low) |
| ESP32-C3 SuperMini | `esp32:esp32:esp32c3` | GPIO 8 | | ESP32-C3 SuperMini | `esp32:esp32:esp32c3` | GPIO 8 |
--- ---
## 3. Arquitectura del emulador ## 3. Emulator Architecture
``` ```
Arduino Sketch (.ino) Arduino Sketch (.ino)
▼ arduino-cli (backend) ▼ arduino-cli (backend)
sketch.ino.bin ← ESP32 image format (segmentos IROM/DRAM/IRAM) sketch.ino.bin ← ESP32 image format (IROM/DRAM/IRAM segments)
▼ base64 → frontend ▼ base64 → frontend
compileBoardProgram(boardId, base64) compileBoardProgram(boardId, base64)
▼ Esp32C3Simulator.loadFlashImage(base64) ▼ Esp32C3Simulator.loadFlashImage(base64)
parseMergedFlashImage() ← lee segmentos de la imagen 4MB parseMergedFlashImage() ← reads segments from the 4MB image
├── IROM segment → flash buffer (0x42000000) ├── IROM segment → flash buffer (0x42000000)
├── DROM segment → flash buffer (0x3C000000, alias) ├── DROM segment → flash buffer (0x3C000000, alias)
@ -77,56 +77,56 @@ Arduino Sketch (.ino)
▼ core.reset(entryPoint) ▼ core.reset(entryPoint)
RiscVCore.step() ← requestAnimationFrame @ 60 FPS RiscVCore.step() ← requestAnimationFrame @ 60 FPS
│ 2.666.667 ciclos/frame (160 MHz ÷ 60) │ 2,666,667 cycles/frame (160 MHz ÷ 60)
├── MMIO GPIO_W1TS/W1TC → onPinChangeWithTime → componentes visuales ├── MMIO GPIO_W1TS/W1TC → onPinChangeWithTime → visual components
└── MMIO UART0 FIFO → onSerialData → Serial Monitor └── MMIO UART0 FIFO → onSerialData → Serial Monitor
``` ```
### Clases principales ### Main Classes
| Clase | Archivo | Responsabilidad | | Class | File | Responsibility |
|-------|---------|----------------| | ----- | ---- | -------------- |
| `RiscVCore` | `simulation/RiscVCore.ts` | Decodificador/ejecutor RV32IMC, MMIO genérico | | `RiscVCore` | `simulation/RiscVCore.ts` | RV32IMC decoder/executor, generic MMIO |
| `Esp32C3Simulator` | `simulation/Esp32C3Simulator.ts` | Mapa de memoria ESP32-C3, GPIO, UART0, ciclo RAF | | `Esp32C3Simulator` | `simulation/Esp32C3Simulator.ts` | ESP32-C3 memory map, GPIO, UART0, RAF loop |
| `parseMergedFlashImage` | `utils/esp32ImageParser.ts` | Parseo formato imagen ESP32 (segmentos, entry point) | | `parseMergedFlashImage` | `utils/esp32ImageParser.ts` | ESP32 image format parsing (segments, entry point) |
--- ---
## 4. Memoria y periféricos emulados ## 4. Emulated Memory and Peripherals
### Mapa de memoria ### Memory Map
| Región | Dirección base | Tamaño | Descripción | | Region | Base Address | Size | Description |
|--------|---------------|--------|-------------| | ------ | ------------ | ---- | ----------- |
| Flash IROM | `0x42000000` | 4 MB | Código ejecutable (buffer principal del core) | | Flash IROM | `0x42000000` | 4 MB | Executable code (core's main buffer) |
| Flash DROM | `0x3C000000` | 4 MB | Datos de solo lectura (alias del mismo buffer) | | Flash DROM | `0x3C000000` | 4 MB | Read-only data (alias of the same buffer) |
| DRAM | `0x3FC80000` | 384 KB | RAM de datos (stack, variables globales) | | DRAM | `0x3FC80000` | 384 KB | Data RAM (stack, global variables) |
| IRAM | `0x4037C000` | 384 KB | RAM de instrucciones (ISR, código time-critical) | | IRAM | `0x4037C000` | 384 KB | Instruction RAM (ISR, time-critical code) |
| UART0 | `0x60000000` | 1 KB | Serial port 0 | | UART0 | `0x60000000` | 1 KB | Serial port 0 |
| GPIO | `0x60004000` | 512 B | Registros GPIO | | GPIO | `0x60004000` | 512 B | GPIO registers |
### GPIO — registros implementados ### GPIO — Implemented Registers
| Registro | Offset | Función | | Register | Offset | Function |
|----------|--------|---------| | -------- | ------ | -------- |
| `GPIO_OUT_REG` | `+0x04` | Leer/escribir estado de salida de todos los pines | | `GPIO_OUT_REG` | `+0x04` | Read/write output state of all pins |
| `GPIO_OUT_W1TS_REG` | `+0x08` | **Set bits**poner pines a HIGH (write-only) | | `GPIO_OUT_W1TS_REG` | `+0x08` | **Set bits**drive pins HIGH (write-only) |
| `GPIO_OUT_W1TC_REG` | `+0x0C` | **Clear bits**poner pines a LOW (write-only) | | `GPIO_OUT_W1TC_REG` | `+0x0C` | **Clear bits**drive pins LOW (write-only) |
| `GPIO_IN_REG` | `+0x3C` | Leer estado de entrada de pines | | `GPIO_IN_REG` | `+0x3C` | Read input state of pins |
| `GPIO_ENABLE_REG` | `+0x20` | Dirección de pines (siempre devuelve `0xFF`) | | `GPIO_ENABLE_REG` | `+0x20` | Pin direction (always returns `0xFF`) |
Cubre **GPIO 021** (todos los disponibles en ESP32-C3). Covers **GPIO 021** (all available on ESP32-C3).
### UART0 — registros implementados ### UART0 — Implemented Registers
| Registro | Offset | Función | | Register | Offset | Function |
|----------|--------|---------| | -------- | ------ | -------- |
| `UART_FIFO_REG` | `+0x00` | Escribir byte TX / leer byte RX | | `UART_FIFO_REG` | `+0x00` | Write TX byte / read RX byte |
| `UART_STATUS_REG` | `+0x1C` | Estado FIFO (siempre devuelve `0` = listo) | | `UART_STATUS_REG` | `+0x1C` | FIFO status (always returns `0` = ready) |
Lectura de byte de RX disponible para simular input desde Serial Monitor. RX byte reading is available to simulate input from the Serial Monitor.
### Periféricos NO emulados (retornan 0 en lectura) ### Peripherals NOT Emulated (return 0 on read)
- Interrupt Matrix (`0x600C2000`) - Interrupt Matrix (`0x600C2000`)
- System / Clock (`0x600C0000`, `0x60008000`) - System / Clock (`0x600C0000`, `0x60008000`)
@ -136,40 +136,40 @@ Lectura de byte de RX disponible para simular input desde Serial Monitor.
- BLE / WiFi MAC - BLE / WiFi MAC
- ADC / DAC - ADC / DAC
> Estos periféricos retornan `0` por defecto. El código que los requiere puede no funcionar correctamente (ver [Limitaciones](#10-limitaciones)). > These peripherals return `0` by default. Code that depends on them may not function correctly (see [Limitations](#10-limitations)).
--- ---
## 5. Flujo completo: compilar y ejecutar ## 5. Full Flow: Compile and Run
### 5.1 Compilar el sketch ### 5.1 Compile the Sketch
```bash ```bash
# arduino-cli compila para ESP32-C3: # arduino-cli compiles for ESP32-C3:
arduino-cli compile \ arduino-cli compile \
--fqbn esp32:esp32:esp32c3 \ --fqbn esp32:esp32:esp32c3 \
--output-dir build/ \ --output-dir build/ \
mi_sketch/ mi_sketch/
# El backend crea automáticamente la imagen fusionada (merged): # The backend automatically creates the merged image:
# build/mi_sketch.ino.bootloader.bin → 0x01000 # build/mi_sketch.ino.bootloader.bin → 0x01000
# build/mi_sketch.ino.partitions.bin → 0x08000 # build/mi_sketch.ino.partitions.bin → 0x08000
# build/mi_sketch.ino.bin → 0x10000 (app) # build/mi_sketch.ino.bin → 0x10000 (app)
# → merged: sketch.ino.merged.bin (4 MB) # → merged: sketch.ino.merged.bin (4 MB)
``` ```
El backend de Velxio produce esta imagen automáticamente y la envía al frontend como base64. The Velxio backend produces this image automatically and sends it to the frontend as base64.
### 5.2 Sketch mínimo para ESP32-C3 ### 5.2 Minimal Sketch for ESP32-C3
```cpp ```cpp
// LED en GPIO 8 (ESP32-C3 DevKit) // LED on GPIO 8 (ESP32-C3 DevKit)
#define LED_PIN 8 #define LED_PIN 8
void setup() { void setup() {
pinMode(LED_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT);
Serial.begin(115200); Serial.begin(115200);
Serial.println("ESP32-C3 iniciado"); Serial.println("ESP32-C3 started");
} }
void loop() { void loop() {
@ -182,12 +182,12 @@ void loop() {
} }
``` ```
### 5.3 Sketch bare-metal (para tests de emulación directos) ### 5.3 Bare-Metal Sketch (for direct emulation tests)
Para verificar la emulación sin el framework Arduino, se puede compilar con el toolchain RISC-V directamente: To verify the emulation without the Arduino framework, you can compile directly with the RISC-V toolchain:
```c ```c
/* blink.c — bare-metal, sin ESP-IDF */ /* blink.c — bare-metal, no ESP-IDF */
#define GPIO_W1TS (*(volatile unsigned int *)0x60004008u) #define GPIO_W1TS (*(volatile unsigned int *)0x60004008u)
#define GPIO_W1TC (*(volatile unsigned int *)0x6000400Cu) #define GPIO_W1TC (*(volatile unsigned int *)0x6000400Cu)
#define LED_BIT (1u << 8) #define LED_BIT (1u << 8)
@ -204,10 +204,10 @@ void _start(void) {
} }
``` ```
Compilar con el toolchain bundled en arduino-cli: Compile with the toolchain bundled in arduino-cli:
```bash ```bash
# Toolchain instalado con: arduino-cli core install esp32:esp32 # Toolchain installed with: arduino-cli core install esp32:esp32
TOOLCHAIN="$LOCALAPPDATA/Arduino15/packages/esp32/tools/riscv32-esp-elf-gcc/esp-2021r2-patch5-8.4.0/bin" TOOLCHAIN="$LOCALAPPDATA/Arduino15/packages/esp32/tools/riscv32-esp-elf-gcc/esp-2021r2-patch5-8.4.0/bin"
"$TOOLCHAIN/riscv32-esp-elf-gcc" \ "$TOOLCHAIN/riscv32-esp-elf-gcc" \
@ -217,156 +217,156 @@ TOOLCHAIN="$LOCALAPPDATA/Arduino15/packages/esp32/tools/riscv32-esp-elf-gcc/esp-
"$TOOLCHAIN/riscv32-esp-elf-objcopy" -O binary blink.elf blink.bin "$TOOLCHAIN/riscv32-esp-elf-objcopy" -O binary blink.elf blink.bin
``` ```
Ver script completo: `frontend/src/__tests__/fixtures/esp32c3-blink/build.sh` See full script: `frontend/src/__tests__/fixtures/esp32c3-blink/build.sh`
--- ---
## 6. Formato de imagen ESP32 ## 6. ESP32 Image Format
El backend produce una imagen fusionada de **4 MB**: The backend produces a merged **4 MB** image:
``` ```
Offset 0x00000: 0xFF (vacío) Offset 0x00000: 0xFF (empty)
Offset 0x01000: bootloader (imagen ESP32 format, magic 0xE9) Offset 0x01000: bootloader (ESP32 format image, magic 0xE9)
Offset 0x08000: partition table Offset 0x08000: partition table
Offset 0x10000: app binary (imagen ESP32 format, magic 0xE9) ← parseamos aquí Offset 0x10000: app binary (ESP32 format image, magic 0xE9) ← parsed here
``` ```
### Cabecera de imagen ESP32 (24 bytes) ### ESP32 Image Header (24 bytes)
``` ```
+0x00 magic (0xE9) +0x00 magic (0xE9)
+0x01 segment_count +0x01 segment_count
+0x02 spi_mode +0x02 spi_mode
+0x03 spi_speed_size +0x03 spi_speed_size
+0x04 entry_addr ← uint32 LE — PC de entrada del firmware +0x04 entry_addr ← uint32 LE — firmware entry point PC
+0x08 extended fields (16 bytes) +0x08 extended fields (16 bytes)
``` ```
### Cabecera de segmento (8 bytes) ### Segment Header (8 bytes)
``` ```
+0x00 load_addr ← dirección virtual destino (e.g. 0x42000000) +0x00 load_addr ← destination virtual address (e.g. 0x42000000)
+0x04 data_len +0x04 data_len
+0x08 data[data_len] +0x08 data[data_len]
``` ```
El parser `parseMergedFlashImage()` en `utils/esp32ImageParser.ts` extrae todos los segmentos y el entry point, que se usa para el reset del core (`core.reset(entryPoint)`). The `parseMergedFlashImage()` parser in `utils/esp32ImageParser.ts` extracts all segments and the entry point, which is used for the core reset (`core.reset(entryPoint)`).
--- ---
## 7. ISA soportada — RV32IMC ## 7. Supported ISA — RV32IMC
`RiscVCore.ts` implementa las tres extensiones necesarias para ejecutar código compilado para ESP32-C3: `RiscVCore.ts` implements the three extensions required to run code compiled for ESP32-C3:
### RV32I — Base integer (40 instrucciones) ### RV32I — Base Integer (40 instructions)
Incluye: LUI, AUIPC, JAL, JALR, BEQ/BNE/BLT/BGE/BLTU/BGEU, LB/LH/LW/LBU/LHU, SB/SH/SW, ADDI/SLTI/SLTIU/XORI/ORI/ANDI/SLLI/SRLI/SRAI, ADD/SUB/SLL/SLT/SLTU/XOR/SRL/SRA/OR/AND, FENCE, ECALL/EBREAK, CSR (lectura devuelve 0) Includes: LUI, AUIPC, JAL, JALR, BEQ/BNE/BLT/BGE/BLTU/BGEU, LB/LH/LW/LBU/LHU, SB/SH/SW, ADDI/SLTI/SLTIU/XORI/ORI/ANDI/SLLI/SRLI/SRAI, ADD/SUB/SLL/SLT/SLTU/XOR/SRL/SRA/OR/AND, FENCE, ECALL/EBREAK, CSR (reads return 0)
### RV32M — Multiplicación y división (8 instrucciones) ### RV32M — Multiply and Divide (8 instructions)
| Instrucción | Operación | | Instruction | Operation |
|-------------|-----------| | ----------- | --------- |
| `MUL` | Producto entero (32 bits bajos) | | `MUL` | Integer product (low 32 bits) |
| `MULH` | Producto con signo (32 bits altos) | | `MULH` | Signed product (high 32 bits) |
| `MULHSU` | Producto mixto firmado×sin firma (altos) | | `MULHSU` | Mixed signed×unsigned product (high bits) |
| `MULHU` | Producto sin firma (32 bits altos) | | `MULHU` | Unsigned product (high 32 bits) |
| `DIV` | División entera con signo | | `DIV` | Signed integer division |
| `DIVU` | División entera sin firma | | `DIVU` | Unsigned integer division |
| `REM` | Resto con signo | | `REM` | Signed remainder |
| `REMU` | Resto sin firma | | `REMU` | Unsigned remainder |
### RV32C — Instrucciones comprimidas (16 bits) ### RV32C — Compressed Instructions (16-bit)
Todas las instrucciones de 16 bits del estándar C son soportadas. Se detectan por `(halfword & 3) !== 3` y se descomprimen a su equivalente RV32I antes de ejecutar. Esto es crítico: el compilador GCC para ESP32-C3 genera intensamente instrucciones C (`c.addi`, `c.sw`, `c.lw`, `c.j`, `c.beqz`, `c.bnez`, etc.) que representan ~30-40% de todas las instrucciones en el binario final. All 16-bit instructions from the standard C extension are supported. They are detected by `(halfword & 3) !== 3` and decompressed to their RV32I equivalent before execution. This is critical: the GCC compiler for ESP32-C3 heavily generates C instructions (`c.addi`, `c.sw`, `c.lw`, `c.j`, `c.beqz`, `c.bnez`, etc.) which represent ~30-40% of all instructions in the final binary.
--- ---
## 8. GPIO ## 8. GPIO
El manejo de GPIO sigue el modelo de registros W1TS/W1TC del ESP32-C3: GPIO handling follows the W1TS/W1TC register model of the ESP32-C3:
```typescript ```typescript
// Sketch Arduino: // Arduino sketch:
digitalWrite(8, HIGH); // → internamente escribe 1<<8 a GPIO_OUT_W1TS_REG digitalWrite(8, HIGH); // → internally writes 1<<8 to GPIO_OUT_W1TS_REG
// En el simulador: // In the simulator:
// SW x10, 0(x12) donde x10=256 (1<<8), x12=0x60004008 (W1TS) // SW x10, 0(x12) where x10=256 (1<<8), x12=0x60004008 (W1TS)
// → escribe 4 bytes a 0x60004008..0x6000400B // → writes 4 bytes to 0x60004008..0x6000400B
// → byteIdx=1 (offset 0x09): val=0x01, shift=8 → gpioOut |= 0x100 // → byteIdx=1 (offset 0x09): val=0x01, shift=8 → gpioOut |= 0x100
// → changed = prev ^ gpioOut ≠ 0 → dispara onPinChangeWithTime(8, true, timeMs) // → changed = prev ^ gpioOut ≠ 0 → fires onPinChangeWithTime(8, true, timeMs)
``` ```
El callback `onPinChangeWithTime(pin, state, timeMs)` es el punto de integración con los componentes visuales. `timeMs` es el tiempo simulado en milisegundos (calculado como `core.cycles / CPU_HZ * 1000`). The callback `onPinChangeWithTime(pin, state, timeMs)` is the integration point with the visual components. `timeMs` is the simulated time in milliseconds (calculated as `core.cycles / CPU_HZ * 1000`).
--- ---
## 9. UART0 — Serial Monitor ## 9. UART0 — Serial Monitor
Cualquier byte escrito a `UART0_FIFO_REG` (0x60000000) llama al callback `onSerialData(char)`: Any byte written to `UART0_FIFO_REG` (0x60000000) calls the `onSerialData(char)` callback:
```cpp ```cpp
// Sketch Arduino: // Arduino sketch:
Serial.println("Hola!"); Serial.println("Hello!");
// → Arduino framework escribe los bytes de "Hola!\r\n" a UART0_FIFO_REG // → Arduino framework writes the bytes of "Hello!\r\n" to UART0_FIFO_REG
// → simulador llama onSerialData("H"), onSerialData("o"), ... // → simulator calls onSerialData("H"), onSerialData("e"), ...
// → Serial Monitor muestra "Hola!" // → Serial Monitor displays "Hello!"
``` ```
Para enviar datos al sketch desde el Serial Monitor: To send data to the sketch from the Serial Monitor:
```typescript ```typescript
sim.serialWrite("COMANDO\n"); sim.serialWrite("COMMAND\n");
// → bytes se añaden a rxFifo // → bytes are added to rxFifo
// → lectura de UART0_FIFO_REG dequeue un byte del rxFifo // → reading UART0_FIFO_REG dequeues one byte from rxFifo
``` ```
--- ---
## 10. Limitaciones ## 10. Limitations
### Framework ESP-IDF / Arduino ### ESP-IDF / Arduino Framework
El framework Arduino para ESP32-C3 (basado en ESP-IDF 4.4.x) tiene una secuencia de inicialización compleja que accede a periféricos no emulados: The Arduino framework for ESP32-C3 (based on ESP-IDF 4.4.x) has a complex initialization sequence that accesses non-emulated peripherals:
| Periférico | Por qué lo accede ESP-IDF | Efecto en emulador | | Peripheral | Why ESP-IDF accesses it | Effect in emulator |
|------------|--------------------------|-------------------| | ---------- | ----------------------- | ------------------ |
| Cache controller | Configura MMU para mapeo flash/DRAM | Lee 0, puede que no loop | | Cache controller | Configures MMU for flash/DRAM mapping | Reads 0, may not loop |
| Interrupt Matrix | Registra vectores ISR | Sin efecto (silenciado) | | Interrupt Matrix | Registers ISR vectors | No effect (silenced) |
| System registers | Configura PLLs y clocks | Lee 0 (asume velocidad por defecto) | | System registers | Configures PLLs and clocks | Reads 0 (assumes default speed) |
| FreeRTOS tick timer | Timer 0 → interrupción periódica | Sin interrupción = tareas no se planifican | | FreeRTOS tick timer | Timer 0 → periodic interrupt | No interrupt = tasks not scheduled |
Como resultado, un sketch Arduino compilado con el framework completo puede ejecutarse parcialmente — el código anterior a la inicialización de FreeRTOS puede funcionar, pero `setup()` y `loop()` dependen de que FreeRTOS esté corriendo. As a result, an Arduino sketch compiled with the full framework may execute partially — code prior to FreeRTOS initialization may work, but `setup()` and `loop()` depend on FreeRTOS running.
**Escenarios que SÍ funcionan:** **Scenarios that DO work:**
- Código bare-metal (sin framework, acceso directo a GPIO MMIO) - Bare-metal code (no framework, direct GPIO MMIO access)
- Fragmentos de código que no usen FreeRTOS (`delay()`, `millis()`, `digitalWrite()` requieren FreeRTOS) - Code fragments that do not use FreeRTOS (`delay()`, `millis()`, `digitalWrite()` require FreeRTOS)
- Programas de prueba de ISA (operaciones aritméticas, branches, loads/stores a DRAM) - ISA test programs (arithmetic operations, branches, loads/stores to DRAM)
**Roadmap para soporte completo:** **Roadmap for full support:**
1. Stub del cache controller (devolver valores que indiquen "cache ya configurada") 1. Cache controller stub (return values indicating "cache already configured")
2. Stub del interrupt matrix (aceptar writes, ignorar) 2. Interrupt matrix stub (accept writes, ignore)
3. Timer peripheral básico (generar tick FreeRTOS periódicamente) 3. Basic timer peripheral (generate FreeRTOS tick periodically)
4. Una vez activo FreeRTOS: sketches Arduino normales deberían funcionar 4. Once FreeRTOS is active: normal Arduino sketches should work
### Otras limitaciones ### Other Limitations
| Limitación | Detalle | | Limitation | Detail |
|------------|---------| | ---------- | ------ |
| Sin WiFi | El ESP32-C3 tiene radio BLE/WiFi; no emulada | | No WiFi | ESP32-C3 has BLE/WiFi radio; not emulated |
| Sin ADC | GPIO 0-5 como ADC no implementado | | No ADC | GPIO 0-5 as ADC not implemented |
| Sin SPI/I2C hardware | Los periféricos hardware SPI/I2C retornan 0 | | No hardware SPI/I2C | Hardware SPI/I2C peripherals return 0 |
| Sin interrupciones | `attachInterrupt()` no funciona | | No interrupts | `attachInterrupt()` does not work |
| Sin RTC | `esp_sleep_*`, `rtc_*` no implementados | | No RTC | `esp_sleep_*`, `rtc_*` not implemented |
| Sin NVS/Flash writes | `Preferences`, `SPIFFS` no implementados | | No NVS/Flash writes | `Preferences`, `SPIFFS` not implemented |
--- ---
## 11. Tests ## 11. Tests
Los tests de la emulación RISC-V están en `frontend/src/__tests__/`: RISC-V emulation tests are in `frontend/src/__tests__/`:
```bash ```bash
cd frontend cd frontend
@ -375,76 +375,76 @@ npm test -- esp32c3
### `esp32c3-simulation.test.ts` — 30 tests (ISA unit tests) ### `esp32c3-simulation.test.ts` — 30 tests (ISA unit tests)
Verifica directamente el decodificador de instrucciones de `RiscVCore`: Directly verifies the instruction decoder in `RiscVCore`:
| Grupo | Tests | Qué verifica | | Group | Tests | What it verifies |
|-------|-------|--------------| | ----- | ----- | ---------------- |
| RV32M | 8 | MUL, MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU | | RV32M | 8 | MUL, MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU |
| RV32C | 7 | C.ADDI, C.LI, C.LWSP, C.SWSP, C.MV, C.ADD, C.J, C.BEQZ | | RV32C | 7 | C.ADDI, C.LI, C.LWSP, C.SWSP, C.MV, C.ADD, C.J, C.BEQZ |
| UART | 3 | Escritura a FIFO → onSerialData, lectura de RX, múltiples bytes | | UART | 3 | Write to FIFO → onSerialData, RX read, multiple bytes |
| GPIO | 8 | W1TS set bit, W1TC clear bit, toggle, timestamp, múltiples pines | | GPIO | 8 | W1TS set bit, W1TC clear bit, toggle, timestamp, multiple pins |
| Lifecycle | 4 | reset(), start/stop, loadHex básico | | Lifecycle | 4 | reset(), start/stop, basic loadHex |
### `esp32c3-blink.test.ts` — 8 tests (integración end-to-end) ### `esp32c3-blink.test.ts` — 8 tests (end-to-end integration)
Compila `blink.c` con `riscv32-esp-elf-gcc` (el toolchain de arduino-cli) y verifica la ejecución en el simulador: Compiles `blink.c` with `riscv32-esp-elf-gcc` (the arduino-cli toolchain) and verifies execution in the simulator:
| Test | Qué verifica | | Test | What it verifies |
|------|-------------| | ---- | ---------------- |
| `build.sh produces blink.bin` | El toolchain compila correctamente | | `build.sh produces blink.bin` | Toolchain compiles correctly |
| `binary starts with valid RV32 instruction` | El entry point es código RISC-V válido | | `binary starts with valid RV32 instruction` | Entry point is valid RISC-V code |
| `loadBin() resets PC to 0x42000000` | Carga correcta en flash | | `loadBin() resets PC to 0x42000000` | Correct loading into flash |
| `GPIO 8 goes HIGH after first SW` | Primer toggle correcto | | `GPIO 8 goes HIGH after first SW` | First toggle correct |
| `GPIO 8 toggles ON and OFF` | 7 toggles en 2000 pasos (4 ON, 3 OFF) | | `GPIO 8 toggles ON and OFF` | 7 toggles in 2000 steps (4 ON, 3 OFF) |
| `PinManager.setPinState called` | Integración con el sistema de componentes | | `PinManager.setPinState called` | Integration with the component system |
| `timestamps increase monotonically` | El tiempo simulado es consistente | | `timestamps increase monotonically` | Simulated time is consistent |
| `reset() clears GPIO state` | Reset funcional | | `reset() clears GPIO state` | Functional reset |
**Resultado esperado:** **Expected result:**
``` ```
✓ esp32c3-simulation.test.ts (30 tests) ~500ms ✓ esp32c3-simulation.test.ts (30 tests) ~500ms
✓ esp32c3-blink.test.ts (8 tests) ~300ms ✓ esp32c3-blink.test.ts (8 tests) ~300ms
``` ```
### Binario de prueba bare-metal ### Bare-Metal Test Binary
``` ```
frontend/src/__tests__/fixtures/esp32c3-blink/ frontend/src/__tests__/fixtures/esp32c3-blink/
├── blink.c ← código fuente bare-metal ├── blink.c ← bare-metal source code
├── link.ld ← linker script (IROM @ 0x42000000, DRAM @ 0x3FC80000) ├── link.ld ← linker script (IROM @ 0x42000000, DRAM @ 0x3FC80000)
├── build.sh ← script de compilación (usa toolchain de arduino-cli) ├── build.sh ← build script (uses arduino-cli toolchain)
├── blink.elf ← (generado) ELF con debug info ├── blink.elf ← (generated) ELF with debug info
├── blink.bin ← (generado) binario raw de 58 bytes ├── blink.bin ← (generated) raw 58-byte binary
└── blink.dis ← (generado) desensamblado para inspección └── blink.dis ← (generated) disassembly for inspection
``` ```
--- ---
## 12. Diferencias vs emulación Xtensa (ESP32 / ESP32-S3) ## 12. Differences vs Xtensa Emulation (ESP32 / ESP32-S3)
| Aspecto | ESP32-C3 (RISC-V) | ESP32 / ESP32-S3 (Xtensa) | | Aspect | ESP32-C3 (RISC-V) | ESP32 / ESP32-S3 (Xtensa) |
|---------|-------------------|--------------------------| | ------ | ----------------- | ------------------------- |
| Motor | `Esp32C3Simulator` (TypeScript, navegador) | `Esp32Bridge` + backend QEMU | | Engine | `Esp32C3Simulator` (TypeScript, browser) | `Esp32Bridge` + backend QEMU |
| Dependencia backend | **No** — 100% en el navegador | Sí — WebSocket a proceso QEMU | | Backend dependency | **No** — 100% in the browser | Yes — WebSocket to QEMU process |
| Arranque | Instantáneo | ~1-2 segundos | | Startup | Instant | ~1-2 seconds |
| GPIO | Via MMIO W1TS/W1TC | Via QEMU callbacks → WebSocket | | GPIO | Via MMIO W1TS/W1TC | Via QEMU callbacks → WebSocket |
| WiFi | No emulada | Emulada (SSIDs hardcoded) | | WiFi | Not emulated | Emulated (hardcoded SSIDs) |
| I2C/SPI hardware | No emulado | Emulado (callbacks síncronos) | | Hardware I2C/SPI | Not emulated | Emulated (synchronous callbacks) |
| LEDC/PWM | No emulado | Emulado (poll periódico) | | LEDC/PWM | Not emulated | Emulated (periodic polling) |
| NeoPixel/RMT | No emulado | Emulado (decodificador RMT) | | NeoPixel/RMT | Not emulated | Emulated (RMT decoder) |
| Arduino framework | Parcial (FreeRTOS no activo) | Completo | | Arduino framework | Partial (FreeRTOS not active) | Full |
| Tests CI | Sí (Vitest) | No (requiere lib nativa) | | CI tests | Yes (Vitest) | No (requires native lib) |
--- ---
## 13. Archivos clave ## 13. Key Files
| Archivo | Descripción | | File | Description |
|---------|-------------| | ---- | ----------- |
| `frontend/src/simulation/RiscVCore.ts` | Núcleo del emulador RV32IMC (I + M + C extensions) | | `frontend/src/simulation/RiscVCore.ts` | RV32IMC emulator core (I + M + C extensions) |
| `frontend/src/simulation/Esp32C3Simulator.ts` | Mapa de memoria ESP32-C3, GPIO, UART0, ciclo RAF | | `frontend/src/simulation/Esp32C3Simulator.ts` | ESP32-C3 memory map, GPIO, UART0, RAF loop |
| `frontend/src/utils/esp32ImageParser.ts` | Parser del formato imagen ESP32 (merged flash → segmentos) | | `frontend/src/utils/esp32ImageParser.ts` | ESP32 image format parser (merged flash → segments) |
| `frontend/src/store/useSimulatorStore.ts` | `ESP32_RISCV_KINDS`, `createSimulator()`, `compileBoardProgram()` | | `frontend/src/store/useSimulatorStore.ts` | `ESP32_RISCV_KINDS`, `createSimulator()`, `compileBoardProgram()` |
| `frontend/src/__tests__/esp32c3-simulation.test.ts` | Unit tests ISA (30 tests) | | `frontend/src/__tests__/esp32c3-simulation.test.ts` | ISA unit tests (30 tests) |
| `frontend/src/__tests__/esp32c3-blink.test.ts` | Integration test end-to-end (8 tests) | | `frontend/src/__tests__/esp32c3-blink.test.ts` | End-to-end integration test (8 tests) |
| `frontend/src/__tests__/fixtures/esp32c3-blink/` | Firmware bare-metal de prueba + toolchain script | | `frontend/src/__tests__/fixtures/esp32c3-blink/` | Bare-metal test firmware + toolchain script |