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 from typing import List, Type
import riscemu import riscemu
from . import AssemblyFileLoader, RunConfig
from .MMU import MMU from .MMU import MMU
from .base import BinaryDataMemorySection from .base import BinaryDataMemorySection
from .colors import FMT_CPU, FMT_NONE from .colors import FMT_CPU, FMT_NONE
from .debug import launch_debug_session from .debug import launch_debug_session
from .exceptions import RiscemuBaseException, LaunchDebuggerException from .exceptions import RiscemuBaseException, LaunchDebuggerException
from .syscall import SyscallInterface, get_syscall_symbols from .syscall import SyscallInterface, get_syscall_symbols
from .types import CPU from .types import CPU, ProgramLoader
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from .instructions.instruction_set import InstructionSet 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. 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. Creates a CPU instance.
:param instruction_sets: A list of instruction set classes. These must inherit from the InstructionSet class :param instruction_sets: A list of instruction set classes. These must inherit from the InstructionSet class
""" """
# setup CPU states # setup CPU states
super().__init__(MMU(), instruction_sets) super().__init__(MMU(), instruction_sets, conf)
self.exit_code = 0 self.exit_code = 0
@ -104,3 +105,7 @@ class UserModeCPU(CPU):
return False return False
self.regs.set('sp', stack_sec.base + stack_sec.size) 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: try:
cpu = UserModeCPU(ins_to_load) cpu = UserModeCPU(ins_to_load, cfg)
opts = AssemblyFileLoader.get_options(sys.argv) opts = AssemblyFileLoader.get_options(sys.argv)
for file in args.files: for file in args.files:
loader = AssemblyFileLoader.instantiate(file, opts) loader = AssemblyFileLoader.instantiate(file, opts)
cpu.load_program(loader.parse()) cpu.load_program(loader.parse())
# run the last loaded executable
# set up a stack
cpu.setup_stack(cfg.stack_size) cpu.setup_stack(cfg.stack_size)
# launch the last loaded program # launch the last loaded program

