Guía · RISC-V

Cómo decodificar una instrucción RV32I

Un método en cinco pasos y un ejemplo de cada formato (R, I, S, B, U y J), con la reconstrucción de los inmediatos y los errores más habituales.

Revisada el con las herramientas del sitio.

Decodificar a mano una instrucción RV32I es un procedimiento mecánico. Si siempre sigues el mismo orden, los formatos con los bits «desordenados» dejan de ser un problema. Esta guía da el método y lo aplica a una instrucción de cada formato. Todas las tablas las genera el decodificador del sitio.

El método en cinco pasos

  1. Pasa la palabra a binario y numera los bits del 31 (izquierda) al 0 (derecha). Cada dígito hexadecimal son 4 bits.
  2. Lee el opcode (bits 6–0). Decide el grupo y, con él, el formato. Sin este paso no sabes qué significan los demás bits.
  3. Lee los campos fijos que tenga ese formato: rd (11–7), funct3 (14–12), rs1 (19–15), rs2 (24–20) y funct7 (31–25).
  4. Recompón el inmediato juntando sus trozos en el orden que marca el formato y extiende el signo desde el bit 31.
  5. Elige el mnemónico con funct3 (y funct7 cuando exista) y escribe la instrucción con los nombres ABI de los registros.

Tabla de opcodes de RV32I

opcodeGrupoFormatoInstrucciones
0110011OPRadd, sub, and, or, xor, sll, srl, sra, slt, sltu
0010011OP-IMMIaddi, andi, ori, xori, slti, sltiu, slli, srli, srai
0000011LOADIlb, lh, lw, lbu, lhu
1100111JALRIjalr
0100011STORESsb, sh, sw
1100011BRANCHBbeq, bne, blt, bge, bltu, bgeu
0110111LUIUlui
0010111AUIPCUauipc
1101111JALJjal

Faltan tres instrucciones de la base: FENCE (0001111) y ECALL/EBREAK (1110011). El decodificador del sitio no las reconoce.

Formato R: 0x40B50533

Opcode 0110011 → grupo OP. En formato R no hay inmediato: los 32 bits se reparten entre dos registros fuente, uno destino y dos campos de función. funct3 = 000 corresponde a add o sub, y funct7 decide: 0100000 es sub.

0x40B50533 · R-Type
CampoBitsBinarioSignificado
funct7[31:25]0100000distingue variantes (0100000 = sub/sra)
rs2[24:20]01011registro x11 (a1)
rs1[19:15]01010registro x10 (a0)
funct3[14:12]000selecciona la operación dentro del grupo
rd[11:7]01010registro x10 (a0)
opcode[6:0]0110011grupo OP
Instrucciónsub a0, a0, a1

Formato I: 0xFFF28293

Opcode 0010011 → OP-IMM. Los bits 31–20 son el inmediato completo: 111111111111. Como el bit 31 es 1, al extenderlo a 32 bits se rellena con unos y el valor es −1. Resultado: addi t0, t0, -1.

0xFFF28293 · I-Type
CampoBitsBinarioSignificado
imm[11:0][31:20]111111111111trozo del inmediato
rs1[19:15]00101registro x5 (t0)
funct3[14:12]000selecciona la operación dentro del grupo
rd[11:7]00101registro x5 (t0)
opcode[6:0]0010011grupo OP-IMM
imm-1 (0xFFFFFFFF)inmediato reconstruido (con signo)
Instrucciónaddi t0, t0, -1

En los desplazamientos con inmediato (slli, srli, srai) los bits 24–20 son la cantidad de desplazamiento y los bits 31–25 hacen de funct7: 0100000 distingue srai de srli.

Formato S: 0xFEA42E23

Opcode 0100011 → STORE. Un almacenamiento no escribe ningún registro, así que el hueco de rd (bits 11–7) se usa para los 5 bits bajos del inmediato. El resto está en los bits 31–25: imm[11:5] = 1111111 e imm[4:0] = 11100. Juntos: 111111111100 = −4.

0xFEA42E23 · S-Type
CampoBitsBinarioSignificado
imm[11:5][31:25]1111111trozo del inmediato
rs2[24:20]01010registro x10 (a0)
rs1[19:15]01000registro x8 (s0)
funct3[14:12]010selecciona la operación dentro del grupo
imm[4:0][11:7]11100trozo del inmediato
opcode[6:0]0100011grupo STORE
imm-4 (0xFFFFFFFC)inmediato reconstruido (con signo)
Instrucciónsw a0, -4(s0)

