From 7e07eeed67e427e3252e6745565ad5ddcdc59584 Mon Sep 17 00:00:00 2001 From: Yaman Umuroglu <maltanar@gmail.com> Date: Mon, 10 Feb 2020 23:49:56 +0100 Subject: [PATCH] [Notebook] some updates to the end2end notebook --- notebooks/9-FINN-EndToEndFlow.ipynb | 270 ++++++++++++++++++++++------ 1 file changed, 211 insertions(+), 59 deletions(-) diff --git a/notebooks/9-FINN-EndToEndFlow.ipynb b/notebooks/9-FINN-EndToEndFlow.ipynb index 48a87d2bd..73049e096 100644 --- a/notebooks/9-FINN-EndToEndFlow.ipynb +++ b/notebooks/9-FINN-EndToEndFlow.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -82,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -116,7 +116,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -134,7 +134,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -262,7 +262,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -282,7 +282,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -315,7 +315,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -367,16 +367,18 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Stopping http://0.0.0.0:8081\n", - "Serving 'lfc_w1_a1.onnx' at http://0.0.0.0:8081\n" + "ename": "NameError", + "evalue": "name 'netron' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-7-9a67441e65ab>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mStreamline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msave\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"lfc_w1_a1.onnx\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mnetron\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"lfc_w1_a1.onnx\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mport\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m8081\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhost\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"0.0.0.0\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'netron' is not defined" ] } ], @@ -388,7 +390,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -421,7 +423,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -447,16 +449,18 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 9, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Stopping http://0.0.0.0:8081\n", - "Serving 'lfc_w1_a1.onnx' at http://0.0.0.0:8081\n" + "ename": "NameError", + "evalue": "name 'netron' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-9-c6c083e4118c>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mto_hls\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mInferBinaryStreamingFCLayer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msave\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"lfc_w1_a1.onnx\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mnetron\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"lfc_w1_a1.onnx\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mport\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m8081\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhost\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"0.0.0.0\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'netron' is not defined" ] } ], @@ -469,7 +473,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 31, "metadata": { "scrolled": true }, @@ -511,7 +515,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -540,37 +544,87 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now we can use the [HLSCustomOp](https://github.com/Xilinx/finn/blob/dev/src/finn/custom_op/fpgadataflow/__init__.py) class to create a [StreamingFCLayer_Batch](https://github.com/Xilinx/finn/blob/dev/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py) object for each node to set PE and SIMD. This procedure is identical for each node. For more details about custom ops, see Jupyter notebook [7-FINN-CustomOps](7-FINN-CustomOps.ipynb)." + "We can use the higher-level [HLSCustomOp](https://github.com/Xilinx/finn/blob/dev/src/finn/custom_op/fpgadataflow/__init__.py) wrappers for these nodes. These wrappers provide easy access to specific properties of these nodes, such as the folding factors (PE and SIMD). For more details about custom ops, see Jupyter notebook [7-FINN-CustomOps](7-FINN-CustomOps.ipynb). Let's have a look at which node attributes are defined by the CustomOp wrapper, and adjust the SIMD and PE attributes." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CustomOp wrapper is of class StreamingFCLayer_Batch\n" + ] + }, + { + "data": { + "text/plain": [ + "{'PE': ('i', True, 0),\n", + " 'SIMD': ('i', True, 0),\n", + " 'MW': ('i', True, 0),\n", + " 'MH': ('i', True, 0),\n", + " 'resType': ('s', True, ''),\n", + " 'ActVal': ('i', False, 0),\n", + " 'inputDataType': ('s', True, ''),\n", + " 'weightDataType': ('s', True, ''),\n", + " 'outputDataType': ('s', True, ''),\n", + " 'binaryXnorMode': ('i', False, 0),\n", + " 'noActivation': ('i', False, 0),\n", + " 'backend': ('s', True, 'fpgadataflow'),\n", + " 'code_gen_dir_npysim': ('s', False, ''),\n", + " 'code_gen_dir_ipgen': ('s', False, ''),\n", + " 'executable_path': ('s', False, ''),\n", + " 'ipgen_path': ('s', False, ''),\n", + " 'sim_mode': ('s', False, ''),\n", + " 'sim_cycles': ('i', False, 0)}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from finn.custom_op.registry import getCustomOp\n", + "\n", + "fc0w = getCustomOp(fc0)\n", + "fc1w = getCustomOp(fc1)\n", + "fc2w = getCustomOp(fc2)\n", + "fc3w = getCustomOp(fc3)\n", + "\n", + "print(\"CustomOp wrapper is of class \" + fc0w.__class__.__name__)\n", + "\n", + "fc0w.get_nodeattr_types()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, "outputs": [], "source": [ - "from finn.custom_op.fpgadataflow.streamingfclayer_batch import StreamingFCLayer_Batch\n", + "# SIMD controls the folding over the input vector\n", + "# PE controls the folding over the output vector\n", "\n", - "fc0w = StreamingFCLayer_Batch(fc0)\n", "fc0w.set_nodeattr(\"SIMD\", 16)\n", "fc0w.set_nodeattr(\"PE\", 32)\n", "\n", - "fc1w = StreamingFCLayer_Batch(fc1)\n", - "fc1w.set_nodeattr(\"SIMD\", 16)\n", + "fc1w.set_nodeattr(\"SIMD\", 32)\n", "fc1w.set_nodeattr(\"PE\", 32)\n", "\n", - "fc2w = StreamingFCLayer_Batch(fc2)\n", - "fc2w.set_nodeattr(\"SIMD\", 16)\n", + "fc2w.set_nodeattr(\"SIMD\", 32)\n", "fc2w.set_nodeattr(\"PE\", 32)\n", "\n", - "fc3w = StreamingFCLayer_Batch(fc3)\n", - "fc3w.set_nodeattr(\"SIMD\", 16)\n", - "fc3w.set_nodeattr(\"PE\", 10)\n" + "fc3w.set_nodeattr(\"SIMD\", 32)\n", + "fc3w.set_nodeattr(\"PE\", 10)" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -590,17 +644,31 @@ "metadata": {}, "source": [ "## 3. Vivado HLS and Vivado synthesis <a id='vivado'></a>\n", - "* [HLS IP per layer](#hls_per_layer)\n", + "* [Generating HLS Code](#hls_per_layer)\n", + "* [Synthesizing HLS to IP Blocks](#hls_synth)\n", "* [Creation of stitched design](#stitched_design)\n", "* [PYNQ shell project](#pynq_shell)\n", - "* [Synthesis, place and route](#synth_pl_ro)" + "* [Synthesis, place and route](#synth_pl_ro)\n", + "\n", + "As we will be performing FPGA synthesis in these tasks, we'll define two helper variables that describe the Xilinx FPGA part name and the PYNQ board name that we are targeting." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "fpga_part = \"xczu3eg-sbva484-1-e\"\n", + "pynq_board = \"Ultra96\"\n", + "target_clk_ns = 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### HLS IP per layer <a id='hls_per_layer'></a>\n", + "### Generating HLS Code <a id='hls_per_layer'></a>\n", "This section deals with the generation of an IP block from the different layers. These can then be stitched to a block design that corresponds to the complete model. The single conversion into IP blocks allows a good transparency and we can check the functionality of each IP block and compare it with the behaviour of the corresponding ONNX node. The emulation of such an IP block is performed with PyVerilator and is described in detail in section [Emulation (rtlsim) using Pyverilator](#rtlsim)." ] }, @@ -609,41 +677,125 @@ "metadata": {}, "source": [ "Two transformations are required to generate HLS IP blocks for each layer: \n", - "* `CodeGen_ipgen` which generates the C++ code for the node and a tcl-script which starts the HLS synthesis and exports the design as IP. \n", - "* `HLSSynth_IPGen` which passes the tcl-script to Vivado and thus performs the actual IP generation. \n", + "* `CodeGen_ipgen` which generates the HLS C++ code for the node and a tcl-script which starts the HLS synthesis and exports the design as IP. \n", + "* `HLSSynth_IPGen` which passes the tcl-script to Vivado HLS and thus performs the actual IP generation. \n", "\n", - "First the basic transformation `GiveUniqueNodeNames` is applied and then the two transformations necessary for the IP block creation are performed. This will take some time as Vivado is called and for each StreamingFCLayer_Batch node in the design HLS synthesis is performed and the node is exported as IP block. `CodeGen_ipgen` gets as arguments an FPGA part as string and the clock in ns as integer." + "We start off by giving unique node names using the basic transformation `GiveUniqueNodeNames`, and then proceed with the HLS C++ code generation with `CodeGen_ipgen`." ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "model = model.transform(GiveUniqueNodeNames())\n", + "\n", + "from finn.transformation.fpgadataflow.codegen_ipgen import CodeGen_ipgen\n", + "model = model.transform(CodeGen_ipgen(fpga_part, target_clk_ns))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each `fpgadataflow` node will have its own code generation directory, which we can examine as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, "metadata": {}, "outputs": [ { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m<ipython-input-17-8f990c5295da>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mCodeGen_ipgen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"xc7z020clg400-1\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mHLSSynth_IPGen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/workspace/finn/src/finn/core/modelwrapper.py\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, transformation, make_deepcopy)\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0mmodel_was_changed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 67\u001b[0m (transformed_model, model_was_changed) = transformation.apply(\n\u001b[0;32m---> 68\u001b[0;31m \u001b[0mtransformed_model\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 69\u001b[0m )\n\u001b[1;32m 70\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtransformed_model\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/workspace/finn/src/finn/transformation/fpgadataflow/hlssynth_ipgen.py\u001b[0m in \u001b[0;36mapply\u001b[0;34m(self, model)\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_nodeattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"code_gen_dir_ipgen\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[0;31m# call the compilation function for this node\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 27\u001b[0;31m \u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mipgen_singlenode_code\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 28\u001b[0m \u001b[0;31m# ensure that executable path is now set\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0minst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_nodeattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ipgen_path\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;34m\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/workspace/finn/src/finn/custom_op/fpgadataflow/__init__.py\u001b[0m in \u001b[0;36mipgen_singlenode_code\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0mbuilder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend_tcl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcode_gen_dir\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"/hls_syn_{}.tcl\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[0mbuilder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_ipgen_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcode_gen_dir\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"/project_{}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 102\u001b[0;31m \u001b[0mbuilder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuild\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcode_gen_dir\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 103\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_nodeattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ipgen_path\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbuilder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mipgen_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 104\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/workspace/finn/src/finn/core/utils.py\u001b[0m in \u001b[0;36mbuild\u001b[0;34m(self, code_gen_dir)\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0mbash_command\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m\"bash\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mipgen_script\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0mprocess_compile\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbash_command\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstdout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPIPE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 322\u001b[0;31m \u001b[0mprocess_compile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcommunicate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m/opt/conda/lib/python3.6/subprocess.py\u001b[0m in \u001b[0;36mcommunicate\u001b[0;34m(self, input, timeout)\u001b[0m\n\u001b[1;32m 848\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_stdin_write\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 849\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstdout\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 850\u001b[0;31m \u001b[0mstdout\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstdout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 851\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstdout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 852\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstderr\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + "name": "stdout", + "output_type": "stream", + "text": [ + "hls_syn_StreamingFCLayer_Batch_0.tcl thresh.h\r\n", + "params.h\t\t\t top_StreamingFCLayer_Batch_0.cpp\r\n" ] } ], "source": [ - "model = model.transform(GiveUniqueNodeNames())\n", + "fc0w = getCustomOp(model.graph.node[2])\n", + "codegen_dir = fc0w.get_nodeattr(\"code_gen_dir_ipgen\")\n", + "! ls {codegen_dir}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see the various generated files. In particular, the `top*.cpp` will contain the Vivado HLS function call that instantiates the correct `finn-hlslib` library component for this node:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r\n", + "#include \"bnn-library.h\"\r\n", + "// includes for network parameters\r\n", + "#include \"weights.hpp\"\r\n", + "#include \"activations.hpp\"\r\n", + "#include \"params.h\"\r\n", + "#include \"thresh.h\"\r\n", + "\r\n", + "// defines for network parameters\r\n", + "#define MW1 784\r\n", + " #define MH1 1024\r\n", + " #define SIMD1 16\r\n", + "\r\n", + " #define PE1 32\r\n", + " #define WMEM1 1568\r\n", + " #define TMEM1 32\r\n", + "\r\n", + " #define numReps 1\r\n", + "#define PRAGMA_SUB(x) _Pragma (#x)\r\n", + "#define DO_PRAGMA(x) PRAGMA_SUB(x)\r\n", + "\r\n", + "void StreamingFCLayer_Batch_0(hls::stream<ap_uint<16>> &in0,\r\n", + " hls::stream<ap_uint<32>> &out\r\n", + " )\r\n", + "{\r\n", + "#pragma HLS INTERFACE axis port=in0\r\n", + "#pragma HLS INTERFACE axis port=out\r\n", + "#pragma HLS INTERFACE ap_ctrl_none port=return\r\n", + "DO_PRAGMA(HLS ARRAY_PARTITION variable=weights.m_weights complete dim=1)\r\n", + "DO_PRAGMA(HLS ARRAY_PARTITION variable=threshs.m_thresholds complete dim=1)\r\n", + "DO_PRAGMA(HLS ARRAY_PARTITION variable=threshs.m_thresholds complete dim=3)\r\n", + "StreamingFCLayer_Batch<MW1, MH1, SIMD1, PE1, Recast<XnorMul>, Identity, Identity>\r\n", + " (in0, out, weights, threshs, numReps, ap_resource_lut());\r\n", + "}\r\n" + ] + } + ], + "source": [ + "! cat {codegen_dir}/top_StreamingFCLayer_Batch_0.cpp" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Synthesizing HLS to IP Blocks <a id='hls_synth'></a>\n", "\n", - "from finn.transformation.fpgadataflow.codegen_ipgen import CodeGen_ipgen\n", + "Now that we have generated the HLS code for each layer, we can call the `HLSSynth_IPGen` transformation to convert the generated HLS into Vivado IP blocks. As this involves calling HLS synthesis, this transformation will run for some time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "from finn.transformation.fpgadataflow.hlssynth_ipgen import HLSSynth_IPGen\n", "\n", - "model = model.transform(CodeGen_ipgen(\"xc7z020clg400-1\", 5))\n", "model = model.transform(HLSSynth_IPGen())" ] }, -- GitLab