[PrivCPU] overhaul of instruction cycle, adding more CSR interaction

kernel-mode
Anton Lydike 4 years ago
parent 7239212729
commit 85af9b992f

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

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

Loading…
Cancel
Save