added lots of documentation in pydoc style

float_support
Anton Lydike 4 years ago
parent 8c1714116e
commit 2a68f16e99

@ -1,15 +1,23 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
This file contains the CPU logic (not the individual instruction sets). See instructions/InstructionSet.py for more info
on them.
"""
import sys import sys
import traceback
from typing import Tuple, List, Dict, Callable, Type from typing import Tuple, List, Dict, Callable, Type
from .Tokenizer import RiscVTokenizer from .Tokenizer import RiscVTokenizer
from .Syscall import * from .Syscall import SyscallInterface
from .Exceptions import * from .Exceptions import RiscemuBaseException, LaunchDebuggerException
from .MMU import MMU from .MMU import MMU
from .Config import RunConfig from .Config import RunConfig
from .Registers import Registers from .Registers import Registers
from .debug import launch_debug_session from .debug import launch_debug_session
from .colors import FMT_CPU, FMT_NONE, FMT_ERROR
import typing import typing
@ -19,7 +27,18 @@ if typing.TYPE_CHECKING:
class CPU: class CPU:
"""
This class represents a single CPU. It holds references to it's mmu, registers and syscall interrupt handler.
It is initialized with a configuration and a list of instruction sets.
"""
def __init__(self, conf: RunConfig, instruction_sets: List[Type['InstructionSet']]): def __init__(self, conf: RunConfig, instruction_sets: List[Type['InstructionSet']]):
"""
Creates a CPU instance.
:param conf: An instance of the current RunConfiguration
:param instruction_sets: A list of instruction set classes. These must inherit from the InstructionSet class
"""
# setup CPU states # setup CPU states
self.pc = 0 self.pc = 0
self.cycle = 0 self.cycle = 0
@ -48,6 +67,8 @@ class CPU:
def get_tokenizer(self, tokenizer_input): def get_tokenizer(self, tokenizer_input):
""" """
Returns a tokenizer that respects the language of the CPU Returns a tokenizer that respects the language of the CPU
:param tokenizer_input: an instance of the RiscVTokenizerInput class
""" """
return RiscVTokenizer(tokenizer_input, self.all_instructions()) return RiscVTokenizer(tokenizer_input, self.all_instructions())
@ -70,11 +91,16 @@ class CPU:
def continue_from_debugger(self, verbose=True): def continue_from_debugger(self, verbose=True):
""" """
dalled from the debugger to continue running called from the debugger to continue running
:param verbose: If True, will print each executed instruction to STDOUT
""" """
self.__run(verbose) self.__run(verbose)
def step(self): def step(self):
"""
Execute a single instruction, then return.
"""
if self.exit: if self.exit:
print(FMT_CPU + "[CPU] Program exited with code {}".format(self.exit_code) + FMT_NONE) print(FMT_CPU + "[CPU] Program exited with code {}".format(self.exit_code) + FMT_NONE)
else: else:
@ -131,11 +157,18 @@ class CPU:
raise RuntimeError("Unknown instruction: {}".format(ins)) raise RuntimeError("Unknown instruction: {}".format(ins))
def all_instructions(self) -> List[str]: def all_instructions(self) -> List[str]:
"""
Return a list of all instructions this CPU can execute.
"""
return list(self.instructions.keys()) return list(self.instructions.keys())
def __repr__(self): def __repr__(self):
return "CPU(pc=0x{:08X}, cycle={}, instructions={})".format( """
Returns a representation of the CPU and some of its state.
"""
return "CPU(pc=0x{:08X}, cycle={}, exit={}, instructions={})".format(
self.pc, self.pc,
self.cycle, self.cycle,
self.exit,
" ".join(s.name for s in self.instruction_sets) " ".join(s.name for s in self.instruction_sets)
) )

@ -1,15 +1,15 @@
from .Exceptions import * from .Exceptions import RiscemuBaseException, LaunchDebuggerException, InvalidSyscallException, LinkerException, \
ParseException, NumberFormatException, InvalidRegisterException, MemoryAccessException, OutOfMemoryException
from .Tokenizer import RiscVToken, RiscVInput, RiscVTokenizer, RiscVInstructionToken, RiscVSymbolToken, \ from .Tokenizer import RiscVInput, RiscVTokenizer
RiscVPseudoOpToken, TokenType
from .Executable import Executable, LoadedExecutable
from .Executable import Executable, LoadedExecutable, LoadedMemorySection, LoadedInstruction
from .ExecutableParser import ExecutableParser from .ExecutableParser import ExecutableParser
from .MMU import MMU from .MMU import MMU
from .Registers import Registers
from .Syscall import SyscallInterface, Syscall
from .CPU import CPU
from .CPU import CPU, Registers, Syscall, SyscallInterface from .Config import RunConfig
from .Config import RunConfig

@ -1,3 +1,11 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: MIT
This file holds the logic for starting the emulator from the CLI
"""
if __name__ == '__main__': if __name__ == '__main__':
from . import * from . import *
from .helpers import * from .helpers import *

