This cheat sheet provides a handy guide to 32-bit RISC-V instructions. I’ve aimed it at software developers, so group instructions by purpose and include common pseudoinstructions. Clicking on a Guide link takes you to the relevant section of the Project F RISC-V assembler guide for instruction explanation and examples.
Instructions are from the base integer instruction set (RV32I) unless otherwise noted.
Share your thoughts with @WillFlux on Mastodon or X. If you like what I do, sponsor me. 🙏
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
add | Add | add rd, rs1, rs2 | rd = rs1 + rs2 | arithmetic |
addi | Add Immediate | addi rd, rs1, imm | rd = rs1 + imm | arithmetic |
neg | Negate (p) | neg rd, rs2 | rd = -rs2 | arithmetic |
sub | Subtract | sub rd, rs1, rs2 | rd = rs1 - rs2 | arithmetic |
mul | Multiply | mul rd, rs1, rs2 | rd = (rs1 * rs2)[31:0] | multiply |
mulh | Multiply High | mulh rd, rs1, rs2 | rd = (rs1 * rs2)[63:32] | multiply |
mulhu | Multiply High Unsigned | mulhu rd, rs1, rs2 | rd = (rs1 * rs2)[63:32] | multiply |
mulhsu | Multiply High Signed Unsigned | mulhsu rd, rs1, rs2 | rd = (rs1 * rs2)[63:32] | multiply |
div | Divide | div rd, rs1, rs2 | rd = rs1 / rs2 | divide |
rem | Remainder | rem rd, rs1, rs2 | rd = rs1 % rs2 | divide |
Use addi for subtract immediate too. Multiply and divide instructions require the M extension.
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
and | AND | and rd, rs1, rs2 | rd = rs1 & rs2 | logical |
andi | AND Immediate | andi rd, rs1, imm | rd = rs1 & imm | logical |
not | NOT (p) | not rd, rs1 | rd = ~rs1 | logical |
or | OR | or rd, rs1, rs2 | rd = rs1 | rs2 | logical |
ori | OR Immediate | ori rd, rs1, imm | rd = rs1 | imm | logical |
xor | XOR | xor rd, rs1, rs2 | rd = rs1 ^ rs2 | logical |
xori | XOR Immediate | xori rd, rs1, imm | rd = rs1 ^ imm | logical |
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
sll | Shift Left Logical | sll rd, rs1, rs2 | rd = rs1 | shift |
slli | Shift Left Logical Immediate | slli rd, rs1, imm | rd = rs1 | shift |
srl | Shift Right Logical | srl rd, rs1, rs2 | rd = rs1 >> rs2 | shift |
srli | Shift Right Logical Immediate | srli rd, rs1, imm | rd = rs1 >> imm | shift |
sra | Shift Right Arithmetic | sra rd, rs1, rs2 | rd = rs1 >>> rs2 | shift |
srai | Shift Right Arithmetic Immediate | srai rd, rs1, imm | rd = rs1 >>> imm | shift |
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
li | Load Immediate (p) | li rd, imm | rd = imm | arithmetic |
lui | Load Upper Immediate | lui rd, imm | rd = imm | arithmetic |
auipc | Add Upper Immediate to PC | auipc rd, imm | rd = pc + (imm | branch |
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
lw | Load Word | lw rd, imm(rs1) | rd = mem[rs1+imm] | load |
lh | Load Half | lh rd, imm(rs1) | rd = mem[rs1+imm][0:15] | load |
lhu | Load Half Unsigned | lhu rd, imm(rs1) | rd = mem[rs1+imm][0:15] | load |
lb | Load Byte | lb rd, imm(rs1) | rd = mem[rs1+imm][0:7] | load |
lbu | Load Byte Unsigned | lbu rd, imm(rs1) | rd = mem[rs1+imm][0:7] | load |
la | Load Symbol Address (p) | la rd, symbol | rd = &symbol | load |
sw | Store Word | sw rs2, imm(rs1) | mem[rs1+imm] = rs2 | store |
sh | Store Half | sh rs2, imm(rs1) | mem[rs1+imm][0:15] = rs2 | store |
sb | Store Byte | sb rs2, imm(rs1) | mem[rs1+imm][0:7] = rs2 | store |
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
j | Jump (p) | j imm | pc += imm | jump |
jal | Jump and Link | jal rd, imm | rd = pc+4; pc += imm | jump |
jalr | Jump and Link Register | jalr rd, rs1, imm | rd = pc+4; pc = rs1+imm | jump |
call | Call Function (p) | call symbol | ra = pc+4; pc = &symbol | function |
ret | Return from Function (p) | ret | pc = ra | function |
You can use a label in place of a jump immediate, for example: j label_name
This page lists all branch instructions but you may prefer the branch instruction summary.
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
beq | Branch Equal | beq rs1, rs2, imm | if(rs1 == rs2) pc += imm | branch |
beqz | Branch Equal Zero (p) | beqz rs1, imm | if(rs1 == 0) pc += imm | branch |
bne | Branch Not Equal | bne rs1, rs2, imm | if(rs1 ≠ rs2) pc += imm | branch |
bnez | Branch Not Equal Zero (p) | bnez rs1, rs2, imm | if(rs1 ≠ 0) pc += imm | branch |
blt | Branch Less Than | blt rs1, rs2, imm | if(rs1 < rs2) pc += imm | branch |
bltu | Branch Less Than Unsigned | bltu rs1, rs2, imm | if(rs1 < rs2) pc += imm | branch |
bltz | Branch Less Than Zero (p) | bltz rs1, imm | if(rs1 < 0) pc += imm | branch |
bgt | Branch Greater Than (p) | bgt rs1, rs2, imm | if(rs1 > rs2) pc += imm | branch |
bgtu | Branch Greater Than Unsigned (p) | bgtu rs1, rs2, imm | if(rs1 > rs2) pc += imm | branch |
bgtz | Branch Greater Than Zero (p) | bgtz rs1, imm | if(rs1 > 0) pc += imm | branch |
ble | Branch Less or Equal (p) | ble rs1, rs2, imm | if(rs1 ≤ rs2) pc += imm | branch |
bleu | Branch Less or Equal Unsigned (p) | bleu rs1, rs2, imm | if(rs1 ≤ rs2) pc += imm | branch |
blez | Branch Less or Equal Zero (p) | blez rs1, imm | if(rs1 ≤ 0) pc += imm | branch |
bge | Branch Greater or Equal | bge rs1, rs2, imm | if(rs1 ≥ rs2) pc += imm | branch |
bgeu | Branch Greater or Equal Unsigned | bgeu rs1, rs2, imm | if(rs1 ≥ rs2) pc += imm | branch |
bgez | Branch Greater or Equal Zero (p) | bgez rs1, imm | if(rs1 ≥ 0) pc += imm | branch |
You can use a label in place of a branch immediate, for example: beq t0, t1, label_name
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
slt | Set Less Than | slt rd, rs1, rs2 | rd = (rs1 < rs2) | set |
slti | Set Less Than Immediate | slti rd, rs1, imm | rd = (rs1 < imm) | set |
sltu | Set Less Than Unsigned | sltu rd, rs1, rs2 | rd = (rs1 < rs2) | set |
sltiu | Set Less Than Immediate Unsigned | sltui rd, rs1, imm | rd = (rs1 < imm) | set |
seqz | Set Equal Zero | seqz rd, rs1 | rd = (rs1 == 0) | set |
snez | Set Not Equal Zero | snez rd, rs1 | rd = (rs1 ≠ 0) | set |
sltz | Set Less Than Zero | sltz rd, rs1 | rd = (rs < 0) | set |
sgtz | Set Greater Than Zero | sgtz rd, rs1 | rd = (rs1 > 0) | set |
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
rdcycle | CPU Cycle Count (p) | rdcycle rd | rd = csr_cycle[31:0] | not yet avail |
rdcycleh | CPU Cycle Count High (p) | rdcycleh rd | rd = csr_cycle[63:32] | not yet avail |
rdtime | Current Time (p) | rdtime rd | rd = csr_time[31:0] | not yet avail |
rdtimeh | Current Time High (p) | rdtimeh rd | rd = csr_time[63:32] | not yet avail |
rdinstret | CPU Instructions Retired (p) | rdinstret rd | rd = csr_instret[31:0] | not yet avail |
rdinstreth | CPU Instructions Retired High (p) | rdinstreth rd | rd = csr_instret[63:32] | not yet avail |
The counter instructions require the Zicsr extension but were originally part of the base instruction set.
Instr | Description | Use | Result | Guide |
---|---|---|---|---|
ebreak | Environment Break (Debugger Call) | ebreak | - | not yet avail |
ecall | Environment Call (OS Function) | ecall | - | not yet avail |
fence | I/O Ordering | fence | - | not yet avail |
mv | Copy Register (p) | mv rd, rs1 | rd = rs1 | arithmetic |
nop | No Operation (p) | nop | - | arithmetic |
The fence instruction requires the Zifencei extension but was originally part of the base instruction set.
Check out all my FPGA & RISC-V Tutorials and my series on early Macintosh History.
If you enjoyed this post, please sponsor me. Sponsors help me create more FPGA and RISC-V projects for everyone, and they get early access to blog posts and source code. 🙏