diff --git a/riscemu/decoder/__init__.py b/riscemu/decoder/__init__.py new file mode 100644 index 0000000..3ba852e --- /dev/null +++ b/riscemu/decoder/__init__.py @@ -0,0 +1 @@ +from .decoder import decode \ No newline at end of file diff --git a/riscemu/decoder/__main__.py b/riscemu/decoder/__main__.py new file mode 100644 index 0000000..98fcd49 --- /dev/null +++ b/riscemu/decoder/__main__.py @@ -0,0 +1,17 @@ +if __name__ == '__main__': + import code + import readline + import rlcompleter + + from .decoder import * + from .formats import * + from instruction_table import * + from regs import * + + sess_vars = globals() + sess_vars.update(locals()) + + readline.set_completer(rlcompleter.Completer(sess_vars).complete) + readline.set_completer(rlcompleter.Completer(sess_vars).complete) + readline.parse_and_bind("tab: complete") + code.InteractiveConsole(sess_vars).interact(banner="Interaktive decoding session started...", exitmsg="Closing...") \ No newline at end of file diff --git a/riscemu/decoder/decoder.py b/riscemu/decoder/decoder.py new file mode 100644 index 0000000..d34dded --- /dev/null +++ b/riscemu/decoder/decoder.py @@ -0,0 +1,86 @@ +from .instruction_table import * +from typing import Tuple, List + + +def print_ins(ins: int): + print(" f7 rs2 rs1 f3 rd op") + print( + f"0b{ins >> 25 :07b}_{(ins >> 20) & 0b11111:05b}_{(ins >> 15) & 0b11111:03b}_{(ins >> 12) & 0b111:03b}_{(ins >> 7) & 0b11111:05b}_{ins & 0b1111111:07b}"); + + +STATIC_INSN = { + 0x00000013: ("nop", []), + 0x00008067: ("ret", []), + 0xfe010113: ("addi", ["sp", "sp", -32]), + 0x02010113: ("addi", ["sp", "sp", 32]), + 0x00100073: ("ebreak", []), + 0x00000073: ("ecall", []) +} + + +def int_from_ins(insn: bytearray): + return (insn[3] << (8 * 3)) + \ + (insn[2] << (8 * 2)) + \ + (insn[1] << 8) + \ + insn[0] + + +def name_from_insn(ins: int): + opcode = op(ins) + if opcode not in RV32: + print_ins(ins) + raise RuntimeError(f"Invalid opcode: {opcode:0x} in insn {ins:x}") + dec = RV32[opcode] + + if isinstance(dec, str): + return dec + + fun = funct3(ins) + if fun not in dec: + print_ins(ins) + raise RuntimeError(f"Invalid funct3: {fun:0x} in insn {ins:x}") + + dec = dec[fun] + if isinstance(dec, str): + return dec + + if opcode == 0x1c and fun == 0: + # we have ecall/ebreak + token = imm110(ins) + if token in dec: + return dec[token] + print_ins(ins) + raise RuntimeError(f"Invalid instruction in ebreak/ecall region: {ins:x}") + + fun = funct7(ins) + if fun in dec: + if opcode == 0x0C or (opcode == 0x04 and fun == 5): + mode = imm110(ins) + dec = dec[fun] + if mode in dec: + return dec[mode] + print_ins(ins) + raise RuntimeError("Unknown instruction!") + + return dec[fun] + + print_ins(ins) + raise RuntimeError(f"Invalid instruction: {ins:x}") + + +def decode(ins: bytearray) -> Tuple[str, List[Union[str, int]]]: + insn = int_from_ins(ins) + + if insn & 3 != 3: + print_ins(insn) + raise RuntimeError("Not a RV32 instruction!") + + if insn in STATIC_INSN: + return STATIC_INSN[insn] + + opcode = op(insn) + if opcode not in INSTRUCTION_ARGS_DECODER: + print_ins(insn) + raise RuntimeError("No instruction decoder found for instruction") + + return name_from_insn(insn), INSTRUCTION_ARGS_DECODER[opcode](insn) diff --git a/riscemu/decoder/formats.py b/riscemu/decoder/formats.py new file mode 100644 index 0000000..aaafb06 --- /dev/null +++ b/riscemu/decoder/formats.py @@ -0,0 +1,114 @@ +from typing import Dict, Callable, List, Union +from .regs import RISCV_REGS + +def op(ins: int): + return (ins >> 2) & 31 + + +def rd(ins: int): + return (ins >> 7) & 31 + + +def funct3(ins: int): + return (ins >> 12) & 7 + + +def rs1(ins: int): + return (ins >> 15) & 31 + + +def rs2(ins: int): + return (ins >> 20) & 31 + + +def funct7(ins: int): + return ins >> 25 + + +def imm110(ins: int): + return ins >> 20 + + +def imm3112(ins: int): + return ins >> 12 + + +def imm_i(ins: int): + return sign_extend(imm110(ins), 12) + + +def imm_s(ins: int): + num = (funct7(ins) << 5) + rd(ins) + return sign_extend(num, 12) + + +def imm_b(ins: int): + lower = rd(ins) + higher = funct7(ins) + + num = (lower & 0b11110) + ((higher & 0b0111111) << 5) + ((lower & 1) << 11) + ((higher >> 6) << 12) + return sign_extend(num, 13) + + +def imm_u(ins: int): + return sign_extend(imm3112(ins), 20) + + +def imm_j(ins: int): + imm = ins >> 12 + return sign_extend( + ((imm >> 8) & 0b1111111111) + + ((imm & 1) << 10) + + ((imm & 0b11111111) << 11) + + (imm & 0b10000000000000000000), 20 + ) + + +def sign_extend(num, bits): + sign_mask = 1 << (bits - 1) + return (num & (sign_mask - 1)) - (num & sign_mask) + + +def decode_i(ins: int) -> List[Union[str, int]]: + return [RISCV_REGS[rd(ins)], RISCV_REGS[rs1(ins)], imm_i(ins)] + + +def decode_b(ins: int) -> List[Union[str, int]]: + return [RISCV_REGS[rs1(ins)], RISCV_REGS[rs2(ins)], imm_b(ins)] + + +def decode_u(ins: int) -> List[Union[str, int]]: + return [RISCV_REGS[rd(ins)], imm_u(ins)] + + +def decode_r(ins: int) -> List[Union[str, int]]: + return [RISCV_REGS[rd(ins)], RISCV_REGS[rs1(ins)], RISCV_REGS[rs2(ins)]] + + +def decode_s(ins: int) -> List[Union[str, int]]: + return [RISCV_REGS[rs1(ins)], RISCV_REGS[rs2(ins)], imm_s(ins)] + + +def decode_j(ins: int) -> List[Union[str, int]]: + return [RISCV_REGS[rd(ins)], imm_j(ins)] + + +def decode_i_shamt(ins: int) -> List[Union[str, int]]: + if funct3(ins) in (1, 5): + return [RISCV_REGS[rd(ins)], RISCV_REGS[rs1(ins)], rs2(ins)] + else: + return [RISCV_REGS[rd(ins)], RISCV_REGS[rs1(ins)], imm110(ins)] + + +INSTRUCTION_ARGS_DECODER: Dict[int, Callable[[int], List[Union[str, int]]]] = { + 0x00: decode_i, + 0x04: decode_i_shamt, + 0x05: decode_u, + 0x08: decode_s, + 0x0C: decode_r, + 0x0D: decode_u, + 0x18: decode_b, + 0x19: decode_i, + 0x1b: decode_j, + 0x1c: decode_i +} diff --git a/riscemu/decoder/instruction_table.py b/riscemu/decoder/instruction_table.py new file mode 100644 index 0000000..4ab4417 --- /dev/null +++ b/riscemu/decoder/instruction_table.py @@ -0,0 +1,69 @@ +from collections import defaultdict +from .formats import * + +tbl = lambda: defaultdict(tbl) + +RV32 = tbl() +RV32[0x1b] = "jal" +RV32[0x0D] = "lui" +RV32[0x05] = "auipc" +RV32[0x19][0] = "jalr" + +RV32[0x04][0] = "addi" +RV32[0x04][1] = "slli" +RV32[0x04][2] = "slti" +RV32[0x04][3] = "sltiu" +RV32[0x04][4] = "xori" +RV32[0x04][5][0x00] = "srli" +RV32[0x04][5][0x20] = "srai" +RV32[0x04][6] = "ori" +RV32[0x04][7] = "andi" + +RV32[0x18][0] = "beq" +RV32[0x18][1] = "bne" +RV32[0x18][4] = "blt" +RV32[0x18][5] = "bge" +RV32[0x18][6] = "bltu" +RV32[0x18][7] = "bgeu" + +RV32[0x00][0] = "lb" +RV32[0x00][1] = "lh" +RV32[0x00][2] = "lw" +RV32[0x00][4] = "lbu" +RV32[0x00][5] = "lhu" + +RV32[0x08][0] = "sb" +RV32[0x08][1] = "sh" +RV32[0x08][2] = "sw" + +RV32[0x1c][1] = "csrrw" +RV32[0x1c][2] = "csrrs" +RV32[0x1c][3] = "csrrc" +RV32[0x1c][5] = "csrrwi" +RV32[0x1c][6] = "csrrsi" +RV32[0x1c][7] = "csrrci" + +RV32[0x1c][0][0] = "ecall" +RV32[0x1c][0][1] = "ebreak" + +RV32[0x0C][0][0] = "add" +RV32[0x0C][0][32] = "sub" +RV32[0x0C][1][0] = "sll" +RV32[0x0C][2][0] = "slt" +RV32[0x0C][3][0] = "sltu" +RV32[0x0C][4][0] = "xor" +RV32[0x0C][5][0] = "srl" +RV32[0x0C][5][32] = "sra" +RV32[0x0C][6][0] = "or" +RV32[0x0C][7][0] = "and" + +# rv32m +RV32[0x0C][0][1] = "mul" +RV32[0x0C][1][1] = "mulh" +RV32[0x0C][2][1] = "mulhsu" +RV32[0x0C][3][1] = "mulhu" +RV32[0x0C][4][1] = "div" +RV32[0x0C][5][1] = "divu" +RV32[0x0C][6][1] = "rem" +RV32[0x0C][7][1] = "remu" + diff --git a/riscemu/decoder/regs.py b/riscemu/decoder/regs.py new file mode 100644 index 0000000..44d70ac --- /dev/null +++ b/riscemu/decoder/regs.py @@ -0,0 +1,6 @@ +RISCV_REGS = [ + 'zero', 'ra', 'sp', 'gp', 'tp', 't0', 't1', 't2', + 's0', 's1', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', + 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', + 't3', 't4', 't5', 't6' +] diff --git a/riscemu/priv/PrivMMU.py b/riscemu/priv/PrivMMU.py new file mode 100644 index 0000000..e69de29 diff --git a/riscemu/priv/privmodes.py b/riscemu/priv/privmodes.py new file mode 100644 index 0000000..e69de29