diff --git a/5_accelerator_backends/vitis_unified/5_vu_a_predict.ipynb b/5_accelerator_backends/vitis_unified/5_vu_a_predict.ipynb new file mode 100644 index 00000000..5439202a --- /dev/null +++ b/5_accelerator_backends/vitis_unified/5_vu_a_predict.ipynb @@ -0,0 +1,224 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cell-0", + "metadata": {}, + "source": [ + "# Part 5a: The Vitis Unified backend\n", + "\n", + "The previous parts of this tutorial stop at the HLS IP: a piece of RTL that still has to be integrated into a full system by hand. The **VitisUnified** backend of hls4ml goes further. It wraps the hls4ml kernel with an AXI interface, links it into a SoC platform (processing system, interconnect, interrupt controller, DMA) with `v++`, and runs place and route, so that the output is a bitstream plus a PYNQ driver ready to run on a board.\n", + "\n", + "Part 5 walks through the full flow:\n", + "\n", + "| Notebook | What it does |\n", + "|----------|--------------|\n", + "| **5a** (this one) | Converts a small U-Net with both the `VitisUnified` and the `Vitis` backends and checks that the software predictions are identical |\n", + "| 5b | Runs **C simulation** (`csim`) and compares it with the software prediction. If this passes, the generated C++ is correct |\n", + "| 5c | Runs **RTL co-simulation** (`cosim`): the C++ is synthesized to RTL and simulated |\n", + "| 5d | Enables the **FIFO depth optimization** flow, which profiles the FIFOs during co-simulation and writes `fifo_depths.json` |\n", + "| 5e | Generates the **bitfile**: synthesis, system link, place and route. Writes `system.bit`, `system.hwh` and the PYNQ driver |\n", + "| 5f | A guide to building your own SoC platform in Vivado and adding a new board to the backend |\n", + "\n", + "## Prerequisites\n", + "\n", + "- Vitis and Vivado **2023.2** (tested). The `kv260` platform also supports 2025.2.\n", + "- A supported board: **zcu102** or **kv260**. The backend ships a platform for both, so you do not need to build one yourself.\n", + "- hls4ml with the Vitis Unified backend. The backend is not merged into the main branch of hls4ml yet; install it from pull request [#1376](https://github.com/fastmachinelearning/hls4ml/pull/1376):\n", + "\n", + "```bash\n", + "pip install git+https://github.com/fastmachinelearning/hls4ml.git@refs/pull/1376/head\n", + "```\n", + "\n", + "In this notebook we only use the C++ bridge (`hls_model.predict`), so no Vitis run is needed yet, but the backend still needs `XILINX_VITIS` to locate the platform." + ] + }, + { + "cell_type": "markdown", + "id": "cell-1", + "metadata": {}, + "source": [ + "## Settings\n", + "\n", + "Set the target board here. The backend picks the platform (`.xpfm` or `.xsa`) from its `supported_boards.json` using the board and the AXI mode, so nothing else needs to be configured. To add another board, see `5_vu_f_platform_setup.md`.\n", + "\n", + "| Variable | Meaning |\n", + "|----------|---------|\n", + "| `BOARD` | `'zcu102'` or `'kv260'` |\n", + "| `AXI_MODE` | `'axi_master'` (default) or `'axi_stream'`; see the *AXI interface modes* page of the hls4ml docs |\n", + "\n", + "The Vitis tools are located through the `XILINX_VITIS` environment variable, as in the other parts of this tutorial." + ] + }, + { + "cell_type": "code", + "id": "cell-2", + "metadata": {}, + "source": [ + "import os\n", + "\n", + "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", + "\n", + "import numpy as np\n", + "import sys\n", + "\n", + "sys.path.append('../..')\n", + "from models import create_simple_unet\n", + "import hls4ml\n", + "\n", + "os.environ['PATH'] = os.environ['XILINX_VITIS'] + '/bin:' + os.environ['PATH']\n", + "\n", + "BOARD = 'zcu102' # or 'kv260'\n", + "AXI_MODE = 'axi_master' # or 'axi_stream'\n", + "\n", + "DATA_DIR = '../../data/vitis_unified'\n", + "os.makedirs(DATA_DIR, exist_ok=True)\n", + "np.random.seed(0)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-3", + "metadata": {}, + "source": [ + "## Create the model and some test data\n", + "\n", + "We use a tiny U-Net with a single skip connection, defined in `models.py`. The weights are left untrained on purpose: the goal of this part is to check that every stage of the accelerator flow reproduces the same numbers, not to reach a good accuracy. A `Concatenate` skip connection is a good stress test for the streaming interface, because it forces two branches to be merged again." + ] + }, + { + "cell_type": "code", + "id": "cell-4", + "metadata": {}, + "source": [ + "X = np.random.rand(10, 4, 4, 1).astype(np.float32)\n", + "np.save(f'{DATA_DIR}/X_part5a.npy', X)\n", + "print(f'Input shape: {X.shape}')\n", + "\n", + "model = create_simple_unet()\n", + "model.summary()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-5", + "metadata": {}, + "source": [ + "## Convert with both backends\n", + "\n", + "We convert the same model twice: once with the `VitisUnified` backend and once with the plain `Vitis` backend as a reference. Both use `io_stream`, which is required for the CNN layers.\n", + "\n", + "The `VitisUnified` backend takes the same arguments as the `Vitis` backend, plus:\n", + "\n", + "| Argument | Meaning |\n", + "|----------|---------|\n", + "| `board` | the target board; the FPGA part and the platform are looked up from it |\n", + "| `axi_mode` | `'axi_master'` or `'axi_stream'`, the interface between the PS and the kernel |\n", + "| `input_type`, `output_type` | the data type on the AXI interface (`'float'` here, so the driver can send `float32` NumPy arrays directly) |" + ] + }, + { + "cell_type": "code", + "id": "cell-6", + "metadata": {}, + "source": [ + "config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Vitis')\n", + "\n", + "hls_model_unified = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5a_unified_{AXI_MODE}'),\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " clock_period=10,\n", + ")\n", + "hls_model_unified.compile()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "id": "cell-7", + "metadata": {}, + "source": [ + "hls_model_vitis = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='Vitis',\n", + " io_type='io_stream',\n", + " output_dir='../../hls4ml_prjs/hls4ml_prj_part5a_vitis',\n", + " part='xczu9eg-ffvb1156-2-e',\n", + " clock_period=10,\n", + ")\n", + "hls_model_vitis.compile()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-8", + "metadata": {}, + "source": [ + "## Predict and compare\n", + "\n", + "The AXI wrapper added by the `VitisUnified` backend must not change the arithmetic: the fixed-point results of the two backends should be bit-for-bit identical." + ] + }, + { + "cell_type": "code", + "id": "cell-9", + "metadata": {}, + "source": [ + "y_unified = hls_model_unified.predict(X)\n", + "y_vitis = hls_model_vitis.predict(X)\n", + "\n", + "print(f'VitisUnified output shape: {np.array(y_unified).shape}')\n", + "print(f'Vitis output shape: {np.array(y_vitis).shape}')\n", + "\n", + "assert np.array_equal(y_unified, y_vitis), 'The results from VitisUnified and Vitis are NOT equal!'\n", + "print('Backend prediction comparison passed: both are equal')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-10", + "metadata": {}, + "source": [ + "Continue with `5_vu_b_csim.ipynb` to check that the generated C++ behaves the same when compiled by the Vitis HLS compiler." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hls4ml-tutorial", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/5_accelerator_backends/vitis_unified/5_vu_b_csim.ipynb b/5_accelerator_backends/vitis_unified/5_vu_b_csim.ipynb new file mode 100644 index 00000000..adbd285d --- /dev/null +++ b/5_accelerator_backends/vitis_unified/5_vu_b_csim.ipynb @@ -0,0 +1,218 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cell-0", + "metadata": {}, + "source": [ + "# Part 5b: C simulation\n", + "\n", + "In Part 5a we checked that the `VitisUnified` backend produces the same predictions as the `Vitis` backend through the C++ bridge. The bridge compiles the generated code with `g++`, though. In this notebook we run **C simulation** (`csim`) with the Vitis HLS compiler instead, using the testbench generated by hls4ml, and compare its result with the bridge prediction. If this passes, the generated C++ is correct as seen by the HLS tools.\n", + "\n", + "Both `axi_master` and `axi_stream` modes are supported." + ] + }, + { + "cell_type": "markdown", + "id": "cell-1", + "metadata": {}, + "source": [ + "## Settings\n", + "\n", + "Set the target board here. The backend picks the platform (`.xpfm` or `.xsa`) from its `supported_boards.json` using the board and the AXI mode, so nothing else needs to be configured. To add another board, see `5_vu_f_platform_setup.md`.\n", + "\n", + "| Variable | Meaning |\n", + "|----------|---------|\n", + "| `BOARD` | `'zcu102'` or `'kv260'` |\n", + "| `AXI_MODE` | `'axi_master'` (default) or `'axi_stream'`; see the *AXI interface modes* page of the hls4ml docs |\n", + "\n", + "The Vitis tools are located through the `XILINX_VITIS` environment variable, as in the other parts of this tutorial." + ] + }, + { + "cell_type": "code", + "id": "cell-2", + "metadata": {}, + "source": [ + "import os\n", + "\n", + "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", + "\n", + "import numpy as np\n", + "import sys\n", + "\n", + "sys.path.append('../..')\n", + "from models import create_simple_unet\n", + "import hls4ml\n", + "\n", + "os.environ['PATH'] = os.environ['XILINX_VITIS'] + '/bin:' + os.environ['PATH']\n", + "\n", + "BOARD = 'zcu102' # or 'kv260'\n", + "AXI_MODE = 'axi_master' # or 'axi_stream'\n", + "\n", + "DATA_DIR = '../../data/vitis_unified'\n", + "os.makedirs(DATA_DIR, exist_ok=True)\n", + "np.random.seed(0)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-3", + "metadata": {}, + "source": [ + "## Create the model and some test data\n", + "\n", + "We use a tiny U-Net with a single skip connection, defined in `models.py`. The weights are left untrained on purpose: the goal of this part is to check that every stage of the accelerator flow reproduces the same numbers, not to reach a good accuracy. A `Concatenate` skip connection is a good stress test for the streaming interface, because it forces two branches to be merged again." + ] + }, + { + "cell_type": "code", + "id": "cell-4", + "metadata": {}, + "source": [ + "X = np.random.rand(10, 4, 4, 1).astype(np.float32)\n", + "np.save(f'{DATA_DIR}/X_part5b.npy', X)\n", + "print(f'Input shape: {X.shape}')\n", + "\n", + "model = create_simple_unet()\n", + "model.summary()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-5", + "metadata": {}, + "source": [ + "## Reference predictions for the testbench\n", + "\n", + "The testbench generated by hls4ml reads its inputs and expected outputs from files. We pass them with `input_data_tb` and `output_data_tb`. The expected outputs are produced with the C++ bridge, exactly as in Part 5a, so the comparison checks the tool flow rather than the model." + ] + }, + { + "cell_type": "code", + "id": "cell-6", + "metadata": {}, + "source": [ + "config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Vitis')\n", + "\n", + "# Reference predictions from the C++ bridge\n", + "hls_model_ref = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5b_ref_{AXI_MODE}'),\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " clock_period=10,\n", + ")\n", + "hls_model_ref.compile()\n", + "y_ref = hls_model_ref.predict(X)\n", + "np.save(f'{DATA_DIR}/y_part5b.npy', y_ref)\n", + "print(f'Reference predictions saved: shape {np.array(y_ref).shape}')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-7", + "metadata": {}, + "source": [ + "## Build and run the C simulation\n", + "\n", + "`build(csim=True)` runs the testbench under the Vitis HLS C simulator. `synth=True` is also passed: the Vitis Unified flow runs the simulation from the same project as the synthesis. **This can take several minutes.**" + ] + }, + { + "cell_type": "code", + "id": "cell-8", + "metadata": {}, + "source": [ + "output_dir = os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5b_{AXI_MODE}')\n", + "\n", + "hls_model = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=output_dir,\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " input_data_tb=f'{DATA_DIR}/X_part5b.npy',\n", + " output_data_tb=f'{DATA_DIR}/y_part5b.npy',\n", + " clock_period=10,\n", + ")\n", + "hls_model.compile()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "id": "cell-9", + "metadata": {}, + "source": [ + "hls_model.build(synth=True, csim=True, log_to_stdout=True)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-10", + "metadata": {}, + "source": [ + "## Compare results\n", + "\n", + "The testbench writes the expected outputs (from the bridge) and the C simulation outputs to `tb_data/` in the project directory." + ] + }, + { + "cell_type": "code", + "id": "cell-11", + "metadata": {}, + "source": [ + "y_bridge = np.loadtxt(f'{output_dir}/tb_data/tb_output_predictions.dat')\n", + "y_csim = np.loadtxt(f'{output_dir}/tb_data/csim_results.log')\n", + "\n", + "print(f'Bridge shape: {y_bridge.shape}')\n", + "print(f'CSim shape: {y_csim.shape}')\n", + "\n", + "assert np.allclose(y_bridge, y_csim, rtol=0.0, atol=1e-4), 'The results from bridge and csim are NOT equal!'\n", + "print('C simulation comparison passed (atol=1e-4)')" + ], + "execution_count": null, + "outputs": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hls4ml-tutorial", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/5_accelerator_backends/vitis_unified/5_vu_c_cosim.ipynb b/5_accelerator_backends/vitis_unified/5_vu_c_cosim.ipynb new file mode 100644 index 00000000..b5da0807 --- /dev/null +++ b/5_accelerator_backends/vitis_unified/5_vu_c_cosim.ipynb @@ -0,0 +1,218 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cell-0", + "metadata": {}, + "source": [ + "# Part 5c: RTL co-simulation\n", + "\n", + "C simulation (Part 5b) only checks the C++. **RTL co-simulation** (`cosim`) goes one step further: Vitis HLS synthesizes the C++ to RTL and then simulates the RTL with the same testbench, comparing the results against the C++ model. This is the last check before spending hours on a bitstream, and it is the stage where interface problems (handshakes, stream widths, FIFO depths) show up.\n", + "\n", + "Both `axi_master` and `axi_stream` modes are supported." + ] + }, + { + "cell_type": "markdown", + "id": "cell-1", + "metadata": {}, + "source": [ + "## Settings\n", + "\n", + "Set the target board here. The backend picks the platform (`.xpfm` or `.xsa`) from its `supported_boards.json` using the board and the AXI mode, so nothing else needs to be configured. To add another board, see `5_vu_f_platform_setup.md`.\n", + "\n", + "| Variable | Meaning |\n", + "|----------|---------|\n", + "| `BOARD` | `'zcu102'` or `'kv260'` |\n", + "| `AXI_MODE` | `'axi_master'` (default) or `'axi_stream'`; see the *AXI interface modes* page of the hls4ml docs |\n", + "\n", + "The Vitis tools are located through the `XILINX_VITIS` environment variable, as in the other parts of this tutorial." + ] + }, + { + "cell_type": "code", + "id": "cell-2", + "metadata": {}, + "source": [ + "import os\n", + "\n", + "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", + "\n", + "import numpy as np\n", + "import sys\n", + "\n", + "sys.path.append('../..')\n", + "from models import create_simple_unet\n", + "import hls4ml\n", + "\n", + "os.environ['PATH'] = os.environ['XILINX_VITIS'] + '/bin:' + os.environ['PATH']\n", + "\n", + "BOARD = 'zcu102' # or 'kv260'\n", + "AXI_MODE = 'axi_master' # or 'axi_stream'\n", + "\n", + "DATA_DIR = '../../data/vitis_unified'\n", + "os.makedirs(DATA_DIR, exist_ok=True)\n", + "np.random.seed(0)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-3", + "metadata": {}, + "source": [ + "## Create the model and some test data\n", + "\n", + "We use a tiny U-Net with a single skip connection, defined in `models.py`. The weights are left untrained on purpose: the goal of this part is to check that every stage of the accelerator flow reproduces the same numbers, not to reach a good accuracy. A `Concatenate` skip connection is a good stress test for the streaming interface, because it forces two branches to be merged again." + ] + }, + { + "cell_type": "code", + "id": "cell-4", + "metadata": {}, + "source": [ + "X = np.random.rand(10, 4, 4, 1).astype(np.float32)\n", + "np.save(f'{DATA_DIR}/X_part5c.npy', X)\n", + "print(f'Input shape: {X.shape}')\n", + "\n", + "model = create_simple_unet()\n", + "model.summary()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-5", + "metadata": {}, + "source": [ + "## Reference predictions for the testbench\n", + "\n", + "The testbench generated by hls4ml reads its inputs and expected outputs from files. We pass them with `input_data_tb` and `output_data_tb`. The expected outputs are produced with the C++ bridge, exactly as in Part 5a, so the comparison checks the tool flow rather than the model." + ] + }, + { + "cell_type": "code", + "id": "cell-6", + "metadata": {}, + "source": [ + "config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Vitis')\n", + "\n", + "# Reference predictions from the C++ bridge\n", + "hls_model_ref = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5c_ref_{AXI_MODE}'),\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " clock_period=10,\n", + ")\n", + "hls_model_ref.compile()\n", + "y_ref = hls_model_ref.predict(X)\n", + "np.save(f'{DATA_DIR}/y_part5c.npy', y_ref)\n", + "print(f'Reference predictions saved: shape {np.array(y_ref).shape}')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-7", + "metadata": {}, + "source": [ + "## Build and run the RTL co-simulation\n", + "\n", + "`build(cosim=True)` synthesizes the design and then simulates the generated RTL. **This can take 10-20 minutes** even for this tiny model, since an RTL simulation runs cycle by cycle." + ] + }, + { + "cell_type": "code", + "id": "cell-8", + "metadata": {}, + "source": [ + "output_dir = os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5c_{AXI_MODE}')\n", + "\n", + "hls_model = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=output_dir,\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " input_data_tb=f'{DATA_DIR}/X_part5c.npy',\n", + " output_data_tb=f'{DATA_DIR}/y_part5c.npy',\n", + " clock_period=10,\n", + ")\n", + "hls_model.compile()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "id": "cell-9", + "metadata": {}, + "source": [ + "hls_model.build(synth=True, cosim=True, log_to_stdout=True)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-10", + "metadata": {}, + "source": [ + "## Compare results\n", + "\n", + "The RTL result must match the bridge prediction within `1e-4`." + ] + }, + { + "cell_type": "code", + "id": "cell-11", + "metadata": {}, + "source": [ + "y_bridge = np.loadtxt(f'{output_dir}/tb_data/tb_output_predictions.dat')\n", + "y_cosim = np.loadtxt(f'{output_dir}/tb_data/rtl_cosim_results.log')\n", + "\n", + "print(f'Bridge shape: {y_bridge.shape}')\n", + "print(f'CoSim shape: {y_cosim.shape}')\n", + "\n", + "assert np.allclose(y_bridge, y_cosim, rtol=0.0, atol=1e-4), 'The results from bridge and cosim are NOT equal!'\n", + "print('RTL co-simulation comparison passed (atol=1e-4)')" + ], + "execution_count": null, + "outputs": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hls4ml-tutorial", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/5_accelerator_backends/vitis_unified/5_vu_d_fifo_depth.ipynb b/5_accelerator_backends/vitis_unified/5_vu_d_fifo_depth.ipynb new file mode 100644 index 00000000..b173a542 --- /dev/null +++ b/5_accelerator_backends/vitis_unified/5_vu_d_fifo_depth.ipynb @@ -0,0 +1,218 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cell-0", + "metadata": {}, + "source": [ + "# Part 5d: FIFO depth optimization\n", + "\n", + "With `io_stream`, every connection between two layers is a FIFO. hls4ml sizes these FIFOs conservatively, which wastes BRAM. The `vitisunified:fifo_depth_optimization` flow runs an RTL co-simulation with FIFO profiling enabled, records the maximum occupancy of every FIFO, and writes the optimized depths to `fifo_depths.json` in the project directory. These depths are then applied to the design.\n", + "\n", + "This notebook enables the flow and checks that the file is produced. Because the flow includes a co-simulation, it takes about as long as Part 5c." + ] + }, + { + "cell_type": "markdown", + "id": "cell-1", + "metadata": {}, + "source": [ + "## Settings\n", + "\n", + "Set the target board here. The backend picks the platform (`.xpfm` or `.xsa`) from its `supported_boards.json` using the board and the AXI mode, so nothing else needs to be configured. To add another board, see `5_vu_f_platform_setup.md`.\n", + "\n", + "| Variable | Meaning |\n", + "|----------|---------|\n", + "| `BOARD` | `'zcu102'` or `'kv260'` |\n", + "| `AXI_MODE` | `'axi_master'` (default) or `'axi_stream'`; see the *AXI interface modes* page of the hls4ml docs |\n", + "\n", + "The Vitis tools are located through the `XILINX_VITIS` environment variable, as in the other parts of this tutorial." + ] + }, + { + "cell_type": "code", + "id": "cell-2", + "metadata": {}, + "source": [ + "import os\n", + "\n", + "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", + "\n", + "import numpy as np\n", + "import sys\n", + "import json\n", + "\n", + "sys.path.append('../..')\n", + "from models import create_simple_unet\n", + "import hls4ml\n", + "\n", + "os.environ['PATH'] = os.environ['XILINX_VITIS'] + '/bin:' + os.environ['PATH']\n", + "\n", + "BOARD = 'zcu102' # or 'kv260'\n", + "AXI_MODE = 'axi_master' # or 'axi_stream'\n", + "\n", + "DATA_DIR = '../../data/vitis_unified'\n", + "os.makedirs(DATA_DIR, exist_ok=True)\n", + "np.random.seed(0)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-3", + "metadata": {}, + "source": [ + "## Create the model and some test data\n", + "\n", + "We use a tiny U-Net with a single skip connection, defined in `models.py`. The weights are left untrained on purpose: the goal of this part is to check that every stage of the accelerator flow reproduces the same numbers, not to reach a good accuracy. A `Concatenate` skip connection is a good stress test for the streaming interface, because it forces two branches to be merged again." + ] + }, + { + "cell_type": "code", + "id": "cell-4", + "metadata": {}, + "source": [ + "X = np.random.rand(10, 4, 4, 1).astype(np.float32)\n", + "np.save(f'{DATA_DIR}/X_part5d.npy', X)\n", + "print(f'Input shape: {X.shape}')\n", + "\n", + "model = create_simple_unet()\n", + "model.summary()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-5", + "metadata": {}, + "source": [ + "## Reference predictions for the testbench\n", + "\n", + "The testbench generated by hls4ml reads its inputs and expected outputs from files. We pass them with `input_data_tb` and `output_data_tb`. The expected outputs are produced with the C++ bridge, exactly as in Part 5a, so the comparison checks the tool flow rather than the model." + ] + }, + { + "cell_type": "code", + "id": "cell-6", + "metadata": {}, + "source": [ + "config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Vitis')\n", + "\n", + "# Reference predictions from the C++ bridge\n", + "hls_model_ref = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5d_ref_{AXI_MODE}'),\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " clock_period=10,\n", + ")\n", + "hls_model_ref.compile()\n", + "y_ref = hls_model_ref.predict(X)\n", + "np.save(f'{DATA_DIR}/y_part5d.npy', y_ref)\n", + "print(f'Reference predictions saved: shape {np.array(y_ref).shape}')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-7", + "metadata": {}, + "source": [ + "## Enable the FIFO depth optimization flow\n", + "\n", + "The optimization is an hls4ml *flow*, enabled through the `Flows` key of the configuration. It runs when the model is compiled, so the co-simulation happens inside `compile()` here. **This can take 10-20 minutes.**" + ] + }, + { + "cell_type": "code", + "id": "cell-8", + "metadata": {}, + "source": [ + "config['Flows'] = ['vitisunified:fifo_depth_optimization']" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "id": "cell-9", + "metadata": {}, + "source": [ + "output_dir = os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5d_{AXI_MODE}')\n", + "\n", + "hls_model = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=output_dir,\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " input_data_tb=f'{DATA_DIR}/X_part5d.npy',\n", + " output_data_tb=f'{DATA_DIR}/y_part5d.npy',\n", + " clock_period=10,\n", + ")\n", + "hls_model.compile()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-10", + "metadata": {}, + "source": [ + "## Check the FIFO depths file" + ] + }, + { + "cell_type": "code", + "id": "cell-11", + "metadata": {}, + "source": [ + "fifo_depths_path = f'{output_dir}/fifo_depths.json'\n", + "assert os.path.exists(fifo_depths_path), f'fifo_depths.json not found at {fifo_depths_path}'\n", + "\n", + "with open(fifo_depths_path) as f:\n", + " fifo_depths = json.load(f)\n", + "\n", + "print('FIFO depths:')\n", + "for name, depth in fifo_depths.items():\n", + " print(f' {name}: {depth}')" + ], + "execution_count": null, + "outputs": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hls4ml-tutorial", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/5_accelerator_backends/vitis_unified/5_vu_e_bitfile.ipynb b/5_accelerator_backends/vitis_unified/5_vu_e_bitfile.ipynb new file mode 100644 index 00000000..df77bfa9 --- /dev/null +++ b/5_accelerator_backends/vitis_unified/5_vu_e_bitfile.ipynb @@ -0,0 +1,199 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cell-0", + "metadata": {}, + "source": [ + "# Part 5e: Generate the bitfile\n", + "\n", + "This notebook runs the full flow of the `VitisUnified` backend: HLS synthesis, linking the kernel into the SoC platform with `v++`, place and route, and bitstream generation. The result is written to `export/` in the project directory:\n", + "\n", + "| File | Purpose |\n", + "|------|---------|\n", + "| `system.bit` | the bitstream |\n", + "| `system.hwh` | the hardware handoff file that PYNQ uses to discover the IPs and their addresses |\n", + "| `*_driver.py` | the PYNQ driver for the selected `axi_mode` |\n", + "\n", + "Copy these three files to the board and load them with the driver from a PYNQ notebook.\n", + "\n", + "> **This step takes a long time** (typically well over an hour).\n", + ">\n", + "> With `BOARD = 'zcu102'` and `AXI_MODE = 'axi_stream'`, or with `BOARD = 'kv260'`, the backend first builds the platform from a Tcl script, so **Vivado must be on `PATH` as well**. This notebook adds `$XILINX_VIVADO/bin` for that reason." + ] + }, + { + "cell_type": "markdown", + "id": "cell-1", + "metadata": {}, + "source": [ + "## Settings\n", + "\n", + "Set the target board here. The backend picks the platform (`.xpfm` or `.xsa`) from its `supported_boards.json` using the board and the AXI mode, so nothing else needs to be configured. To add another board, see `5_vu_f_platform_setup.md`.\n", + "\n", + "| Variable | Meaning |\n", + "|----------|---------|\n", + "| `BOARD` | `'zcu102'` or `'kv260'` |\n", + "| `AXI_MODE` | `'axi_master'` (default) or `'axi_stream'`; see the *AXI interface modes* page of the hls4ml docs |\n", + "\n", + "The Vitis tools are located through the `XILINX_VITIS` environment variable, as in the other parts of this tutorial." + ] + }, + { + "cell_type": "code", + "id": "cell-2", + "metadata": {}, + "source": [ + "import os\n", + "\n", + "os.environ['KERAS_BACKEND'] = 'tensorflow'\n", + "\n", + "import numpy as np\n", + "import sys\n", + "\n", + "sys.path.append('../..')\n", + "from models import create_simple_unet\n", + "import hls4ml\n", + "\n", + "os.environ['PATH'] = os.environ['XILINX_VITIS'] + '/bin:' + os.environ['PATH']\n", + "os.environ['PATH'] = os.environ['XILINX_VIVADO'] + '/bin:' + os.environ['PATH']\n", + "\n", + "BOARD = 'zcu102' # or 'kv260'\n", + "AXI_MODE = 'axi_master' # or 'axi_stream'\n", + "\n", + "DATA_DIR = '../../data/vitis_unified'\n", + "os.makedirs(DATA_DIR, exist_ok=True)\n", + "np.random.seed(0)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-3", + "metadata": {}, + "source": [ + "## Create the model and some test data\n", + "\n", + "We use a tiny U-Net with a single skip connection, defined in `models.py`. The weights are left untrained on purpose: the goal of this part is to check that every stage of the accelerator flow reproduces the same numbers, not to reach a good accuracy. A `Concatenate` skip connection is a good stress test for the streaming interface, because it forces two branches to be merged again." + ] + }, + { + "cell_type": "code", + "id": "cell-4", + "metadata": {}, + "source": [ + "X = np.random.rand(10000, 4, 4, 1).astype(np.float32)\n", + "np.save(f'{DATA_DIR}/X_part5e.npy', X)\n", + "print(f'Input shape: {X.shape}')\n", + "\n", + "model = create_simple_unet()\n", + "model.summary()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-5", + "metadata": {}, + "source": [ + "## Convert and predict\n", + "\n", + "We save the bridge predictions for 10000 inputs alongside the inputs, so that they can be copied to the board together with the bitfile and used to validate the hardware.\n", + "\n", + "The `VitisUnified` backend takes the same arguments as the `Vitis` backend, plus:\n", + "\n", + "| Argument | Meaning |\n", + "|----------|---------|\n", + "| `board` | the target board; the FPGA part and the platform are looked up from it |\n", + "| `axi_mode` | `'axi_master'` or `'axi_stream'`, the interface between the PS and the kernel |\n", + "| `input_type`, `output_type` | the data type on the AXI interface (`'float'` here, so the driver can send `float32` NumPy arrays directly) |" + ] + }, + { + "cell_type": "code", + "id": "cell-6", + "metadata": {}, + "source": [ + "config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Vitis')\n", + "\n", + "output_dir = os.path.abspath(f'../../hls4ml_prjs/hls4ml_prj_part5e_{AXI_MODE}')\n", + "\n", + "hls_model = hls4ml.converters.convert_from_keras_model(\n", + " model,\n", + " hls_config=config,\n", + " backend='VitisUnified',\n", + " io_type='io_stream',\n", + " output_dir=output_dir,\n", + " board=BOARD,\n", + " axi_mode=AXI_MODE,\n", + " input_type='float',\n", + " output_type='float',\n", + " clock_period=10,\n", + ")\n", + "hls_model.compile()\n", + "\n", + "y_ref = hls_model.predict(X)\n", + "np.save(f'{DATA_DIR}/y_part5e.npy', y_ref)\n", + "print(f'Predictions saved: shape {np.array(y_ref).shape}')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "id": "cell-7", + "metadata": {}, + "source": [ + "## Synthesize and generate the bitfile\n", + "\n", + "`bitfile=True` runs everything: HLS synthesis, platform generation (if needed), `v++ --link`, and Vivado implementation. **This cell takes a long time.**" + ] + }, + { + "cell_type": "code", + "id": "cell-8", + "metadata": {}, + "source": [ + "hls_model.build(synth=True, bitfile=True, log_to_stdout=True)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "id": "cell-9", + "metadata": {}, + "source": [ + "export_dir = f'{output_dir}/export'\n", + "print('Files to copy to the board:')\n", + "for f in sorted(os.listdir(export_dir)):\n", + " print(f' {export_dir}/{f}')" + ], + "execution_count": null, + "outputs": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hls4ml-tutorial", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/5_accelerator_backends/vitis_unified/5_vu_f_platform_setup.md b/5_accelerator_backends/vitis_unified/5_vu_f_platform_setup.md new file mode 100644 index 00000000..eef07d76 --- /dev/null +++ b/5_accelerator_backends/vitis_unified/5_vu_f_platform_setup.md @@ -0,0 +1,194 @@ +# Part 5f: Building a SoC platform for the Vitis Unified backend + +This guide shows how to create a platform (XSA file) in Vivado and how to add a new board to the hls4ml `VitisUnified` backend. + +The backend already ships platforms for the **zcu102** and **kv260**, so Parts 5a-5e work out of the box on those boards. You need this guide when: + +- you use another board, +- you use another Vivado version, or +- you want to change the system design for your own workload. + +## What the platform must contain + +The platform is the hardware skeleton of the system: the processing system (PS), the AXI interconnect, the interrupt controller, and the DMA. The backend links the hls4ml kernel into your platform with `v++`, and the PYNQ driver expects a few fixed names. Your block design must have: + +| Item | Requirement | +|------|-------------| +| Project | **Project is extensible to Vitis platform** must be enabled | +| Block design name | **`vitis_design`** (the backend copies `vitis_design.hwh` after linking) | +| Processing system | Zynq / Zynq UltraScale+ PS with DDR enabled | +| AXI master port from the PS | for the kernel control registers (AXI-Lite), exposed in Platform Setup | +| AXI slave port to the PS memory | for the kernel data (`axi_master` mode) or the DMA (`axi_stream` mode), exposed in Platform Setup | +| Clock | one clock exposed in Platform Setup and marked as default | +| Interrupt | an AXI interrupt controller (`axi_intc`) connected to the PS, exposed in Platform Setup | +| AXI DMA (`axi_stream` mode only) | one `axi_dma` block named **`axi_dma_0`**, its `s2mm_introut` connected to the interrupt controller, and its stream ports exposed in Platform Setup | + +Reference designs are in `hls4ml/templates/vitis_unified//tcl_scripts/` in the hls4ml repository. + +## 1. Create a Vivado project + +Create a normal Vivado project for your board, but you MUST tick **Project is extensible to Vitis platform**. + +![Extensible project](../../images/part5f_extensible.png) + +## 2. Create the block design + +The block design name MUST be `vitis_design`. + +![Create block design](../../images/part5f_createBlock.png) + +## 3. Build the block design + +The block design should look like this picture. + +![Block design connections](../../images/part5f_connections.png) + +## 4. Platform setup + +Open the **Platform Setup** tab and enable the ports that `v++` may use. + +- AXI ports + +![Platform AXI ports](../../images/part5f_platform_axi.png) +![Platform AXI ports 2](../../images/part5f_platform_axi2.png) + +- AXI-Stream ports (`axi_stream` mode only) + +![Platform AXI-Stream ports](../../images/part5f_platform_axis.png) + +- Clock + +![Platform clock](../../images/part5f_clock.png) + +- Interrupt + +![Platform interrupt](../../images/part5f_interrupt.png) + +## 5. Create the HDL wrapper and generate output products + +Right-click the block design, choose **Create HDL Wrapper**, then **Generate Output Products**. + +![Create HDL wrapper](../../images/part5f_createHDLWrapper.png) + +## 6. Generate the bitstream + +Run **Generate Bitstream** once, so that Vivado checks the design. + +## 7. Export the platform + +### 7.1 Check for a DCP file + +If a DCP file exists under `utils_1`, you MUST delete it before the next step. + +![Delete DCP](../../images/part5f_dcpDelete.png) + +### 7.2 Export + +Choose **File > Export > Export Platform** and follow the wizard. It writes the XSA file to the folder you choose. +The XSA file can be used in place of an XPFM file. + +![Export platform](../../images/part5f_export_platform.png) + +## 8. Add the board to the hls4ml backend + +The backend finds the platform only through `hls4ml/backends/vitis_unified/supported_boards.json`. +There is no option to pass a platform path from Python, so you must add a board entry. +Choose one of the two ways below. In both cases `` is the name you will pass as `board=''`. + +### 8.1 Create the board folder + +Copy the driver templates from an existing board: + +```bash +cd hls4ml/templates/vitis_unified +mkdir -p /python_drivers +cp kv260/python_drivers/*.hls4ml /python_drivers/ +``` + +The drivers work without changes as long as your platform follows the table in "What the platform must contain". + +### 8.2 Way A: use the exported XSA file directly + +Add this entry to `supported_boards.json`. Use an **absolute path** to your XSA (a relative path is resolved from `$XILINX_VITIS`). +Do not add `platform_generator_tcl`; if it exists, the backend uses it instead of `platform_file`. + +```json +"": { + "part": "", + "axi_master": { + "platform_file": "/absolute/path/to/_platform.xsa", + "python_driver": "axi_master_driver.py", + "c_drivers": "" + }, + "axi_stream": { + "platform_file": "/absolute/path/to/_platform.xsa", + "python_driver": "axi_stream_driver.py", + "c_drivers": "" + } +} +``` + +An XPFM file from AMD works the same way; see the `zcu102` `axi_master` entry. + +### 8.3 Way B: let the backend build the XSA from a Tcl script + +This is how the shipped boards work. The backend copies `/tcl_scripts/` into the project and runs Vivado in batch mode +when the XSA is missing or older than the script. + +1. In Vivado, export your block design: **File > Export > Export Block Design** (or `write_bd_tcl`). + Save it as `hls4ml/templates/vitis_unified//tcl_scripts/_platform_.tcl`. +2. Copy `kv260/tcl_scripts/create_xsa.tcl` to `/tcl_scripts/create_xsa.tcl` and edit the file name pattern, + the supported Vivado versions, and the output XSA name at the top of the script. +3. Add this entry to `supported_boards.json`: + +```json +"": { + "part": "", + "axi_master": { + "platform_generator_tcl": "/tcl_scripts/create_xsa.tcl", + "platform_output": "/tcl_scripts/output/_platform.xsa", + "python_driver": "axi_master_driver.py", + "c_drivers": "" + }, + "axi_stream": { + "platform_generator_tcl": "/tcl_scripts/create_xsa.tcl", + "platform_output": "/tcl_scripts/output/_platform.xsa", + "python_driver": "axi_stream_driver.py", + "c_drivers": "" + } +} +``` + +Both paths are relative to `vitis_workspace/` inside the generated project. + +### 8.4 Install and use + +If you installed hls4ml with `pip install .`, run it again so the new board folder and `supported_boards.json` are copied. +An editable install (`pip install -e .`) picks up the change directly. + +```python +hls_model = hls4ml.converters.convert_from_keras_model( + model, + hls_config=config, + output_dir='hls4ml_prj', + backend='VitisUnified', + board='', + axi_mode='axi_master', +) +hls_model.compile() +hls_model.build(synth=True, bitfile=True) +``` + +The bitstream, the hardware handoff, and the Python driver are written to `hls4ml_prj/export/`. + +## Quick fix without changing hls4ml + +If you only want to try your XSA once, build the project for a shipped board (for example `kv260`) and, after `compile()`, +copy your XSA over the generated one with the same name: + +```bash +cp my_platform.xsa hls4ml_prj/vitis_workspace/kv260/tcl_scripts/output/kv260_axi_all_platform.xsa +``` + +`link_system.sh` rebuilds the XSA only when it is missing or older than `create_xsa.tcl`, so your file is used as it is. +Use plain `cp` (not `cp -p`) so the file gets a new timestamp. diff --git a/README.md b/README.md index 45fd2bff..b024537f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,18 @@ conda activate hls4ml-tutorial source /path/to/your/installtion/Xilinx/Vitis_HLS/202X.X/settings64.(c)sh ``` +## Accelerator backends (Part 5) +Part 5 uses the **VitisUnified** backend of hls4ml, which produces a bitstream and a PYNQ driver for a SoC board (zcu102 or kv260). +It requires Vitis and Vivado 2023.2 (Vivado for the platform build and place-and-route): +```bash +source /path/to/your/installtion/Xilinx/Vitis/2023.2/settings64.(c)sh +``` +The backend is not merged into the main branch of hls4ml yet. Install it from pull request +[#1376](https://github.com/fastmachinelearning/hls4ml/pull/1376) on top of the environment above: +```bash +pip install git+https://github.com/fastmachinelearning/hls4ml.git@refs/pull/1376/head +``` + ## Companion material We have prepared a set of slides with some introduction and more details on each of the exercises. Please find them [here](https://docs.google.com/presentation/d/1c4LvEc6yMByx2HJs8zUP5oxLtY6ACSizQdKvw5cg5Ck/edit?usp=sharing). diff --git a/_toc.yml b/_toc.yml index e13b3a7d..d80c2796 100644 --- a/_toc.yml +++ b/_toc.yml @@ -10,5 +10,11 @@ chapters: - file: 3_advanced_config/3b_profiling.ipynb - file: 4_advanced_models/4a_qkeras_cnn_svhn.ipynb - file: 4_advanced_models/4b_snn.ipynb + - file: 5_accelerator_backends/vitis_unified/5_vu_a_predict.ipynb + - file: 5_accelerator_backends/vitis_unified/5_vu_b_csim.ipynb + - file: 5_accelerator_backends/vitis_unified/5_vu_c_cosim.ipynb + - file: 5_accelerator_backends/vitis_unified/5_vu_d_fifo_depth.ipynb + - file: 5_accelerator_backends/vitis_unified/5_vu_e_bitfile.ipynb + - file: 5_accelerator_backends/vitis_unified/5_vu_f_platform_setup.md - file: 6_more_models/6a_bdt.ipynb - file: 6_more_models/6b_symbolic_regression.ipynb diff --git a/images/part5f_clock.png b/images/part5f_clock.png new file mode 100644 index 00000000..14c34322 Binary files /dev/null and b/images/part5f_clock.png differ diff --git a/images/part5f_connections.png b/images/part5f_connections.png new file mode 100644 index 00000000..911a4ede Binary files /dev/null and b/images/part5f_connections.png differ diff --git a/images/part5f_createBlock.png b/images/part5f_createBlock.png new file mode 100644 index 00000000..cbf682f9 Binary files /dev/null and b/images/part5f_createBlock.png differ diff --git a/images/part5f_createHDLWrapper.png b/images/part5f_createHDLWrapper.png new file mode 100644 index 00000000..31ac0ef6 Binary files /dev/null and b/images/part5f_createHDLWrapper.png differ diff --git a/images/part5f_dcpDelete.png b/images/part5f_dcpDelete.png new file mode 100644 index 00000000..c0f3049e Binary files /dev/null and b/images/part5f_dcpDelete.png differ diff --git a/images/part5f_export_platform.png b/images/part5f_export_platform.png new file mode 100644 index 00000000..38b31c7d Binary files /dev/null and b/images/part5f_export_platform.png differ diff --git a/images/part5f_extensible.png b/images/part5f_extensible.png new file mode 100644 index 00000000..36f7e571 Binary files /dev/null and b/images/part5f_extensible.png differ diff --git a/images/part5f_interrupt.png b/images/part5f_interrupt.png new file mode 100644 index 00000000..91897272 Binary files /dev/null and b/images/part5f_interrupt.png differ diff --git a/images/part5f_platform_axi.png b/images/part5f_platform_axi.png new file mode 100644 index 00000000..fcf0cf3d Binary files /dev/null and b/images/part5f_platform_axi.png differ diff --git a/images/part5f_platform_axi2.png b/images/part5f_platform_axi2.png new file mode 100644 index 00000000..d0af20fd Binary files /dev/null and b/images/part5f_platform_axi2.png differ diff --git a/images/part5f_platform_axis.png b/images/part5f_platform_axis.png new file mode 100644 index 00000000..c03f2d7f Binary files /dev/null and b/images/part5f_platform_axis.png differ diff --git a/models.py b/models.py index 4f1008f0..05c6de01 100644 --- a/models.py +++ b/models.py @@ -1,4 +1,4 @@ -"""Shared PyTorch model definitions used across the Part 1, 2, 3 and 6 notebooks. +"""Shared model definitions used across the Part 1, 2, 3, 5 and 6 notebooks. Keeping these in one place means the architecture only needs to be changed here, rather than in every notebook that loads a model trained in `1b_train_pytorch.ipynb`. @@ -51,3 +51,29 @@ def logits(self, x): def forward(self, x): return torch.softmax(self.logits(x), dim=1) + + +def create_simple_unet(input_shape=(4, 4, 1)): + """Tiny Keras U-Net with one skip connection, used by the Part 5 (Vitis Unified) notebooks. + + The model is intentionally small so that C simulation, RTL co-simulation and bitfile generation + finish in a reasonable time. The weights are random: these notebooks check the *flow*, not the accuracy. + """ + import keras + from keras.layers import Concatenate, Conv2D, Input, MaxPooling2D, UpSampling2D + + inputs = Input(input_shape) + # Encoder + c1 = Conv2D(2, (3, 3), activation='relu', padding='same')(inputs) + p1 = MaxPooling2D((2, 2))(c1) + # Bottleneck + bn = Conv2D(4, (3, 3), activation='relu', padding='same')(p1) + # Decoder with skip connection + u1 = UpSampling2D((2, 2))(bn) + concat1 = Concatenate()([u1, c1]) + c2 = Conv2D(2, (3, 3), activation='relu', padding='same')(concat1) + # Output layer (1 channel) + outputs = Conv2D(1, (1, 1), activation='sigmoid')(c2) + model = keras.Model(inputs, outputs) + model.compile(optimizer='adam', loss='binary_crossentropy') + return model