@ -1,12 +1,25 @@
import typing """
RiscEmu (c) 2021 Anton Lydike
from abc import ABC, abstractmethod SPDX-License-Identifier: BSD-2-Clause
from ..CPU import * """
from typing import Tuple, Callable, Dict
from abc import ABC
from ..CPU import CPU
from ..helpers import ASSERT_LEN, ASSERT_IN, to_unsigned
class InstructionSet(ABC): class InstructionSet(ABC):
""" """
Represents a collection of instructions Represents a collection of instructions
Each instruction set has to inherit from this class. Each instruction should be it's own method:
instruction_<name>(self, ins: LoadedInstruction)
instructions containing a dot '.' should replace it with an underscore.
""" """
def __init__(self, cpu: 'CPU'): def __init__(self, cpu: 'CPU'):
@ -15,7 +28,7 @@ class InstructionSet(ABC):
self.mmu = cpu.mmu self.mmu = cpu.mmu
self.regs = cpu.regs self.regs = cpu.regs
def load(self) -> typing.Dict[str, typing.Callable[['LoadedInstruction'], None]]: def load(self) -> Dict[str, Callable[['LoadedInstruction'], None]]:
""" """
This is called by the CPU once it instantiates this instruction set This is called by the CPU once it instantiates this instruction set
@ -27,13 +40,18 @@ class InstructionSet(ABC):
} }
def get_instructions(self): def get_instructions(self):
"""
Returns a list of all valid instruction names included in this instruction set
converts underscores in names to dots
"""
for member in dir(self): for member in dir(self):
if member.startswith('instruction_'): if member.startswith('instruction_'):
yield member[12:].replace('_', '.'), getattr(self, member) yield member[12:].replace('_', '.'), getattr(self, member)
def parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]: def parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]:
""" """
parses both rd, rs, imm and rd, imm(rs) arguments and returns (rd, imm+rs1) parses both rd, rs, imm and rd, imm(rs) argument format and returns (rd, imm+rs1)
(so a register and address tuple for memory instructions) (so a register and address tuple for memory instructions)
""" """
if len(ins.args) == 3: if len(ins.args) == 3:
@ -101,6 +119,9 @@ class InstructionSet(ABC):
@property @property
def pc(self): def pc(self):
"""
shorthand for self.cpu.pc
"""
return self.cpu.pc return self.cpu.pc
@pc.setter @pc.setter

@ -1,10 +1,27 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
from .InstructionSet import * from .InstructionSet import *
from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed
from ..colors import FMT_DEBUG, FMT_NONE from ..colors import FMT_DEBUG, FMT_NONE
from ..debug import launch_debug_session
from ..Exceptions import LaunchDebuggerException
from ..Syscall import Syscall
class RV32I(InstructionSet): class RV32I(InstructionSet):
"""
The RV32I instruction set. Some instructions are missing, such as
fence, fence.i, rdcycle, rdcycleh, rdtime, rdtimeh, rdinstret, rdinstreth
All atomic read/writes are also not implemented yet
See https://maxvytech.com/images/RV32I-11-2018.pdf for a more detailed overview
"""
def instruction_lb(self, ins: 'LoadedInstruction'): def instruction_lb(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins) rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1))) self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1)))
@ -281,13 +298,13 @@ class RV32I(InstructionSet):
def instruction_sbreak(self, ins: 'LoadedInstruction'): def instruction_sbreak(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 0) ASSERT_LEN(ins.args, 0)
if self.cpu.active_debug: if self.cpu.active_debug:
print(FMT_DEBUG + "Debug instruction encountered at 0x{:08X}".format(self.pc-1) + FMT_NONE) print(FMT_DEBUG + "Debug instruction encountered at 0x{:08X}".format(self.pc - 1) + FMT_NONE)
raise LaunchDebuggerException() raise LaunchDebuggerException()
launch_debug_session( launch_debug_session(
self.cpu, self.cpu,
self.mmu, self.mmu,
self.regs, self.regs,
"Debug instruction encountered at 0x{:08X}".format(self.pc-1) "Debug instruction encountered at 0x{:08X}".format(self.pc - 1)
) )
def instruction_nop(self, ins: 'LoadedInstruction'): def instruction_nop(self, ins: 'LoadedInstruction'):

@ -1,8 +1,17 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
"""
from .InstructionSet import * from .InstructionSet import *
from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed from ..Exceptions import INS_NOT_IMPLEMENTED
class RV32M(InstructionSet): class RV32M(InstructionSet):
"""
The RV32M Instruction set, containing multiplication and division instructions
"""
def instruction_mul(self, ins: 'LoadedInstruction'): def instruction_mul(self, ins: 'LoadedInstruction'):
rd, rs1, rs2 = self.parse_rd_rs_rs(ins) rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set( self.regs.set(

@ -1,3 +1,11 @@
"""
RiscEmu (c) 2021 Anton Lydike
SPDX-License-Identifier: BSD-2-Clause
This package holds all instruction sets, available to the processor
"""
from .InstructionSet import InstructionSet from .InstructionSet import InstructionSet
from .RV32M import RV32M from .RV32M import RV32M
from .RV32I import RV32I from .RV32I import RV32I

Loading…
Cancel
Save