RV32I simulator in C++.
https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf#page=22
https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf#page=121
https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf#page=116
https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf#page=116
- note that instruction classifiction is based on not only opcode but also with funct7 and fucnt3.
- also, RISC-V contains pseudo-instructions that is more complex instructions that will be translated into within RISC-V instruction sets by assembler.
https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf#page=122 \- For instance :
mv a0,a5 -> add a0, a5, zero
- For instance :
ADDI rd, rs1, imm -> rd = rs1 + imm
- equivalent to mv instruction when used with addi rd, rs1, 0. RISC-V does not have mv instruction, so that assembler changes mv to addi.
ANDI rd, rs1, imm -> rd = rs1 & imm
ORI rd, rs1, imm -> rd = rs1 | imm
XORI rd, rs1, imm -> rd = rs1 ^ imm
SLL rd, rs1, imm -> rd = rs1 << imm
- note that in order to perform logical operation, the operand value should be cast to (uint 32).
SRA rd, rs1, imm -> rd = rs1 >> imm
- perform arithmatic shift, which means sign extension is operated during the operatin.
SRL rd, rs1, imm -> rd = rs1 >> imm
- note that in order to perform logical operation, the operand value should be cast to (uint 32).
SLTI rd, rs1, imm -> rd = rs1 << imm
SLTIU rd, rs1, rs2 -> rd = (uint32_t)rs1 < (uint32_t)imm
JALR rd, rs1, offset -> pc = rs1 + offset; rd = pc + 4
- note that JAL is J-type instruction, and JALR is I-type instruction.
LW rd, rs1, offset -> rd = mem[rs1 + offset]
LH rd, rs1, offset -> rd = mem[rs1 + offset] & 0x0000ffff
LB rd, rs1, offset -> rd = mem[rs1 + offset] & 0x000000ff
LHU rd, rs1, offset -> rd = (uint32_t)mem[rs1 + offset] & 0x0000ffff
LBU rd, rs1, offset -> rd = (uint32_t)mem[rs1 + offset] & 0x000000ff
ADD rd, rs1, rs2 -> rd = rs1 + rs2
SUB rd, rs1, rs2 -> rd = rs1 - rs2
AND rd, rs1, rs2 -> rd = rs1 & rs2
- note that the AND and similar operation in RISC-V is bitwise operation, not logical.
OR rd, rs1, rs2 -> rd = rs1 | rs2
XOR rd, rs1, rs2 -> rd = rs1 ^ rs2
SLL rd, rs1, rs2 -> rd = rs1 << rs2
- note that in order to perform logical operation, the operand value should be cast to (uint 32).
SRA rd, rs1, rs2 -> rd = rs1 >> rs2
SRL rd, rs1, rs2 -> rd = rs1 >> rs2
- note that in order to perform logical operation, the operand value should be cast to (uint 32).
SLT rd, rs1, rs2 -> rd = rs1 < rs2
SLTU rd, rs1, rs2 -> rd = (uint32_t)rs1 < (uint32_t)rs2
LUI rd, imm -> rd = (int32_t)imm << 12
- note that imm is sign extended before shift left by 12 bits.
AUIPC rd, imm -> rd = pc + ((int32_t)imm << 12)
- note that imm is sign extended before shift left by 12 bits.
BEQ rs1, rs2, offset -> if (rs1 == rs2) pc += offset
- note that offset is 12 bits length, which means the offset range is within ±4 KiB.
BNE rs1, rs2, offset -> if (rs1 != rs2) pc += offset
BLT rs1, rs2, offset -> if (rs1 < rs2) pc += offset
BGE rs1, rs2, offset -> if (rs1 >= rs2) pc += offset
- not that bge include equality (bge stands for branch if greater or equal to).
BLTU rs1, rs2, offset -> if ((uint32_t)rs1 < (uint32_t)rs2) pc += offset
BGEU rs1, rs2, offset -> if ((uint32_t)rs1 >= (uint32_t)rs2) pc += offset
JAL rd, offset -> pc += offset; rd = pc + 4
SW rs1, rs2, offset -> mem[rs2 + offset] = rs1 (*original: mem[rs1 + offset] = rs2)
SH rs1, rs2, offset -> mem[rs2 + offset] = rs1 & 0x0000ffff (*original: mem[rs1 + offset] = rs2 & 0x0000ffff)
- note that 0x0000ffff = half word which is 16 bits. one word is 4 bytes. in hex format 1 bit stand for 4 bits in binary, so 2 digits of hex is 1 byte. threfore in order to achive 32 bit, it needs 2 * 4 = 8 digits in hex.
SB rs1, rs2, offset -> mem[rs2 + offset] = rs1 & 0x0000000ff (*original: SB rs1, rs2, offset -> mem[rs1 + offset] = rs2 & 0x0000000ff)