Skip to content

Commit

Permalink
Add support for flen=64 (#49)
Browse files Browse the repository at this point in the history
Currently still missing a lot of the D extension (all except `fmadd, fmsub, fnmsub, fnmadd, fadd, fsub, fmul, fdiv, fsqrt, fsgnj, fsgnjn, fsgnjx, fmin, fmax, feq, flt, fle, fld, fsd`), missing conversion and move instructions.

This may break some of the float32 stuff, so we should be very careful with this.
  • Loading branch information
AntonLydike committed Oct 10, 2023
1 parent f01b312 commit 9015094
Show file tree
Hide file tree
Showing 24 changed files with 838 additions and 733 deletions.
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
# Changelog

# 2.2.2
## 2.2.3

- Feature: Adding support for 64 bit floating point operations
- BugFix: Fix `__all__` to now properly work (use name strings instead of values)

## 2.2.2

- Dev: Add `__all__` to `riscemu.{core,instructions,decoder}` modules to make pyright in other projects happy
- Perf: very minor fix related to not converting values twice when loaded from memory

# 2.2.1
## 2.2.1

Version bump to re-trigger CI run.

# 2.2.0
## 2.2.0

- Feature: Added Zicsr extension and with that support for CSRs
- Feature: Starting to add support for Snitch architecture (Xssr)
Expand Down
2 changes: 1 addition & 1 deletion examples/estimate-cpu-freq.asm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ measure_loop:
fcvt.s.w ft1, t0 // ft1 = 1k
fdiv.s ft2, ft2, ft1 // ft2 = kins/sec

printf "executed {} instructions in {:.4f} seconds ({:.2f}ki/s)", s0, ft0, ft2
printf "executed {} instructions in {:.4f32} seconds ({:.2f32}ki/s)", s0, ft0, ft2
mv ra, s4
ret

Expand Down
141 changes: 76 additions & 65 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ 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"
pytest = "^7.4.0"
filecheck = "^0.0.23"
lit = "^16.0.6"
pre-commit = "^3.3.3"
psutil = "^5.9.5"

[build-system]
requires = ["poetry-core"]
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 = 64
# runtime config
use_libc: bool = False
ignore_exit_code: bool = False
Expand Down
78 changes: 40 additions & 38 deletions 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 All @@ -47,41 +47,43 @@
from .usermode_cpu import UserModeCPU

__all__ = [
T_RelativeAddress,
T_AbsoluteAddress,
T_ParserOpts,
NUMBER_SYMBOL_PATTERN,
ParseException,
NumberFormatException,
MemoryAccessException,
OutOfMemoryException,
LinkerException,
LaunchDebuggerException,
RiscemuBaseException,
InvalidRegisterException,
InvalidAllocationException,
InvalidSyscallException,
UnimplementedInstruction,
INS_NOT_IMPLEMENTED,
MemoryFlags,
UInt32,
Int32,
Float32,
RTClock,
Instruction,
Immediate,
InstructionWithEncoding,
InstructionContext,
MemorySection,
Program,
ProgramLoader,
PrivModes,
MMU,
CSR,
Registers,
CPU,
SimpleInstruction,
InstructionMemorySection,
BinaryDataMemorySection,
UserModeCPU,
"T_RelativeAddress",
"T_AbsoluteAddress",
"T_ParserOpts",
"NUMBER_SYMBOL_PATTERN",
"ParseException",
"NumberFormatException",
"MemoryAccessException",
"OutOfMemoryException",
"LinkerException",
"LaunchDebuggerException",
"RiscemuBaseException",
"InvalidRegisterException",
"InvalidAllocationException",
"InvalidSyscallException",
"UnimplementedInstruction",
"INS_NOT_IMPLEMENTED",
"MemoryFlags",
"UInt32",
"Int32",
"BaseFloat",
"Float32",
"Float64",
"RTClock",
"Instruction",
"Immediate",
"InstructionWithEncoding",
"InstructionContext",
"MemorySection",
"Program",
"ProgramLoader",
"PrivModes",
"MMU",
"CSR",
"Registers",
"CPU",
"SimpleInstruction",
"InstructionMemorySection",
"BinaryDataMemorySection",
"UserModeCPU",
]
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
147 changes: 75 additions & 72 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, Type
from abc import ABC

bytes_t = bytes


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

_val: c_float
_type: ClassVar[Type[Union[c_float, c_double]]]
_struct_fmt_str: ClassVar[str]

_val: Union[c_float, c_double]

@property
def value(self) -> float:
Expand All @@ -22,73 +26,66 @@ 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 cls(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])
elif isinstance(val, self.__class__):
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 @@ -107,35 +104,38 @@ def __repr__(self):
def __str__(self):
return str(self.value)

def __format__(self, format_spec: str):
return self.value.__format__(format_spec)

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

def __bool__(self):
return bool(self.value)

def __int__(self):
return int(self.value)

def __float__(self):
return self.value

def __pow__(self, power, modulo=None):
if modulo is not None:
raise ValueError("Float32 pow with modulo unsupported")
Expand All @@ -161,40 +161,43 @@ 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
@classmethod
def bitcast(cls, f: "BaseFloat") -> "BaseFloat":
"""
bitcast the struct up or down to another type.
Fills upper bits with zero.
def __rxor__(self, other: Any):
return self.__class__(other) ^ self
Use Float64.bitcast(Float32(...)) to bitcast a f32 to f64
"""
if isinstance(f, cls):
return f
return cls.from_bytes(
(b"\x00\x00\x00\x00\x00\x00\x00\x00" + f.bytes)[
-struct.calcsize(cls._struct_fmt_str) :
]
)

# bytewise operators:
@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 __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)
def __format__(self, spec: str):
if spec[-2:] == "32":
return Float32.bitcast(self).__format__(spec[:-2])
if spec[-2:] == "64":
return Float64.bitcast(self).__format__(spec[:-2])
return format(self.value, spec)

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)

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)
class Float32(BaseFloat):
_type = c_float
_struct_fmt_str = "f"

def __lshift__(self, other: int):
return self.from_bytes(self.bits << other)

def __rshift__(self, other: int):
return self.from_bytes(self.bits >> other)
class Float64(BaseFloat):
_type = c_double
_struct_fmt_str = "d"
Loading

0 comments on commit 9015094

Please sign in to comment.