added config and better loading code to CPU base

assembly-parser-rework
Anton Lydike 3 years ago
parent 2880a59dbb
commit 185ae8b94e

@ -10,13 +10,14 @@ import typing
from typing import List, Type
import riscemu
from . import AssemblyFileLoader, RunConfig
from .MMU import MMU
from .base import BinaryDataMemorySection
from .colors import FMT_CPU, FMT_NONE
from .debug import launch_debug_session
from .exceptions import RiscemuBaseException, LaunchDebuggerException
from .syscall import SyscallInterface, get_syscall_symbols
from .types import CPU
from .types import CPU, ProgramLoader
if typing.TYPE_CHECKING:
from .instructions.instruction_set import InstructionSet
@ -29,14 +30,14 @@ class UserModeCPU(CPU):
It is initialized with a configuration and a list of instruction sets.
"""
def __init__(self, instruction_sets: List[Type['riscemu.InstructionSet']]):
def __init__(self, instruction_sets: List[Type['riscemu.InstructionSet']], conf: RunConfig):
"""
Creates a CPU instance.
:param instruction_sets: A list of instruction set classes. These must inherit from the InstructionSet class
"""
# setup CPU states
super().__init__(MMU(), instruction_sets)
super().__init__(MMU(), instruction_sets, conf)
self.exit_code = 0
@ -104,3 +105,7 @@ class UserModeCPU(CPU):
return False
self.regs.set('sp', stack_sec.base + stack_sec.size)
@classmethod
def get_loaders(cls) -> typing.Iterable[Type[ProgramLoader]]:
return [AssemblyFileLoader]

@ -99,15 +99,14 @@ if __name__ == '__main__':
]
try:
cpu = UserModeCPU(ins_to_load)
cpu = UserModeCPU(ins_to_load, cfg)
opts = AssemblyFileLoader.get_options(sys.argv)
for file in args.files:
loader = AssemblyFileLoader.instantiate(file, opts)
cpu.load_program(loader.parse())
# run the last loaded executable
# set up a stack
cpu.setup_stack(cfg.stack_size)
# launch the last loaded program

@ -3,19 +3,20 @@ RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: MIT
"""
import sys
import time
from riscemu.CPU import *
from .CSR import CSR
from .ElfLoader import ElfBinaryFileLoader
from .Exceptions import *
from .PrivMMU import PrivMMU
from .ImageLoader import MemoryImageLoader
from .PrivRV32I import PrivRV32I
from .privmodes import PrivModes
from ..IO import TextIO
from ..instructions import RV32A, RV32M
from ..types import Program
if typing.TYPE_CHECKING:
from riscemu import types, LoadedExecutable, LoadedInstruction
from riscemu.instructions.instruction_set import InstructionSet
@ -38,21 +39,20 @@ class PrivCPU(CPU):
controls the resolution of the time csr register (in nanoseconds)
"""
INS_XLEN = 4
pending_traps: List[CpuTrap]
"""
Size of an instruction in memory. Should be 4, but since our loading code is shit, instruction take up
the equivalent of "1 byte" (this is actually impossible)
A list of traps which are pending to be handled
"""
def __init__(self, conf):
super().__init__(conf, [PrivRV32I, RV32M, RV32A])
super().__init__(MMU(), [PrivRV32I, RV32M, RV32A], conf)
# start in machine mode
self.mode: PrivModes = PrivModes.MACHINE
self.syscall_int = None
self.launch_debug = False
self.pending_traps: List[CpuTrap] = list()
self.exit_code = 0
self._time_start = 0
self._time_timecmp = 0
self._time_interrupt_enabled = False
@ -63,45 +63,37 @@ class PrivCPU(CPU):
# init csr
self._init_csr()
def _run(self, verbose=False):
def run(self, verbose=False):
if self.pc <= 0:
return False
ins = None
launch_debug = False
try:
while not self.exit:
while not self.halted:
self.step(verbose)
except RiscemuBaseException as ex:
if isinstance(ex, LaunchDebuggerException):
self.launch_debug = True
launch_debug = True
self.pc += self.INS_XLEN
if self.exit:
if self.halted:
print()
print(FMT_CPU + "Program exited with code {}".format(self.exit_code) + FMT_NONE)
print(FMT_CPU + "[CPU] System halted with code {}".format(self.exit_code) + FMT_NONE)
sys.exit(self.exit_code)
elif self.launch_debug:
self.launch_debug = False
launch_debug_session(self, self.mmu, self.regs,
"Launching debugger:")
elif launch_debug:
launch_debug_session(self)
if not self.debugger_active:
self._run(verbose)
self.run(verbose)
else:
print()
print(FMT_CPU + "Program stopped without exiting - perhaps you stopped the debugger?" + FMT_NONE)
print(FMT_CPU + "[CPU] System stopped without halting - perhaps you stopped the debugger?" + FMT_NONE)
def load(self, e: riscemu.base_types):
raise NotImplementedError("Not supported!")
def run_loaded(self, le: 'riscemu.LoadedExecutable'):
raise NotImplementedError("Not supported!")
def get_tokenizer(self, tokenizer_input):
raise NotImplementedError("Not supported!")
def run(self, verbose: bool = False):
def launch(self, program: Program, verbose: bool = False):
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(self.conf.verbosity > 1)
self.run(self.conf.verbosity > 1 or verbose)
def _init_csr(self):
# set up CSR
@ -184,7 +176,7 @@ class PrivCPU(CPU):
if not (len(self.pending_traps) > 0 and self.csr.get_mstatus('mie')):
return
# select best interrupt
# TODO: actually select based on the official ranking
# FIXME: actually select based on the official ranking
trap = self.pending_traps.pop() # use the most recent trap
if self.conf.verbosity > 0:
print(FMT_CPU + "[CPU] taking trap {}!".format(trap) + FMT_NONE)
@ -209,7 +201,7 @@ class PrivCPU(CPU):
if mtvec & 0b11 == 1:
self.pc = (mtvec & 0b11111111111111111111111111111100) + (trap.code * 4)
self.record_perf_profile()
if len(self._perf_counters) % 100 == 0:
if len(self._perf_counters) > 100:
self.show_perf()
def show_perf(self):
@ -225,11 +217,6 @@ class PrivCPU(CPU):
continue
cps = (cycle - cycled) / (time_ns - timed) * 1000000000
# print(" {:03d} cycles in {:08d}ns ({:.2f} cycles/s)".format(
# cycle - cycled,
# time_ns - timed,
# cps
# ))
cycled = cycle
timed = time_ns
cps_list.append(cps)
@ -238,3 +225,9 @@ class PrivCPU(CPU):
def record_perf_profile(self):
self._perf_counters.append((time.perf_counter_ns(), self.cycle))
@classmethod
def get_loaders(cls) -> typing.Iterable[Type[ProgramLoader]]:
return [
AssemblyFileLoader, MemoryImageLoader, ElfBinaryFileLoader
]

@ -1,7 +1,6 @@
from .PrivCPU import PrivCPU, RunConfig
from .ImageLoader import MemoryImageMMU
from .PrivMMU import LoadedElfMMU
from .ElfLoader import ElfExecutable
from .PrivCPU import PrivCPU
from .ElfLoader import ElfBinaryFileLoader
from .ImageLoader import MemoryImageLoader
import sys

@ -15,6 +15,7 @@ from collections import defaultdict
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple, Set, Union, Iterator, Callable, Type
from . import RunConfig
from .colors import FMT_MEM, FMT_NONE, FMT_UNDERLINE, FMT_ORANGE, FMT_RED, FMT_BOLD
from .exceptions import ParseException
from .helpers import format_bytes, get_section_base_name
@ -371,9 +372,13 @@ class CPU(ABC):
instructions: Dict[str, Callable[[Instruction], None]]
instruction_sets: Set['InstructionSet']
def __init__(self, mmu: 'MMU', instruction_sets: List[Type['InstructionSet']]):
# configuration
conf: RunConfig
def __init__(self, mmu: 'MMU', instruction_sets: List[Type['InstructionSet']], conf: RunConfig):
self.mmu = mmu
self.regs = Registers()
self.conf = conf
self.instruction_sets = set()
self.instructions = dict()
@ -433,3 +438,11 @@ class CPU(ABC):
self.pc = program.entrypoint
self.run(verbose)
@classmethod
@abstractmethod
def get_loaders(cls) -> typing.Iterable[Type[ProgramLoader]]:
pass
def get_best_loader_for(self, file_name: str) -> Type[ProgramLoader]:
return max(self.get_loaders(), key=lambda ld: ld.can_parse(file_name))

Loading…
Cancel
Save