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.
This commit is contained in:
parent
5a23804ad8
commit
be90879f86
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
|
2
.idea/riscemu.iml
generated
2
.idea/riscemu.iml
generated
@ -10,7 +10,7 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/riscemu.egg-info" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (riscemu)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
|
@ -16,6 +16,7 @@ from .types import (
|
||||
Program,
|
||||
InstructionContext,
|
||||
Int32,
|
||||
Float32,
|
||||
)
|
||||
from .types.exceptions import InvalidAllocationException, MemoryAccessException
|
||||
|
||||
@ -187,6 +188,9 @@ class MMU:
|
||||
def read_int(self, addr: int) -> Int32:
|
||||
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:
|
||||
sec = self.get_sec_containing(address)
|
||||
if not sec:
|
||||
|
609
riscemu/instructions/RV32F.py
Normal file
609
riscemu/instructions/RV32F.py
Normal file
@ -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 result’s sign bit is rs2’s 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 result’s sign bit is opposite of rs2’s 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 result’s 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 .RV32I import RV32I
|
||||
from .RV32A import RV32A
|
||||
from .RV32F import RV32F
|
||||
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
|
||||
|
||||
@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, {}
|
||||
|
@ -1,15 +1,15 @@
|
||||
"""
|
||||
RiscEmu (c) 2021-2022 Anton Lydike
|
||||
RiscEmu (c) 2023 Anton Lydike
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
"""
|
||||
|
||||
from collections import defaultdict
|
||||
from typing import Union
|
||||
|
||||
from .helpers import *
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .types import Int32
|
||||
from .types import Int32, Float32
|
||||
|
||||
|
||||
class Registers:
|
||||
@ -51,6 +51,9 @@ class Registers:
|
||||
"a5",
|
||||
"a6",
|
||||
"a7",
|
||||
}
|
||||
|
||||
float_regs = {
|
||||
"ft0",
|
||||
"ft1",
|
||||
"ft2",
|
||||
@ -82,11 +85,12 @@ class Registers:
|
||||
}
|
||||
|
||||
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_read = None
|
||||
|
||||
self.infinite_regs = infinite_regs
|
||||
|
||||
def dump(self, full: bool = False):
|
||||
@ -165,8 +169,6 @@ class Registers:
|
||||
:return: If the operation was successful
|
||||
"""
|
||||
|
||||
from .types import Int32
|
||||
|
||||
# remove after refactoring is complete
|
||||
if not isinstance(val, Int32):
|
||||
raise RuntimeError(
|
||||
@ -208,75 +210,17 @@ class Registers:
|
||||
self.last_read = reg
|
||||
return self.vals[reg]
|
||||
|
||||
@staticmethod
|
||||
def all_registers():
|
||||
"""
|
||||
Return a list of all valid registers
|
||||
:return: The list
|
||||
"""
|
||||
return [
|
||||
"zero",
|
||||
"ra",
|
||||
"sp",
|
||||
"gp",
|
||||
"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",
|
||||
]
|
||||
def get_f(self, reg: str, mark_read: bool = True) -> "Float32":
|
||||
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"]):
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
def named_registers():
|
||||
@ -287,8 +231,9 @@ class Registers:
|
||||
return ["zero", "ra", "sp", "gp", "tp", "fp"]
|
||||
|
||||
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))
|
||||
+ "}"
|
||||
+ "}",
|
||||
)
|
||||
|
@ -13,6 +13,7 @@ NUMBER_SYMBOL_PATTERN = re.compile(r"^\d+[fb]$")
|
||||
# base classes
|
||||
from .flags import MemoryFlags
|
||||
from .int32 import UInt32, Int32
|
||||
from .float32 import Float32
|
||||
from .instruction import Instruction
|
||||
from .instruction_context import InstructionContext
|
||||
from .memory_section import MemorySection
|
||||
@ -36,4 +37,5 @@ from .exceptions import (
|
||||
InvalidAllocationException,
|
||||
InvalidSyscallException,
|
||||
UnimplementedInstruction,
|
||||
INS_NOT_IMPLEMENTED,
|
||||
)
|
||||
|
203
riscemu/types/float32.py
Normal file
203
riscemu/types/float32.py
Normal file
@ -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",)
|
||||
|
||||
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)):
|
||||
signed = len(val) == 4 and self._type == c_int32
|
||||
@ -27,7 +27,7 @@ class Int32:
|
||||
self._val = val
|
||||
elif isinstance(val, (c_uint32, c_int32, Int32)):
|
||||
self._val = self.__class__._type(val.value)
|
||||
elif isinstance(val, int):
|
||||
elif isinstance(val, (int, bool)):
|
||||
self._val = self.__class__._type(val)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
|
21
test/test_float32.py
Normal file
21
test/test_float32.py
Normal file
@ -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
|
26
test/test_regs.py
Normal file
26
test/test_regs.py
Normal file
@ -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…
Reference in New Issue
Block a user