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
from typing import Tuple
from typing import Tuple, List, Dict, Callable
from .Tokenizer import RiscVTokenizer
from .Syscall import *
from .Exceptions import *
from .helpers import *
from .MMU import MMU
from .Config import RunConfig
from .Registers import Registers
from .Syscall import SyscallInterface, Syscall
from .debug import launch_debug_session
import typing
if typing.TYPE_CHECKING:
from . import MMU, Executable, LoadedExecutable, LoadedInstruction
from . import Executable, LoadedExecutable, LoadedInstruction
from .Instructions.InstructionSet import InstructionSet
class CPU:
def __init__(self, conf: RunConfig):
from . import MMU
def __init__(self, conf: RunConfig, instruction_sets: List['InstructionSet']):
# setup CPU states
self.pc = 0
self.cycle = 0
self.exit = False
self.exit_code = 0
self.conf = conf
# setup MMU, registers and syscall handlers
self.mmu = MMU(conf)
self.regs = Registers(conf)
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
if conf.include_scall_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'):
"""
Load an executable into Memory
"""
return self.mmu.load_bin(e)
def run_loaded(self, le: 'LoadedExecutable'):
"""
Run a loaded executable
"""
self.pc = le.run_ptr
sp, hp = le.stack_heap
self.regs.set('sp', sp)
@ -55,7 +77,6 @@ class CPU:
except RiscemuBaseException as ex:
print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc-1, ins) + FMT_NONE)
print(" " + ex.message())
#traceback.print_exception(type(ex), ex, ex.__traceback__)
if self.conf.debug_on_exception:
launch_debug_session(self, self.mmu, self.regs,
"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)
def __run_instruction(self, ins: 'LoadedInstruction'):
name = '_CPU__instruction_' + ins.name
if hasattr(self, name):
getattr(self, name)(ins)
if ins.name in self.instructions:
self.instructions[ins.name](ins)
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))
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)
(so a register and address tuple for memory instructions)
@ -87,299 +107,12 @@ class CPU:
rd = ins.get_reg(0)
return rd, self.regs.get(rs1) + imm
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
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 all_instructions(self) -> List[str]:
return list(self.instructions.keys())
def __repr__(self):
return "CPU(pc=0x{:08X}, cycle={})".format(
return "CPU(pc=0x{:08X}, cycle={}, instructions={})".format(
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 typing import List
from .CPU import CPU, Registers
from .Exceptions import ParseException
REGISTERS = list(Registers.all_registers())
INSTRUCTIONS = list(CPU.all_instructions())
PSEUDO_OPS = [
'.asciiz',
'.double',
@ -245,10 +240,11 @@ class RiscVPseudoOpToken(RiscVToken):
class RiscVTokenizer:
def __init__(self, input: RiscVInput):
def __init__(self, input: RiscVInput, instructions: List[str]):
self.input = input
self.tokens: List[RiscVToken] = []
self.name = input.name
self.instructions = instructions
def tokenize(self):
while self.input.has_next():
@ -268,7 +264,7 @@ class RiscVTokenizer:
self.parse_comment()
# must be instruction
elif self.input.peek_one_of(INSTRUCTIONS):
elif self.input.peek_one_of(self.instructions):
self.parse_instruction()
else:
token = self.input.peek(size=5)
@ -295,7 +291,7 @@ class RiscVTokenizer:
self.input.context()))
def parse_instruction(self):
ins = self.input.consume_one_of(INSTRUCTIONS)
ins = self.input.consume_one_of(self.instructions)
args = []
self.input.consume_whitespace(linebreak=False)
while self.input.peek(regex=REG_VALID_ARGUMENT) and len(args) < 3:

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

Loading…
Cancel
Save