Skip to content

Commit

Permalink
half way to f64
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonLydike committed Oct 6, 2023
1 parent f01b312 commit 4481e0d
Show file tree
Hide file tree
Showing 15 changed files with 180 additions and 189 deletions.
156 changes: 65 additions & 91 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ riscemu = "riscemu.__main__:main"
[tool.poetry.dependencies]
python = "^3.8"
pyelftools = "^0.29"
psutil = "^5.9.5"

[tool.poetry.group.dev.dependencies]
black = "^23.7.0"
Expand Down
1 change: 1 addition & 0 deletions riscemu/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class RunConfig:
verbosity: int = 0
slowdown: float = 1
unlimited_registers: bool = False
flen: int = 32
# runtime config
use_libc: bool = False
ignore_exit_code: bool = False
Expand Down
4 changes: 3 additions & 1 deletion riscemu/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
# base classes
from .flags import MemoryFlags
from .int32 import UInt32, Int32
from .float32 import Float32
from .float import BaseFloat, Float32, Float64
from .rtclock import RTClock
from .instruction import Instruction, Immediate, InstructionWithEncoding
from .instruction_context import InstructionContext
Expand Down Expand Up @@ -66,7 +66,9 @@
MemoryFlags,
UInt32,
Int32,
BaseFloat,
Float32,
Float64,
RTClock,
Instruction,
Immediate,
Expand Down
2 changes: 1 addition & 1 deletion riscemu/core/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def __init__(
conf: RunConfig,
):
self.mmu = mmu
self.regs = Registers(conf.unlimited_registers)
self.regs = Registers(conf.unlimited_registers, conf.flen)
self.conf = conf

self.instruction_sets = set()
Expand Down
134 changes: 63 additions & 71 deletions riscemu/core/float32.py → riscemu/core/float.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import struct
from ctypes import c_float
from typing import Union, Any
from ctypes import c_float, c_double
from typing import Union, Any, ClassVar
from abc import ABC, abstractmethod

bytes_t = bytes


class Float32:
class BaseFloat(ABC):
__slots__ = ("_val",)

_val: c_float
_type: ClassVar[type[c_float | c_double]]
_struct_fmt_str: ClassVar[str]

_val: c_float | c_double

@property
def value(self) -> float:
Expand All @@ -22,73 +26,67 @@ def bytes(self) -> bytes:
"""
The values bit representation (as a bytes object)
"""
return struct.pack("<f", self.value)

@property
def bits(self) -> int:
"""
The values bit representation as an int (for easy bit manipulation)
"""
return int.from_bytes(self.bytes, byteorder="little")
return struct.pack("<" + self._struct_fmt_str, self.value)

@classmethod
def from_bytes(cls, val: Union[int, bytes_t, bytearray]):
if isinstance(val, int):
val = struct.unpack("!f", struct.pack("!I", val))[0]
return Float32(val)
def from_bytes(cls, val: Union[bytes_t, bytearray]):
return BaseFloat(val)