La instrucción es sw a0, -4(s0): guarda a0 en la dirección s0 − 4. Fíjate en que rs2 (a0) es el dato y rs1 (s0) la base de la dirección.

Formato B: 0x00050663

Opcode 1100011 → BRANCH. El inmediato de un salto está en múltiplos de 2 bytes, así que su bit 0 no se guarda. Los otros 12 bits se reparten en cuatro trozos: imm[12] en el bit 31, imm[10:5] en 30–25, imm[4:1] en 11–8 e imm[11] en el bit 7.

0x00050663 · B-Type
CampoBitsBinarioSignificado
imm[12][31]0trozo del inmediato
imm[10:5][30:25]000000trozo del inmediato
rs2[24:20]00000registro x0 (zero)
rs1[19:15]01010registro x10 (a0)
funct3[14:12]000selecciona la operación dentro del grupo
imm[4:1][11:8]0110trozo del inmediato
imm[11][7]0trozo del inmediato
opcode[6:0]1100011grupo BRANCH
imm12 (0xC)inmediato reconstruido (con signo)
Instrucciónbeq a0, zero, 12

Ordenados: imm[12] = 0, imm[11] = 0, imm[10:5] = 000000, imm[4:1] = 0110, y el 0 implícito al final: 0 0 000000 0110 0 = 12. beq a0, zero, 12 salta 12 bytes (3 instrucciones) hacia delante si a0 vale 0.

Formato U: 0x123452B7

Opcode 0110111 → LUI. Los 20 bits altos son el inmediato y van directamente a los bits 31–12 del registro destino; los 12 bits bajos quedan a cero. Aquí t0 pasa a valer 0x12345000.

0x123452B7 · U-Type
CampoBitsBinarioSignificado
imm[31:12][31:12]00010010001101000101trozo del inmediato
rd[11:7]00101registro x5 (t0)
opcode[6:0]0110111grupo LUI
imm74565 (0x12345)inmediato reconstruido (con signo)
Instrucciónlui t0, 0x12345

Formato J: 0x010000EF

Opcode 1101111 → JAL. Como en B, el bit 0 no se guarda y el resto se reparte: imm[20] en el bit 31, imm[10:1] en 30–21, imm[11] en el bit 20 e imm[19:12] en 19–12. Aquí solo imm[4] vale 1: el salto es de 16 bytes y la dirección de retorno se guarda en ra.

0x010000EF · J-Type
CampoBitsBinarioSignificado
imm[20][31]0trozo del inmediato
imm[10:1][30:21]0000001000trozo del inmediato
imm[11][20]0trozo del inmediato
imm[19:12][19:12]00000000trozo del inmediato
rd[11:7]00001registro x1 (ra)
opcode[6:0]1101111grupo JAL
imm16 (0x10)inmediato reconstruido (con signo)
Instrucciónjal ra, 16

¿Por qué los inmediatos están repartidos?

RISC-V fija dos cosas en todos los formatos: rs1, rs2 y rd están siempre en las mismas posiciones, y el bit de signo del inmediato es siempre el bit 31. Así el hardware puede leer los registros antes de saber qué instrucción es y la extensión de signo usa siempre el mismo cable. El precio es que los inmediatos de S, B y J quedan troceados. Entre S y B cambia muy poco: los bits se reutilizan desplazados una posición, porque en B el bit 0 no hace falta.

Errores habituales

  • Leer los bits 31–20 como inmediato sin haber mirado antes el opcode: solo vale en formato I.
  • Olvidar el 0 implícito al final de los inmediatos B y J (el desplazamiento sale la mitad).
  • No extender el signo: 0xFFF en un inmediato I es −1, no 4095.
  • En lui, olvidar que el inmediato ocupa los bits altos: lui t0, 1 carga 4096, no 1.
  • Confundir rs1 y rs2 en los almacenamientos: el dato es rs2 y la base es rs1.

Practica lo que has leído

Decodifica una instrucción tipo R

Identifica los campos de 0x40B50533. Presta atención a funct7: es lo que distingue add de sub.

0x40B50533

Abrir en la herramienta

Decodifica una instrucción con inmediato negativo

Pasa 0xFFF28293 a binario y separa sus campos. Indica el formato, el opcode, los registros, funct3 y el valor decimal del inmediato.

0xFFF28293

Abrir en la herramienta

Identifica el formato a partir del opcode

Mira solo los 7 bits bajos de cada palabra (el último dígito hexadecimal y medio) y di a qué formato pertenece.

Abrir en la herramienta

Referencias