From 3ce42079d44d0ae8c28d0ba74a16b28485d8d6e4 Mon Sep 17 00:00:00 2001 From: Anton Lydike Date: Sun, 18 Apr 2021 23:14:53 +0200 Subject: [PATCH] refactored instruction sets to be modular --- fibs.asm | 29 --- riscemu/CPU.py | 341 +++---------------------- riscemu/Instructions/InstructionSet.py | 46 ++++ riscemu/Instructions/RV32I.py | 293 +++++++++++++++++++++ riscemu/Instructions/__init__.py | 0 riscemu/Tokenizer.py | 12 +- riscemu/__main__.py | 5 +- 7 files changed, 383 insertions(+), 343 deletions(-) delete mode 100644 fibs.asm create mode 100644 riscemu/Instructions/InstructionSet.py create mode 100644 riscemu/Instructions/RV32I.py create mode 100644 riscemu/Instructions/__init__.py diff --git a/fibs.asm b/fibs.asm deleted file mode 100644 index ca485dc..0000000 --- a/fibs.asm +++ /dev/null @@ -1,29 +0,0 @@ - .stack 0xFFFF - .data 0x200 - -fibs: .space 56 - - .text -main: -func_fibs: - addi s1, zero, 0 # storage index - addi s2, zero, 56 # last storage index - addi t0, zero, 1 # t0 = F_{i} - addi t1, zero, 1 # t1 = F_{i+1} -loop: - sw t0, fibs(s1) # save - add t2, t1, t0 # t2 = F_{i+2} - addi t0, t1, 0 # t0 = t1 - addi t1, t2, 0 # t1 = t2 - addi s1, s1, 4 # increment storage pointer - blt s1, s2, loop # loop as long as we did not reach array length - # exit gracefully - addi a0, zero, 0 - addi a7, zero, 93 - ebreak # launch debugger - scall # exit with code 0 - - -.set test_val, 0xFF - -.global func_fibs \ No newline at end of file diff --git a/riscemu/CPU.py b/riscemu/CPU.py index 0db3423..063a925 100644 --- a/riscemu/CPU.py +++ b/riscemu/CPU.py @@ -1,40 +1,62 @@ import traceback -from typing import Tuple +from typing import Tuple, List, Dict, Callable +from .Tokenizer import RiscVTokenizer + +from .Syscall import * from .Exceptions import * -from .helpers import * +from .MMU import MMU from .Config import RunConfig from .Registers import Registers -from .Syscall import SyscallInterface, Syscall from .debug import launch_debug_session import typing if typing.TYPE_CHECKING: - from . import MMU, Executable, LoadedExecutable, LoadedInstruction + from . import Executable, LoadedExecutable, LoadedInstruction + from .Instructions.InstructionSet import InstructionSet class CPU: - def __init__(self, conf: RunConfig): - from . import MMU + def __init__(self, conf: RunConfig, instruction_sets: List['InstructionSet']): + # setup CPU states self.pc = 0 self.cycle = 0 self.exit = False self.exit_code = 0 self.conf = conf + # setup MMU, registers and syscall handlers self.mmu = MMU(conf) self.regs = Registers(conf) self.syscall_int = SyscallInterface() + # load all instruction sets + self.sets = instruction_sets + self.instructions: Dict[str, Callable[[LoadedInstruction], None]] = dict() + for ins_set in instruction_sets: + self.instructions.update(ins_set.load(self)) + # provide global syscall symbols if option is set if conf.include_scall_symbols: self.mmu.global_symbols.update(self.syscall_int.get_syscall_symbols()) + def get_tokenizer(self, input): + """ + Returns a tokenizer that respects the language of the CPU + """ + return RiscVTokenizer(input, self.all_instructions()) + def load(self, e: 'Executable'): + """ + Load an executable into Memory + """ return self.mmu.load_bin(e) def run_loaded(self, le: 'LoadedExecutable'): + """ + Run a loaded executable + """ self.pc = le.run_ptr sp, hp = le.stack_heap self.regs.set('sp', sp) @@ -55,7 +77,6 @@ class CPU: except RiscemuBaseException as ex: print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc-1, ins) + FMT_NONE) print(" " + ex.message()) - #traceback.print_exception(type(ex), ex, ex.__traceback__) if self.conf.debug_on_exception: launch_debug_session(self, self.mmu, self.regs, "Exception encountered, launching debug:".format(self.pc-1)) @@ -63,14 +84,13 @@ class CPU: print(FMT_CPU + "Program exited with code {}".format(self.exit_code) + FMT_NONE) def __run_instruction(self, ins: 'LoadedInstruction'): - name = '_CPU__instruction_' + ins.name - if hasattr(self, name): - getattr(self, name)(ins) + if ins.name in self.instructions: + self.instructions[ins.name](ins) else: - # this should never be reached, as unknown instructions are imparsable + # this should never be reached, as unknown instructions are imparseable raise RuntimeError("Unknown instruction: {}".format(ins)) - def __parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]: + def parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]: """ parses both rd, rs1, imm and rd, imm(rs1) arguments and returns (rd, imm+rs1) (so a register and address tuple for memory instructions) @@ -87,299 +107,12 @@ class CPU: rd = ins.get_reg(0) return rd, self.regs.get(rs1) + imm - def __instruction_lb(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1))) - - def __instruction_lh(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2))) - - def __instruction_lw(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 4))) - - def __instruction_lbu(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1), unsigned=True)) - - def __instruction_lhu(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2), unsigned=True)) - - def __instruction_sb(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.mmu.write(addr, 1, int_to_bytes(self.regs.get(rd), 1)) - - def __instruction_sh(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.mmu.write(addr, 2, int_to_bytes(self.regs.get(rd), 2)) - - def __instruction_sw(self, ins: 'LoadedInstruction'): - rd, addr = self.__parse_mem_ins(ins) - self.mmu.write(addr, 4, int_to_bytes(self.regs.get(rd), 4)) - - def __instruction_sll(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - to_signed(to_unsigned(self.regs.get(src1)) << (self.regs.get(src2) & 0b11111)) - ) - - def __instruction_slli(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - imm = ins.get_imm(2) - self.regs.set( - dst, - to_signed(to_unsigned(self.regs.get(src1)) << (imm & 0b11111)) - ) - - def __instruction_srl(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - to_signed(to_unsigned(self.regs.get(src1)) >> (self.regs.get(src2) & 0b11111)) - ) - - def __instruction_srli(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - imm = ins.get_imm(2) - self.regs.set( - dst, - to_signed(to_unsigned(self.regs.get(src1)) >> (imm & 0b11111)) - ) - - def __instruction_sra(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - self.regs.get(src1) >> (self.regs.get(src2) & 0b11111) - ) - - def __instruction_srai(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - imm = ins.get_imm(2) - self.regs.set( - dst, - self.regs.get(src1) >> (imm & 0b11111) - ) - - def __instruction_add(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - self.regs.get(src1) + self.regs.get(src2) - ) - - def __instruction_addi(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - imm = ins.get_imm(2) - self.regs.set( - dst, - self.regs.get(src1) + imm - ) - - def __instruction_sub(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - self.regs.get(src1) - self.regs.get(src2) - ) - - def __instruction_lui(self, ins: 'LoadedInstruction'): - INS_NOT_IMPLEMENTED(ins) - - def __instruction_auipc(self, ins: 'LoadedInstruction'): - INS_NOT_IMPLEMENTED(ins) - - def __instruction_xor(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - self.regs.get(src1) ^ self.regs.get(src2) - ) - - def __instruction_xori(self, ins: 'LoadedInstruction'): - INS_NOT_IMPLEMENTED(ins) - - def __instruction_or(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - self.regs.get(src1) | self.regs.get(src2) - ) - - def __instruction_ori(self, ins: 'LoadedInstruction'): - INS_NOT_IMPLEMENTED(ins) - - def __instruction_and(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - self.regs.get(src1) & self.regs.get(src2) - ) - - def __instruction_andi(self, ins: 'LoadedInstruction'): - INS_NOT_IMPLEMENTED(ins) - - def __instruction_slt(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - int(self.regs.get(src1) < self.regs.get(src2)) - ) - - def __instruction_slti(self, ins: 'LoadedInstruction'): - INS_NOT_IMPLEMENTED(ins) - - def __instruction_sltu(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - dst = ins.get_reg(0) - src1 = ins.get_reg(1) - src2 = ins.get_reg(2) - self.regs.set( - dst, - int(to_unsigned(self.regs.get(src1)) < to_unsigned(self.regs.get(src2))) - ) - - def __instruction_sltiu(self, ins: 'LoadedInstruction'): - INS_NOT_IMPLEMENTED(ins) - - def __instruction_beq(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - reg1 = ins.get_reg(0) - reg2 = ins.get_reg(1) - dest = ins.get_imm(2) - if self.regs.get(reg1) == self.regs.get(reg2): - self.pc = dest - - def __instruction_bne(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - reg1 = ins.get_reg(0) - reg2 = ins.get_reg(1) - dest = ins.get_imm(2) - if self.regs.get(reg1) != self.regs.get(reg2): - self.pc = dest - - def __instruction_blt(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - reg1 = ins.get_reg(0) - reg2 = ins.get_reg(1) - dest = ins.get_imm(2) - if self.regs.get(reg1) < self.regs.get(reg2): - self.pc = dest - - def __instruction_bge(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - reg1 = ins.get_reg(0) - reg2 = ins.get_reg(1) - dest = ins.get_imm(2) - if self.regs.get(reg1) >= self.regs.get(reg2): - self.pc = dest - - def __instruction_bltu(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - reg1 = to_unsigned(ins.get_reg(0)) - reg2 = to_unsigned(ins.get_reg(1)) - dest = ins.get_imm(2) - if self.regs.get(reg1) < self.regs.get(reg2): - self.pc = dest - - def __instruction_bgeu(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 3) - reg1 = to_unsigned(ins.get_reg(0)) - reg2 = to_unsigned(ins.get_reg(1)) - dest = ins.get_imm(2) - if self.regs.get(reg1) >= self.regs.get(reg2): - self.pc = dest - - def __instruction_j(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 1) - addr = ins.get_imm(0) - self.pc = addr - - def __instruction_jal(self, ins: 'LoadedInstruction'): - reg = 'ra' # default register is ra - if len(ins.args) == 1: - addr = ins.get_imm(0) - else: - ASSERT_LEN(ins.args, 2) - reg = ins.get_reg(0) - addr = ins.get_imm(1) - self.regs.set(reg, self.pc) - self.pc = addr - - def __instruction_jalr(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 2) - reg = ins.get_reg(0) - addr = ins.get_imm(1) - self.regs.set(reg, self.pc) - self.pc = addr - - def __instruction_ret(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 0) - self.pc = self.regs.get('ra') - - def __instruction_ecall(self, ins: 'LoadedInstruction'): - self.__instruction_scall(ins) - - def __instruction_ebreak(self, ins: 'LoadedInstruction'): - self.__instruction_sbreak(ins) - - def __instruction_scall(self, ins: 'LoadedInstruction'): - ASSERT_LEN(ins.args, 0) - syscall = Syscall(self.regs.get('a7'), self.regs, self) - self.syscall_int.handle_syscall(syscall) - - def __instruction_sbreak(self, ins: 'LoadedInstruction'): - launch_debug_session(self, self.mmu, self.regs, "Debug instruction encountered at 0x{:08X}".format(self.pc)) - - def __instruction_nop(self, ins: 'LoadedInstruction'): - pass - - @staticmethod - def all_instructions(): - for method in vars(CPU): - if method.startswith('_CPU__instruction_'): - yield method[18:] + def all_instructions(self) -> List[str]: + return list(self.instructions.keys()) def __repr__(self): - return "CPU(pc=0x{:08X}, cycle={})".format( + return "CPU(pc=0x{:08X}, cycle={}, instructions={})".format( self.pc, - self.cycle + self.cycle, + " ".join(s.name for s in self.sets) ) \ No newline at end of file diff --git a/riscemu/Instructions/InstructionSet.py b/riscemu/Instructions/InstructionSet.py new file mode 100644 index 0000000..2f6bd8d --- /dev/null +++ b/riscemu/Instructions/InstructionSet.py @@ -0,0 +1,46 @@ +import typing + +from abc import ABC, abstractmethod +from ..CPU import * + + +class InstructionSet(ABC): + """ + Represents a collection of instructions + """ + def __init__(self, name): + self.name = name + self.cpu: typing.Optional['CPU'] = None + self.mmu: typing.Optional['MMU'] = None + self.regs: typing.Optional['Registers'] = None + + def load(self, cpu: 'CPU'): + self.cpu = cpu + self.mmu = cpu.mmu + self.regs = cpu.regs + + return { + name: ins for name, ins in self.get_instructions() + } + + def get_instructions(self): + for member in dir(self): + if member.startswith('instruction_'): + yield member[12:].replace('_', '.'), getattr(self, member) + + def parse_mem_ins(self, ins: 'LoadedInstruction'): + return self.cpu.parse_mem_ins(ins) + + @property + def pc(self): + return self.cpu.pc + + @pc.setter + def pc(self, val): + self.cpu.pc = val + + def __repr__(self): + return "InstructionSet[{}] with {} instructions".format( + self.name, + len(list(self.get_instructions())) + ) \ No newline at end of file diff --git a/riscemu/Instructions/RV32I.py b/riscemu/Instructions/RV32I.py new file mode 100644 index 0000000..59236f0 --- /dev/null +++ b/riscemu/Instructions/RV32I.py @@ -0,0 +1,293 @@ +from .InstructionSet import * + +from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed + +class RV32I(InstructionSet): + def __init__(self): + super().__init__('RV32I') + + def instruction_lb(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1))) + + def instruction_lh(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2))) + + def instruction_lw(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 4))) + + def instruction_lbu(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1), unsigned=True)) + + def instruction_lhu(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2), unsigned=True)) + + def instruction_sb(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.mmu.write(addr, 1, int_to_bytes(self.regs.get(rd), 1)) + + def instruction_sh(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.mmu.write(addr, 2, int_to_bytes(self.regs.get(rd), 2)) + + def instruction_sw(self, ins: 'LoadedInstruction'): + rd, addr = self.parse_mem_ins(ins) + self.mmu.write(addr, 4, int_to_bytes(self.regs.get(rd), 4)) + + def instruction_sll(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + to_signed(to_unsigned(self.regs.get(src1)) << (self.regs.get(src2) & 0b11111)) + ) + + def instruction_slli(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + imm = ins.get_imm(2) + self.regs.set( + dst, + to_signed(to_unsigned(self.regs.get(src1)) << (imm & 0b11111)) + ) + + def instruction_srl(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + to_signed(to_unsigned(self.regs.get(src1)) >> (self.regs.get(src2) & 0b11111)) + ) + + def instruction_srli(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + imm = ins.get_imm(2) + self.regs.set( + dst, + to_signed(to_unsigned(self.regs.get(src1)) >> (imm & 0b11111)) + ) + + def instruction_sra(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + self.regs.get(src1) >> (self.regs.get(src2) & 0b11111) + ) + + def instruction_srai(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + imm = ins.get_imm(2) + self.regs.set( + dst, + self.regs.get(src1) >> (imm & 0b11111) + ) + + def instruction_add(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + self.regs.get(src1) + self.regs.get(src2) + ) + + def instruction_addi(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + imm = ins.get_imm(2) + self.regs.set( + dst, + self.regs.get(src1) + imm + ) + + def instruction_sub(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + self.regs.get(src1) - self.regs.get(src2) + ) + + def instruction_lui(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_auipc(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_xor(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + self.regs.get(src1) ^ self.regs.get(src2) + ) + + def instruction_xori(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_or(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + self.regs.get(src1) | self.regs.get(src2) + ) + + def instruction_ori(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_and(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + self.regs.get(src1) & self.regs.get(src2) + ) + + def instruction_andi(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_slt(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + int(self.regs.get(src1) < self.regs.get(src2)) + ) + + def instruction_slti(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_sltu(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + dst = ins.get_reg(0) + src1 = ins.get_reg(1) + src2 = ins.get_reg(2) + self.regs.set( + dst, + int(to_unsigned(self.regs.get(src1)) < to_unsigned(self.regs.get(src2))) + ) + + def instruction_sltiu(self, ins: 'LoadedInstruction'): + INS_NOT_IMPLEMENTED(ins) + + def instruction_beq(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + reg1 = ins.get_reg(0) + reg2 = ins.get_reg(1) + dest = ins.get_imm(2) + if self.regs.get(reg1) == self.regs.get(reg2): + self.pc = dest + + def instruction_bne(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + reg1 = ins.get_reg(0) + reg2 = ins.get_reg(1) + dest = ins.get_imm(2) + if self.regs.get(reg1) != self.regs.get(reg2): + self.pc = dest + + def instruction_blt(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + reg1 = ins.get_reg(0) + reg2 = ins.get_reg(1) + dest = ins.get_imm(2) + if self.regs.get(reg1) < self.regs.get(reg2): + self.pc = dest + + def instruction_bge(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + reg1 = ins.get_reg(0) + reg2 = ins.get_reg(1) + dest = ins.get_imm(2) + if self.regs.get(reg1) >= self.regs.get(reg2): + self.pc = dest + + def instruction_bltu(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + reg1 = to_unsigned(ins.get_reg(0)) + reg2 = to_unsigned(ins.get_reg(1)) + dest = ins.get_imm(2) + if self.regs.get(reg1) < self.regs.get(reg2): + self.pc = dest + + def instruction_bgeu(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 3) + reg1 = to_unsigned(ins.get_reg(0)) + reg2 = to_unsigned(ins.get_reg(1)) + dest = ins.get_imm(2) + if self.regs.get(reg1) >= self.regs.get(reg2): + self.pc = dest + + # technically deprecated + def instruction_j(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 1) + addr = ins.get_imm(0) + self.pc = addr + + def instruction_jal(self, ins: 'LoadedInstruction'): + reg = 'ra' # default register is ra + if len(ins.args) == 1: + addr = ins.get_imm(0) + else: + ASSERT_LEN(ins.args, 2) + reg = ins.get_reg(0) + addr = ins.get_imm(1) + self.regs.set(reg, self.pc) + self.pc = addr + + def instruction_jalr(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 2) + reg = ins.get_reg(0) + addr = ins.get_imm(1) + self.regs.set(reg, self.pc) + self.pc = addr + + def instruction_ret(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 0) + self.pc = self.regs.get('ra') + + def instruction_ecall(self, ins: 'LoadedInstruction'): + self.instruction_scall(ins) + + def instruction_ebreak(self, ins: 'LoadedInstruction'): + self.instruction_sbreak(ins) + + def instruction_scall(self, ins: 'LoadedInstruction'): + ASSERT_LEN(ins.args, 0) + syscall = Syscall(self.regs.get('a7'), self.regs, self.cpu) + self.cpu.syscall_int.handle_syscall(syscall) + + def instruction_sbreak(self, ins: 'LoadedInstruction'): + launch_debug_session(self.cpu, self.mmu, self.regs, "Debug instruction encountered at 0x{:08X}".format(self.pc)) + + def instruction_nop(self, ins: 'LoadedInstruction'): + pass \ No newline at end of file diff --git a/riscemu/Instructions/__init__.py b/riscemu/Instructions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/riscemu/Tokenizer.py b/riscemu/Tokenizer.py index ec6e0dd..434309f 100644 --- a/riscemu/Tokenizer.py +++ b/riscemu/Tokenizer.py @@ -2,13 +2,8 @@ import re from enum import IntEnum from typing import List -from .CPU import CPU, Registers from .Exceptions import ParseException -REGISTERS = list(Registers.all_registers()) - -INSTRUCTIONS = list(CPU.all_instructions()) - PSEUDO_OPS = [ '.asciiz', '.double', @@ -245,10 +240,11 @@ class RiscVPseudoOpToken(RiscVToken): class RiscVTokenizer: - def __init__(self, input: RiscVInput): + def __init__(self, input: RiscVInput, instructions: List[str]): self.input = input self.tokens: List[RiscVToken] = [] self.name = input.name + self.instructions = instructions def tokenize(self): while self.input.has_next(): @@ -268,7 +264,7 @@ class RiscVTokenizer: self.parse_comment() # must be instruction - elif self.input.peek_one_of(INSTRUCTIONS): + elif self.input.peek_one_of(self.instructions): self.parse_instruction() else: token = self.input.peek(size=5) @@ -295,7 +291,7 @@ class RiscVTokenizer: self.input.context())) def parse_instruction(self): - ins = self.input.consume_one_of(INSTRUCTIONS) + ins = self.input.consume_one_of(self.instructions) args = [] self.input.consume_whitespace(linebreak=False) while self.input.peek(regex=REG_VALID_ARGUMENT) and len(args) < 3: diff --git a/riscemu/__main__.py b/riscemu/__main__.py index 357e497..9babc36 100644 --- a/riscemu/__main__.py +++ b/riscemu/__main__.py @@ -1,6 +1,7 @@ if __name__ == '__main__': from . import * from .helpers import * + from .Instructions.RV32I import RV32I import argparse import sys @@ -60,10 +61,10 @@ if __name__ == '__main__': FMT_PRINT = FMT_BOLD + FMT_MAGENTA try: - cpu = CPU(cfg) + cpu = CPU(cfg, [RV32I()]) loaded_exe = None for file in args.files: - tk = RiscVTokenizer(RiscVInput.from_file(file)) + tk = cpu.get_tokenizer(RiscVInput.from_file(file)) tk.tokenize() loaded_exe = cpu.load(ExecutableParser(tk).parse()) # run the last loaded executable