summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/sim/decode.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/sim/decode.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/sim/decode.py399
1 files changed, 399 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/sim/decode.py b/debug/risc-v-gsp-rm-vm/sim/decode.py
new file mode 100644
index 0000000..0f3ef3a
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/sim/decode.py
@@ -0,0 +1,399 @@
+"""RV64IMAC instruction decoder (pure Python, stdlib only).
+
+The decoder canonicalises every compressed instruction into the equivalent
+32-bit operation, so breakpoints, single-step and watchpoints behave uniformly
+regardless of encoding. ``decode16``/``decode32`` return a 7-tuple
+
+ (kind, sub, rd, rs1, rs2, imm, next_pc)
+
+where ``next_pc`` is the fall-through (or branch/jump target for the
+unconditional control transfers), ``imm`` is already sign/zero-normalised for
+its use, and branch/jump targets are absolute virtual addresses. Results are
+cached per PC by the Machine; the cache is dropped when the debugger writes
+memory (M1 does not model self-modifying code).
+"""
+from __future__ import annotations
+
+MASK64 = (1 << 64) - 1
+
+# Instruction kinds.
+(
+ K_LUI,
+ K_AUIPC,
+ K_JAL,
+ K_JALR,
+ K_BRANCH,
+ K_LOAD,
+ K_STORE,
+ K_OPI,
+ K_OP,
+ K_OPI32,
+ K_OP32,
+ K_FENCE,
+ K_CSR,
+ K_ECALL,
+ K_EBREAK,
+ K_MRET,
+ K_AMO,
+ K_ILLEGAL,
+) = range(18)
+
+# ALU ops shared by K_OP and (width-truncated) K_OP32.
+(
+ A_ADD,
+ A_SLL,
+ A_SLT,
+ A_SLTU,
+ A_XOR,
+ A_SRL,
+ A_OR,
+ A_AND,
+ A_SUB,
+ A_SRA,
+ A_MUL,
+ A_MULH,
+ A_MULHSU,
+ A_MULHU,
+ A_DIV,
+ A_DIVU,
+ A_REM,
+ A_REMU,
+) = range(18)
+
+# K_OPI / K_OPI32 immediate ops (funct3 values, with srai/sraiw offset).
+I_ADDI, I_SLLI, I_SLTI, I_SLTIU, I_XORI, I_SRLI, I_ORI, I_ANDI = range(8)
+I_SRAI = 0x15
+I_ADDIW, I_SLLIW, I_SRLIW, I_SRAIW = 0, 1, 5, 0x15
+
+# LOAD/STORE funct3.
+LOAD_SIZE = (1, 2, 4, 8, 1, 2, 4)
+STORE_SIZE = (1, 2, 4, 8)
+
+_ILLEGAL = (K_ILLEGAL, 0, 0, 0, 0, 0, 0)
+
+
+def sext(value: int, bits: int) -> int:
+ sign = 1 << (bits - 1)
+ return (value & (sign - 1)) - (value & sign)
+
+
+def _bits(value: int, hi: int, lo: int) -> int:
+ return (value >> lo) & ((1 << (hi - lo + 1)) - 1)
+
+
+def _op_alu(sub: int) -> int:
+ """Map an R-type funct3 to the shared ALU op code (non-M)."""
+ return {
+ 0: A_ADD,
+ 1: A_SLL,
+ 2: A_SLT,
+ 3: A_SLTU,
+ 4: A_XOR,
+ 5: A_SRL,
+ 6: A_OR,
+ 7: A_AND,
+ }[sub]
+
+
+def decode32(pc: int, insn: int):
+ opcode = insn & 0x7F
+ rd = (insn >> 7) & 0x1F
+ funct3 = (insn >> 12) & 7
+ rs1 = (insn >> 15) & 0x1F
+ rs2 = (insn >> 20) & 0x1F
+ funct7 = (insn >> 25) & 0x7F
+ nxt = pc + 4
+
+ if opcode == 0x37: # LUI
+ return (K_LUI, 0, rd, 0, 0, (insn & 0xFFFFF000) & MASK64, nxt)
+ if opcode == 0x17: # AUIPC
+ return (K_AUIPC, 0, rd, 0, 0, sext(insn & 0xFFFFF000, 32) & MASK64, nxt)
+ if opcode == 0x6F: # JAL
+ imm = sext(
+ (_bits(insn, 31, 31) << 20)
+ | (_bits(insn, 30, 21) << 1)
+ | (_bits(insn, 20, 20) << 11)
+ | (_bits(insn, 19, 12) << 12),
+ 21,
+ )
+ return (K_JAL, 0, rd, 0, 0, (pc + imm) & MASK64, nxt)
+ if opcode == 0x67: # JALR
+ return (K_JALR, funct3, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt)
+ if opcode == 0x63: # BRANCH
+ imm = sext(
+ (_bits(insn, 31, 31) << 12)
+ | (_bits(insn, 30, 25) << 5)
+ | (_bits(insn, 11, 8) << 1)
+ | (_bits(insn, 7, 7) << 11),
+ 13,
+ )
+ return (K_BRANCH, funct3, 0, rs1, rs2, (pc + imm) & MASK64, nxt)
+ if opcode == 0x03: # LOAD
+ if funct3 == 7:
+ return _ILLEGAL
+ return (K_LOAD, funct3, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt)
+ if opcode == 0x23: # STORE
+ if funct3 > 3:
+ return _ILLEGAL
+ imm = sext((_bits(insn, 31, 25) << 5) | _bits(insn, 11, 7), 12)
+ return (K_STORE, funct3, 0, rs1, rs2, imm, nxt)
+ if opcode == 0x13: # OP-IMM
+ shamt = (insn >> 20) & 0x3F
+ top = (insn >> 26) & 0x3F
+ if funct3 == 1:
+ if top:
+ return _ILLEGAL
+ return (K_OPI, I_SLLI, rd, rs1, 0, shamt, nxt)
+ if funct3 == 5:
+ if top == 0:
+ sub = I_SRLI
+ elif top == 0x10:
+ sub = I_SRAI
+ else:
+ return _ILLEGAL
+ return (K_OPI, sub, rd, rs1, 0, shamt, nxt)
+ return (K_OPI, funct3, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt)
+ if opcode == 0x33: # OP
+ if funct7 == 0x01: # M extension
+ return (K_OP, A_MUL + funct3, rd, rs1, rs2, 0, nxt)
+ if funct7 == 0x00:
+ sub = _op_alu(funct3)
+ elif funct7 == 0x20 and funct3 == 0:
+ sub = A_SUB
+ elif funct7 == 0x20 and funct3 == 5:
+ sub = A_SRA
+ else:
+ return _ILLEGAL
+ return (K_OP, sub, rd, rs1, rs2, 0, nxt)
+ if opcode == 0x1B: # OP-IMM-32
+ if funct3 == 0:
+ return (K_OPI32, I_ADDIW, rd, rs1, 0, sext(_bits(insn, 31, 20), 12), nxt)
+ if funct3 == 1:
+ if (insn >> 25) & 0x7F:
+ return _ILLEGAL
+ return (K_OPI32, I_SLLIW, rd, rs1, 0, (insn >> 20) & 0x1F, nxt)
+ if funct3 == 5:
+ top = (insn >> 25) & 0x7F
+ if top == 0:
+ sub = I_SRLIW
+ elif top == 0x20:
+ sub = I_SRAIW
+ else:
+ return _ILLEGAL
+ return (K_OPI32, sub, rd, rs1, 0, (insn >> 20) & 0x1F, nxt)
+ return _ILLEGAL
+ if opcode == 0x3B: # OP-32
+ if funct7 == 0x00:
+ if funct3 == 0:
+ sub = A_ADD
+ elif funct3 == 1:
+ sub = A_SLL
+ elif funct3 == 5:
+ sub = A_SRL
+ else:
+ return _ILLEGAL
+ elif funct7 == 0x20:
+ if funct3 == 0:
+ sub = A_SUB
+ elif funct3 == 5:
+ sub = A_SRA
+ else:
+ return _ILLEGAL
+ elif funct7 == 0x01:
+ if funct3 == 0:
+ sub = A_MUL
+ elif funct3 == 4:
+ sub = A_DIV
+ elif funct3 == 5:
+ sub = A_DIVU
+ elif funct3 == 6:
+ sub = A_REM
+ elif funct3 == 7:
+ sub = A_REMU
+ else:
+ return _ILLEGAL
+ else:
+ return _ILLEGAL
+ return (K_OP32, sub, rd, rs1, rs2, 0, nxt)
+ if opcode == 0x0F: # FENCE / FENCE.I: ordering is not modelled (single core)
+ if funct3 in (0, 1):
+ return (K_FENCE, 0, 0, 0, 0, 0, nxt)
+ return _ILLEGAL
+ if opcode == 0x73: # SYSTEM
+ if funct3 == 0:
+ imm12 = insn >> 20
+ if imm12 == 0:
+ return (K_ECALL, 0, 0, 0, 0, 0, nxt)
+ if imm12 == 1:
+ return (K_EBREAK, 0, 0, 0, 0, 0, nxt)
+ if imm12 in (0x302, 0x102): # mret / sret (treated alike, M1)
+ return (K_MRET, 0, 0, 0, 0, 0, nxt)
+ return _ILLEGAL
+ return (K_CSR, funct3, rd, rs1, 0, insn >> 20, nxt)
+ if opcode == 0x2F: # AMO
+ if funct3 not in (2, 3):
+ return _ILLEGAL
+ funct5 = funct7 >> 2
+ if funct5 not in (0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C):
+ return _ILLEGAL
+ sub = funct5 | (0x40 if funct3 == 3 else 0)
+ return (K_AMO, sub, rd, rs1, rs2, 0, nxt)
+ return _ILLEGAL
+
+
+def _rvc_j_imm(insn: int) -> int:
+ return sext(
+ (_bits(insn, 12, 12) << 11)
+ | (_bits(insn, 11, 11) << 4)
+ | (_bits(insn, 10, 9) << 8)
+ | (_bits(insn, 8, 8) << 10)
+ | (_bits(insn, 7, 7) << 6)
+ | (_bits(insn, 6, 6) << 7)
+ | (_bits(insn, 5, 3) << 1)
+ | (_bits(insn, 2, 2) << 5),
+ 12,
+ )
+
+
+def _rvc_b_imm(insn: int) -> int:
+ return sext(
+ (_bits(insn, 12, 12) << 8)
+ | (_bits(insn, 11, 10) << 3)
+ | (_bits(insn, 6, 5) << 6)
+ | (_bits(insn, 4, 3) << 1)
+ | (_bits(insn, 2, 2) << 5),
+ 9,
+ )
+
+
+def decode16(pc: int, insn: int):
+ op = insn & 3
+ funct3 = (insn >> 13) & 7
+ nxt = pc + 2
+ rd = (insn >> 7) & 0x1F
+
+ if op == 0:
+ if funct3 == 0: # c.addi4spn
+ imm = (
+ ((insn >> 7) & 0x30)
+ | ((insn >> 1) & 0x3C0)
+ | ((insn >> 4) & 0x4)
+ | ((insn >> 2) & 0x8)
+ )
+ if imm == 0:
+ return _ILLEGAL
+ return (K_OPI, I_ADDI, 8 + ((insn >> 2) & 7), 2, 0, imm, nxt)
+ if funct3 == 2: # c.lw
+ imm = ((insn >> 7) & 0x38) | ((insn >> 4) & 0x4) | ((insn << 1) & 0x40)
+ return (K_LOAD, 2, 8 + ((insn >> 2) & 7), 8 + ((insn >> 7) & 7), 0, imm, nxt)
+ if funct3 == 3: # c.ld
+ imm = ((insn >> 7) & 0x38) | ((insn << 1) & 0xC0)
+ return (K_LOAD, 3, 8 + ((insn >> 2) & 7), 8 + ((insn >> 7) & 7), 0, imm, nxt)
+ if funct3 == 6: # c.sw
+ imm = ((insn >> 7) & 0x38) | ((insn >> 4) & 0x4) | ((insn << 1) & 0x40)
+ return (K_STORE, 2, 0, 8 + ((insn >> 7) & 7), 8 + ((insn >> 2) & 7), imm, nxt)
+ if funct3 == 7: # c.sd
+ imm = ((insn >> 7) & 0x38) | ((insn << 1) & 0xC0)
+ return (K_STORE, 3, 0, 8 + ((insn >> 7) & 7), 8 + ((insn >> 2) & 7), imm, nxt)
+ return _ILLEGAL
+
+ if op == 1:
+ if funct3 == 0: # c.addi / c.nop
+ imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6)
+ return (K_OPI, I_ADDI, rd, rd, 0, imm, nxt)
+ if funct3 == 1: # c.addiw
+ if rd == 0:
+ return _ILLEGAL
+ imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6)
+ return (K_OPI32, I_ADDIW, rd, rd, 0, imm, nxt)
+ if funct3 == 2: # c.li
+ imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6)
+ return (K_OPI, I_ADDI, rd, 0, 0, imm, nxt)
+ if funct3 == 3:
+ if rd == 2: # c.addi16sp
+ imm = sext(
+ ((insn >> 3) & 0x200)
+ | ((insn >> 2) & 0x10)
+ | ((insn << 1) & 0x40)
+ | ((insn << 4) & 0x180)
+ | ((insn << 3) & 0x20),
+ 10,
+ )
+ if imm == 0:
+ return _ILLEGAL
+ return (K_OPI, I_ADDI, 2, 2, 0, imm, nxt)
+ imm6 = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6)
+ if rd == 0 or imm6 == 0: # c.lui
+ return _ILLEGAL
+ return (K_LUI, 0, rd, 0, 0, (imm6 << 12) & MASK64, nxt)
+ if funct3 == 4:
+ sub3 = (insn >> 10) & 3
+ rd_p = 8 + ((insn >> 7) & 7)
+ if sub3 == 0: # c.srli
+ shamt = (_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2)
+ return (K_OPI, I_SRLI, rd_p, rd_p, 0, shamt, nxt)
+ if sub3 == 1: # c.srai
+ shamt = (_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2)
+ return (K_OPI, I_SRAI, rd_p, rd_p, 0, shamt, nxt)
+ if sub3 == 2: # c.andi
+ imm = sext((_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2), 6)
+ return (K_OPI, I_ANDI, rd_p, rd_p, 0, imm, nxt)
+ f2 = (insn >> 5) & 3
+ if _bits(insn, 12, 12) == 0:
+ sub = (A_SUB, A_XOR, A_OR, A_AND)[f2]
+ return (K_OP, sub, rd_p, rd_p, 8 + ((insn >> 2) & 7), 0, nxt)
+ if f2 == 0: # c.subw
+ return (K_OP32, A_SUB, rd_p, rd_p, 8 + ((insn >> 2) & 7), 0, nxt)
+ if f2 == 1: # c.addw
+ return (K_OP32, A_ADD, rd_p, rd_p, 8 + ((insn >> 2) & 7), 0, nxt)
+ return _ILLEGAL
+ if funct3 == 5: # c.j
+ return (K_JAL, 0, 0, 0, 0, (pc + _rvc_j_imm(insn)) & MASK64, nxt)
+ if funct3 == 6: # c.beqz
+ return (K_BRANCH, 0, 0, 8 + ((insn >> 7) & 7), 0, (pc + _rvc_b_imm(insn)) & MASK64, nxt)
+ if funct3 == 7: # c.bnez
+ return (K_BRANCH, 1, 0, 8 + ((insn >> 7) & 7), 0, (pc + _rvc_b_imm(insn)) & MASK64, nxt)
+ return _ILLEGAL
+
+ if op == 2:
+ if funct3 == 0: # c.slli
+ if rd == 0:
+ return _ILLEGAL
+ shamt = (_bits(insn, 12, 12) << 5) | _bits(insn, 6, 2)
+ return (K_OPI, I_SLLI, rd, rd, 0, shamt, nxt)
+ if funct3 == 2: # c.lwsp
+ if rd == 0:
+ return _ILLEGAL
+ imm = ((insn >> 7) & 0x20) | ((insn >> 2) & 0x1C) | ((insn << 4) & 0xC0)
+ return (K_LOAD, 2, rd, 2, 0, imm, nxt)
+ if funct3 == 3: # c.ldsp
+ if rd == 0:
+ return _ILLEGAL
+ imm = ((insn >> 7) & 0x20) | ((insn >> 2) & 0x18) | ((insn << 4) & 0x1C0)
+ return (K_LOAD, 3, rd, 2, 0, imm, nxt)
+ if funct3 == 4:
+ rs2 = (insn >> 2) & 0x1F
+ if _bits(insn, 12, 12) == 0:
+ if rs2 == 0: # c.jr
+ if rd == 0:
+ return _ILLEGAL
+ return (K_JALR, 0, 0, rd, 0, 0, nxt)
+ if rd == 0: # c.mv
+ return _ILLEGAL
+ return (K_OP, A_ADD, rd, 0, rs2, 0, nxt)
+ if rs2 == 0 and rd == 0: # c.ebreak
+ return (K_EBREAK, 0, 0, 0, 0, 0, nxt)
+ if rs2 == 0: # c.jalr
+ if rd == 0:
+ return _ILLEGAL
+ return (K_JALR, 0, 1, rd, 0, 0, nxt)
+ return (K_OP, A_ADD, rd, rd, rs2, 0, nxt) # c.add
+ if funct3 == 6: # c.swsp
+ imm = ((insn >> 7) & 0x3C) | ((insn >> 1) & 0xC0)
+ return (K_STORE, 2, 0, 2, (insn >> 2) & 0x1F, imm, nxt)
+ if funct3 == 7: # c.sdsp
+ imm = ((insn >> 7) & 0x38) | ((insn >> 1) & 0x1C0)
+ return (K_STORE, 3, 0, 2, (insn >> 2) & 0x1F, imm, nxt)
+ return _ILLEGAL
+
+ return _ILLEGAL