li t0, 3 loop: addi t0, t0, -1 bnez t0, loop
El bucle carga 3 en t0 y resta 1 hasta que t0 vale 0. Parece trivial, pero contiene los tres temas: una instrucción con inmediato negativo, un salto condicional hacia atrás y una dependencia entre instrucciones consecutivas.
1. Codificación: bnez es un bne con desplazamiento negativo
bnez no existe en la ISA: es una pseudoinstrucción que el ensamblador traduce a bne t0, zero, loop. Como loop está 4 bytes antes del salto, el desplazamiento es −4 y la palabra resultante es 0xFE029EE3. En el formato B el inmediato está repartido en cuatro trozos y su bit 0 no se guarda, porque los destinos siempre son pares:
| Campo | Bits | Binario | Significado |
|---|---|---|---|
| imm[12] | [31] | 1 | trozo del inmediato |
| imm[10:5] | [30:25] | 111111 | trozo del inmediato |
| rs2 | [24:20] | 00000 | registro x0 (zero) |
| rs1 | [19:15] | 00101 | registro x5 (t0) |
| funct3 | [14:12] | 001 | selecciona la operación dentro del grupo |
| imm[4:1] | [11:8] | 1110 | trozo del inmediato |
| imm[11] | [7] | 1 | trozo del inmediato |
| opcode | [6:0] | 1100011 | grupo BRANCH |
| imm | -4 (0xFFFFFFFC) | inmediato reconstruido (con signo) | |
| Instrucción | bne t0, zero, -4 | ||
Uniendo imm[12], imm[11], imm[10:5] e imm[4:1] y añadiendo el 0 final sale 1 1 111111 1110 0 = −4. Ábrelo en el decodificador.
Los seis formatos de un vistazo
| Formato | Campos (bit 31 → bit 0) | Se usa para |
|---|---|---|
| R | funct7 · rs2 · rs1 · funct3 · rd · opcode | operaciones entre registros (add, sub, and, sll…) |
| I | imm[11:0] · rs1 · funct3 · rd · opcode | operaciones con inmediato, cargas y jalr |
| S | imm[11:5] · rs2 · rs1 · funct3 · imm[4:0] · opcode | almacenamientos (sw, sh, sb) |
| B | imm[12|10:5] · rs2 · rs1 · funct3 · imm[4:1|11] · opcode | saltos condicionales (beq, bne, blt…) |
| U | imm[31:12] · rd · opcode | lui y auipc |
| J | imm[20|10:1|11|19:12] · rd · opcode | jal |
Fíjate en que rs1, rs2 y rd ocupan siempre las mismas posiciones. Es una decisión de diseño de RISC-V: el hardware puede empezar a leer los registros antes de saber qué instrucción es, a cambio de repartir los bits del inmediato.
2. Ejecución: seguir el PC
Al ejecutar, cada instrucción cambia registros y el PC. bnez no escribe ningún registro: solo decide si el PC vuelve a loop o sigue. El cuerpo se ejecuta 3 veces, así que se ejecutan 1 + 3 × 2 = 7 instrucciones y t0 termina en 0. En el depurador, pulsa «Paso» y observa cómo el PC salta de 0x00000008 a 0x00000004 dos veces.
3. Pipeline: lo que cuesta un salto
En un pipeline de 5 etapas el salto no se resuelve hasta EX. Para entonces ya se han buscado las dos instrucciones siguientes, y si el salto se toma hay que descartarlas. El simulador trata todos los saltos como tomados, así que el diagrama muestra exactamente esa penalización de 2 ciclos. La dependencia de bne con addi (t0) no cuesta nada gracias al forwarding.
| Etapa | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| IF | I0 | I1 | I2 | I3 | I2 | I3 | — | — | — | — |
| ID | — | I0 | I1 | I2 | FLUSH | I2 | I3 | — | — | — |
| EX | — | — | I0 | I1 | FLUSH | FLUSH | I2 | I3 | — | — |
| MEM | — | — | — | I0 | I1 | FLUSH | FLUSH | I2 | I3 | — |
| WB | — | — | — | — | I0 | I1 | FLUSH | FLUSH | I2 | I3 |
Abre este ejemplo en el simulador de pipeline.
Itinerario recomendado
- Guía: cómo decodificar una instrucción RV32I y los ejercicios de decodificación y formatos.
- Guía: del código máquina al ensamblador, que traduce una rutina completa con saltos y etiquetas.
- Depurador y los ejercicios de traza de registros y bucles.
- Guía: el pipeline de 5 etapas y riesgos RAW, forwarding y burbujas, con el ejercicio de carga-uso.