@ -3,19 +3,20 @@ RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: MIT SPDX-License-Identifier: MIT
""" """
import sys
import time import time
from riscemu.CPU import * from riscemu.CPU import *
from .CSR import CSR from .CSR import CSR
from .ElfLoader import ElfBinaryFileLoader
from .Exceptions import * from .Exceptions import *
from .PrivMMU import PrivMMU from .ImageLoader import MemoryImageLoader
from .PrivRV32I import PrivRV32I from .PrivRV32I import PrivRV32I
from .privmodes import PrivModes from .privmodes import PrivModes
from ..IO import TextIO
from ..instructions import RV32A, RV32M from ..instructions import RV32A, RV32M
from ..types import Program
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from riscemu import types, LoadedExecutable, LoadedInstruction
from riscemu.instructions.instruction_set import InstructionSet from riscemu.instructions.instruction_set import InstructionSet
@ -38,21 +39,20 @@ class PrivCPU(CPU):
controls the resolution of the time csr register (in nanoseconds) 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 A list of traps which are pending to be handled
the equivalent of "1 byte" (this is actually impossible)
""" """
def __init__(self, conf): def __init__(self, conf):
super().__init__(conf, [PrivRV32I, RV32M, RV32A]) super().__init__(MMU(), [PrivRV32I, RV32M, RV32A], conf)
# start in machine mode # start in machine mode
self.mode: PrivModes = PrivModes.MACHINE self.mode: PrivModes = PrivModes.MACHINE
self.syscall_int = None
self.launch_debug = False
self.pending_traps: List[CpuTrap] = list() self.pending_traps: List[CpuTrap] = list()
self.exit_code = 0
self._time_start = 0 self._time_start = 0
self._time_timecmp = 0 self._time_timecmp = 0
self._time_interrupt_enabled = False self._time_interrupt_enabled = False
@ -63,45 +63,37 @@ class PrivCPU(CPU):
# init csr # init csr
self._init_csr() self._init_csr()
def _run(self, verbose=False): def run(self, verbose=False):
if self.pc <= 0: if self.pc <= 0:
return False return False
ins = None
launch_debug = False
try: try:
while not self.exit: while not self.halted:
self.step(verbose) self.step(verbose)
except RiscemuBaseException as ex: except RiscemuBaseException as ex:
if isinstance(ex, LaunchDebuggerException): if isinstance(ex, LaunchDebuggerException):
self.launch_debug = True launch_debug = True
self.pc += self.INS_XLEN self.pc += self.INS_XLEN
if self.exit: if self.halted:
print() 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) sys.exit(self.exit_code)
elif self.launch_debug:
self.launch_debug = False elif launch_debug:
launch_debug_session(self, self.mmu, self.regs, launch_debug_session(self)
"Launching debugger:")
if not self.debugger_active: if not self.debugger_active:
self._run(verbose) self.run(verbose)
else: else:
print() 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): def launch(self, program: Program, verbose: bool = False):
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):
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._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): def _init_csr(self):
# set up CSR # set up CSR
@ -184,7 +176,7 @@ class PrivCPU(CPU):
if not (len(self.pending_traps) > 0 and self.csr.get_mstatus('mie')): if not (len(self.pending_traps) > 0 and self.csr.get_mstatus('mie')):
return return
# select best interrupt # 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 trap = self.pending_traps.pop() # use the most recent trap
if self.conf.verbosity > 0: if self.conf.verbosity > 0:
print(FMT_CPU + "[CPU] taking trap {}!".format(trap) + FMT_NONE) print(FMT_CPU + "[CPU] taking trap {}!".format(trap) + FMT_NONE)
@ -209,7 +201,7 @@ class PrivCPU(CPU):
if mtvec & 0b11 == 1: if mtvec & 0b11 == 1:
self.pc = (mtvec & 0b11111111111111111111111111111100) + (trap.code * 4) self.pc = (mtvec & 0b11111111111111111111111111111100) + (trap.code * 4)
self.record_perf_profile() self.record_perf_profile()
if len(self._perf_counters) % 100 == 0: if len(self._perf_counters) > 100:
self.show_perf() self.show_perf()
def show_perf(self): def show_perf(self):
@ -225,11 +217,6 @@ class PrivCPU(CPU):
continue continue
cps = (cycle - cycled) / (time_ns - timed) * 1000000000 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 cycled = cycle
timed = time_ns timed = time_ns
cps_list.append(cps) cps_list.append(cps)
@ -238,3 +225,9 @@ class PrivCPU(CPU):
def record_perf_profile(self): def record_perf_profile(self):
self._perf_counters.append((time.perf_counter_ns(), self.cycle)) 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 .PrivCPU import PrivCPU
from .ImageLoader import MemoryImageMMU from .ElfLoader import ElfBinaryFileLoader
from .PrivMMU import LoadedElfMMU from .ImageLoader import MemoryImageLoader
from .ElfLoader import ElfExecutable
import sys import sys

@ -15,6 +15,7 @@ from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple, Set, Union, Iterator, Callable, Type 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 .colors import FMT_MEM, FMT_NONE, FMT_UNDERLINE, FMT_ORANGE, FMT_RED, FMT_BOLD
from .exceptions import ParseException from .exceptions import ParseException
from .helpers import format_bytes, get_section_base_name from .helpers import format_bytes, get_section_base_name
@ -371,9 +372,13 @@ class CPU(ABC):
instructions: Dict[str, Callable[[Instruction], None]] instructions: Dict[str, Callable[[Instruction], None]]
instruction_sets: Set['InstructionSet'] 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.mmu = mmu
self.regs = Registers() self.regs = Registers()
self.conf = conf
self.instruction_sets = set() self.instruction_sets = set()
self.instructions = dict() self.instructions = dict()
@ -433,3 +438,11 @@ class CPU(ABC):
self.pc = program.entrypoint self.pc = program.entrypoint
self.run(verbose) 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