diff --git a/riscemu/decoder/decoder.py b/riscemu/decoder/decoder.py index 267fdf6..4bdaed8 100644 --- a/riscemu/decoder/decoder.py +++ b/riscemu/decoder/decoder.py @@ -54,6 +54,11 @@ def name_from_insn(ins: int): raise RuntimeError(f"Invalid instruction in ebreak/ecall region: {ins:x}") fun7 = funct7(ins) + if opcode == 0b1011 and fun3 == 0b10: + # ignore the two aq/lr bits located in the fun7 block + # riscemu has no memory reordering, therefore we don't need to look at these bits ever + fun7 = fun7 >> 2 + if fun7 in dec: if opcode == 0x0C or (opcode == 0x04 and fun3 == 5): dec = dec[fun7] diff --git a/riscemu/decoder/formats.py b/riscemu/decoder/formats.py index 9d3e544..2f9af85 100644 --- a/riscemu/decoder/formats.py +++ b/riscemu/decoder/formats.py @@ -112,5 +112,6 @@ INSTRUCTION_ARGS_DECODER: Dict[int, Callable[[int], List[int]]] = { 0x18: decode_b, 0x19: decode_i, 0x1b: decode_j, - 0x1c: decode_i_unsigned + 0x1c: decode_i_unsigned, + 0b1011: decode_r } diff --git a/riscemu/decoder/instruction_table.py b/riscemu/decoder/instruction_table.py index 4ab4417..84dbd2e 100644 --- a/riscemu/decoder/instruction_table.py +++ b/riscemu/decoder/instruction_table.py @@ -67,3 +67,15 @@ RV32[0x0C][5][1] = "divu" RV32[0x0C][6][1] = "rem" RV32[0x0C][7][1] = "remu" +# rv32a +RV32[0b1011][0b10][0b00010] = "lr.w" +RV32[0b1011][0b10][0b00011] = "sc.w" +RV32[0b1011][0b10][0b00001] = "amoswap.w" +RV32[0b1011][0b10][0b00000] = "amoadd.w" +RV32[0b1011][0b10][0b00100] = "amoxor.w" +RV32[0b1011][0b10][0b01100] = "amoand.w" +RV32[0b1011][0b10][0b01000] = "amoor.w" +RV32[0b1011][0b10][0b10000] = "amomin.w" +RV32[0b1011][0b10][0b10100] = "amomax.w" +RV32[0b1011][0b10][0b11000] = "amominu.w" +RV32[0b1011][0b10][0b11100] = "amomaxu.w" diff --git a/riscemu/instructions/RV32A.py b/riscemu/instructions/RV32A.py new file mode 100644 index 0000000..9432c83 --- /dev/null +++ b/riscemu/instructions/RV32A.py @@ -0,0 +1,78 @@ +from .InstructionSet import InstructionSet, LoadedInstruction +from ..Exceptions import INS_NOT_IMPLEMENTED +from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed + + +class RV32A(InstructionSet): + """ + The RV32A instruction set. Currently, load-reserved and store conditionally are not supported + due to limitations in the way the MMU is implemented. Maybe a later implementation will add support + for this? + """ + + def instruction_lr_w(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_sc_w(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_amoswap_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + if dest == 'zero': + self.mmu.write(addr, int_to_bytes(addr, 4)) + else: + old = int_from_bytes(self.mmu.read(addr, 4)) + self.mmu.write(addr, int_to_bytes(val, 4)) + self.regs.set(dest, old) + + def instruction_amoadd_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + old = int_from_bytes(self.mmu.read(addr, 4)) + self.mmu.write(addr, int_to_bytes(old + val, 4)) + self.regs.set(dest, old) + + def instruction_amoand_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + old = int_from_bytes(self.mmu.read(addr, 4)) + self.mmu.write(addr, int_to_bytes(old & val, 4)) + self.regs.set(dest, old) + + def instruction_amoor_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + old = int_from_bytes(self.mmu.read(addr, 4)) + self.mmu.write(addr, int_to_bytes(old | val, 4)) + self.regs.set(dest, old) + + def instruction_amoxor_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + old = int_from_bytes(self.mmu.read(addr, 4)) + self.mmu.write(addr, int_to_bytes(old ^ val, 4)) + self.regs.set(dest, old) + + def instruction_amomax_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + old = int_from_bytes(self.mmu.read(addr, 4)) + self.mmu.write(addr, int_to_bytes(max(old, val), 4)) + self.regs.set(dest, old) + + def instruction_amomaxu_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + val = to_unsigned(val) + old = int_from_bytes(self.mmu.read(addr, 4), unsigned=True) + + self.mmu.write(addr, int_to_bytes(to_signed(max(old, val)), 4)) + self.regs.set(dest, old) + + def instruction_amomin_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + old = int_from_bytes(self.mmu.read(addr, 4)) + self.mmu.write(addr, int_to_bytes(min(old, val), 4)) + self.regs.set(dest, old) + + def instruction_amominu_w(self, ins: 'LoadedInstruction'): + dest, addr, val = self.parse_rd_rs_rs(ins) + val = to_unsigned(val) + old = int_from_bytes(self.mmu.read(addr, 4), unsigned=True) + + self.mmu.write(addr, int_to_bytes(to_signed(min(old, val)), 4)) + self.regs.set(dest, old) diff --git a/riscemu/instructions/__init__.py b/riscemu/instructions/__init__.py index fb6a17c..65bda29 100644 --- a/riscemu/instructions/__init__.py +++ b/riscemu/instructions/__init__.py @@ -9,7 +9,8 @@ This package holds all instruction sets, available to the processor from .InstructionSet import InstructionSet from .RV32M import RV32M from .RV32I import RV32I +from .RV32A import RV32A InstructionSetDict = { - v.__name__: v for v in [RV32I, RV32M] + v.__name__: v for v in [RV32I, RV32M, RV32A] } diff --git a/riscemu/priv/PrivCPU.py b/riscemu/priv/PrivCPU.py index 424c51d..e3886ac 100644 --- a/riscemu/priv/PrivCPU.py +++ b/riscemu/priv/PrivCPU.py @@ -14,7 +14,7 @@ from .PrivMMU import PrivMMU from ..IO import TextIO from .PrivRV32I import PrivRV32I from .privmodes import PrivModes -from ..instructions.RV32M import RV32M +from ..instructions import RV32A, RV32M import json if typing.TYPE_CHECKING: @@ -48,7 +48,7 @@ class PrivCPU(CPU): """ def __init__(self, conf, mmu: PrivMMU): - super().__init__(conf, [PrivRV32I, RV32M]) + super().__init__(conf, [PrivRV32I, RV32M, RV32A]) self.mode: PrivModes = PrivModes.MACHINE mmu.set_cpu(self)