The Python golden model

The golden is one vectorized MAC per numeric kind — a*b + c over DataArrays, with no per-element loop. The same expression, three inner types. (For the concepts — operators, growth rules, the .val escape — see the vectorization guide; here we just build this example.)

Integer — growth-aware operators

from waveflow.hw.dataschema import DataArray, IntField

I8 = IntField.specialize(8, True)                         # ap_int<8>
ia = lambda vals: DataArray.specialize(I8, max_shape=(len(vals),))(vals)

a, b, c = ia([3, -4, 5, 7]), ia([6, 7, -8, 2]), ia([1, -1, 2, -3])
y = a * b + c                                             # -> Int17
y.element_type.get_bitwidth()                             # 17  (a*b is 16, +c adds a carry bit)

The operators derive the result width (a*b8+8=16, +c+1), so Int17 exactly mirrors ap_int<17> y = a*b + c; — no overflow, no manual sizing.

Float — numpy passthrough

import numpy as np
from waveflow.hw.dataschema import FloatField

F32 = FloatField.specialize(32)
fa = lambda vals: DataArray.specialize(F32, max_shape=(len(vals),))(np.array(vals, dtype=np.float32))

a, b, c = fa([1.5, 2.5, -3.0]), fa([2.0, -1.5, 0.5]), fa([0.25, 1.0, -0.5])
y = a * b + c                                             # float32, no growth

For float, the operators are numpy passthrough — a*b + c is the same two roundings numpy does (multiply, then add). The Vitis kernel must reproduce exactly that (no fused FMA) — see the float kernel.

Fixed — grow then quantize

from waveflow.hw.fixpoint import FixedField, from_real, quantize

Q = FixedField.specialize(8, 4)                          # ap_fixed<8,4>
a = from_real([1.5, -2.0, 0.5], Q)
b = from_real([2.0, 1.5, -1.0], Q)
c = from_real([0.5, 0.25, -0.5], Q)
y = quantize(a * b + c, Q)                               # full precision, then ONE quantize

a*b + c grows to full precision (the product widens, the sum grows a bit); the explicit quantize(..., Q) rounds back to ap_fixed<8,4> — exactly what ap_fixed<8,4> y = a*b + c; does on assignment.

The golden bits

Each result is serialized to the stored bits the kernel will emit — integer/fixed via to_bits, float via the IEEE bit-view:

import numpy as np
from waveflow.utils.fixputils import to_bits

# integer / fixed: stored W-bit integers
int_bits = [int(x) for x in to_bits(np.asarray(y), 8)]
# float: raw IEEE bits
float_bits = [int(u) for u in np.asarray(y).astype(np.float32).view(np.uint32)]

These golden bit-vectors go into expected.json; the kernel’s output is compared against them in the eval step.


This site uses Just the Docs, a documentation theme for Jekyll.