def __init__(
self, val: Union[float, c_float, "Float32", bytes_t, bytearray, int] = 0
self, val: Union[float, c_float, "BaseFloat", bytes_t, bytearray, int] = 0
):
if isinstance(val, (float, int)):
self._val = c_float(val)
elif isinstance(val, c_float):
self._val = c_float(val.value)
self._val = self._type(val)
elif isinstance(val, c_float | c_double):
self._val = self._type(val.value)
elif isinstance(val, (bytes, bytearray)):
self._val = c_float(struct.unpack("<f", val)[0])
elif isinstance(val, Float32):
self._val = self._type(struct.unpack("<" + self._struct_fmt_str, val)[0])
self._val = self._type(struct.unpack("<" + self._struct_fmt_str, val)[0])
elif isinstance(val, BaseFloat):
self._val = val._val
else:
raise ValueError(
"Unsupported value passed to Float32: {} ({})".format(
repr(val), type(val)
"Unsupported value passed to {}: {} ({})".format(
self.__class__.__name__,
repr(val),
type(val),
)
)

def __add__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
def __add__(self, other: Union["BaseFloat", float]):
if isinstance(other, BaseFloat):
other = other.value
return self.__class__(self.value + other)

def __sub__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
def __sub__(self, other: Union["BaseFloat", float]):
if isinstance(other, BaseFloat):
other = other.value
return self.__class__(self.value - other)

def __mul__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
def __mul__(self, other: Union["BaseFloat", float]):
if isinstance(other, BaseFloat):
other = other.value
return self.__class__(self.value * other)

def __truediv__(self, other: Any):
if isinstance(other, Float32):
if isinstance(other, BaseFloat):
other = other.value
return self.__class__(self.value / other)

def __floordiv__(self, other: Any):
if isinstance(other, Float32):
if isinstance(other, BaseFloat):
other = other.value
return self.__class__(self.value // other)

def __mod__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
def __mod__(self, other: Union["BaseFloat", float]):
if isinstance(other, BaseFloat):
other = other.value
return self.__class__(self.value % other)

def __eq__(self, other: object) -> bool:
if isinstance(other, (float, int)):
return self.value == other
elif isinstance(other, Float32):
elif isinstance(other, BaseFloat):
return self.value == other.value
return False

Expand All @@ -114,22 +112,22 @@ def __hash__(self):
return hash(self.value)

def __gt__(self, other: Any):
if isinstance(other, Float32):
if isinstance(other, BaseFloat):
other = other.value
return self.value > other

def __lt__(self, other: Any):
if isinstance(other, Float32):
if isinstance(other, BaseFloat):
other = other.value
return self.value < other

def __le__(self, other: Any):
if isinstance(other, Float32):
if isinstance(other, BaseFloat):
other = other.value
return self.value <= other

def __ge__(self, other: Any):
if isinstance(other, Float32):
if isinstance(other, BaseFloat):
other = other.value
return self.value >= other

Expand Down Expand Up @@ -161,40 +159,34 @@ def __rfloordiv__(self, other: Any):
def __rmod__(self, other: Any):
return self.__class__(other) % self

def __rand__(self, other: Any):
return self.__class__(other) & self

def __ror__(self, other: Any):
return self.__class__(other) | self

def __rxor__(self, other: Any):
return self.__class__(other) ^ self

# bytewise operators:
@classmethod
def bitcast(cls, f: "BaseFloat") -> "BaseFloat":
"""
bitcast the struct up or down to another type.
def __and__(self, other: Union["Float32", float, int]):
if isinstance(other, float):
other = Float32(other)
if isinstance(other, Float32):
other = other.bits
return self.from_bytes(self.bits & other)
Use Float64.bitcast(Float32(...)) to bitcast a f32 to f64
"""
return cls.from_bytes((b"\x00\x00\x00\x00\x00\x00\x00\x00" + f.bytes)[-struct.calcsize(cls._struct_fmt_str):])

def __or__(self, other: Union["Float32", float]):
if isinstance(other, float):
other = Float32(other)
if isinstance(other, Float32):
other = other.bits
return self.from_bytes(self.bits | other)
@classmethod
def flen_to_cls(cls, bits: int) -> type['BaseFloat']:
if bits == 32:
return Float32
if bits == 64:
return Float64
raise ValueError(f"Unsupported flen: {bits}")

def __xor__(self, other: Union["Float32", float]):
if isinstance(other, float):
other = Float32(other)
if isinstance(other, Float32):
other = other.bits
return self.from_bytes(self.bits ^ other)

def __lshift__(self, other: int):
return self.from_bytes(self.bits << other)
class Float32(BaseFloat):
_type = c_float
_struct_fmt_str = 'f'

def __rshift__(self, other: int):
return self.from_bytes(self.bits >> other)
@classmethod
def bitcast(cls, f: "BaseFloat") -> "BaseFloat":
if isinstance(f, Float32):
return f


class Float64(BaseFloat):
_type = c_double
_struct_fmt_str = 'd'
4 changes: 4 additions & 0 deletions riscemu/core/mmu.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
InstructionContext,
Int32,
Float32,
Float64,
InvalidAllocationException,
MemoryAccessException,
)
Expand Down Expand Up @@ -215,6 +216,9 @@ def read_int(self, addr: int) -> Int32:
def read_float(self, addr: int) -> Float32:
return Float32(self.read(addr, 4))

def read_double(self, addr: int) -> Float64:
return Float64(self.read(addr, 8))

def translate_address(self, address: T_AbsoluteAddress) -> str:
sec = self.get_sec_containing(address)
if not sec:
Expand Down
25 changes: 15 additions & 10 deletions riscemu/core/registers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from ..helpers import *

from . import Int32, Float32
from . import Int32, BaseFloat, Float32, Float64


class Registers:
Expand Down Expand Up @@ -84,9 +84,14 @@ class Registers:
"fa7",
}

