From 9b1c84828b2b75c9467f1dd1f900aaf05f5574c5 Mon Sep 17 00:00:00 2001 From: auphelia <jakobapk@web.de> Date: Mon, 9 Dec 2019 10:41:58 +0000 Subject: [PATCH] [notebook - code gen] Finished code generation part of the notebook --- .../FINN-CodeGenerationAndCompilation.ipynb | 131 +++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/notebooks/FINN-CodeGenerationAndCompilation.ipynb b/notebooks/FINN-CodeGenerationAndCompilation.ipynb index d911d9dec..e3f0f03e8 100644 --- a/notebooks/FINN-CodeGenerationAndCompilation.ipynb +++ b/notebooks/FINN-CodeGenerationAndCompilation.ipynb @@ -30,7 +30,8 @@ "## Outline\n", "-------------\n", "* <font size=\"3\">Example model</font>\n", - "* <font size=\"3\">Code generation</font>" + "* <font size=\"3\">Code generation</font>\n", + "* <font size=\"3\">Compilation</font>" ] }, { @@ -329,7 +330,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -385,7 +386,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -422,6 +423,130 @@ "showSrc(StreamingFCLayer_Batch.code_generation)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font size=\"3\">Except for the function `generate_params(model)` all functions needed to fill the template correspond to the `$ $` variable names, i.e. function `defines()` returns the part of the c++ code that replaces `$DEFINES$` in the template. The individual functions are member functions of the class HLSCustomOp and are defined in each CustomOp. The code for a StreamingFCLayer_Batch node can be looked up in the [code](https://github.com/Xilinx/finn/blob/dev/src/finn/custom_op/fpgadataflow/streamingfclayer_batch.py).</font> " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font size=\"3\">A special function for code generation for the StreamingFCLayer_Batch node is the `generate_params(model)` function. Besides the normal input tensor, a fc layer has weight values as input and can get additional thresholds for activation. This function reads the values for the weights and thresholds via the `get_initializer` function of the ModelWrapper and writes them c++ conform in .h files, which are added to the includes. \n", + "\n", + "The `generate_params` function of the StreamingFCLayer_Batch is shown below.</font>" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " def generate_params(self, model):\n", + " # weights\n", + " weights = model.get_initializer(self.onnx_node.input[1])\n", + " # convert weights into hlslib-compatible format\n", + " weight_tensor = self.get_hls_compatible_weight_tensor(weights)\n", + " export_wdt = self.get_weight_datatype()\n", + " # we have converted bipolar weights to binary for export,\n", + " # so use it as such for weight generation\n", + " if self.get_weight_datatype() == DataType.BIPOLAR:\n", + " export_wdt = DataType.BINARY\n", + " weight_hls_code = numpy_to_hls_code(\n", + " weight_tensor, export_wdt, \"weights\", True, True\n", + " )\n", + " # write weights into params.h\n", + " code_gen_dir = self.get_nodeattr(\"code_gen_dir\")\n", + " f_weights = open(\"{}/params.h\".format(code_gen_dir), \"w\")\n", + "\n", + " if export_wdt.bitwidth() != 1:\n", + " f_weights.write(\n", + " \"static FixedPointWeights<{},{},{},{}> weights = \".format(\n", + " self.get_nodeattr(\"SIMD\"),\n", + " export_wdt.get_hls_datatype_str(),\n", + " self.get_nodeattr(\"PE\"),\n", + " self.calc_wmem(),\n", + " )\n", + " )\n", + " else:\n", + " f_weights.write(\n", + " \"static BinaryWeights<{},{},{}> weights = \".format(\n", + " self.get_nodeattr(\"SIMD\"), self.get_nodeattr(\"PE\"), self.calc_wmem()\n", + " )\n", + " )\n", + " f_weights.write(weight_hls_code)\n", + " f_weights.close()\n", + " # thresholds\n", + " if len(self.onnx_node.input) > 2:\n", + " thresholds = model.get_initializer(self.onnx_node.input[2])\n", + " if thresholds is not None:\n", + " threshold_tensor = self.get_hls_compatible_threshold_tensor(thresholds)\n", + " tdt = DataType.INT32\n", + " # use UINT32 threshold export for bipolar times bipolar\n", + " inp_is_bipolar = self.get_input_datatype() == DataType.BIPOLAR\n", + " wt_is_bipolar = self.get_weight_datatype() == DataType.BIPOLAR\n", + " # reinterpret inp/wt as bipolar if bin_xnor_mode is iset\n", + " inp_is_binary = self.get_input_datatype() == DataType.BINARY\n", + " wt_is_binary = self.get_weight_datatype() == DataType.BINARY\n", + " bin_xnor_mode = self.get_nodeattr(\"binaryXnorMode\") == 1\n", + " inp_is_bipolar = inp_is_bipolar or (inp_is_binary and bin_xnor_mode)\n", + " wt_is_bipolar = wt_is_bipolar or (wt_is_binary and bin_xnor_mode)\n", + " if inp_is_bipolar and wt_is_bipolar:\n", + " tdt = DataType.UINT32\n", + " thresholds_hls_code = numpy_to_hls_code(\n", + " threshold_tensor, tdt, \"thresholds\", False, True\n", + " )\n", + " # write thresholds into thresh.h\n", + " code_gen_dir = self.get_nodeattr(\"code_gen_dir\")\n", + " f_thresh = open(\"{}/thresh.h\".format(code_gen_dir), \"w\")\n", + " tdt_hls = tdt.get_hls_datatype_str()\n", + " # use binary to export bipolar activations\n", + " export_odt = self.get_output_datatype()\n", + " if self.get_output_datatype() == DataType.BIPOLAR:\n", + " export_odt = DataType.BINARY\n", + " odt_hls = export_odt.get_hls_datatype_str()\n", + " f_thresh.write(\n", + " \"static ThresholdsActivation<{},{},{},{},{},{},{}> threshs \\\n", + " = \".format(\n", + " self.calc_tmem(),\n", + " self.get_nodeattr(\"PE\"),\n", + " threshold_tensor.shape[-1],\n", + " tdt_hls,\n", + " odt_hls,\n", + " self.get_nodeattr(\"ActVal\"),\n", + " \"std::less_equal<%s>\" % tdt_hls,\n", + " )\n", + " )\n", + " f_thresh.write(thresholds_hls_code)\n", + " f_thresh.close()\n", + "\n" + ] + } + ], + "source": [ + "showSrc(StreamingFCLayer_Batch.generate_params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<font size=\"3\">The generated code is written to the previously created temporary directory and the node attribute `code_gen_dir` is set. This completes the code generation for executing a single CustomOp and the next step is compilation. </font>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compilation" + ] + }, { "cell_type": "code", "execution_count": null, -- GitLab