diff --git a/riscemu/priv/Exceptions.py b/riscemu/priv/Exceptions.py index 37388ad..60e653a 100644 --- a/riscemu/priv/Exceptions.py +++ b/riscemu/priv/Exceptions.py @@ -3,8 +3,17 @@ from typing import Optional class CpuTrap(BaseException): code: int + """ + 31-bit value encoding the exception code in the mstatus register + """ interrupt: int + """ + The isInterrupt bit in the mstatus register + """ mtval: int + """ + contents of the mtval register + """ def __init__(self, interrupt: int, code: int, mtval=0): assert 0 <= interrupt <= 1 diff --git a/riscemu/priv/PrivCPU.py b/riscemu/priv/PrivCPU.py index 43fd4df..6654b92 100644 --- a/riscemu/priv/PrivCPU.py +++ b/riscemu/priv/PrivCPU.py @@ -3,21 +3,16 @@ RiscEmu (c) 2021 Anton Lydike SPDX-License-Identifier: MIT """ +import time from riscemu.CPU import * -from enum import IntEnum -from riscemu.Executable import LoadedMemorySection -from .Exceptions import * from .CSR import CSR -from .PrivRV32I import PrivRV32I -from ..instructions.RV32M import RV32M -from .PrivMMU import PrivMMU from .ElfLoader import ElfExecutable +from .Exceptions import * +from .PrivMMU import PrivMMU +from .PrivRV32I import PrivRV32I from .privmodes import PrivModes - -from collections import defaultdict - -from typing import Union +from ..instructions.RV32M import RV32M if typing.TYPE_CHECKING: from riscemu import Executable, LoadedExecutable, LoadedInstruction @@ -33,6 +28,14 @@ class PrivCPU(CPU): """ csr: CSR + """ + Reference to the control and status registers + """ + + TIME_RESOLUTION_NS: int = 1000000 + """ + controls the resolution of the time csr register (in nanoseconds) + """ INS_XLEN = 4 """ @@ -49,6 +52,14 @@ class PrivCPU(CPU): self.pc = kernel.run_ptr self.syscall_int = None + self.launch_debug = False + + self.pending_traps: List[CpuTrap] = list() + + self._time_start = 0 + self._time_timecmp = 0 + self._time_interrupt_enabled = False + # init csr self._init_csr() @@ -58,59 +69,23 @@ class PrivCPU(CPU): ins = None try: while not self.exit: - try: - self.cycle += 1 - ins = self.mmu.read_ins(self.pc) - if verbose: - print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins)) - self.run_instruction(ins) - self.pc += self.INS_XLEN - except CpuTrap as trap: - mie = self.csr.get_mstatus('mie') - if not mie: - print("Caught trap while mie={}!".format(mie)) - # TODO: handle this a lot better - continue - # raise trap - - # caught a trap! - self.csr.set('mepc', self.pc) # store MEPC - self.csr.set_mstatus('mpp', self.mode) # save mpp - self.csr.set_mstatus('mpie', mie) # save mie - self.csr.set_mstatus('mie', 0) # disable further interrupts - self.csr.set('mcause', trap.mcause) # store cause - - # set mtval csr - self.csr.set('mtval', trap.mtval) - - # set priv mode to machine - self.mode = PrivModes.MACHINE - - # trap vector - mtvec = self.csr.get('mtvec') - if mtvec & 3 == 1: - # vectored mode! - self.pc = (mtvec >> 2) + (self.INS_XLEN * trap.code) - else: - # standard mode - self.pc = (mtvec >> 2) + self.step(verbose=False) except RiscemuBaseException as ex: - if not isinstance(ex, LaunchDebuggerException): + if isinstance(ex, LaunchDebuggerException): + self.launch_debug = True + else: print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc - 1, ins) + FMT_NONE) print(ex.message()) - self.pc -= 1 - - if self.active_debug: - print(FMT_CPU + "[CPU] Returning to debugger!" + FMT_NONE) - return - if self.conf.debug_on_exception: - launch_debug_session(self, self.mmu, self.regs, - "Exception encountered, launching debug:") - + if self.conf.debug_on_exception: + self.launch_debug = True if self.exit: print() print(FMT_CPU + "Program exited with code {}".format(self.exit_code) + FMT_NONE) sys.exit(self.exit_code) + elif self.launch_debug: + launch_debug_session(self, self.mmu, self.regs, + "Launching debugger:") + self._run(verbose) else: print() print(FMT_CPU + "Program stopped without exiting - perhaps you stopped the debugger?" + FMT_NONE) @@ -126,6 +101,7 @@ class PrivCPU(CPU): def run(self): print(FMT_CPU + '[CPU] Started running from 0x{:08X} ({})'.format(self.pc, "kernel") + FMT_NONE) + self._time_start = time.perf_counter_ns() // self.TIME_RESOLUTION_NS self._run(True) def _init_csr(self): @@ -133,10 +109,12 @@ class PrivCPU(CPU): self.csr = CSR() self.csr.set('mhartid', 0) # core id # TODO: set correct value - self.csr.set('mimpid', 1) # implementation id + self.csr.set('mimpid', 0) # implementation id # set mxl to 1 (32 bit) and set bits for i and m isa self.csr.set('misa', (1 << 30) + (1 << 8) + (1 << 12)) # available ISA + # CSR write callbacks: + @self.csr.callback('halt') def halt(old: int, new: int): if new != 0: @@ -146,3 +124,52 @@ class PrivCPU(CPU): @self.csr.callback('mstatus') def mstatus(old: int, new: int): pass + + @self.csr.callback('mtimecmp') + def mtimecmp(old, new): + self._time_timecmp = (self.csr.get('mtimecmph') << 32) + new + self._time_interrupt_enabled = True + + @self.csr.callback('mtimecmph') + def mtimecmp(old, new): + self._time_timecmp = (new << 32) + self.csr.get('mtimecmp') + self._time_interrupt_enabled = True + + # virtual CSR registers: + + @self.csr.virtual_register('time') + def get_time(): + return (time.perf_counter_ns() // self.TIME_RESOLUTION_NS) & (2 ** 32 - 1) + + @self.csr.virtual_register('timeh') + def get_timeh(): + return (time.perf_counter_ns() // self.TIME_RESOLUTION_NS) >> 32 + + # add minstret and mcycle counters + + def _handle_trap(self, trap: CpuTrap): + # implement trap handling! + self.pending_traps.append(trap) + + def step(self, verbose = True): + try: + self.cycle += 1 + self._timer_step() + self._check_interrupt() + ins = self.mmu.read_ins(self.pc) + if verbose: + print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins)) + self.run_instruction(ins) + self.pc += self.INS_XLEN + except CpuTrap as trap: + self._handle_trap(trap) + + def _timer_step(self): + if not self._time_interrupt_enabled: + return + if self._time_timecmp < (time.perf_counter_ns() // self.TIME_RESOLUTION_NS) - self._time_start: + self.pending_traps.append(CpuTrap(1, 7, 0)) + self._time_interrupt_enabled = False + + def _check_interrupt(self): + pass