def __init__(self, infinite_regs: bool = False):
flen: int
_float_type: type[BaseFloat]

def __init__(self, infinite_regs: bool = False, flen: int = 32):
self.vals: dict[str, Int32] = defaultdict(UInt32)
self.float_vals: dict[str, Float32] = defaultdict(Float32)
self.flen = flen
self._float_type = BaseFloat.flen_to_cls(flen)
self.float_vals: dict[str, BaseFloat] = defaultdict(self._float_type)

self.last_set = None
self.last_read = None
Expand Down Expand Up @@ -169,7 +174,7 @@ def _reg_repr(self, reg: str, name_len=4, fmt="08X"):
return FMT_GRAY + txt + FMT_NONE
return txt

def set(self, reg: str, val: "Int32", mark_set: bool = True) -> bool:
def set(self, reg: str, val: Int32, mark_set: bool = True) -> bool:
"""
Set a register content to val
:param reg: The register to set
Expand All @@ -194,7 +199,7 @@ def set(self, reg: str, val: "Int32", mark_set: bool = True) -> bool:
self.vals[reg] = val.unsigned()
return True

def get(self, reg: str, mark_read: bool = True) -> "Int32":
def get(self, reg: str, mark_read: bool = True) -> Int32:
"""
Returns the contents of register reg
:param reg: The register name
Expand All @@ -213,17 +218,17 @@ def get(self, reg: str, mark_read: bool = True) -> "Int32":
self.last_read = reg
return self.vals[reg]

def get_f(self, reg: str, mark_read: bool = True) -> "Float32":
def get_f(self, reg: str, mark_read: bool = True) -> BaseFloat:
if not self.infinite_regs and reg not in self.float_regs:
raise RuntimeError("Invalid float register: {}".format(reg))
if mark_read:
self.last_read = reg
return self.float_vals[reg]

def set_f(self, reg: str, val: Union[float, "Float32"]):
def set_f(self, reg: str, val: Union[float, BaseFloat]):
if not self.infinite_regs and reg not in self.float_regs:
raise RuntimeError("Invalid float register: {}".format(reg))
self.float_vals[reg] = Float32(val)
self.float_vals[reg] = self._float_type(val)

@staticmethod
def named_registers():
Expand All @@ -234,8 +239,8 @@ def named_registers():
return ["zero", "ra", "sp", "gp", "tp", "fp"]

def __repr__(self):
return "<Registers[{}]{}>".format(
"float" if self.supports_floats else "nofloat",
return "<Registers[xlen=32,flen={}]{}>".format(
self.flen,
"{"
+ ", ".join(self._reg_repr("a{}".format(i), 2, "0x") for i in range(8))
+ "}",
Expand Down
2 changes: 1 addition & 1 deletion riscemu/instructions/RV32F.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ def instruction_fmv_x_w(self, ins: Instruction):
| x[rd] = sext(f[rs1][31:0])
"""
rd, rs = self.parse_rd_rs(ins)
self.regs.set(rd, UInt32(self.regs.get_f(rs).bits))
self.regs.set(rd, UInt32(self.regs.get_f(rs).bytes))

def instruction_feq_s(self, ins: Instruction):
"""
Expand Down
2 changes: 2 additions & 0 deletions riscemu/instructions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
}

__all__ = [
Instruction,
InstructionSet,
InstructionSetDict,
RV32I,
RV32M,
Expand Down
Loading

0 comments on commit 4481e0d

Please sign in to comment.