diff --git a/docker/Dockerfile.finn b/docker/Dockerfile.finn index bd951f7857..d93a588f91 100644 --- a/docker/Dockerfile.finn +++ b/docker/Dockerfile.finn @@ -124,6 +124,7 @@ RUN pip install pytest-metadata==1.7.0 RUN pip install pytest-html==3.0.0 RUN pip install pytest-html-merger==0.0.8 RUN pip install pytest-cov==4.1.0 +RUN pip install pyyaml==6.0.1 # extra dependencies from other FINN deps # installed in Docker image to make entrypoint script go faster diff --git a/run-docker.sh b/run-docker.sh index ec55299f6c..66ef8f00f2 100755 --- a/run-docker.sh +++ b/run-docker.sh @@ -88,7 +88,7 @@ SCRIPTPATH=$(dirname "$SCRIPT") : ${PLATFORM_REPO_PATHS="/opt/xilinx/platforms"} : ${XRT_DEB_VERSION="xrt_202220.2.14.354_22.04-amd64-xrt"} : ${FINN_HOST_BUILD_DIR="/tmp/$DOCKER_INST_NAME"} -: ${FINN_DOCKER_TAG="xilinx/finn:$(git describe --always --tags --dirty).$XRT_DEB_VERSION"} +: ${FINN_DOCKER_TAG="xilinx/finn:$(OLD_PWD=$(pwd); cd $SCRIPTPATH; git describe --always --tags --dirty; cd $OLD_PWD).$XRT_DEB_VERSION"} : ${FINN_DOCKER_PREBUILT="0"} : ${FINN_DOCKER_RUN_AS_ROOT="0"} : ${FINN_DOCKER_GPU="$(docker info | grep nvidia | wc -m)"} diff --git a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py index 98a04b0bc9..93cd1157f6 100644 --- a/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py +++ b/src/finn/custom_op/fpgadataflow/hls/lookup_hls.py @@ -28,6 +28,7 @@ import numpy as np import os +import warnings from math import ceil, log2 from qonnx.core.datatype import DataType @@ -87,31 +88,6 @@ def defines(self, var): my_defines.append("#define EmbeddingType %s" % emb_hls_type) self.code_gen_dict["$DEFINES$"] = my_defines - def read_npy_data(self): - code_gen_dir = self.get_nodeattr("code_gen_dir_cppsim") - dtype = self.get_input_datatype() - if dtype == DataType["BIPOLAR"]: - # use binary for bipolar storage - dtype = DataType["BINARY"] - elem_bits = dtype.bitwidth() - packed_bits = self.get_instream_width() - packed_hls_type = "ap_uint<%d>" % packed_bits - elem_hls_type = dtype.get_hls_datatype_str() - npy_type = "int64_t" - npy_in = "%s/input_0.npy" % code_gen_dir - self.code_gen_dict["$READNPYDATA$"] = [] - self.code_gen_dict["$READNPYDATA$"].append( - 'npy2apintstream<%s, %s, %d, %s>("%s", in0_%s);' - % ( - packed_hls_type, - elem_hls_type, - elem_bits, - npy_type, - npy_in, - self.hls_sname(), - ) - ) - def dataoutstrm(self): code_gen_dir = self.get_nodeattr("code_gen_dir_cppsim") dtype = self.get_output_datatype() @@ -273,7 +249,18 @@ def execute_node(self, context, graph): ) inp = context[node.input[0]] - assert inp.dtype == np.int64, "Inputs must be contained in int64 ndarray" + + # Make sure the input has the right container datatype + if inp.dtype is not np.float32: + # Issue a warning to make the user aware of this type-cast + warnings.warn( + f"{node.name}: Changing input container datatype from " + f"{inp.dtype} to {np.float32}" + ) + # Convert the input to floating point representation as the + # container datatype + inp = inp.astype(np.float32) + assert inp.shape == exp_ishape, """Input shape doesn't match expected shape.""" export_idt = self.get_input_datatype() odt = self.get_output_datatype() diff --git a/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py b/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py index 05b45f9e4b..ad49978ee2 100644 --- a/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py +++ b/src/finn/custom_op/fpgadataflow/rtl/streamingfifo_rtl.py @@ -127,10 +127,18 @@ def execute_node(self, context, graph): elif mode == "rtlsim": code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen") # create a npy file for the input of the node - assert ( - str(inp.dtype) == "float32" - ), """Input datatype is - not float32 as expected.""" + + # Make sure the input has the right container datatype + if inp.dtype is not np.float32: + # Issue a warning to make the user aware of this type-cast + warnings.warn( + f"{node.name}: Changing input container datatype from " + f"{inp.dtype} to {np.float32}" + ) + # Convert the input to floating point representation as the + # container datatype + inp = inp.astype(np.float32) + expected_inp_shape = self.get_folded_input_shape() reshaped_input = inp.reshape(expected_inp_shape) if DataType[self.get_nodeattr("dataType")] == DataType["BIPOLAR"]: diff --git a/tests/brevitas/test_brevitas_avg_pool_export.py b/tests/brevitas/test_brevitas_avg_pool_export.py index 053b632221..ad04314510 100644 --- a/tests/brevitas/test_brevitas_avg_pool_export.py +++ b/tests/brevitas/test_brevitas_avg_pool_export.py @@ -41,8 +41,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN - -base_export_onnx_path = "test_brevitas_avg_pool_export.onnx" +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -62,7 +61,8 @@ def test_brevitas_avg_pool_export( channels, idim, ): - export_onnx_path = base_export_onnx_path.replace(".onnx", "test_QONNX.onnx") + build_dir = make_build_dir(prefix="test_brevitas_avg_pool_export") + export_onnx_path = os.path.join(build_dir, "test.onnx") if signed: quant_node = QuantIdentity( bit_width=input_bit_width, @@ -112,5 +112,3 @@ def test_brevitas_avg_pool_export( finn_output = odict[model.graph.output[0].name] # compare outputs assert np.isclose(ref_output_array, finn_output).all() - # cleanup - os.remove(export_onnx_path) diff --git a/tests/brevitas/test_brevitas_cnv.py b/tests/brevitas/test_brevitas_cnv.py index 3950a5b6a7..e0a15f50a4 100644 --- a/tests/brevitas/test_brevitas_cnv.py +++ b/tests/brevitas/test_brevitas_cnv.py @@ -41,10 +41,9 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN +from finn.util.basic import make_build_dir from finn.util.test import get_test_model_trained -export_onnx_path = "test_brevitas_cnv.onnx" - @pytest.mark.brevitas_export @pytest.mark.parametrize("abits", [1, 2]) @@ -52,6 +51,8 @@ def test_brevitas_cnv_export_exec(wbits, abits): if wbits > abits: pytest.skip("No wbits > abits cases at the moment") + build_dir = make_build_dir("test_brevitas_cnv_export_exec") + export_onnx_path = os.path.join(build_dir, "test_brevitas_cnv.onnx") cnv = get_test_model_trained("CNV", wbits, abits) ishape = (1, 3, 32, 32) export_qonnx(cnv, torch.randn(ishape), export_onnx_path) @@ -78,4 +79,3 @@ def test_brevitas_cnv_export_exec(wbits, abits): expected = cnv.forward(input_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() assert np.argmax(produced) == 3 - os.remove(export_onnx_path) diff --git a/tests/brevitas/test_brevitas_debug.py b/tests/brevitas/test_brevitas_debug.py index 3d059a6856..1e31c31e3c 100644 --- a/tests/brevitas/test_brevitas_debug.py +++ b/tests/brevitas/test_brevitas_debug.py @@ -42,13 +42,15 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN +from finn.util.basic import make_build_dir from finn.util.test import get_test_model_trained @pytest.mark.brevitas_export @pytest.mark.parametrize("QONNX_FINN_conversion", [False, True]) def test_brevitas_debug(QONNX_FINN_conversion): - finn_onnx = "test_brevitas_debug.onnx" + build_dir = make_build_dir("test_brevitas_debug") + finn_onnx = os.path.join(build_dir, "test_brevitas_debug.onnx") fc = get_test_model_trained("TFC", 2, 2) ishape = (1, 1, 28, 28) dbg_hook = bo.enable_debug(fc, proxy_level=True) @@ -94,4 +96,3 @@ def test_brevitas_debug(QONNX_FINN_conversion): tensor_pytorch = _unpack_quant_tensor(dbg_hook.values[dbg_name]).detach().numpy() tensor_finn = output_dict[dbg_name] assert np.isclose(tensor_finn, tensor_pytorch, atol=1e-5).all() - os.remove(finn_onnx) diff --git a/tests/brevitas/test_brevitas_deconv.py b/tests/brevitas/test_brevitas_deconv.py index dfcecc9187..b7650f5b2d 100644 --- a/tests/brevitas/test_brevitas_deconv.py +++ b/tests/brevitas/test_brevitas_deconv.py @@ -38,8 +38,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN - -export_path = "test_brevitas_deconv.onnx" +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -66,6 +65,8 @@ def test_brevitas_QTransposeConv(ifm_ch, ofm_ch, mh, mw, padding, stride, kw, bi padding=padding, bias=bias, ) + build_dir = make_build_dir("test_brevitas_QTransposeConv") + export_path = os.path.join(build_dir, "test_brevitas_deconv.onnx") # outp = el(inp) # expects NCHW data format export_qonnx(b_deconv, input_t=inp, export_path=export_path, opset_version=11) qonnx_cleanup(export_path, out_file=export_path) @@ -79,4 +80,3 @@ def test_brevitas_QTransposeConv(ifm_ch, ofm_ch, mh, mw, padding, stride, kw, bi inp_tensor = torch.from_numpy(inp_tensor).float() expected = b_deconv.forward(inp_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_path) diff --git a/tests/brevitas/test_brevitas_non_scaled_quanthardtanh_export.py b/tests/brevitas/test_brevitas_non_scaled_quanthardtanh_export.py index 08a193714a..b63fd16119 100644 --- a/tests/brevitas/test_brevitas_non_scaled_quanthardtanh_export.py +++ b/tests/brevitas/test_brevitas_non_scaled_quanthardtanh_export.py @@ -43,8 +43,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN - -export_onnx_path = "test_brevitas_non_scaled_QuantHardTanh_export.onnx" +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -72,7 +71,8 @@ def get_quant_type(bit_width): scaling_impl_type=ScalingImplType.CONST, narrow_range=narrow_range, ) - m_path = export_onnx_path + build_dir = make_build_dir(prefix="test_brevitas_act_export_qhardtanh_nonscaled") + m_path = os.path.join(build_dir, "test_brevitas_non_scaled_QuantHardTanh_export.onnx") export_qonnx(b_act, torch.randn(ishape), m_path) qonnx_cleanup(m_path, out_file=m_path) model = ModelWrapper(m_path) @@ -85,4 +85,3 @@ def get_quant_type(bit_width): inp_tensor = torch.from_numpy(inp_tensor).float() expected = b_act.forward(inp_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_onnx_path) diff --git a/tests/brevitas/test_brevitas_qconv2d.py b/tests/brevitas/test_brevitas_qconv2d.py index 4b27671891..a919a4dbc5 100644 --- a/tests/brevitas/test_brevitas_qconv2d.py +++ b/tests/brevitas/test_brevitas_qconv2d.py @@ -45,8 +45,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN - -export_onnx_path = "test_brevitas_conv.onnx" +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -92,7 +91,8 @@ def test_brevitas_QConv2d(dw, bias, in_channels): weight_tensor = gen_finn_dt_tensor(DataType["INT4"], w_shape) b_conv.weight = torch.nn.Parameter(torch.from_numpy(weight_tensor).float()) b_conv.eval() - m_path = export_onnx_path + build_dir = make_build_dir(prefix="test_brevitas_QConv2d") + m_path = os.path.join(build_dir, "test_brevitas_conv.onnx") export_qonnx(b_conv, torch.randn(ishape), m_path) qonnx_cleanup(m_path, out_file=m_path) model = ModelWrapper(m_path) @@ -106,4 +106,3 @@ def test_brevitas_QConv2d(dw, bias, in_channels): expected = b_conv.forward(inp_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_onnx_path) diff --git a/tests/brevitas/test_brevitas_qlinear.py b/tests/brevitas/test_brevitas_qlinear.py index a6ea077e7a..10b128ed3c 100644 --- a/tests/brevitas/test_brevitas_qlinear.py +++ b/tests/brevitas/test_brevitas_qlinear.py @@ -42,8 +42,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN - -export_onnx_path = "test_brevitas_qlinear.onnx" +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -67,7 +66,8 @@ def test_brevitas_qlinear(bias, out_features, in_features, w_bits, i_dtype): weight_tensor_fp = np.random.uniform(low=-1.0, high=1.0, size=w_shape).astype(np.float32) b_linear.weight.data = torch.from_numpy(weight_tensor_fp) b_linear.eval() - m_path = export_onnx_path + build_dir = make_build_dir(prefix="test_brevitas_qlinear") + m_path = os.path.join(build_dir, "test_brevitas_qlinear.onnx") export_qonnx(b_linear, torch.randn(i_shape), m_path) qonnx_cleanup(m_path, out_file=m_path) model = ModelWrapper(m_path) @@ -81,4 +81,3 @@ def test_brevitas_qlinear(bias, out_features, in_features, w_bits, i_dtype): expected = b_linear.forward(inp_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_onnx_path) diff --git a/tests/brevitas/test_brevitas_relu_act_export.py b/tests/brevitas/test_brevitas_relu_act_export.py index 2254670202..e48c78aa53 100644 --- a/tests/brevitas/test_brevitas_relu_act_export.py +++ b/tests/brevitas/test_brevitas_relu_act_export.py @@ -41,8 +41,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN - -export_onnx_path = "test_brevitas_relu_act_export.onnx" +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -55,7 +54,8 @@ def test_brevitas_act_export_relu( b_act = QuantReLU( bit_width=abits, ) - m_path = export_onnx_path + build_dir = make_build_dir(prefix="test_brevitas_act_export_relu") + m_path = os.path.join(build_dir, "test_brevitas_relu_act_export.onnx") export_qonnx(b_act, torch.randn(ishape), m_path) qonnx_cleanup(m_path, out_file=m_path) model = ModelWrapper(m_path) @@ -70,7 +70,6 @@ def test_brevitas_act_export_relu( expected = b_act.forward(inp_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_onnx_path) @pytest.mark.brevitas_export @@ -88,7 +87,8 @@ def test_brevitas_act_export_relu_channel( scaling_per_output_channel=True, per_channel_broadcastable_shape=(1, ch, 1, 1), ) - m_path = export_onnx_path + build_dir = make_build_dir(prefix="test_brevitas_act_export_relu_channel") + m_path = os.path.join(build_dir, "test_brevitas_relu_act_export.onnx") export_qonnx(b_act, torch.randn(ishape), m_path) qonnx_cleanup(m_path, out_file=m_path) model = ModelWrapper(m_path) @@ -103,4 +103,3 @@ def test_brevitas_act_export_relu_channel( expected = b_act.forward(inp_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_onnx_path) diff --git a/tests/brevitas/test_brevitas_scaled_qhardtanh_export.py b/tests/brevitas/test_brevitas_scaled_qhardtanh_export.py index e7d87faed8..ad523466f7 100644 --- a/tests/brevitas/test_brevitas_scaled_qhardtanh_export.py +++ b/tests/brevitas/test_brevitas_scaled_qhardtanh_export.py @@ -43,8 +43,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN - -export_onnx_path = "test_brevitas_scaled_QHardTanh_export.onnx" +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -85,7 +84,8 @@ def get_quant_type(bit_width): ) } b_act.load_state_dict(checkpoint) - m_path = export_onnx_path + build_dir = make_build_dir(prefix="test_brevitas_act_export_qhardtanh_scaled") + m_path = os.path.join(build_dir, "test_brevitas_scaled_QHardTanh_export.onnx") export_qonnx(b_act, torch.randn(ishape), m_path) qonnx_cleanup(m_path, out_file=m_path) model = ModelWrapper(m_path) @@ -121,4 +121,3 @@ def get_quant_type(bit_width): print("expec:", ", ".join(["{:8.4f}".format(x) for x in expected[0]])) assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_onnx_path) diff --git a/tests/brevitas/test_brevitas_selu_act_export.py b/tests/brevitas/test_brevitas_selu_act_export.py index c8d040dbee..9dabc1fec2 100644 --- a/tests/brevitas/test_brevitas_selu_act_export.py +++ b/tests/brevitas/test_brevitas_selu_act_export.py @@ -40,6 +40,7 @@ import finn.core.onnx_exec as oxe from finn.transformation.qonnx.convert_qonnx_to_finn import ConvertQONNXtoFINN +from finn.util.basic import make_build_dir @pytest.mark.brevitas_export @@ -47,7 +48,8 @@ @pytest.mark.parametrize("ishape", [(1, 15), (1, 32, 1, 1)]) @pytest.mark.parametrize("narrow", [True, False]) def test_brevitas_act_export_selu(abits, ishape, narrow): - export_path = "test_brevitas_selu_act_export_%s.onnx" % str(abits) + build_dir = make_build_dir(prefix="test_brevitas_act_export_selu") + export_path = os.path.join(build_dir, "test_brevitas_selu_act_export_%s.onnx" % str(abits)) b_act = torch.nn.Sequential(torch.nn.SELU(), QuantIdentity(bit_width=abits, narrow=narrow)) export_qonnx( @@ -69,4 +71,3 @@ def test_brevitas_act_export_selu(abits, ishape, narrow): expected = b_act.forward(inp_tensor).detach().numpy() assert np.isclose(produced, expected, atol=1e-3).all() - os.remove(export_path) diff --git a/tests/end2end/test_end2end_bnn_pynq.py b/tests/end2end/test_end2end_bnn_pynq.py index 4946428324..8eb2855567 100644 --- a/tests/end2end/test_end2end_bnn_pynq.py +++ b/tests/end2end/test_end2end_bnn_pynq.py @@ -111,8 +111,9 @@ rtlsim_trace = False -def get_checkpoint_name(topology, wbits, abits, step): - return build_dir + "/end2end_%s_w%da%d_%s.onnx" % ( +def get_checkpoint_name(board, topology, wbits, abits, step): + return build_dir + "/end2end_%s_%s_w%da%d_%s.onnx" % ( + board, topology, wbits, abits, @@ -450,11 +451,16 @@ def pytest_generate_tests(metafunc): scenarios.extend(test_cases) if len(scenarios) > 0: - for scenario in scenarios: + for i, scenario in enumerate(scenarios): idlist.append(scenario[0]) items = scenario[1].items() argnames = [x[0] for x in items] - argvalues.append([x[1] for x in items]) + argvalues_scenario = [x[1] for x in items] + argvalues.append( + pytest.param( + *argvalues_scenario, marks=pytest.mark.xdist_group(name="bnn_pynq_%d" % i) + ) + ) metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class") @@ -470,7 +476,7 @@ def test_export(self, topology, wbits, abits, board): if topology == "lfc" and not (wbits == 1 and abits == 1): pytest.skip("Skipping certain lfc configs") (model, ishape) = get_trained_network_and_ishape(topology, wbits, abits) - chkpt_name = get_checkpoint_name(topology, wbits, abits, "export") + chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "export") export_qonnx(model, torch.randn(ishape), chkpt_name, opset_version=13) qonnx_cleanup(chkpt_name, out_file=chkpt_name) model = ModelWrapper(chkpt_name) @@ -479,7 +485,7 @@ def test_export(self, topology, wbits, abits, board): assert os.path.isfile(chkpt_name) def test_import_and_tidy(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "export") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "export") model = load_test_checkpoint_or_skip(prev_chkpt_name) model = model.transform(InferShapes()) model = model.transform(FoldConstants()) @@ -487,17 +493,17 @@ def test_import_and_tidy(self, topology, wbits, abits, board): model = model.transform(GiveReadableTensorNames()) model = model.transform(InferDataTypes()) model = model.transform(RemoveStaticGraphInputs()) - chkpt = get_checkpoint_name(topology, wbits, abits, "import_and_tidy") + chkpt = get_checkpoint_name(board, topology, wbits, abits, "import_and_tidy") model.save(chkpt) def test_add_pre_and_postproc(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "import_and_tidy") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "import_and_tidy") model = load_test_checkpoint_or_skip(prev_chkpt_name) global_inp_name = model.graph.input[0].name ishape = model.get_tensor_shape(global_inp_name) # preprocessing: torchvision's ToTensor divides uint8 inputs by 255 totensor_pyt = ToTensor() - chkpt_preproc_name = get_checkpoint_name(topology, wbits, abits, "preproc") + chkpt_preproc_name = get_checkpoint_name(board, topology, wbits, abits, "preproc") export_qonnx(totensor_pyt, torch.randn(ishape), chkpt_preproc_name, opset_version=13) qonnx_cleanup(chkpt_preproc_name, out_file=chkpt_preproc_name) pre_model = ModelWrapper(chkpt_preproc_name) @@ -514,7 +520,7 @@ def test_add_pre_and_postproc(self, topology, wbits, abits, board): model.set_tensor_datatype(global_inp_name, DataType["UINT8"]) # postprocessing: insert Top-1 node at the end model = model.transform(InsertTopK(k=1)) - chkpt_name = get_checkpoint_name(topology, wbits, abits, "pre_post") + chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "pre_post") # tidy-up again model = model.transform(InferShapes()) model = model.transform(FoldConstants()) @@ -526,7 +532,7 @@ def test_add_pre_and_postproc(self, topology, wbits, abits, board): assert os.path.isfile(chkpt_name) def test_streamline(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "pre_post") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "pre_post") model = load_test_checkpoint_or_skip(prev_chkpt_name) model = model.transform(absorb.AbsorbSignBiasIntoMultiThreshold()) # move past any reshapes to be able to streamline input scaling @@ -542,10 +548,10 @@ def test_streamline(self, topology, wbits, abits, board): model = model.transform(absorb.AbsorbScalarMulAddIntoTopK()) model = model.transform(InferDataLayouts()) model = model.transform(RemoveUnusedTensors()) - model.save(get_checkpoint_name(topology, wbits, abits, "streamline")) + model.save(get_checkpoint_name(board, topology, wbits, abits, "streamline")) def test_convert_to_hw_layers(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "streamline") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "streamline") model = load_test_checkpoint_or_skip(prev_chkpt_name) if topology == "tfc" and wbits == 1 and abits == 1: # use standalone thresholds for tfc-w1a1 to also exercise that option @@ -567,7 +573,7 @@ def test_convert_to_hw_layers(self, topology, wbits, abits, board): model = model.transform(absorb.AbsorbConsecutiveTransposes()) model = model.transform(GiveUniqueNodeNames()) model = model.transform(InferDataLayouts()) - model.save(get_checkpoint_name(topology, wbits, abits, "convert_to_hw_layers")) + model.save(get_checkpoint_name(board, topology, wbits, abits, "convert_to_hw_layers")) exp_layer_counts = { "tfc": [ ("Reshape", 1), @@ -606,11 +612,11 @@ def test_convert_to_hw_layers(self, topology, wbits, abits, board): def test_specialize_layers(self, topology, wbits, abits, board): build_data = get_build_env(board, target_clk_ns) - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "convert_to_hw_layers") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "convert_to_hw_layers") model = load_test_checkpoint_or_skip(prev_chkpt_name) model = model.transform(SpecializeLayers(build_data["part"])) model = model.transform(GiveUniqueNodeNames()) - model.save(get_checkpoint_name(topology, wbits, abits, "specialize_layers")) + model.save(get_checkpoint_name(board, topology, wbits, abits, "specialize_layers")) exp_layer_counts = { "tfc": [ ("Reshape", 1), @@ -648,45 +654,45 @@ def test_specialize_layers(self, topology, wbits, abits, board): assert len(model.get_nodes_by_op_type(op_type)) == exp_count def test_create_dataflow_partition(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "specialize_layers") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "specialize_layers") model = load_test_checkpoint_or_skip(prev_chkpt_name) parent_model = model.transform(CreateDataflowPartition()) - parent_model_chkpt = get_checkpoint_name(topology, wbits, abits, "dataflow_parent") + parent_model_chkpt = get_checkpoint_name(board, topology, wbits, abits, "dataflow_parent") parent_model.save(parent_model_chkpt) sdp_node = parent_model.get_nodes_by_op_type("StreamingDataflowPartition")[0] sdp_node = getCustomOp(sdp_node) dataflow_model_filename = sdp_node.get_nodeattr("model") dataflow_model = load_test_checkpoint_or_skip(dataflow_model_filename) - dataflow_model_chkpt = get_checkpoint_name(topology, wbits, abits, "dataflow_model") + dataflow_model_chkpt = get_checkpoint_name(board, topology, wbits, abits, "dataflow_model") dataflow_model.save(dataflow_model_chkpt) def test_fold(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "dataflow_model") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "dataflow_model") model = load_test_checkpoint_or_skip(prev_chkpt_name) folding_fxn = get_folding_function(topology, wbits, abits) model = folding_fxn(model) - model.save(get_checkpoint_name(topology, wbits, abits, "fold")) + model.save(get_checkpoint_name(board, topology, wbits, abits, "fold")) def test_minimize_bit_width(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "fold") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "fold") model = load_test_checkpoint_or_skip(prev_chkpt_name) model = model.transform(MinimizeAccumulatorWidth()) model = model.transform(MinimizeWeightBitWidth()) model = model.transform(RoundAndClipThresholds()) - curr_chkpt_name = get_checkpoint_name(topology, wbits, abits, "minimize_bit_width") + curr_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "minimize_bit_width") model.save(curr_chkpt_name) @pytest.mark.slow @pytest.mark.vivado def test_cppsim(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "minimize_bit_width") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "minimize_bit_width") model = load_test_checkpoint_or_skip(prev_chkpt_name) model = model.transform(PrepareCppSim()) model = model.transform(CompileCppSim()) model = model.transform(SetExecMode("cppsim")) - cppsim_chkpt = get_checkpoint_name(topology, wbits, abits, "cppsim") + cppsim_chkpt = get_checkpoint_name(board, topology, wbits, abits, "cppsim") model.save(cppsim_chkpt) - parent_chkpt = get_checkpoint_name(topology, wbits, abits, "dataflow_parent") + parent_chkpt = get_checkpoint_name(board, topology, wbits, abits, "dataflow_parent") (input_tensor_npy, output_tensor_npy) = get_golden_io_pair( topology, wbits, abits, return_topk=1 ) @@ -699,17 +705,17 @@ def test_ipgen(self, topology, wbits, abits, board): build_data = get_build_env(board, target_clk_ns) if build_data["kind"] == "alveo" and ("VITIS_PATH" not in os.environ): pytest.skip("VITIS_PATH not set") - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "minimize_bit_width") + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "minimize_bit_width") model = load_test_checkpoint_or_skip(prev_chkpt_name) model = model.transform(GiveUniqueNodeNames()) model = model.transform(PrepareIP(build_data["part"], target_clk_ns)) model = model.transform(HLSSynthIP()) - model.save(get_checkpoint_name(topology, wbits, abits, "ipgen_" + board)) + model.save(get_checkpoint_name(board, topology, wbits, abits, "ipgen")) @pytest.mark.slow @pytest.mark.vivado def test_set_fifo_depths(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "ipgen_" + board) + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "ipgen") model = load_test_checkpoint_or_skip(prev_chkpt_name) test_fpga_part = get_build_env(board, target_clk_ns)["part"] if topology == "cnv" and abits == 2 and board == "Pynq-Z1": @@ -724,12 +730,12 @@ def test_set_fifo_depths(self, topology, wbits, abits, board): fifo_layers = model.get_nodes_by_op_type("StreamingFIFO_rtl") assert len(fifo_layers) > 0 - model.save(get_checkpoint_name(topology, wbits, abits, "fifodepth_" + board)) + model.save(get_checkpoint_name(board, topology, wbits, abits, "fifodepth")) @pytest.mark.slow @pytest.mark.vivado def test_ipstitch_rtlsim(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "fifodepth_" + board) + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "fifodepth") model = load_test_checkpoint_or_skip(prev_chkpt_name) test_fpga_part = get_build_env(board, target_clk_ns)["part"] model = model.transform(InsertDWC()) @@ -750,9 +756,9 @@ def test_ipstitch_rtlsim(self, topology, wbits, abits, board): if rtlsim_trace: model.set_metadata_prop("rtlsim_trace", "%s_w%da%d.vcd" % (topology, wbits, abits)) os.environ["RTLSIM_TRACE_DEPTH"] = "3" - rtlsim_chkpt = get_checkpoint_name(topology, wbits, abits, "ipstitch_rtlsim_" + board) + rtlsim_chkpt = get_checkpoint_name(board, topology, wbits, abits, "ipstitch_rtlsim") model.save(rtlsim_chkpt) - parent_chkpt = get_checkpoint_name(topology, wbits, abits, "dataflow_parent") + parent_chkpt = get_checkpoint_name(board, topology, wbits, abits, "dataflow_parent") (input_tensor_npy, output_tensor_npy) = get_golden_io_pair( topology, wbits, abits, return_topk=1 ) @@ -762,7 +768,7 @@ def test_ipstitch_rtlsim(self, topology, wbits, abits, board): @pytest.mark.slow @pytest.mark.vivado def test_throughput_rtlsim(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "ipstitch_rtlsim_" + board) + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "ipstitch_rtlsim") model = load_test_checkpoint_or_skip(prev_chkpt_name) n_nodes = len(model.graph.node) perf_est = model.analysis(dataflow_performance) @@ -780,11 +786,11 @@ def test_throughput_rtlsim(self, topology, wbits, abits, board): def test_validate_top1(self, topology, wbits, abits, board): if "TEST_END2END_VALIDATE_TOP1" not in os.environ: pytest.skip("TEST_END2END_VALIDATE_TOP1 not set") - prepostproc_chkpt = get_checkpoint_name(topology, wbits, abits, "pre_post") - streamline_chkpt = get_checkpoint_name(topology, wbits, abits, "streamline") - parent_chkpt = get_checkpoint_name(topology, wbits, abits, "dataflow_parent") - cppsim_chkpt = get_checkpoint_name(topology, wbits, abits, "cppsim") - rtlsim_chkpt = get_checkpoint_name(topology, wbits, abits, "ipstitch_rtlsim_" + board) + prepostproc_chkpt = get_checkpoint_name(board, topology, wbits, abits, "pre_post") + streamline_chkpt = get_checkpoint_name(board, topology, wbits, abits, "streamline") + parent_chkpt = get_checkpoint_name(board, topology, wbits, abits, "dataflow_parent") + cppsim_chkpt = get_checkpoint_name(board, topology, wbits, abits, "cppsim") + rtlsim_chkpt = get_checkpoint_name(board, topology, wbits, abits, "ipstitch_rtlsim") dataset = topology2dataset(topology) assert measure_top1_accuracy(prepostproc_chkpt, dataset) > 80 assert measure_top1_accuracy(streamline_chkpt, dataset) > 80 @@ -798,11 +804,11 @@ def test_build(self, topology, wbits, abits, board): build_data = get_build_env(board, target_clk_ns) if build_data["kind"] == "alveo" and ("VITIS_PATH" not in os.environ): pytest.skip("VITIS_PATH not set") - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "fifodepth_" + board) + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "fifodepth") model = load_test_checkpoint_or_skip(prev_chkpt_name) model = model.transform(build_data["build_fxn"]) model = model.transform(AnnotateResources("synth", build_data["part"])) - model.save(get_checkpoint_name(topology, wbits, abits, "build_" + board)) + model.save(get_checkpoint_name(board, topology, wbits, abits, "build")) @pytest.mark.slow @pytest.mark.vivado @@ -811,16 +817,16 @@ def test_make_pynq_driver(self, topology, wbits, abits, board): build_data = get_build_env(board, target_clk_ns) if build_data["kind"] == "alveo" and ("VITIS_PATH" not in os.environ): pytest.skip("VITIS_PATH not set") - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "build_" + board) + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "build") model = load_test_checkpoint_or_skip(prev_chkpt_name) board_to_driver_platform = "alveo" if build_data["kind"] == "alveo" else "zynq-iodma" model = model.transform(MakePYNQDriver(board_to_driver_platform)) - model.save(get_checkpoint_name(topology, wbits, abits, "driver_" + board)) + model.save(get_checkpoint_name(board, topology, wbits, abits, "driver")) def test_deploy(self, topology, wbits, abits, board): - prev_chkpt_name = get_checkpoint_name(topology, wbits, abits, "driver_" + board) + prev_chkpt_name = get_checkpoint_name(board, topology, wbits, abits, "driver") model = load_test_checkpoint_or_skip(prev_chkpt_name) model_title = "%s_w%d_a%d_%s" % ("bnn", wbits, abits, topology) deploy_based_on_board(model, model_title, topology, wbits, abits, board) # save the model to be able to link it to the parent - model.save(get_checkpoint_name(topology, wbits, abits, "deploy_" + board)) + model.save(get_checkpoint_name(board, topology, wbits, abits, "deploy")) diff --git a/tests/end2end/test_end2end_cybsec_mlp.py b/tests/end2end/test_end2end_cybsec_mlp.py index 9ee07d57a3..61b9c38ac5 100644 --- a/tests/end2end/test_end2end_cybsec_mlp.py +++ b/tests/end2end/test_end2end_cybsec_mlp.py @@ -79,6 +79,7 @@ def forward(self, x): return out_final +@pytest.mark.xdist_group(name="end2end_cybsec") @pytest.mark.end2end def test_end2end_cybsec_mlp_export(): assets_dir = os.environ["FINN_ROOT"] + "/src/finn/qnn-data/cybsec-mlp" @@ -143,6 +144,7 @@ def test_end2end_cybsec_mlp_export(): assert model.get_tensor_datatype(first_matmul_w_name) == DataType["INT2"] +@pytest.mark.xdist_group(name="end2end_cybsec") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end diff --git a/tests/end2end/test_end2end_mobilenet_v1.py b/tests/end2end/test_end2end_mobilenet_v1.py index 1ce31b5c59..ff8fa1e9b4 100644 --- a/tests/end2end/test_end2end_mobilenet_v1.py +++ b/tests/end2end/test_end2end_mobilenet_v1.py @@ -106,6 +106,7 @@ first_layer_res_type = "dsp" +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_export(): # export preprocessing @@ -163,6 +164,7 @@ def test_end2end_mobilenet_export(): assert os.path.isfile(build_dir + "/end2end_mobilenet_preproc.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_tidy_and_merge_with_preproc(): preproc_model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_preproc.onnx") @@ -185,6 +187,7 @@ def test_end2end_mobilenet_tidy_and_merge_with_preproc(): model.save(build_dir + "/end2end_mobilenet_tidy.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_streamline(): model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_tidy.onnx") @@ -214,6 +217,7 @@ def test_end2end_mobilenet_streamline(): assert len(model.get_nodes_by_op_type("Mul")) == 0 # no Mul ops remain +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_lowering(): model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_streamlined.onnx") @@ -227,6 +231,7 @@ def test_end2end_mobilenet_lowering(): model.save(build_dir + "/end2end_mobilenet_lowered.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_convert_to_hw_layers(): model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_lowered.onnx") @@ -243,6 +248,7 @@ def test_end2end_mobilenet_convert_to_hw_layers(): model.save(build_dir + "/end2end_mobilenet_hw_layers.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_specialize_layers(): model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_hw_layers.onnx") @@ -252,6 +258,7 @@ def test_end2end_mobilenet_specialize_layers(): model.save(build_dir + "/end2end_mobilenet_specialize_layers.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_create_dataflow_partition(): model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_specialize_layers.onnx") @@ -265,6 +272,7 @@ def test_end2end_mobilenet_create_dataflow_partition(): dataflow_model.save(build_dir + "/end2end_mobilenet_dataflow_model.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_folding(): model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_dataflow_model.onnx") @@ -348,6 +356,7 @@ def test_end2end_mobilenet_folding(): model.save(build_dir + "/end2end_mobilenet_folded.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.end2end def test_end2end_mobilenet_minimize_bit_width(): model = load_test_checkpoint_or_skip(build_dir + "/end2end_mobilenet_folded.onnx") @@ -357,6 +366,7 @@ def test_end2end_mobilenet_minimize_bit_width(): model.save(build_dir + "/end2end_mobilenet_minimize_bitwidth.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end @@ -393,6 +403,7 @@ def test_end2end_mobilenet_cppsim(): # assert np.isclose(golden_prob, res_cppsim_prob[0, 0, 0, :5]).all() +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end @@ -403,6 +414,7 @@ def test_end2end_mobilenet_ipgen(): model.save(build_dir + "/end2end_mobilenet_hw_ipgen.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end @@ -439,6 +451,7 @@ def test_end2end_mobilenet_rtlsim(): # assert np.isclose(golden_prob, res_rtlsim_prob[0, 0, 0, :5]).all() +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end @@ -464,6 +477,7 @@ def test_end2end_mobilenet_set_fifo_depths(): model.save(build_dir + "/end2end_mobilenet_set_fifo_depths.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end @@ -480,6 +494,7 @@ def test_end2end_mobilenet_stitched_ip(): model.save(build_dir + "/end2end_mobilenet_stitched_ip.onnx") +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end @@ -517,6 +532,7 @@ def test_end2end_mobilenet_stitched_ip_rtlsim(): # assert np.isclose(golden_prob, res_rtlsim_ip_prob[0, 0, 0, :5]).all() +@pytest.mark.xdist_group(name="end2end_mobilenet") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end diff --git a/tests/end2end/test_ext_weights.py b/tests/end2end/test_ext_weights.py index 29d2f58e66..eeb7d95a49 100644 --- a/tests/end2end/test_ext_weights.py +++ b/tests/end2end/test_ext_weights.py @@ -66,6 +66,7 @@ def get_checkpoint_name(step): return build_dir + "/end2end_ext_weights_%s.onnx" % (step) +@pytest.mark.xdist_group(name="end2end_ext_weights") @pytest.mark.end2end def test_end2end_ext_weights_download(): if not os.path.isfile(onnx_zip_local): @@ -75,6 +76,7 @@ def test_end2end_ext_weights_download(): assert os.path.isfile(get_checkpoint_name("download")) +@pytest.mark.xdist_group(name="end2end_ext_weights") @pytest.mark.slow @pytest.mark.vivado @pytest.mark.end2end diff --git a/tests/notebooks/test_jupyter_notebooks.py b/tests/notebooks/test_jupyter_notebooks.py index e1415b9066..060bb07238 100644 --- a/tests/notebooks/test_jupyter_notebooks.py +++ b/tests/notebooks/test_jupyter_notebooks.py @@ -1,6 +1,7 @@ import pytest import nbformat +import os from nbconvert.preprocessors import ExecutePreprocessor from finn.util.basic import get_finn_root @@ -12,28 +13,67 @@ notebook_bnn_dir = get_finn_root() + "/notebooks/end2end_example/bnn-pynq/" basics_notebooks = [ - pytest.param(notebook_basic_dir + "0_how_to_work_with_onnx.ipynb"), - pytest.param(notebook_basic_dir + "1_brevitas_network_import_via_QONNX.ipynb"), + pytest.param( + notebook_basic_dir + "0_how_to_work_with_onnx.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_general"), + ), + pytest.param( + notebook_basic_dir + "1_brevitas_network_import_via_QONNX.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_general"), + ), ] advanced_notebooks = [ - pytest.param(notebook_advanced_dir + "0_custom_analysis_pass.ipynb"), - pytest.param(notebook_advanced_dir + "1_custom_transformation_pass.ipynb"), - pytest.param(notebook_advanced_dir + "2_custom_op.ipynb"), - pytest.param(notebook_advanced_dir + "3_folding.ipynb"), - pytest.param(notebook_advanced_dir + "4_advanced_builder_settings.ipynb"), + pytest.param( + notebook_advanced_dir + "0_custom_analysis_pass.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_general"), + ), + pytest.param( + notebook_advanced_dir + "1_custom_transformation_pass.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_general"), + ), + pytest.param( + notebook_advanced_dir + "2_custom_op.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_general"), + ), + pytest.param( + notebook_advanced_dir + "3_folding.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_general"), + ), + pytest.param( + notebook_advanced_dir + "4_advanced_builder_settings.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_general"), + ), ] cyber_notebooks = [ - pytest.param(notebook_cyber_dir + "1-train-mlp-with-brevitas.ipynb"), - pytest.param(notebook_cyber_dir + "2-import-into-finn-and-verify.ipynb"), - pytest.param(notebook_cyber_dir + "3-build-accelerator-with-finn.ipynb"), + pytest.param( + notebook_cyber_dir + "1-train-mlp-with-brevitas.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_cybsec"), + ), + pytest.param( + notebook_cyber_dir + "2-import-into-finn-and-verify.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_cybsec"), + ), + pytest.param( + notebook_cyber_dir + "3-build-accelerator-with-finn.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_cybsec"), + ), ] bnn_notebooks = [ - pytest.param(notebook_bnn_dir + "cnv_end2end_example.ipynb"), - pytest.param(notebook_bnn_dir + "tfc_end2end_example.ipynb"), - pytest.param(notebook_bnn_dir + "tfc_end2end_verification.ipynb"), + pytest.param( + notebook_bnn_dir + "cnv_end2end_example.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_cnv"), + ), + pytest.param( + notebook_bnn_dir + "tfc_end2end_example.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_tfc"), + ), + pytest.param( + notebook_bnn_dir + "tfc_end2end_verification.ipynb", + marks=pytest.mark.xdist_group(name="notebooks_tfc"), + ), ] @@ -41,8 +81,16 @@ @pytest.mark.parametrize( "notebook", basics_notebooks + advanced_notebooks + cyber_notebooks + bnn_notebooks ) -def test_notebook_exec(notebook): +def test_notebook_exec(notebook, request): with open(notebook) as f: + # Set different NETRON_PORT for each xdist group to avoid conflicts + xdist_groups = ["notebooks_general", "notebooks_cybsec", "notebooks_cnv", "notebooks_tfc"] + for mark in request.node.own_markers: + if mark.name == "xdist_group": + group = mark.kwargs["name"] + os.environ["NETRON_PORT"] = str(8081 + xdist_groups.index(group)) + break + nb = nbformat.read(f, as_version=4) ep = ExecutePreprocessor(timeout=notebook_timeout_seconds, kernel_name="python3") try: