[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): class CpuTrap(BaseException):
code: int code: int
"""
31-bit value encoding the exception code in the mstatus register
"""
interrupt: int interrupt: int
"""
The isInterrupt bit in the mstatus register
"""
mtval: int mtval: int
"""
contents of the mtval register
"""
def __init__(self, interrupt: int, code: int, mtval=0): def __init__(self, interrupt: int, code: int, mtval=0):
assert 0 <= interrupt <= 1 assert 0 <= interrupt <= 1

@ -3,21 +3,16 @@ RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: MIT SPDX-License-Identifier: MIT
""" """
import time
from riscemu.CPU import * from riscemu.CPU import *
from enum import IntEnum
from riscemu.Executable import LoadedMemorySection
from .Exceptions import *
from .CSR import CSR from .CSR import CSR
from .PrivRV32I import PrivRV32I
from ..instructions.RV32M import RV32M
from .PrivMMU import PrivMMU
from .ElfLoader import ElfExecutable from .ElfLoader import ElfExecutable
from .Exceptions import *
from .PrivMMU import PrivMMU
from .PrivRV32I import PrivRV32I
from .privmodes import PrivModes from .privmodes import PrivModes
from ..instructions.RV32M import RV32M
from collections import defaultdict
from typing import Union
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from riscemu import Executable, LoadedExecutable, LoadedInstruction from riscemu import Executable, LoadedExecutable, LoadedInstruction
@ -33,6 +28,14 @@ class PrivCPU(CPU):
""" """
csr: CSR 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 INS_XLEN = 4
""" """
@ -49,6 +52,14 @@ class PrivCPU(CPU):
self.pc = kernel.run_ptr self.pc = kernel.run_ptr
self.syscall_int = None 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 # init csr
self._init_csr() self._init_csr()
@ -58,59 +69,23 @@ class PrivCPU(CPU):
ins = None ins = None
try: try:
while not self.exit: while not self.exit:
try: self.step(verbose=False)
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)
except RiscemuBaseException as ex: 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(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc - 1, ins) + FMT_NONE)
print(ex.message()) print(ex.message())
self.pc -= 1 if self.conf.debug_on_exception:
self.launch_debug = True
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.exit: if self.exit:
print() print()
print(FMT_CPU + "Program exited with code {}".format(self.exit_code) + FMT_NONE) print(FMT_CPU + "Program exited with code {}".format(self.exit_code) + FMT_NONE)
sys.exit(self.exit_code) sys.exit(self.exit_code)
elif self.launch_debug:
launch_debug_session(self, self.mmu, self.regs,
"Launching debugger:")
self._run(verbose)
else: else:
print() print()
print(FMT_CPU + "Program stopped without exiting - perhaps you stopped the debugger?" + FMT_NONE) print(FMT_CPU + "Program stopped without exiting - perhaps you stopped the debugger?" + FMT_NONE)
@ -126,6 +101,7 @@ class PrivCPU(CPU):
def run(self): def run(self):
print(FMT_CPU + '[CPU] Started running from 0x{:08X} ({})'.format(self.pc, "kernel") + FMT_NONE) 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) self._run(True)
def _init_csr(self): def _init_csr(self):
@ -133,10 +109,12 @@ class PrivCPU(CPU):
self.csr = CSR() self.csr = CSR()
self.csr.set('mhartid', 0) # core id self.csr.set('mhartid', 0) # core id
# TODO: set correct value # 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 # 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 self.csr.set('misa', (1 << 30) + (1 << 8) + (1 << 12)) # available ISA
# CSR write callbacks:
@self.csr.callback('halt') @self.csr.callback('halt')
def halt(old: int, new: int): def halt(old: int, new: int):
if new != 0: if new != 0:
@ -146,3 +124,52 @@ class PrivCPU(CPU):
@self.csr.callback('mstatus') @self.csr.callback('mstatus')
def mstatus(old: int, new: int): def mstatus(old: int, new: int):
pass 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