Add support for floats (#22)

Adding a `Float32` datatype is necessary, since python makes no guarantees to the bitwidth of `float` (it's often a double)

Also adding the `RV32F` extension with most operations implemented, and support for floating point registers.
master
Anton Lydike 1 year ago committed by GitHub
parent 5a23804ad8
commit be90879f86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (riscemu)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (riscemu)" project-jdk-type="Python SDK" />
</project> </project>

@ -10,7 +10,7 @@
<excludeFolder url="file://$MODULE_DIR$/riscemu.egg-info" /> <excludeFolder url="file://$MODULE_DIR$/riscemu.egg-info" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="jdk" jdkName="Python 3.10 (riscemu)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

@ -16,6 +16,7 @@ from .types import (
Program, Program,
InstructionContext, InstructionContext,
Int32, Int32,
Float32,
) )
from .types.exceptions import InvalidAllocationException, MemoryAccessException from .types.exceptions import InvalidAllocationException, MemoryAccessException
@ -187,6 +188,9 @@ class MMU:
def read_int(self, addr: int) -> Int32: def read_int(self, addr: int) -> Int32:
return Int32(self.read(addr, 4)) return Int32(self.read(addr, 4))
def read_float(self, addr: int) -> Float32:
return Float32(self.read(addr, 4))
def translate_address(self, address: T_AbsoluteAddress) -> str: def translate_address(self, address: T_AbsoluteAddress) -> str:
sec = self.get_sec_containing(address) sec = self.get_sec_containing(address)
if not sec: if not sec:

@ -0,0 +1,609 @@
"""
RiscEmu (c) 2023 Anton Lydike
SPDX-License-Identifier: MIT
This file contains copious amounts of docstrings that were all taken
from https://msyksphinz-self.github.io/riscv-isadoc/html/rvfd.html
(all the docstrings on the instruction methods documenting the opcodes
and their function)
"""
from typing import Tuple
from .instruction_set import InstructionSet, Instruction
from riscemu.types import INS_NOT_IMPLEMENTED, Float32, Int32, UInt32
class RV32F(InstructionSet):
def instruction_fmadd_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|rs3 |00 |rs2 |rs1 |rm |rd |10000|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fmadd.s rd,rs1,rs2,rs3
:Description:
| Perform single-precision fused multiply addition.
:Implementation:
| f[rd] = f[rs1]×f[rs2]+f[rs3]
"""
rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
self.regs.set_f(rd, rs1 * rs2 + rs3)
def instruction_fmsub_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|rs3 |00 |rs2 |rs1 |rm |rd |10001|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fmsub.s rd,rs1,rs2,rs3
:Description:
| Perform single-precision fused multiply addition.
:Implementation:
| f[rd] = f[rs1]×f[rs2]-f[rs3]
"""
rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
self.regs.set_f(rd, rs1 * rs2 - rs3)
def instruction_fnmsub_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|rs3 |00 |rs2 |rs1 |rm |rd |10010|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fnmsub.s rd,rs1,rs2,rs3
:Description:
| Perform single-precision fused multiply addition.
:Implementation:
| f[rd] = -f[rs1]×f[rs2]+f[rs3]
"""
rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
self.regs.set_f(rd, -rs1 * rs2 + rs3)
def instruction_fnmadd_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|rs3 |00 |rs2 |rs1 |rm |rd |10011|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fnmadd.s rd,rs1,rs2,rs3
:Description:
| Perform single-precision fused multiply addition.
:Implementation:
| f[rd] = -f[rs1]×f[rs2]-f[rs3]
"""
rd, rs1, rs2, rs3 = self.parse_rd_rs_rs_rs(ins)
self.regs.set_f(rd, -rs1 * rs2 - rs3)
def instruction_fadd_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00000|00 |rs2 |rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fadd.s rd,rs1,rs2
:Description:
| Perform single-precision floating-point addition.
:Implementation:
| f[rd] = f[rs1] + f[rs2]
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set_f(
rd,
self.regs.get_f(rs1) + self.regs.get_f(rs2),
)
def instruction_fsub_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00001|00 |rs2 |rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fsub.s rd,rs1,rs2
:Description:
| Perform single-precision floating-point substraction.
:Implementation:
| f[rd] = f[rs1] - f[rs2]
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set_f(
rd,
self.regs.get_f(rs1) - self.regs.get_f(rs2),
)
def instruction_fmul_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00010|00 |rs2 |rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fmul.s rd,rs1,rs2
:Description:
| Perform single-precision floating-point multiplication.
:Implementation:
| f[rd] = f[rs1] × f[rs2]
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set_f(
rd,
self.regs.get_f(rs1) * self.regs.get_f(rs2),
)
def instruction_fdiv_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00011|00 |rs2 |rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fdiv.s rd,rs1,rs2
:Description:
| Perform single-precision floating-point division.
:Implementation:
| f[rd] = f[rs1] / f[rs2]
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set_f(
rd,
self.regs.get_f(rs1) / self.regs.get_f(rs2),
)
def instruction_fsqrt_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|01011|00 |00000|rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fsqrt.s rd,rs1
:Description:
| Perform single-precision square root.
:Implementation:
| f[rd] = sqrt(f[rs1])
"""
rd, rs = self.parse_rd_rs(ins)
self.regs.set_f(self.regs.get_f(rs) ** 0.5)
def instruction_fsgnj_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00100|00 |rs2 |rs1 |000 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fsgnj.s rd,rs1,rs2
:Description:
| Produce a result that takes all bits except the sign bit from rs1.
| The results sign bit is rs2s sign bit.
:Implementation:
| f[rd] = {f[rs2][31], f[rs1][30:0]}
"""
INS_NOT_IMPLEMENTED(ins)
def instruction_fsgnjn_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00100|00 |rs2 |rs1 |001 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fsgnjn.s rd,rs1,rs2
:Description:
| Produce a result that takes all bits except the sign bit from rs1.
| The results sign bit is opposite of rs2s sign bit.
:Implementation:
| f[rd] = {~f[rs2][31], f[rs1][30:0]}
"""
INS_NOT_IMPLEMENTED(ins)
def instruction_fsgnjx_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00100|00 |rs2 |rs1 |010 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fsgnjx.s rd,rs1,rs2
:Description:
| Produce a result that takes all bits except the sign bit from rs1.
| The results sign bit is XOR of sign bit of rs1 and rs2.
:Implementation:
| f[rd] = {f[rs1][31] ^ f[rs2][31], f[rs1][30:0]}
"""
INS_NOT_IMPLEMENTED(ins)
def instruction_fmin_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00101|00 |rs2 |rs1 |000 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fmin.s rd,rs1,rs2
:Description:
| Write the smaller of single precision data in rs1 and rs2 to rd.
:Implementation:
| f[rd] = min(f[rs1], f[rs2])
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set_f(
rd,
Float32(
min(
self.regs.get_f(rs1).value,
self.regs.get_f(rs2).value,
)
),
)
def instruction_fmax_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|00101|00 |rs2 |rs1 |001 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fmax.s rd,rs1,rs2
:Description:
| Write the larger of single precision data in rs1 and rs2 to rd.
:Implementation:
| f[rd] = max(f[rs1], f[rs2])
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set_f(
rd,
Float32(
max(
self.regs.get_f(rs1).value,
self.regs.get_f(rs2).value,
)
),
)
def instruction_fcvt_w_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|11000|00 |00000|rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fcvt.w.s rd,rs1
:Description:
| Convert a floating-point number in floating-point register rs1 to a signed 32-bit in integer register rd.
:Implementation:
| x[rd] = sext(s32_{f32}(f[rs1]))
"""
rd, rs = self.parse_rd_rs(ins)
self.regs.set(rd, Int32(self.regs.get_f(rs).value))
def instruction_fcvt_wu_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|11000|00 |00001|rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fcvt.wu.s rd,rs1
:Description:
| Convert a floating-point number in floating-point register rs1 to a signed 32-bit in unsigned integer register rd.
:Implementation:
| x[rd] = sext(u32_{f32}(f[rs1]))
"""
rd, rs = self.parse_rd_rs(ins)
self.regs.set(rd, UInt32(self.regs.get_f(rs).value))
def instruction_fmv_x_w(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|11100|00 |00000|rs1 |000 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fmv.x.w rd,rs1
:Description:
| Move the single-precision value in floating-point register rs1 represented in IEEE 754-2008 encoding to the lower 32 bits of integer register rd.
:Implementation:
| 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))
def instruction_feq_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|10100|00 |rs2 |rs1 |010 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| feq.s rd,rs1,rs2
:Description:
| Performs a quiet equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.
| Only signaling NaN inputs cause an Invalid Operation exception.
| The result is 0 if either operand is NaN.
:Implementation:
| x[rd] = f[rs1] == f[rs2]
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set(rd, Int32(rs1 == rs2))
def instruction_flt_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|10100|00 |rs2 |rs1 |001 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| flt.s rd,rs1,rs2
:Description:
| Performs a quiet less comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.
| Only signaling NaN inputs cause an Invalid Operation exception.
| The result is 0 if either operand is NaN.
:Implementation:
| x[rd] = f[rs1] < f[rs2]
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set(rd, Int32(rs1 < rs2))
def instruction_fle_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|10100|00 |rs2 |rs1 |000 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fle.s rd,rs1,rs2
:Description:
| Performs a quiet less or equal comparison between floating-point registers rs1 and rs2 and record the Boolean result in integer register rd.
| Only signaling NaN inputs cause an Invalid Operation exception.
| The result is 0 if either operand is NaN.
:Implementation:
| x[rd] = f[rs1] <= f[rs2]
"""
rd, rs1, rs2 = self.parse_rd_rs_rs(ins)
self.regs.set(rd, Int32(rs1 <= rs2))
def instruction_fclass_s(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|11100|00 |00000|rs1 |001 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fclass.s rd,rs1
:Description:
| Examines the value in floating-point register rs1 and writes to integer register rd a 10-bit mask that indicates the class of the floating-point number.
| The format of the mask is described in [classify table]_.
| The corresponding bit in rd will be set if the property is true and clear otherwise.
| All other bits in rd are cleared. Note that exactly one bit in rd will be set.
:Implementation:
| x[rd] = classifys(f[rs1])
"""
INS_NOT_IMPLEMENTED(ins)
def instruction_fcvt_s_w(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|11010|00 |00000|rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fcvt.s.w rd,rs1
:Description:
| Converts a 32-bit signed integer, in integer register rs1 into a floating-point number in floating-point register rd.
:Implementation:
| f[rd] = f32_{s32}(x[rs1])
"""
rd, rs = self.parse_rd_rs(ins)
self.regs.set_f(rd, Float32(self.regs.get(rs).signed().value))
def instruction_fcvt_s_wu(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|11010|00 |00001|rs1 |rm |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fcvt.s.wu rd,rs1
:Description:
| Converts a 32-bit unsigned integer, in integer register rs1 into a floating-point number in floating-point register rd.
:Implementation:
| f[rd] = f32_{u32}(x[rs1])
"""
rd, rs = self.parse_rd_rs(ins)
self.regs.set_f(rd, Float32(self.regs.get(rs).unsigned_value))
def instruction_fmv_w_x(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|11110|00 |00000|rs1 |000 |rd |10100|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| fmv.w.x rd,rs1
:Description:
| Move the single-precision value encoded in IEEE 754-2008 standard encoding from the lower 32 bits of integer register rs1 to the floating-point register rd.
:Implementation:
| f[rd] = x[rs1][31:0]
"""
rd, rs = self.parse_rd_rs(ins)
self.regs.set_f(rd, Float32.from_bytes(self.regs.get(rs).unsigned_value))
def instruction_flw(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+-----+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+-----+-----+---+
|imm[11:0] |rs1 |010 |rd |00001|11 |
+-----+-----+-----+-----+-----+-----+-----+---+
:Format:
| flw rd,offset(rs1)
:Description:
| Load a single-precision floating-point value from memory into floating-point register rd.
:Implementation:
| f[rd] = M[x[rs1] + sext(offset)][31:0]
"""
rd, addr = self.parse_mem_ins(ins)
self.regs.set_f(rd, self.mmu.read_float(addr))
def instruction_fsw(self, ins: Instruction):
"""
+-----+-----+-----+-----+-----+--------+-----+---+
|31-27|26-25|24-20|19-15|14-12|11-7 |6-2 |1-0|
+-----+-----+-----+-----+-----+--------+-----+---+
|imm[11:5] |rs2 |rs1 |010 |imm[4:0]|01001|11 |
+-----+-----+-----+-----+-----+--------+-----+---+
:Format:
| fsw rs2,offset(rs1)
:Description:
| Store a single-precision value from floating-point register rs2 to memory.
:Implementation:
| M[x[rs1] + sext(offset)] = f[rs2][31:0]
"""
rs, addr = self.parse_mem_ins(ins)
val = self.regs.get_f(rs)
self.mmu.write(addr, 4, bytearray(val.bytes))
def parse_rd_rs(self, ins: Instruction) -> Tuple[str, str]:
assert len(ins.args) == 2
return ins.get_reg(0), ins.get_reg(1)
def parse_rd_rs_rs(self, ins: Instruction) -> Tuple[str, Float32, Float32]:
assert len(ins.args) == 3
return (
ins.get_reg(0),
self.regs.get_f(ins.get_reg(1)),
self.regs.get_f(ins.get_reg(2)),
)
def parse_rd_rs_rs_rs(
self, ins: Instruction
) -> Tuple[str, Float32, Float32, Float32]:
assert len(ins.args) == 4
return (
ins.get_reg(0),
self.regs.get_f(ins.get_reg(1)),
self.regs.get_f(ins.get_reg(2)),
self.regs.get_f(ins.get_reg(3)),
)

@ -10,6 +10,7 @@ from .instruction_set import InstructionSet, Instruction
from .RV32M import RV32M from .RV32M import RV32M
from .RV32I import RV32I from .RV32I import RV32I
from .RV32A import RV32A from .RV32A import RV32A
from .RV32F import RV32F
from .RV_Debug import RV_Debug from .RV_Debug import RV_Debug
InstructionSetDict = {v.__name__: v for v in [RV32I, RV32M, RV32A, RV_Debug]} InstructionSetDict = {v.__name__: v for v in [RV32I, RV32M, RV32A, RV32F, RV_Debug]}

@ -139,5 +139,5 @@ class AssemblyFileLoader(ProgramLoader):
return 0.01 return 0.01
@classmethod @classmethod
def get_options(cls, argv: List[str]) -> tuple[List[str], T_ParserOpts]: def get_options(cls, argv: List[str]) -> Tuple[List[str], T_ParserOpts]:
return argv, {} return argv, {}

@ -1,15 +1,15 @@
""" """
RiscEmu (c) 2021-2022 Anton Lydike RiscEmu (c) 2023 Anton Lydike
SPDX-License-Identifier: MIT SPDX-License-Identifier: MIT
""" """
from collections import defaultdict from collections import defaultdict
from typing import Union
from .helpers import * from .helpers import *
if typing.TYPE_CHECKING: from .types import Int32, Float32
from .types import Int32
class Registers: class Registers:
@ -51,6 +51,9 @@ class Registers:
"a5", "a5",
"a6", "a6",
"a7", "a7",
}
float_regs = {
"ft0", "ft0",
"ft1", "ft1",
"ft2", "ft2",
@ -82,11 +85,12 @@ class Registers:
} }
def __init__(self, infinite_regs: bool = False): def __init__(self, infinite_regs: bool = False):
from .types import Int32 self.vals: dict[str, Int32] = defaultdict(UInt32)
self.float_vals: dict[str, Float32] = defaultdict(Float32)
self.vals: defaultdict[str, Int32] = defaultdict(lambda: Int32(0))
self.last_set = None self.last_set = None
self.last_read = None self.last_read = None
self.infinite_regs = infinite_regs self.infinite_regs = infinite_regs
def dump(self, full: bool = False): def dump(self, full: bool = False):
@ -165,8 +169,6 @@ class Registers:
:return: If the operation was successful :return: If the operation was successful
""" """
from .types import Int32
# remove after refactoring is complete # remove after refactoring is complete
if not isinstance(val, Int32): if not isinstance(val, Int32):
raise RuntimeError( raise RuntimeError(
@ -208,75 +210,17 @@ class Registers:
self.last_read = reg self.last_read = reg
return self.vals[reg] return self.vals[reg]
@staticmethod def get_f(self, reg: str, mark_read: bool = True) -> "Float32":
def all_registers(): if not self.infinite_regs and reg not in self.float_regs:
""" raise RuntimeError("Invalid float register: {}".format(reg))
Return a list of all valid registers if mark_read:
:return: The list self.last_read = reg
""" return self.float_vals[reg]
return [
"zero", def set_f(self, reg: str, val: Union[float, "Float32"]):
"ra", if not self.infinite_regs and reg not in self.float_regs:
"sp", raise RuntimeError("Invalid float register: {}".format(reg))
"gp", self.float_vals[reg] = Float32(val)
"tp",
"s0",
"fp",
"t0",
"t1",
"t2",
"t3",
"t4",
"t5",
"t6",
"s1",
"s2",
"s3",
"s4",
"s5",
"s6",
"s7",
"s8",
"s9",
"s10",
"s11",
"a0",
"a1",
"a2",
"a3",
"a4",
"a5",
"a6",
"a7",
"ft0",
"ft1",
"ft2",
"ft3",
"ft4",
"ft5",
"ft6",
"ft7",
"fs0",
"fs1",
"fs2",
"fs3",
"fs4",
"fs5",
"fs6",
"fs7",
"fs8",
"fs9",
"fs10",
"fs11",
"fa0",
"fa1",
"fa2",
"fa3",
"fa4",
"fa5",
"fa6",
"fa7",
]
@staticmethod @staticmethod
def named_registers(): def named_registers():
@ -287,8 +231,9 @@ class Registers:
return ["zero", "ra", "sp", "gp", "tp", "fp"] return ["zero", "ra", "sp", "gp", "tp", "fp"]
def __repr__(self): def __repr__(self):
return "<Registers{}>".format( return "<Registers[{}]{}>".format(
"float" if self.supports_floats else "nofloat",
"{" "{"
+ ", ".join(self._reg_repr("a{}".format(i), 2, "0x") for i in range(8)) + ", ".join(self._reg_repr("a{}".format(i), 2, "0x") for i in range(8))
+ "}" + "}",
) )

@ -13,6 +13,7 @@ NUMBER_SYMBOL_PATTERN = re.compile(r"^\d+[fb]$")
# base classes # base classes
from .flags import MemoryFlags from .flags import MemoryFlags
from .int32 import UInt32, Int32 from .int32 import UInt32, Int32
from .float32 import Float32
from .instruction import Instruction from .instruction import Instruction
from .instruction_context import InstructionContext from .instruction_context import InstructionContext
from .memory_section import MemorySection from .memory_section import MemorySection
@ -36,4 +37,5 @@ from .exceptions import (
InvalidAllocationException, InvalidAllocationException,
InvalidSyscallException, InvalidSyscallException,
UnimplementedInstruction, UnimplementedInstruction,
INS_NOT_IMPLEMENTED,
) )

@ -0,0 +1,203 @@
import struct
from ctypes import c_float
from typing import Union, Any
bytes_t = bytes
class Float32:
__slots__ = ("_val",)
_val: c_float
@property
def value(self) -> float:
"""
The value represented by this float
"""
return self._val.value
@property
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")
@classmethod
def from_bytes(cls, val: Union[int, bytes_t, bytearray]):
if isinstance(val, int):
val = int.to_bytes(byteorder="little")
return Float32(val)
def __init__(
self, val: Union[float, c_float, "Float32", 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)
elif isinstance(val, (bytes, bytearray)):
self._val = c_float(struct.unpack("<f", val)[0])
elif isinstance(val, Float32):
self._val = val._val
else:
raise ValueError(
"Unsupported value passed to Float32: {} ({})".format(
repr(val), type(val)
)
)
def __add__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
other = other.value
return self.__class__(self.value + other)
def __sub__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
other = other.value
return self.__class__(self.value - other)
def __mul__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
other = other.value
return self.__class__(self.value * other)
def __truediv__(self, other: Any):
return self // other
def __floordiv__(self, other: Any):
if isinstance(other, Float32):
other = other.value
return self.__class__(self.value // other)
def __mod__(self, other: Union["Float32", float]):
if isinstance(other, Float32):
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):
return self.value == other.value
return False
def __neg__(self):
return self.__class__(-self.value)
def __abs__(self):
return self.__class__(abs(self.value))
def __bytes__(self) -> bytes_t:
return self.bytes
def __repr__(self):
return "{}({})".format(self.__class__.__name__, self.value)
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):
other = other.value
return self.value > other
def __lt__(self, other: Any):
if isinstance(other, Float32):
other = other.value
return self.value < other
def __le__(self, other: Any):
if isinstance(other, Float32):
other = other.value
return self.value <= other
def __ge__(self, other: Any):
if isinstance(other, Float32):
other = other.value
return self.value >= other
def __bool__(self):
return bool(self.value)
def __cmp__(self, other: Any):
if isinstance(other, Float32):
other = other.value
return self.value.__cmp__(other)
def __pow__(self, power, modulo=None):
if modulo is not None:
raise ValueError("Float32 pow with modulo unsupported")
return self.__class__(self.value**power)
# right handed binary operators
def __radd__(self, other: Any):
return self + other
def __rsub__(self, other: Any):
return self.__class__(other) - self
def __rmul__(self, other: Any):
return self * other
def __rtruediv__(self, other: Any):
return self.__class__(other) // self
def __rfloordiv__(self, other: Any):
return self.__class__(other) // self
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:
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 __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)
def __lshift__(self, other: int):
return self.from_bytes(self.bits << other)
def __rshift__(self, other: int):
return self.from_bytes(self.bits >> other)

