diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 10:58:21 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 10:58:21 -0400 |
| commit | f909a5f822c5c4a4e9526b861d4171f8925f5113 (patch) | |
| tree | ed362021c83569f49b48079a774f05775e84b0bd /debug/risc-v-gsp-rm-vm/sim/decode.py | |
| parent | 09f241601423b1ae98e97846ee451768ff8a0a9e (diff) | |
| download | open-iov-f909a5f822c5c4a4e9526b861d4171f8925f5113.tar.gz open-iov-f909a5f822c5c4a4e9526b861d4171f8925f5113.zip | |
debug: green-eye wave 3 - RM harness, local simulator, device map, MCP plugin, value-validation demos, CI pipeline, toolchain docs; add host RM tools rmtool/rmtop
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/sim/decode.py')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/sim/decode.py | 399 |
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 |
