|
|
|
@ -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
|
|
|
|
|