diff --git a/tests/fpgadataflow/test_fpgadataflow_fclayer.py b/tests/fpgadataflow/test_fpgadataflow_fclayer.py index 4f1648feabd690da4a73b2d201dcaad4149725e7..058cb74396176bee5fcde3ccfffb312bbaede45a 100644 --- a/tests/fpgadataflow/test_fpgadataflow_fclayer.py +++ b/tests/fpgadataflow/test_fpgadataflow_fclayer.py @@ -7,7 +7,8 @@ import finn.core.onnx_exec as oxe import finn.custom_op.xnorpopcount as xp from finn.core.datatype import DataType from finn.core.modelwrapper import ModelWrapper -from finn.core.utils import gen_finn_dt_tensor +from finn.core.utils import calculate_signed_dot_prod_range, gen_finn_dt_tensor +from finn.custom_op.multithreshold import multithreshold from finn.transformation.fpgadataflow.cleanup import CleanUp from finn.transformation.fpgadataflow.codegen import CodeGen from finn.transformation.fpgadataflow.compile import Compile @@ -31,9 +32,14 @@ def make_single_fclayer_modelwrapper(W, pe, simd, wdt, idt, odt, T=None, tdt=Non outp = helper.make_tensor_value_info("outp", TensorProto.FLOAT, [1, nf, pe]) if T is not None: node_inp_list = ["inp", "weights", "thresh"] + if odt == DataType.BIPOLAR: + actval = 0 + else: + actval = odt.min() else: # no thresholds node_inp_list = ["inp", "weights"] + actval = 0 FCLayer_node = helper.make_node( "StreamingFCLayer_Batch", node_inp_list, @@ -50,6 +56,7 @@ def make_single_fclayer_modelwrapper(W, pe, simd, wdt, idt, odt, T=None, tdt=Non inputDataType=idt.name, weightDataType=wdt.name, outputDataType=odt.name, + ActVal=actval, ) graph = helper.make_graph( nodes=[FCLayer_node], name="fclayer_graph", inputs=[inp], outputs=[outp] @@ -77,6 +84,8 @@ def prepare_inputs(model, input_tensor, idt): return {"inp": input_tensor} +# activation: None or DataType +@pytest.mark.parametrize("act", [None, DataType.BIPOLAR]) # weight datatype @pytest.mark.parametrize("wdt", [DataType.BIPOLAR, DataType.INT2]) # input datatype @@ -89,7 +98,7 @@ def prepare_inputs(model, input_tensor, idt): @pytest.mark.parametrize("mw", [4]) # HLS matrix height (output features) @pytest.mark.parametrize("mh", [4]) -def test_fpgadataflow_fclayer_noact(idt, wdt, nf, sf, mw, mh): +def test_fpgadataflow_fclayer(idt, wdt, act, nf, sf, mw, mh): if nf == -1: nf = mh if sf == -1: @@ -98,15 +107,32 @@ def test_fpgadataflow_fclayer_noact(idt, wdt, nf, sf, mw, mh): simd = mw // sf assert mh % pe == 0 assert mw % sf == 0 - if wdt == DataType.BIPOLAR and idt == DataType.BIPOLAR: - odt = DataType.UINT32 - else: - odt = DataType.INT32 # generate weights W = gen_finn_dt_tensor(wdt, (mw, mh)) # generate input data x = gen_finn_dt_tensor(idt, (1, mw)) - model = make_single_fclayer_modelwrapper(W, pe, simd, wdt, idt, odt) + if act is None: + # no activation, produce accumulators + T = None + tdt = None + if wdt == DataType.BIPOLAR and idt == DataType.BIPOLAR: + odt = DataType.UINT32 + else: + odt = DataType.INT32 + else: + odt = act + (min, max) = calculate_signed_dot_prod_range(idt, wdt, mw) + n_steps = act.get_num_possible_values() - 1 + T = np.random.randint(min, max - 1, (mh, n_steps)).astype(np.float32) + # generate thresholds for activation + if wdt == DataType.BIPOLAR and idt == DataType.BIPOLAR: + tdt = DataType.UINT32 + # bias thresholds to be positive + T = np.ceil((T + mw) / 2) + assert (T >= 0).all() + else: + tdt = DataType.INT32 + model = make_single_fclayer_modelwrapper(W, pe, simd, wdt, idt, odt, T, tdt) model = model.transform(CodeGen()) model = model.transform(Compile()) # prepare input data @@ -116,6 +142,14 @@ def test_fpgadataflow_fclayer_noact(idt, wdt, nf, sf, mw, mh): y = xp.xnorpopcountmatmul((x + 1) / 2, (W + 1) / 2) else: y = np.matmul(x, W) + if T is not None: + y = multithreshold(y, T) + if act == DataType.BIPOLAR: + # binary to bipolar + y = 2 * y - 1 + else: + # signed offset + y -= act.min() oshape = model.get_tensor_shape("outp") y_expected = y.reshape(oshape) # execute model