refactored instruction sets to be modular

float_support
Anton Lydike 4 years ago
parent c20ab4cfb1
commit 3ce42079d4

@ -1,29 +0,0 @@
.stack 0xFFFF
.data 0x200
fibs: .space 56
.text
main:
func_fibs:
addi s1, zero, 0 # storage index
addi s2, zero, 56 # last storage index
addi t0, zero, 1 # t0 = F_{i}
addi t1, zero, 1 # t1 = F_{i+1}
loop:
sw t0, fibs(s1) # save
add t2, t1, t0 # t2 = F_{i+2}
addi t0, t1, 0 # t0 = t1
addi t1, t2, 0 # t1 = t2
addi s1, s1, 4 # increment storage pointer
blt s1, s2, loop # loop as long as we did not reach array length
# exit gracefully
addi a0, zero, 0
addi a7, zero, 93
ebreak # launch debugger
scall # exit with code 0
.set test_val, 0xFF
.global func_fibs

@ -1,40 +1,62 @@
import traceback import traceback
from typing import Tuple from typing import Tuple, List, Dict, Callable
from .Tokenizer import RiscVTokenizer
from .Syscall import *
from .Exceptions import * from .Exceptions import *
from .helpers import * from .MMU import MMU
from .Config import RunConfig from .Config import RunConfig
from .Registers import Registers from .Registers import Registers
from .Syscall import SyscallInterface, Syscall
from .debug import launch_debug_session from .debug import launch_debug_session
import typing import typing
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from . import MMU, Executable, LoadedExecutable, LoadedInstruction from . import Executable, LoadedExecutable, LoadedInstruction
from .Instructions.InstructionSet import InstructionSet
class CPU: class CPU:
def __init__(self, conf: RunConfig): def __init__(self, conf: RunConfig, instruction_sets: List['InstructionSet']):
from . import MMU # setup CPU states
self.pc = 0 self.pc = 0
self.cycle = 0 self.cycle = 0
self.exit = False self.exit = False
self.exit_code = 0 self.exit_code = 0
self.conf = conf self.conf = conf
# setup MMU, registers and syscall handlers
self.mmu = MMU(conf) self.mmu = MMU(conf)
self.regs = Registers(conf) self.regs = Registers(conf)
self.syscall_int = SyscallInterface() self.syscall_int = SyscallInterface()
# load all instruction sets
self.sets = instruction_sets
self.instructions: Dict[str, Callable[[LoadedInstruction], None]] = dict()
for ins_set in instruction_sets:
self.instructions.update(ins_set.load(self))
# provide global syscall symbols if option is set # provide global syscall symbols if option is set
if conf.include_scall_symbols: if conf.include_scall_symbols:
self.mmu.global_symbols.update(self.syscall_int.get_syscall_symbols()) self.mmu.global_symbols.update(self.syscall_int.get_syscall_symbols())
def get_tokenizer(self, input):
"""
Returns a tokenizer that respects the language of the CPU
"""
return RiscVTokenizer(input, self.all_instructions())
def load(self, e: 'Executable'): def load(self, e: 'Executable'):
"""
Load an executable into Memory
"""
return self.mmu.load_bin(e) return self.mmu.load_bin(e)
def run_loaded(self, le: 'LoadedExecutable'): def run_loaded(self, le: 'LoadedExecutable'):
"""
Run a loaded executable
"""
self.pc = le.run_ptr self.pc = le.run_ptr
sp, hp = le.stack_heap sp, hp = le.stack_heap
self.regs.set('sp', sp) self.regs.set('sp', sp)
@ -55,7 +77,6 @@ class CPU:
except RiscemuBaseException as ex: except RiscemuBaseException as ex:
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())
#traceback.print_exception(type(ex), ex, ex.__traceback__)
if self.conf.debug_on_exception: if self.conf.debug_on_exception:
launch_debug_session(self, self.mmu, self.regs, launch_debug_session(self, self.mmu, self.regs,
"Exception encountered, launching debug:".format(self.pc-1)) "Exception encountered, launching debug:".format(self.pc-1))
@ -63,14 +84,13 @@ class CPU:
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)
def __run_instruction(self, ins: 'LoadedInstruction'): def __run_instruction(self, ins: 'LoadedInstruction'):
name = '_CPU__instruction_' + ins.name if ins.name in self.instructions:
if hasattr(self, name): self.instructions[ins.name](ins)
getattr(self, name)(ins)
else: else:
# this should never be reached, as unknown instructions are imparsable # this should never be reached, as unknown instructions are imparseable
raise RuntimeError("Unknown instruction: {}".format(ins)) raise RuntimeError("Unknown instruction: {}".format(ins))
def __parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]: def parse_mem_ins(self, ins: 'LoadedInstruction') -> Tuple[str, int]:
""" """
parses both rd, rs1, imm and rd, imm(rs1) arguments and returns (rd, imm+rs1) parses both rd, rs1, imm and rd, imm(rs1) arguments and returns (rd, imm+rs1)
(so a register and address tuple for memory instructions) (so a register and address tuple for memory instructions)
@ -87,299 +107,12 @@ class CPU:
rd = ins.get_reg(0) rd = ins.get_reg(0)
return rd, self.regs.get(rs1) + imm return rd, self.regs.get(rs1) + imm
def __instruction_lb(self, ins: 'LoadedInstruction'): def all_instructions(self) -> List[str]:
rd, addr = self.__parse_mem_ins(ins) return list(self.instructions.keys())
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1)))
def __instruction_lh(self, ins: 'LoadedInstruction'):
rd, addr = self.__parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2)))
def __instruction_lw(self, ins: 'LoadedInstruction'):
rd, addr = self.__parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 4)))
def __instruction_lbu(self, ins: 'LoadedInstruction'):
rd, addr = self.__parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1), unsigned=True))
def __instruction_lhu(self, ins: 'LoadedInstruction'):
rd, addr = self.__parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2), unsigned=True))
def __instruction_sb(self, ins: 'LoadedInstruction'):
rd, addr = self.__parse_mem_ins(ins)
self.mmu.write(addr, 1, int_to_bytes(self.regs.get(rd), 1))
def __instruction_sh(self, ins: 'LoadedInstruction'):
rd, addr = self.__parse_mem_ins(ins)
self.mmu.write(addr, 2, int_to_bytes(self.regs.get(rd), 2))
def __instruction_sw(self, ins: 'LoadedInstruction'):
rd, addr = self.__parse_mem_ins(ins)
self.mmu.write(addr, 4, int_to_bytes(self.regs.get(rd), 4))
def __instruction_sll(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) << (self.regs.get(src2) & 0b11111))
)
def __instruction_slli(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) << (imm & 0b11111))
)
def __instruction_srl(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) >> (self.regs.get(src2) & 0b11111))
)
def __instruction_srli(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) >> (imm & 0b11111))
)
def __instruction_sra(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) >> (self.regs.get(src2) & 0b11111)
)
def __instruction_srai(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
self.regs.get(src1) >> (imm & 0b11111)
)
def __instruction_add(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) + self.regs.get(src2)
)
def __instruction_addi(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
self.regs.get(src1) + imm
)
def __instruction_sub(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) - self.regs.get(src2)
)
def __instruction_lui(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def __instruction_auipc(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def __instruction_xor(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) ^ self.regs.get(src2)
)
def __instruction_xori(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def __instruction_or(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) | self.regs.get(src2)
)
def __instruction_ori(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def __instruction_and(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) & self.regs.get(src2)
)
def __instruction_andi(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def __instruction_slt(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
int(self.regs.get(src1) < self.regs.get(src2))
)
def __instruction_slti(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def __instruction_sltu(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
int(to_unsigned(self.regs.get(src1)) < to_unsigned(self.regs.get(src2)))
)
def __instruction_sltiu(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def __instruction_beq(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) == self.regs.get(reg2):
self.pc = dest
def __instruction_bne(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) != self.regs.get(reg2):
self.pc = dest
def __instruction_blt(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) < self.regs.get(reg2):
self.pc = dest
def __instruction_bge(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) >= self.regs.get(reg2):
self.pc = dest
def __instruction_bltu(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = to_unsigned(ins.get_reg(0))
reg2 = to_unsigned(ins.get_reg(1))
dest = ins.get_imm(2)
if self.regs.get(reg1) < self.regs.get(reg2):
self.pc = dest
def __instruction_bgeu(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = to_unsigned(ins.get_reg(0))
reg2 = to_unsigned(ins.get_reg(1))
dest = ins.get_imm(2)
if self.regs.get(reg1) >= self.regs.get(reg2):
self.pc = dest
def __instruction_j(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 1)
addr = ins.get_imm(0)
self.pc = addr
def __instruction_jal(self, ins: 'LoadedInstruction'):
reg = 'ra' # default register is ra
if len(ins.args) == 1:
addr = ins.get_imm(0)
else:
ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0)
addr = ins.get_imm(1)
self.regs.set(reg, self.pc)
self.pc = addr
def __instruction_jalr(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0)
addr = ins.get_imm(1)
self.regs.set(reg, self.pc)
self.pc = addr
def __instruction_ret(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 0)
self.pc = self.regs.get('ra')
def __instruction_ecall(self, ins: 'LoadedInstruction'):
self.__instruction_scall(ins)
def __instruction_ebreak(self, ins: 'LoadedInstruction'):
self.__instruction_sbreak(ins)
def __instruction_scall(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 0)
syscall = Syscall(self.regs.get('a7'), self.regs, self)
self.syscall_int.handle_syscall(syscall)
def __instruction_sbreak(self, ins: 'LoadedInstruction'):
launch_debug_session(self, self.mmu, self.regs, "Debug instruction encountered at 0x{:08X}".format(self.pc))
def __instruction_nop(self, ins: 'LoadedInstruction'):
pass
@staticmethod
def all_instructions():
for method in vars(CPU):
if method.startswith('_CPU__instruction_'):
yield method[18:]
def __repr__(self): def __repr__(self):
return "CPU(pc=0x{:08X}, cycle={})".format( return "CPU(pc=0x{:08X}, cycle={}, instructions={})".format(
self.pc, self.pc,
self.cycle self.cycle,
" ".join(s.name for s in self.sets)
) )

@ -0,0 +1,46 @@
import typing
from abc import ABC, abstractmethod
from ..CPU import *
class InstructionSet(ABC):
"""
Represents a collection of instructions
"""
def __init__(self, name):
self.name = name
self.cpu: typing.Optional['CPU'] = None
self.mmu: typing.Optional['MMU'] = None
self.regs: typing.Optional['Registers'] = None
def load(self, cpu: 'CPU'):
self.cpu = cpu
self.mmu = cpu.mmu
self.regs = cpu.regs
return {
name: ins for name, ins in self.get_instructions()
}
def get_instructions(self):
for member in dir(self):
if member.startswith('instruction_'):
yield member[12:].replace('_', '.'), getattr(self, member)
def parse_mem_ins(self, ins: 'LoadedInstruction'):
return self.cpu.parse_mem_ins(ins)
@property
def pc(self):
return self.cpu.pc
@pc.setter
def pc(self, val):
self.cpu.pc = val
def __repr__(self):
return "InstructionSet[{}] with {} instructions".format(
self.name,
len(list(self.get_instructions()))
)

@ -0,0 +1,293 @@
from .InstructionSet import *
from ..helpers import int_from_bytes, int_to_bytes, to_unsigned, to_signed
class RV32I(InstructionSet):
def __init__(self):
super().__init__('RV32I')
def instruction_lb(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1)))
def instruction_lh(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2)))
def instruction_lw(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 4)))
def instruction_lbu(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 1), unsigned=True))
def instruction_lhu(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.regs.set(rd, int_from_bytes(self.mmu.read(addr, 2), unsigned=True))
def instruction_sb(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.mmu.write(addr, 1, int_to_bytes(self.regs.get(rd), 1))
def instruction_sh(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.mmu.write(addr, 2, int_to_bytes(self.regs.get(rd), 2))
def instruction_sw(self, ins: 'LoadedInstruction'):
rd, addr = self.parse_mem_ins(ins)
self.mmu.write(addr, 4, int_to_bytes(self.regs.get(rd), 4))
def instruction_sll(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) << (self.regs.get(src2) & 0b11111))
)
def instruction_slli(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) << (imm & 0b11111))
)
def instruction_srl(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) >> (self.regs.get(src2) & 0b11111))
)
def instruction_srli(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
to_signed(to_unsigned(self.regs.get(src1)) >> (imm & 0b11111))
)
def instruction_sra(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) >> (self.regs.get(src2) & 0b11111)
)
def instruction_srai(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
self.regs.get(src1) >> (imm & 0b11111)
)
def instruction_add(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) + self.regs.get(src2)
)
def instruction_addi(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
imm = ins.get_imm(2)
self.regs.set(
dst,
self.regs.get(src1) + imm
)
def instruction_sub(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) - self.regs.get(src2)
)
def instruction_lui(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def instruction_auipc(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def instruction_xor(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) ^ self.regs.get(src2)
)
def instruction_xori(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def instruction_or(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) | self.regs.get(src2)
)
def instruction_ori(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def instruction_and(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
self.regs.get(src1) & self.regs.get(src2)
)
def instruction_andi(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def instruction_slt(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
int(self.regs.get(src1) < self.regs.get(src2))
)
def instruction_slti(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def instruction_sltu(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
dst = ins.get_reg(0)
src1 = ins.get_reg(1)
src2 = ins.get_reg(2)
self.regs.set(
dst,
int(to_unsigned(self.regs.get(src1)) < to_unsigned(self.regs.get(src2)))
)
def instruction_sltiu(self, ins: 'LoadedInstruction'):
INS_NOT_IMPLEMENTED(ins)
def instruction_beq(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) == self.regs.get(reg2):
self.pc = dest
def instruction_bne(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) != self.regs.get(reg2):
self.pc = dest
def instruction_blt(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) < self.regs.get(reg2):
self.pc = dest
def instruction_bge(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = ins.get_reg(0)
reg2 = ins.get_reg(1)
dest = ins.get_imm(2)
if self.regs.get(reg1) >= self.regs.get(reg2):
self.pc = dest
def instruction_bltu(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = to_unsigned(ins.get_reg(0))
reg2 = to_unsigned(ins.get_reg(1))
dest = ins.get_imm(2)
if self.regs.get(reg1) < self.regs.get(reg2):
self.pc = dest
def instruction_bgeu(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 3)
reg1 = to_unsigned(ins.get_reg(0))
reg2 = to_unsigned(ins.get_reg(1))
dest = ins.get_imm(2)
if self.regs.get(reg1) >= self.regs.get(reg2):
self.pc = dest
# technically deprecated
def instruction_j(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 1)
addr = ins.get_imm(0)
self.pc = addr
def instruction_jal(self, ins: 'LoadedInstruction'):
reg = 'ra' # default register is ra
if len(ins.args) == 1:
addr = ins.get_imm(0)
else:
ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0)
addr = ins.get_imm(1)
self.regs.set(reg, self.pc)
self.pc = addr
def instruction_jalr(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 2)
reg = ins.get_reg(0)
addr = ins.get_imm(1)
self.regs.set(reg, self.pc)
self.pc = addr
def instruction_ret(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 0)
self.pc = self.regs.get('ra')
def instruction_ecall(self, ins: 'LoadedInstruction'):
self.instruction_scall(ins)
def instruction_ebreak(self, ins: 'LoadedInstruction'):
self.instruction_sbreak(ins)
def instruction_scall(self, ins: 'LoadedInstruction'):
ASSERT_LEN(ins.args, 0)
syscall = Syscall(self.regs.get('a7'), self.regs, self.cpu)
self.cpu.syscall_int.handle_syscall(syscall)
def instruction_sbreak(self, ins: 'LoadedInstruction'):
launch_debug_session(self.cpu, self.mmu, self.regs, "Debug instruction encountered at 0x{:08X}".format(self.pc))
def instruction_nop(self, ins: 'LoadedInstruction'):
pass

@ -2,13 +2,8 @@ import re
from enum import IntEnum from enum import IntEnum
from typing import List from typing import List
from .CPU import CPU, Registers
from .Exceptions import ParseException from .Exceptions import ParseException
REGISTERS = list(Registers.all_registers())
INSTRUCTIONS = list(CPU.all_instructions())
PSEUDO_OPS = [ PSEUDO_OPS = [
'.asciiz', '.asciiz',
'.double', '.double',
@ -245,10 +240,11 @@ class RiscVPseudoOpToken(RiscVToken):
class RiscVTokenizer: class RiscVTokenizer:
def __init__(self, input: RiscVInput): def __init__(self, input: RiscVInput, instructions: List[str]):
self.input = input self.input = input
self.tokens: List[RiscVToken] = [] self.tokens: List[RiscVToken] = []
self.name = input.name self.name = input.name
self.instructions = instructions
def tokenize(self): def tokenize(self):
while self.input.has_next(): while self.input.has_next():
@ -268,7 +264,7 @@ class RiscVTokenizer:
self.parse_comment() self.parse_comment()
# must be instruction # must be instruction
elif self.input.peek_one_of(INSTRUCTIONS): elif self.input.peek_one_of(self.instructions):
self.parse_instruction() self.parse_instruction()
else: else:
token = self.input.peek(size=5) token = self.input.peek(size=5)
@ -295,7 +291,7 @@ class RiscVTokenizer:
self.input.context())) self.input.context()))
def parse_instruction(self): def parse_instruction(self):
ins = self.input.consume_one_of(INSTRUCTIONS) ins = self.input.consume_one_of(self.instructions)
args = [] args = []
self.input.consume_whitespace(linebreak=False) self.input.consume_whitespace(linebreak=False)
while self.input.peek(regex=REG_VALID_ARGUMENT) and len(args) < 3: while self.input.peek(regex=REG_VALID_ARGUMENT) and len(args) < 3:

@ -1,6 +1,7 @@
if __name__ == '__main__': if __name__ == '__main__':
from . import * from . import *
from .helpers import * from .helpers import *
from .Instructions.RV32I import RV32I
import argparse import argparse
import sys import sys
@ -60,10 +61,10 @@ if __name__ == '__main__':
FMT_PRINT = FMT_BOLD + FMT_MAGENTA FMT_PRINT = FMT_BOLD + FMT_MAGENTA
try: try:
cpu = CPU(cfg) cpu = CPU(cfg, [RV32I()])
loaded_exe = None loaded_exe = None
for file in args.files: for file in args.files:
tk = RiscVTokenizer(RiscVInput.from_file(file)) tk = cpu.get_tokenizer(RiscVInput.from_file(file))
tk.tokenize() tk.tokenize()
loaded_exe = cpu.load(ExecutableParser(tk).parse()) loaded_exe = cpu.load(ExecutableParser(tk).parse())
# run the last loaded executable # run the last loaded executable

Loading…
Cancel
Save