Skip to content

Commit

Permalink
Floating-point support in fud (#2318)
Browse files Browse the repository at this point in the history
Extract floating-point support for `fud` from
#1928
  • Loading branch information
rachitnigam authored Nov 1, 2024
1 parent c455733 commit 720e287
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 10 deletions.
23 changes: 23 additions & 0 deletions calyx-py/calyx/numeric_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from decimal import Decimal, getcontext
import math
import logging as log
import struct


class InvalidNumericType(Exception):
Expand Down Expand Up @@ -335,3 +336,25 @@ def bitnum_to_fixed(bitnum: Bitnum, int_width: int) -> FixedPoint:
int_width=int_width,
is_signed=bitnum.is_signed,
)


@dataclass
class FloatingPoint(NumericType):
"""Represents a floating point number."""

def __init__(self, value: str, width: int, is_signed: bool):
super().__init__(value, width, is_signed)

if self.bit_string_repr is None and self.hex_string_repr is None:
# The decimal representation was passed in.
packed = struct.pack("!f", float(self.string_repr))
unpacked = struct.unpack(">I", packed)[0]
self.bit_string_repr = f"{unpacked:0{self.width}b}"
self.uint_repr = int(self.bit_string_repr, 2)
self.hex_string_repr = np.base_repr(self.uint_repr, 16)

def to_dec(self, round_place: int):
float_value = struct.unpack(
"!f", int(self.bit_string_repr, 2).to_bytes(4, byteorder="big")
)[0]
return round(float_value, round_place)
30 changes: 20 additions & 10 deletions fud/fud/stages/verilator/json_to_dat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import simplejson as sjson
import numpy as np
from calyx.numeric_types import FixedPoint, Bitnum, InvalidNumericType
from calyx.numeric_types import FixedPoint, Bitnum, FloatingPoint, InvalidNumericType
from pathlib import Path
from fud.errors import Malformed
import logging as log
Expand All @@ -14,7 +14,7 @@ def float_to_fixed(value: float, N: int) -> float:
return round(value * w) / float(w)


def parse_dat(path, args):
def parse_dat(path, is_bn, args):
"""Parses a number with the given numeric type
arguments from the array at the given `path`.
"""
Expand All @@ -34,12 +34,15 @@ def parse(hex_value: str):
hex_value = f"0x{hex_value}"
if "int_width" in args:
return FixedPoint(hex_value, **args).str_value()
else:
elif is_bn:
bn = Bitnum(hex_value, **args)
if bn.is_undef:
return bn.str_value()
else:
return int(bn.str_value())
else:
fp = FloatingPoint(hex_value, **args)
return fp.to_dec(round_place=2)

with path.open("r") as f:
lines = []
Expand Down Expand Up @@ -90,11 +93,14 @@ def provided(x, y):
)


def convert(x, round: bool, is_signed: bool, width: int, int_width=None):
def convert(x, round: bool, is_signed: bool, width: int, is_bn: bool, int_width=None):
with_prefix = False
# If `int_width` is not defined, then this is a `Bitnum`
if int_width is None:
return Bitnum(x, width, is_signed).hex_string(with_prefix)
if is_bn:
return Bitnum(x, width, is_signed).hex_string(with_prefix)
else:
return FloatingPoint(x, width, is_signed).hex_string(with_prefix)

try:
return FixedPoint(x, width, int_width, is_signed).hex_string(with_prefix)
Expand Down Expand Up @@ -133,14 +139,16 @@ def convert2dat(output_dir, data, extension, round: bool):
numeric_type = format["numeric_type"]
is_signed = format["is_signed"]

if numeric_type not in {"bitnum", "fixed_point"}:
raise InvalidNumericType('Fud only supports "fixed_point" and "bitnum".')
if numeric_type not in {"bitnum", "fixed_point", "ieee754_float"}:
raise InvalidNumericType(
'Fud only supports "fixed_point", "bitnum", and "ieee754_float".'
)

is_fp = numeric_type == "fixed_point"
if is_fp:
width, int_width = parse_fp_widths(format)
else:
# `Bitnum`s only have a bit width.
# `Bitnum`s and `FloatingPoint`s only have a bit width
width = format["width"]
int_width = None

Expand All @@ -154,7 +162,8 @@ def convert2dat(output_dir, data, extension, round: bool):

with path.open("w") as f:
for v in arr.flatten():
f.write(convert(v, round, is_signed, width, int_width) + "\n")
is_bn = numeric_type == "bitnum"
f.write(convert(v, round, is_signed, width, is_bn, int_width) + "\n")

shape[k]["shape"] = list(arr.shape)
shape[k]["numeric_type"] = numeric_type
Expand Down Expand Up @@ -185,8 +194,9 @@ def convert2json(input_dir, extension):
# for building the FixedPoint or Bitnum classes.
args = form.copy()
del args["shape"]
is_bn = args["numeric_type"] == "bitnum"
del args["numeric_type"]
arr = parse_dat(path, args)
arr = parse_dat(path, is_bn, args)
if form["shape"] == [0]:
raise Malformed(
"Data format shape",
Expand Down
1 change: 1 addition & 0 deletions runt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ paths = [
"tests/correctness/ref-cells/*.futil",
"tests/correctness/sync/*.futil",
"tests/correctness/static-interface/*.futil",
"tests/correctness/ieee754-float/*.futil",
]
cmd = """
fud exec --from calyx --to jq \
Expand Down
9 changes: 9 additions & 0 deletions tests/correctness/ieee754-float/parse-and-print.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"inp": [
15.12
],
"out": [
15.12,
0.56
]
}
38 changes: 38 additions & 0 deletions tests/correctness/ieee754-float/parse-and-print.futil
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import "primitives/core.futil";
import "primitives/memories/seq.futil";
import "primitives/float.futil";

component main() -> () {
cells {
f = std_float_const(0, 32, 0.56);
@external inp = seq_mem_d1(32, 1, 1);
@external out = seq_mem_d1(32, 2, 2);
}

wires {
group set_in {
inp.addr0 = 1'd0;
inp.content_en = 1'd1;
set_in[done] = inp.done;
}
group write_from_in {
out.content_en = 1'd1;
out.addr0 = 2'd0;
out.write_en = 1'd1;
out.write_data = inp.read_data;
write_from_in[done] = out.done;
}
group write_from_const {
out.content_en = 1'd1;
out.addr0 = 2'd1;
out.write_en = 1'd1;
out.write_data = f.out;
write_from_const[done] = out.done;
}
}
control {
set_in;
write_from_in;
write_from_const;
}
}
23 changes: 23 additions & 0 deletions tests/correctness/ieee754-float/parse-and-print.futil.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"inp": {
"data": [
15.1239
],
"format": {
"numeric_type": "ieee754_float",
"is_signed": false,
"width": 32
}
},
"out": {
"data": [
0.0,
0.0
],
"format": {
"numeric_type": "ieee754_float",
"is_signed": false,
"width": 32
}
}
}

0 comments on commit 720e287

Please sign in to comment.