@ -16,7 +16,7 @@ class Int32:
__slots__ = ("_val",) __slots__ = ("_val",)
def __init__( def __init__(
self, val: Union[int, c_int32, c_uint32, "Int32", bytes, bytearray] = 0 self, val: Union[int, c_int32, c_uint32, "Int32", bytes, bytearray, bool] = 0
): ):
if isinstance(val, (bytes, bytearray)): if isinstance(val, (bytes, bytearray)):
signed = len(val) == 4 and self._type == c_int32 signed = len(val) == 4 and self._type == c_int32
@ -27,7 +27,7 @@ class Int32:
self._val = val self._val = val
elif isinstance(val, (c_uint32, c_int32, Int32)): elif isinstance(val, (c_uint32, c_int32, Int32)):
self._val = self.__class__._type(val.value) self._val = self.__class__._type(val.value)
elif isinstance(val, int): elif isinstance(val, (int, bool)):
self._val = self.__class__._type(val) self._val = self.__class__._type(val)
else: else:
raise RuntimeError( raise RuntimeError(

@ -0,0 +1,21 @@
import math
from riscemu.types import Float32
# pi encoded as a 32bit little endian float
PI_BYTES_LE = b"\xdb\x0fI@"
def test_float_serialization():
assert Float32(PI_BYTES_LE) == Float32(math.pi)
assert Float32(math.pi).bytes == PI_BYTES_LE
def test_random_float_ops():
val = Float32(5)
assert val**2 == 25
assert val // 2 == 2
assert val * 3 == 15
assert val - 2 == 3
assert val * val == 25
assert Float32(9) ** 0.5 == 3

@ -0,0 +1,26 @@
import pytest
from riscemu.registers import Registers
from riscemu.types import Float32
def test_float_regs():
r = Registers()
# uninitialized register is zero
assert r.get_f("fs0") == 0
# get/set
val = Float32(3.14)
r.set_f("fs0", val)
assert r.get_f("fs0") == val
def test_unlimited_regs_works():
r = Registers(infinite_regs=True)
r.get("infinite")
r.get_f("finfinite")
def test_unknown_reg_fails():
r = Registers(infinite_regs=False)
with pytest.raises(RuntimeError, match="Invalid register: az1"):
r.get("az1")
Loading…
Cancel
Save